The console announces five tools and reaches everything by address (hq ADR 0195)
mesh_overview, mesh_machine, mesh_search, mesh_describe and mesh_call walk the mesh's structure; every tool has one address per layer: <seat>.<verb>, <node>/<seat>.<verb>, <node>/<module>.<tool>, and <module>.<tool> for a module the mesh issued a plain subject. A stateful module called without its machine, a node seat without one, a mesh seat with one, or a module on the wrong machine is refused naming what would work. Answers come from the mesh when asked, kept five seconds, so a tool that arrives mid-session is found. The flat catalogue stays behind MESH_CONSOLE_FLAT=1 and old <module>.<tool> names still answer.
This commit is contained in:
@@ -5,6 +5,7 @@ package console
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -47,10 +48,17 @@ type Surface struct {
|
||||
mu sync.Mutex
|
||||
known *Listing
|
||||
at time.Time
|
||||
idx *index
|
||||
idxAt time.Time
|
||||
// Flat announces the whole catalogue, as the console did before ADR 0195: for a person reading
|
||||
// it or a client that wants it. Off by default; MESH_CONSOLE_FLAT=1 turns it on.
|
||||
Flat bool
|
||||
}
|
||||
|
||||
// NewSurface is the surface over a connection, as `who`.
|
||||
func NewSurface(conn *bus.Conn, who string) *Surface { return &Surface{conn: conn, who: who} }
|
||||
func NewSurface(conn *bus.Conn, who string) *Surface {
|
||||
return &Surface{conn: conn, who: who, Flat: os.Getenv("MESH_CONSOLE_FLAT") == "1"}
|
||||
}
|
||||
|
||||
func (s *Surface) listing() (*Listing, error) {
|
||||
s.mu.Lock()
|
||||
@@ -99,12 +107,7 @@ func (s *Surface) Handle(r Request) *Reply {
|
||||
"protocolVersion": Protocol,
|
||||
"capabilities": map[string]any{"tools": map[string]any{}},
|
||||
"serverInfo": map[string]any{"name": "mesh", "version": "1"},
|
||||
"instructions": "These are the tools of a Novox mesh, reached as " + s.who + ". Every call goes to the module " +
|
||||
"that serves it; what may be called was fixed when this account was issued, so a " +
|
||||
"refusal means the account, not the tool. The list is what the running modules " +
|
||||
"answered, plus every role's tools from the mesh's records — the mesh's own verbs " +
|
||||
"(mesh-controller.status, .push, .assign …) among them; a module that did not answer " +
|
||||
"is named in the list's _meta and can still be called by <module>.<tool>.",
|
||||
"instructions": s.instructions(),
|
||||
})
|
||||
case "notifications/initialized":
|
||||
return nil
|
||||
@@ -114,6 +117,9 @@ func (s *Surface) Handle(r Request) *Reply {
|
||||
}
|
||||
return answer(r.ID, map[string]any{})
|
||||
case "tools/list":
|
||||
if !s.Flat {
|
||||
return answer(r.ID, map[string]any{"tools": discovery()})
|
||||
}
|
||||
l, err := s.listing()
|
||||
if err != nil {
|
||||
return refuse(r.ID, -32603, whyItFailed(catalogueModules, err))
|
||||
@@ -142,6 +148,12 @@ func (s *Surface) Handle(r Request) *Reply {
|
||||
Arguments map[string]any `json:"arguments"`
|
||||
}
|
||||
_ = json.Unmarshal(r.Params, &p)
|
||||
if isDiscovery(p.Name) {
|
||||
if p.Arguments == nil {
|
||||
p.Arguments = map[string]any{}
|
||||
}
|
||||
return answer(r.ID, s.discover(p.Name, p.Arguments))
|
||||
}
|
||||
args := map[string]any{}
|
||||
for k, v := range p.Arguments {
|
||||
args[k] = v
|
||||
@@ -266,3 +278,20 @@ func withNode(schema map[string]any, description string, required bool) map[stri
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// instructions is what an agent host is told about this surface when it connects.
|
||||
func (s *Surface) instructions() string {
|
||||
if s.Flat {
|
||||
return "These are the tools of a Novox mesh, reached as " + s.who + ". Every call goes to the module " +
|
||||
"that serves it; what may be called was fixed when this account was issued, so a " +
|
||||
"refusal means the account, not the tool. The list is what the running modules " +
|
||||
"answered, plus every role's tools from the mesh's records — the mesh's own verbs " +
|
||||
"(mesh-controller.status, .push, .assign …) among them; a module that did not answer " +
|
||||
"is named in the list's _meta and can still be called by <module>.<tool>."
|
||||
}
|
||||
return "The tools of a Novox mesh, reached as " + s.who + ", found by address rather than listed " +
|
||||
"whole (novox/hq ADR 0195). mesh_overview shows the mesh's seats and machines; mesh_machine one " +
|
||||
"machine's seats and modules; mesh_search finds a tool by words; mesh_describe gives one tool's " +
|
||||
"arguments; mesh_call calls it. " + grammar + " What may be called was fixed when this account " +
|
||||
"was issued, so a refusal means the account, not the tool."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user