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>
100 lines
4.4 KiB
Go
100 lines
4.4 KiB
Go
package tool
|
|
|
|
import (
|
|
"context"
|
|
|
|
llm "gitea.stevedudenhoeffer.com/steve/majordomo/llm"
|
|
)
|
|
|
|
// AgentSession is the live-run handle a SessionToolFactory receives.
|
|
// It is implemented by the executors (agentexec / skillexec / scaddy's
|
|
// adapter) on top of majordomo's agent loop and exposes the one mid-run
|
|
// mutation session tools need: feeding content back into the running
|
|
// conversation.
|
|
//
|
|
// Why an interface (vs the concrete agent type): legacy agentkit handed the
|
|
// factory a *agentkit.Agent so tools could call agent.AttachImages.
|
|
// majordomo's *agent.Agent is deliberately immutable mid-run — message
|
|
// injection happens through the run-scoped steer mailbox
|
|
// (agent.WithSteer). A narrow interface lets each executor implement
|
|
// AttachImages over its own steer queue without skilltools importing
|
|
// the agent package, and keeps session tools testable with a two-line
|
|
// fake.
|
|
type AgentSession interface {
|
|
// AttachImages queues a user-role message (text plus image parts)
|
|
// for injection into the conversation before the agent's next
|
|
// step. Used by tools that produce visual feedback the model must
|
|
// see on its following turn (e.g. scaddy's rendered OpenSCAD
|
|
// previews). Safe to call from inside a tool handler; the message
|
|
// lands after the current step's tool results.
|
|
AttachImages(text string, images ...llm.ImagePart)
|
|
}
|
|
|
|
// SessionToolFactory builds per-session tools that close over the live
|
|
// agent session. Called by the executor after the agent is constructed
|
|
// but before it runs. See Invocation.SessionToolFactory for the
|
|
// rationale (static ExtraTools cannot reach the running agent).
|
|
type SessionToolFactory func(session AgentSession) SessionTools
|
|
|
|
// SessionTools carries per-session tools plus optional post-run and
|
|
// teardown hooks. It replaces legacy agentkit's SessionTools with the same
|
|
// three-field shape, re-based on majordomo types.
|
|
type SessionTools struct {
|
|
// Tools to add to the agent's toolbox for this run only.
|
|
Tools []llm.Tool
|
|
|
|
// PostRun, if set, is called after the agent run completes
|
|
// (successfully or not). It receives the full run transcript (the
|
|
// agent Result's Messages — also populated on partial results from
|
|
// agent.ErrMaxSteps / agent.ErrToolLoop), the agent's text output,
|
|
// and the run error, so the hook can decide whether to attempt
|
|
// artifact production on partial success (e.g. scaddy ships the
|
|
// latest SCAD even when the step budget ran out). The returned
|
|
// PostRunResult is attached to the executor's run result. Errors
|
|
// inside PostRun must be handled by the hook itself — the executor
|
|
// logs a nil return but never fails the run over it; the agent's
|
|
// output is the source of truth.
|
|
//
|
|
// Why a transcript slice (vs the live agent): the consumers only
|
|
// ever read the message history (thought-chain transcripts); the
|
|
// majordomo agent exposes that on Result, not on the Agent.
|
|
PostRun func(ctx context.Context, transcript []llm.Message, output string, runErr error) *PostRunResult
|
|
|
|
// Cleanup, if set, is deferred by the executor immediately after
|
|
// the factory returns. Called even if the run fails or PostRun
|
|
// panics. Use for temp directory removal, closing file handles,
|
|
// etc.
|
|
Cleanup func()
|
|
}
|
|
|
|
// PostRunResult carries artifacts produced by the PostRun hook.
|
|
// Attached to the executor's run result so callers (Discord command
|
|
// handlers, HTTP API responses) can inspect and deliver the artifacts.
|
|
//
|
|
// Why a separate struct (vs returning artifacts inline): post-
|
|
// processing may produce multiple typed artifacts (PNGs, STLs, SCAD
|
|
// source) that the delivery layer classifies and routes differently.
|
|
// A flat []Artifact + arbitrary Metadata covers the known use cases
|
|
// without over-specifying the shape.
|
|
type PostRunResult struct {
|
|
// Artifacts are files produced during post-processing
|
|
// (e.g., rendered PNGs, STL files, SCAD source).
|
|
Artifacts []Artifact
|
|
|
|
// Metadata is arbitrary key-value data the delivery layer can
|
|
// use for formatting (e.g., iteration count, model name, notes).
|
|
Metadata map[string]any
|
|
}
|
|
|
|
// Artifact is a named binary blob produced by post-run processing.
|
|
//
|
|
// Why: the delivery layer needs name + type + bytes to classify
|
|
// each artifact (PNG → embed image, STL → filetransfer upload,
|
|
// SCAD → paste upload). A struct with these three fields is the
|
|
// minimal viable description.
|
|
type Artifact struct {
|
|
Name string // e.g., "model.stl", "preview_iso.png"
|
|
MimeType string // e.g., "model/stl", "image/png"
|
|
Data []byte
|
|
}
|