dc28b63ad8
executus CI / test (push) Successful in 36s
The tool registry core (registry, permission model, Invocation, gated-tool wrapper, ssrf guard, hmac, encryption, argcoerce, helpers, rootrun, session_tools, webhook_rate_limit) had zero mort coupling — it imports only majordomo/llm + x/crypto/hkdf — so it moves verbatim with a package rename (skilltools -> tool). All same-package tests came along and pass; the SSRF, gated-wrapper, encryption and output-pattern invariants are re-anchored here. majordomo re-enters the module graph (now pinned to the latest, incl. the front-loaded-output fix). model/ + llmmeta + structured follow next. Docs: CLAUDE.md now requires README/examples to stay in sync with changes in the same commit; CI skips docs/example-only pushes via paths-ignore. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
45 lines
1.4 KiB
Go
45 lines
1.4 KiB
Go
package tool
|
|
|
|
import "fmt"
|
|
|
|
// CheckGate returns an error if the invocation context's SkillName does
|
|
// not match the tool's gate. Tools should call this at the top of their
|
|
// handler when their Permission has a non-empty SkillNameGate.
|
|
//
|
|
// Why: the gate is enforced per-call (not per-build) because the same
|
|
// Tool may be referenced by multiple skills, only one of which is
|
|
// gate-eligible. Build cannot know in advance which skill will call it
|
|
// — that's per-Invocation.
|
|
//
|
|
// What: returns nil if no gate, or the names match. Returns an error
|
|
// suitable for surfacing to the LLM as the tool's failure result.
|
|
func CheckGate(inv Invocation) error {
|
|
if inv.gate == "" {
|
|
return nil
|
|
}
|
|
if inv.currentSkill == inv.gate {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("tool %q is restricted to the %q skill", inv.toolName, inv.gate)
|
|
}
|
|
|
|
// EmitAudit forwards a tool's call+result to the audit hook, if one is
|
|
// wired. Tools should call this once per Execute, after the underlying
|
|
// work has completed (regardless of error). Pass the original args
|
|
// JSON, the result string, and any error.
|
|
//
|
|
// Why: keeping the audit emission inside the tool ensures the captured
|
|
// args are exactly what the tool ran with (after coercion / defaults),
|
|
// not the raw LLM JSON which can drift.
|
|
func EmitAudit(inv Invocation, args, result string, err error) {
|
|
if inv.audit == nil {
|
|
return
|
|
}
|
|
inv.audit(AuditCall{
|
|
Tool: inv.toolName,
|
|
Args: args,
|
|
Result: result,
|
|
Err: err,
|
|
})
|
|
}
|