Files
Maven/internal/ipc/wire.go
T
2026-07-03 00:32:48 +02:00

130 lines
4.5 KiB
Go

package ipc
import (
"encoding/json"
"errors"
"fmt"
)
// Method — one RPC verb. The set is intentionally small: it mirrors exactly
// what a module legitimately needs from core state, and nothing more. Adding
// a method is a core-authority change (every method is a new thing a module
// can ask for); do it deliberately.
type Method string
const (
MethodWriteFact Method = "write_fact"
MethodLatestFact Method = "latest_fact"
MethodLatestFactBySource Method = "latest_fact_by_source"
MethodSince Method = "since"
MethodPresence Method = "presence"
MethodCreateReminder Method = "create_reminder"
MethodMarkReminder Method = "mark_reminder"
MethodRecordNudge Method = "record_nudge"
MethodResolveNudge Method = "resolve_nudge"
MethodRecentOutcomes Method = "recent_outcomes"
MethodRecentFacts Method = "recent_facts"
MethodRecentNudges Method = "recent_nudges"
MethodWriteNote Method = "write_note"
MethodQueryNotes Method = "query_notes"
MethodRecentNotes Method = "recent_notes"
MethodProposeTool Method = "propose_tool"
MethodEnableTool Method = "enable_tool"
MethodLookupTool Method = "lookup_tool"
MethodListTools Method = "list_tools"
)
// Request — one frame from module to core. Params is the JSON-encoded argument
// struct for Method (see api.go for the per-method shapes). The server
// unmarshals Params based on Method; an unknown Method ⇒ ErrUnknownMethod.
type Request struct {
Method Method `json:"m"`
Params json.RawMessage `json:"p,omitempty"`
}
// Response — one frame from core back to module. Exactly one of Result/Error
// is set. Result is the JSON-encoded return value of the method (might be a
// scalar, a struct, or null for void methods).
type Response struct {
Result json.RawMessage `json:"r,omitempty"`
Error *RpcError `json:"e,omitempty"`
}
// RpcError — a typed wire error. Code is one of the sentinel codes below;
// the client rehydrates it into the matching package sentinel so callers can
// use errors.Is like they would in-process (core's contract is the same on
// both sides of the wire — the boundary shouldn't change error semantics).
type RpcError struct {
Code string `json:"c"`
Message string `json:"m,omitempty"`
}
func (e *RpcError) Error() string {
if e.Message != "" {
return fmt.Sprintf("ipc: %s: %s", e.Code, e.Message)
}
return fmt.Sprintf("ipc: %s", e.Code)
}
// Sentinel codes. Stable over the wire — do not rename. Mirror the package
// sentinels in api.go 1:1. The string is the contract.
const (
codeNoFact = "no_fact"
codeConfidence = "confidence"
codeVoidsMissing = "voids_missing"
codeNudgeNotFound = "nudge_not_found"
codeNudgeOutcome = "nudge_outcome"
codeReminderMissing = "reminder_not_found"
codeReminderState = "reminder_state"
codeToolNotFound = "tool_not_found"
codeUnknownMethod = "unknown_method"
codeBadParams = "bad_params"
codeForbidden = "forbidden"
codeInternal = "internal"
)
// codeOf maps a server-side sentinel to its wire code. Anything not matched
// is codeInternal — we never leak internal Go error text to a module; it
// gets a generic "internal" and the daemon logs the real error server-side.
func codeOf(err error) string {
switch {
case err == nil:
return ""
case errors.Is(err, ErrNoFact):
return codeNoFact
case errors.Is(err, ErrConfidence):
return codeConfidence
case errors.Is(err, ErrVoidsMissing):
return codeVoidsMissing
case errors.Is(err, ErrNudgeNotFound):
return codeNudgeNotFound
case errors.Is(err, ErrNudgeOutcome):
return codeNudgeOutcome
case errors.Is(err, ErrReminderNotFound):
return codeReminderMissing
case errors.Is(err, ErrReminderState):
return codeReminderState
case errors.Is(err, ErrToolNotFound):
return codeToolNotFound
case errors.Is(err, ErrUnknownMethod):
return codeUnknownMethod
case errors.Is(err, ErrBadParams):
return codeBadParams
case errors.Is(err, ErrForbidden):
return codeForbidden
default:
return codeInternal
}
}
// rpcErr builds the wire error for a server-side error. message is omitted
// for sentinel codes (the Code carries the meaning; no need to echo text the
// caller can re-derive from errors.Is) and included for internal/bad-params
// where the text is the actual diagnostic.
func rpcErr(err error) *RpcError {
c := codeOf(err)
if c == codeInternal || c == codeBadParams {
return &RpcError{Code: c, Message: err.Error()}
}
return &RpcError{Code: c}
}