package approval import ( "encoding/json" "fmt" "sort" "strings" "time" ) // The mesh MCP server's mesh_approvals reads and steers what the gate asked on this machine (§18). // Listed is one ask of the last day as mesh_approvals lists it: its state and its act's fingerprint. type Listed struct { Ask string `json:"ask"` Address string `json:"address"` Module string `json:"module"` Asked time.Time `json:"asked"` State string `json:"state"` Fingerprint string `json:"fingerprint"` } // Whole is one ask read whole: its arguments and the act's digest. type Whole struct { Listed Digest string `json:"digest"` Act map[string]string `json:"act"` Arguments json.RawMessage `json:"arguments"` Recorded bool `json:"recorded"` } // stateOf is a kept ask's state as the runner knows it. func stateOf(acted, recorded bool, ended string) string { switch { case acted && recorded: return "run, recorded" case acted: return "run, not yet recorded" case ended != "": return ended } return "asked" } // List is the calls asked on this machine in the last day, newest first (§18). func (g *Gate) List() ([]Listed, error) { if g.d.State == nil { return nil, fmt.Errorf("the tool runner's membership binds no state %s", StateName) } keys, err := g.d.State.Keys() if err != nil { return nil, err } prefix := g.d.Node + ".ask." out := []Listed{} for _, key := range keys { if !strings.HasPrefix(key, prefix) { continue } w, err := g.Read(strings.TrimPrefix(key, prefix)) if err != nil || g.d.Now().Sub(w.Asked) > 24*time.Hour { continue } out = append(out, w.Listed) } sort.Slice(out, func(i, j int) bool { return out[i].Asked.After(out[j].Asked) }) return out, nil } // Read is one ask whole: its arguments and the act's digest (§18). func (g *Gate) Read(id string) (Whole, error) { if g.d.State == nil { return Whole{}, fmt.Errorf("the tool runner's membership binds no state %s", StateName) } k, _, err := g.kept(id) if err != nil { return Whole{}, err } if k == nil { return Whole{}, fmt.Errorf("no ask %s is kept on %s", id, g.d.Node) } module := k.Act["module"] if module == "" { module = moduleOf(k) } var ask struct { About string `json:"about"` } _ = json.Unmarshal(k.Ask, &ask) return Whole{Listed: Listed{Ask: id, Address: k.Act["address"], Module: module, Asked: k.Asked, State: stateOf(k.Acted, k.Recorded, k.Ended), Fingerprint: Fingerprint(ask.About)}, Digest: ask.About, Act: k.Act, Arguments: k.Arguments, Recorded: k.Recorded}, nil } // Applied are the marks the gate applies on this machine, each module's marked tools and verbs with their source and the // catalog revision read (§18). func (g *Gate) Applied() []Marks { g.mu.Lock() modules := make([]string, 0, len(g.served)) for m := range g.served { modules = append(modules, m) } g.mu.Unlock() sort.Strings(modules) var out []Marks if g.d.Catalog == nil { return out } for _, m := range modules { if marks := Derive(g.d.Catalog, m, g.servedFingerprint(m)); marks.Any() { out = append(out, marks) } } return out } func (g *Gate) servedFingerprint(module string) string { g.mu.Lock() defer g.mu.Unlock() return g.served[module] } // Describe says whether a tool or a seat verb of a module is marked, and its source, for mesh_describe (§18): the served // build's marks where this machine serves the module, else the newest entry's and its seat's. func (g *Gate) Describe(module, seat, name string) (bool, string) { if g.d.Catalog == nil || g.d.Catalog.Ready() != nil { return false, "" } if seat != "" && module == "" { for s, d := range g.d.Catalog.Definitions() { if s != seat { continue } for _, v := range d.Verbs { if v.Name == name && v.NeedsApproval { return true, "seat " + seat } } } return false, "" } return Derive(g.d.Catalog, module, g.servedFingerprint(module)).Marked(seat, name) } // Again asks `warranted` again for one acted, unrecorded ask, by hand, in the kept-call form of §16 (§18); the caller // records the hand act first. func (g *Gate) Again(id string) error { w, err := g.Read(id) if err != nil { return err } if !strings.HasPrefix(w.State, "run, not yet") { return fmt.Errorf("the ask %s is %s: only a run not yet recorded is asked again", id, w.State) } g.recordRun(w.Module, id) if again, _ := g.Read(id); !again.Recorded { return fmt.Errorf("mesh-hand-acts.warranted has not answered for ask %s yet", id) } return nil } // Cancel takes an open ask back through operator-channel's client (§18); the caller records the hand act first. func (g *Gate) Cancel(id string) error { w, err := g.Read(id) if err != nil { return err } if w.State != "asked" { return fmt.Errorf("the ask %s is %s, not open", id, w.State) } return g.d.Channel.Cancel(w.Module, id) }