Documentation
¶
Overview ¶
Package agents bridges clawtool to the host AI coding agents that embed it (Claude Code, Codex, OpenCode, …). Per ADR-011 the role of this package is the **hard-replacement** of native tools — i.e. flipping the host agent's settings so clawtool's `mcp__clawtool__*` becomes the only Bash/Read/Edit/Write/Grep/Glob/WebFetch/WebSearch the model can see.
Each host gets one Adapter implementation. The Adapter only needs to know how to read its settings, claim a known set of tools (atomic write + idempotent), release them, and report status. The CLI subcommand (internal/cli/agents.go) is a thin dispatcher over this interface.
Claude Code adapter — mutates ~/.claude/settings.json's `permissions.deny` array to take native Bash/Read/Edit/Write/Grep/ Glob/WebFetch/WebSearch out of the model's tool surface, leaving only mcp__clawtool__*.
Wire-format note: earlier versions of this adapter wrote to a top-level `disabledTools` field. Claude Code 2.x ignores that key — the canonical mechanism is `permissions.deny`. On read we still recognize the legacy field and migrate it forward; on write we only emit `permissions.deny`.
Package agents — genericAdapter is a parameterised adapter that mirrors claude-code's settings.json + permissions.deny shape but lets the registration site point at any other config dir + field name. Used to ship support for new agents without writing a full adapter file each time.
Today this is best-effort: hermes-agent and openclaw both presumably load a similar JSON config but their exact deny- field name may shift. The adapter is structured so a future PR can swap the field via a one-line change.
Generic MCP-host adapter — covers Codex / OpenCode / Gemini and any other CLI that exposes `<bin> mcp add <name>` / `<bin> mcp remove <name>` semantics. These hosts don't let us disable their internal Bash/Read/Edit tools the way Claude Code's settings.json deny list does, so "claim" here means "register clawtool as an MCP server in the host's config" — same operator intent: the model gets clawtool tools at all, not just the host's built-ins.
**Fan-in semantics**: by default every host points at ONE shared persistent daemon (`internal/daemon`), so BIAM identity, task store, and notify channels are unified across hosts. Stdio-spawn mode is still available as a fallback (`mode: "stdio"`) but it produces N independent identities and breaks cross-host notify — don't use it unless the host doesn't accept `--url` style HTTP MCP.
One marker per host at <configDir>/clawtool-mcp.lock. Release removes the MCP entry and the marker but leaves the daemon running — other hosts may still be bound to it.
Package agents — peer-prefer routing for SendMessage. Routes a dispatch into a registered live BIAM peer's inbox before falling back to spawning a fresh `<family> exec` subprocess. Solves the "claude'a sordum codex'e agentslara gitti" surprise: an operator's open codex pane should receive prompts addressed to codex instead of being shadowed by an invisible fresh subprocess.
Mode flag (opts["mode"]): "peer-prefer" (default) | "peer-only" | "spawn-only" | "auto-tmux". Env override CLAWTOOL_PEER_ROUTING=0 forces spawn-only for one release while migrations land.
Zero-touch auto-spawn: peer-prefer + no online peer + tmux session active → clawtool transparently spawns the agent in a fresh tmux pane, registers it as a peer, and routes the prompt there. The operator never has to remember `clawtool spawn` — sending a message to an absent peer brings it to life. auto-tmux is the explicit "require tmux delivery or fail" variant for callers that want the guarantee.
Index ¶
- Constants
- Variables
- func ClearSticky() error
- func ElevationFlag(family string) string
- func LinkTaskToPeer(taskID, peerID string)
- func MaybeAutoClosePane(taskID string, reg *a2a.Registry) error
- func Register(a Adapter)
- func SetAutoCloseGraceSeconds(seconds int)
- func SetAutoClosePanes(enabled bool)
- func SetClaudeCodeSettingsPath(p string)
- func SetCloseTmuxPaneAndMaybeWindowFn(fn func(paneID, windowID string) error)
- func SetCloseTmuxPaneFn(fn func(paneID string) error)
- func SetGlobalBiamRunner(r BiamRunner)
- func SetGlobalObserver(obs *observability.Observer)
- func SetGlobalPeerRouter(r PeerRouter)
- func SetGlobalPeerSpawner(s PeerSpawner)
- func WriteSticky(instance string) error
- type Adapter
- type Agent
- type BiamRunner
- type ErrBinaryMissing
- type Options
- type PeerRouter
- type PeerSpawner
- type Plan
- type Policy
- type SendMode
- type SendOptions
- type Status
- type Supervisor
- type Transport
Constants ¶
const MCPServerName = "clawtool"
MCPServerName is the canonical name we register clawtool under in every host. Kept identical so the operator sees the same identifier across `codex mcp list`, `gemini mcp list`, etc.
const MCPTokenEnvVar = "CLAWTOOL_TOKEN"
MCPTokenEnvVar is the env var the host process reads to obtain the bearer token when speaking to the shared daemon. Codex sets this at server-launch time (per --bearer-token-env-var); Gemini bakes the literal token into config so this is unused there.
const MetaAutoSpawned = "auto_spawned"
MetaAutoSpawned is the peer-metadata key that marks a peer as one auto-spawned by SendMessage's tmux fallback. Only auto-spawned peers are eligible for auto-close — a user-attached pane (an operator's manually-opened claude session) carries no such flag and is left alone. Defined as a constant so the spawner + the lifecycle hook agree on the spelling without a typo regression.
const MetaTmuxWindow = "tmux_window"
MetaTmuxWindow is the peer-metadata key holding the tmux window_id (`@<digits>`) of the auto-spawned pane. Set by the spawner alongside MetaAutoSpawned; read by MaybeAutoClosePane so the close hook can optionally reap the empty window after killing the pane (ADR-034 open-question Q1: window cleanup). Empty when the spawner ran against an older tmux that didn't echo `#{window_id}` in the -F format string — the close hook then falls back to legacy pane-only close.
Variables ¶
var ClaimedToolsForClawtool = []string{
"Bash",
"Edit",
"Glob",
"Grep",
"Read",
"WebFetch",
"WebSearch",
"Write",
}
ClaimedToolsForClawtool is the canonical set of native tool names every adapter disables when claiming. Per ADR-011 this is exactly the 1:1 mapping with clawtool's core tools that have native equivalents.
Stored here (not per-adapter) because the mapping is identical across hosts — Claude Code calls them `Bash`, `Read`, etc.; other hosts use the same names since they all converged on Claude Code's vocabulary. If Codex / OpenCode use different native names, override per adapter.
var ErrDangerSandboxRequiresUnsafeYes = errors.New(
"--sandbox danger-full-access requires --unsafe-yes confirmation. " +
"This profile bypasses all sandbox restrictions.")
ErrDangerSandboxRequiresUnsafeYes is returned when a dispatch resolves to the danger-full-access profile without the operator having passed --unsafe-yes (CLI) / opts["unsafe_yes"]=true (MCP).
The error wraps a stable sentinel so CLI / MCP callers can match on it for distinct exit codes if they want, while the wrapped message stays human-readable for stderr.
var ErrNoLivePeer = errors.New("peer-only mode: no online peer matches the resolved family")
ErrNoLivePeer is returned by peer-only mode when no online peer matches the resolved family. Typed so MCP / CLI surfaces can render a guided error instead of treating it as a generic dispatch failure.
var ErrSandboxUnresolvable = errors.New("sandbox profile cannot be resolved (refusing to dispatch unsandboxed)")
ErrSandboxUnresolvable is returned by withSandboxResolved when an EXPLICIT per-call sandbox name fails to resolve. Per audit fix #202: operator's `--sandbox <name>` is a security choice — refuse the dispatch rather than silently fall through to unsandboxed.
var ErrSelfDispatch = errors.New("refusing to dispatch to the calling Claude Code session — would loop")
ErrSelfDispatch is returned when something asks clawtool to dispatch a prompt back to the Claude Code session it's running inside — that's an infinite loop the supervisor refuses to enter.
ErrTmuxUnavailable is returned by auto-tmux mode when no tmux session is detected. Typed so the calling agent can recover by retrying with peer-prefer (or asking the operator to start a tmux session) instead of treating it as a generic dispatch failure.
var ErrUnknownAgent = errors.New("unknown agent")
ErrUnknownAgent is returned by Find when the requested agent isn't in the Registry. CLI catches this to print the list of known agents.
var Registry = []Adapter{}
Registry is the set of available adapters. Adapters self-register in their package's init() function so adding a host is one new file.
Functions ¶
func ClearSticky ¶ added in v0.20.0
func ClearSticky() error
ClearSticky removes the active-agent file (no-op if absent).
func ElevationFlag ¶ added in v0.22.76
ElevationFlag returns the per-family elevation flag (the argv token that disables interactive tool-approval prompts) for the named BIAM peer. Empty string means the family has no published elevation flag — caller should treat that as "agent cannot run unattended" and fall back to interactive mode.
Stable family names: claude, codex, gemini, opencode, hermes, aider.
func LinkTaskToPeer ¶ added in v0.22.107
func LinkTaskToPeer(taskID, peerID string)
LinkTaskToPeer records that the dispatch behind taskID was routed into peerID's inbox via auto-spawn. Idempotent: a second call with the same taskID overwrites the prior peerID — fine, the lifecycle hook only cares about the most recent route. Empty taskID or empty peerID is a no-op so callers don't have to nil-guard.
Side effect: cancels any pending grace-period timer for this peer. The "rapid back-to-back tasks shouldn't kill the pane out from under the second one" rule from ADR-034 Q2 — a second SendMessage to the same auto-spawned codex pane while the first task's grace window is still ticking must keep the pane alive.
func MaybeAutoClosePane ¶ added in v0.22.107
MaybeAutoClosePane is the entry point the BIAM terminal-status hook calls. Resolves taskID → peerID → peer.TmuxPane and fires closeTmuxPaneFn(paneID). Skips silently when:
- autoCloseEnabled is false (operator opted out via config).
- taskID has no link in the table (peer was user-attached, not auto-spawned — we never registered the link in the first place; this is the user-attached safety check).
- registry has no peer with that ID (raced a deregister).
- peer's metadata doesn't carry auto_spawned=true (defence in depth: even if the link table has the row, we re-check the flag before firing tmux).
- peer.TmuxPane is empty (peer registered without a pane — not an auto-spawn, can't close anything).
Returns the close-pane error (or nil). Caller (the BIAM hook) logs but doesn't propagate — auto-close is best-effort.
Grace period (ADR-034 Q2): when autoCloseGraceSeconds > 0 the kill is deferred via time.AfterFunc. The timer is keyed by peerID in graceTimers so a re-trigger on the same peer (LinkTaskToPeer from a back-to-back dispatch) cancels it. Returns nil immediately in the deferred path — the caller can't observe the deferred kill's error, which matches the existing best-effort contract.
Window cleanup (Q1): when the peer's metadata carries a MetaTmuxWindow value, the kill routes through the window-aware seam (closeTmuxPaneAndMaybeWindowFn) which probes the window after killing the pane and reaps it when empty. Empty window_id (peer registered before the spawner started recording it) falls back to the legacy pane-only seam.
func Register ¶
func Register(a Adapter)
Register adds an adapter to the registry. Sorted by Name for stable CLI output across runs.
func SetAutoCloseGraceSeconds ¶ added in v0.22.109
func SetAutoCloseGraceSeconds(seconds int)
SetAutoCloseGraceSeconds sets the grace-period in seconds. Pass 0 (the default) to disable the deferral and fall back to immediate close. Negative values are clamped to 0.
func SetAutoClosePanes ¶ added in v0.22.107
func SetAutoClosePanes(enabled bool)
SetAutoClosePanes flips the master gate. Daemon calls this with the resolved cfg.Peer.AutoClosePanes value at boot.
func SetClaudeCodeSettingsPath ¶
func SetClaudeCodeSettingsPath(p string)
SetClaudeCodeSettingsPath redirects the adapter to a custom settings path. Tests use it; production should never call it.
func SetCloseTmuxPaneAndMaybeWindowFn ¶ added in v0.22.109
SetCloseTmuxPaneAndMaybeWindowFn registers the production pane+window close adapter. Pass nil to clear. Tests that want to observe window-cleanup behaviour rebind this; tests that don't care leave it on the default no-op so the existing pane-only stub (recordCloseFn) continues to drive MaybeAutoClosePane.
func SetCloseTmuxPaneFn ¶ added in v0.22.107
SetCloseTmuxPaneFn registers the production close-pane adapter. Pass nil to clear (e.g. test cleanup); callers pass the real internal/cli.KillTmuxPane wrapper. Idempotent.
func SetGlobalBiamRunner ¶ added in v0.20.0
func SetGlobalBiamRunner(r BiamRunner)
SetGlobalBiamRunner registers the process-wide async runner. Pass nil to disable async submission (callers fall back to streaming).
func SetGlobalObserver ¶ added in v0.20.0
func SetGlobalObserver(obs *observability.Observer)
SetGlobalObserver registers the process-wide observer. Pass nil to disable. Idempotent.
func SetGlobalPeerRouter ¶ added in v0.22.97
func SetGlobalPeerRouter(r PeerRouter)
SetGlobalPeerRouter registers the process-wide router. Pass nil to clear (e.g. daemon shutdown). Idempotent.
func SetGlobalPeerSpawner ¶ added in v0.22.105
func SetGlobalPeerSpawner(s PeerSpawner)
SetGlobalPeerSpawner registers the process-wide spawner. Pass nil to clear (e.g. daemon shutdown). Idempotent.
func WriteSticky ¶ added in v0.20.0
WriteSticky persists the active-agent name. Used by `clawtool agent use`. Atomic temp+rename so a crash mid-write doesn't corrupt the file.
Types ¶
type Adapter ¶
type Adapter interface {
Name() string
// Detected returns true when the host's settings file exists or
// the host is otherwise configured on this machine. False means
// the agent isn't installed; CLI commands should report
// "not detected" instead of failing.
Detected() bool
// Claim disables the native tools clawtool replaces. Idempotent.
Claim(opts Options) (Plan, error)
// Release re-enables every tool clawtool's previous Claim
// disabled (tracked via the per-adapter marker file). Idempotent.
Release(opts Options) (Plan, error)
// Status reports what's currently claimed.
Status() (Status, error)
}
Adapter describes one host AI coding agent. Implementations live in per-host files (claudecode.go for Claude Code, codex.go later, …).
Implementations MUST:
- Be idempotent for Claim and Release (run twice = same result).
- Use atomic writes (no partial state observable on crash).
- Touch ONLY the fields/files they own. User customizations stay.
- Track ownership via a marker file so Release only undoes what this clawtool installation added.
type Agent ¶ added in v0.20.0
type Agent struct {
Instance string `json:"instance"` // user-chosen kebab-case name (claude-personal, claude-work, codex1, …)
Family string `json:"family"` // upstream CLI family (claude / codex / opencode / gemini / hermes)
Bridge string `json:"bridge,omitempty"` // installed bridge name ("codex-bridge", "opencode-bridge", "gemini-bridge", "hermes-bridge"); empty when family lacks a bridge concept (claude self)
Status string `json:"status"` // "callable", "bridge-missing", "binary-missing", "disabled"
Callable bool `json:"callable"` // derived: status == "callable"
AuthScope string `json:"auth_scope,omitempty"` // [secrets.X] section to resolve env from
Tags []string `json:"tags,omitempty"` // labels for tag-routed dispatch (Phase 4)
FailoverTo []string `json:"failover_to,omitempty"` // ordered fallback chain of instance names (Phase 4)
Sandbox string `json:"sandbox,omitempty"` // ADR-020 / #163: name of a [sandboxes.<name>] profile to wrap every dispatch in. Empty = no sandbox. Resolved per-call in dispatch().
}
Agent is one row in the supervisor's registry. Same shape across CLI `--list`, MCP `AgentList`, and HTTP `GET /v1/agents`. Tags and FailoverTo drive Phase 4's dispatch policies.
type BiamRunner ¶ added in v0.20.0
type BiamRunner interface {
Submit(ctx context.Context, instance, prompt string, opts map[string]any) (string, error)
}
BiamRunner is the small subset of *biam.Runner the agents package needs. Defining it as an interface here lets us avoid an import cycle (biam imports agents indirectly through the runner glue) and makes the Supervisor testable without a real SQLite store.
type ErrBinaryMissing ¶ added in v0.20.0
ErrBinaryMissing is returned when a transport's upstream CLI binary is not on PATH. The bridge recipe should have installed it; the supervisor surfaces this so `clawtool bridge add <family>` can be suggested.
func (ErrBinaryMissing) Error ¶ added in v0.20.0
func (e ErrBinaryMissing) Error() string
type Options ¶
type Options struct {
DryRun bool
// RequireAuth tells HTTP-mode adapters to wire the bearer-token
// gate into the host's MCP entry — Codex via
// `--bearer-token-env-var=CLAWTOOL_TOKEN`, Gemini via a baked
// `Authorization: Bearer <tok>` header. Default false: single-
// user local installs run the shared daemon in no-auth mode (the
// operator's machine is the trust boundary), so codex / gemini
// don't need the env var pre-set. Daemon / relay deployments
// (multi-user, exposed beyond loopback) flip this to true via
// `clawtool agents claim --require-auth` or the `agent-claim`
// recipe's `require_auth=true` option.
RequireAuth bool
}
Options carries per-call flags the CLI propagates.
type PeerRouter ¶ added in v0.22.97
type PeerRouter interface {
// FindOnlinePeer returns the first online peer whose backend
// matches `family` AND whose role != orchestrator AND whose
// peer_id != excludePeerID (anti-self-dispatch). Returns
// (peer_id, ok) — empty peer_id + false means no match.
FindOnlinePeer(family, excludePeerID string) (peerID, displayName string, ok bool)
// EnqueueToPeer drops the prompt into peerID's inbox as a
// query message. Returns the assigned message ID so the
// caller can correlate replies later.
EnqueueToPeer(peerID, fromPeerID, prompt string) (msgID string, err error)
// IsAutoSpawnedPeer reports whether peerID's metadata carries
// MetaAutoSpawned=true — i.e. the peer was created by
// SendMessage's tmux auto-spawn fallback rather than an
// operator's manually-attached session. Used by tryPeerRoute
// to decide whether to register a taskID → peerID lifecycle
// link so the BIAM terminal-status hook can close the pane on
// completion. Returns false on unknown peer or any other
// uncertainty (the lifecycle hook's defence-in-depth check
// will recheck the metadata before firing tmux anyway).
IsAutoSpawnedPeer(peerID string) bool
}
PeerRouter is the small subset of *a2a.Registry the supervisor needs for peer-prefer routing. Defining it as an interface keeps the agents package decoupled from the registry's full API surface and makes the routing path unit-testable without spinning up a full a2a registry.
func GetGlobalPeerRouter ¶ added in v0.22.97
func GetGlobalPeerRouter() PeerRouter
GetGlobalPeerRouter returns the process-wide router or nil.
func NewA2APeerRouter ¶ added in v0.22.97
func NewA2APeerRouter(reg *a2a.Registry) PeerRouter
NewA2APeerRouter wraps a registry. Returns nil when reg is nil so the daemon's "registry not initialised" path stays ergonomic.
type PeerSpawner ¶ added in v0.22.105
type PeerSpawner interface {
// TmuxAvailable reports whether a tmux session is currently
// active in this process's environment ($TMUX is set + the
// `tmux` binary is on PATH). The supervisor checks this
// before invoking EnsurePeer so peer-prefer can fall through
// to the spawn-fresh-subprocess path on no-tmux hosts and
// auto-tmux can fail with ErrTmuxUnavailable.
TmuxAvailable() bool
// EnsurePeer ensures a peer for the given family is alive
// and registered. Implementations should be idempotent at
// the family level — repeated calls within a small cooldown
// window return the same peer rather than spawning a second
// pane. Returns the peer_id + display name + a flag noting
// whether the call actually fired a spawn (false = reused
// an existing peer).
EnsurePeer(family, fromPeerID string) (peerID, displayName string, spawned bool, err error)
}
PeerSpawner is the indirection seam for the auto-spawn path. When peer-prefer (or auto-tmux) finds no online peer, the supervisor asks the spawner to bring one to life — typically by opening a new tmux pane, registering the freshly spawned agent in the BIAM peer registry, and returning the assigned peer_id. Defining it as an interface keeps the agents package decoupled from the internal/cli spawn machinery (which depends on daemon HTTP) and makes the auto-spawn flow unit-testable.
func GetGlobalPeerSpawner ¶ added in v0.22.105
func GetGlobalPeerSpawner() PeerSpawner
GetGlobalPeerSpawner returns the process-wide spawner or nil.
func NewA2APeerSpawner ¶ added in v0.22.105
func NewA2APeerSpawner(reg *a2a.Registry) PeerSpawner
NewA2APeerSpawner returns a PeerSpawner backed by the given registry. Returns nil when reg is nil — the supervisor gracefully falls through (no auto-spawn) in that case.
type Plan ¶
type Plan struct {
Adapter string `json:"adapter"` // "claude-code"
Action string `json:"action"` // "claim" | "release" | "noop"
SettingsPath string `json:"settings_path,omitempty"` // file the adapter would mutate
MarkerPath string `json:"marker_path,omitempty"` // file the adapter would write to track ownership
ToolsAdded []string `json:"tools_added,omitempty"` // tools claim is about to disable
ToolsRemoved []string `json:"tools_removed,omitempty"` // tools release is about to re-enable
WasNoop bool `json:"was_noop,omitempty"` // already in the requested state
DryRun bool `json:"dry_run,omitempty"`
}
Plan is what an adapter would do (or did, in non-dry-run). The CLI renders this for human consumption; `agents claim --json` / `agents release --json` emit the same struct via Go's default marshal so a script can log claim/release events structurally. snake_case JSON tags follow the project-wide convention (mirrors Status, BuildInfo, agentListEntry).
type Policy ¶ added in v0.20.0
Policy chooses an Agent for a dispatch and (optionally) provides a fallback chain. The supervisor invokes Pick once per Send.
`requested` is the caller's --agent flag value (empty when unset). `tag` is the caller's --tag value (empty when unset). `all` is the supervisor's full registry snapshot.
Returns: the Agent to try first, plus an ordered slice of fallback instances (zero-length means no fallback). An empty primary + non-nil error stops the dispatch.
type SendMode ¶ added in v0.22.97
type SendMode string
SendMode is the typed routing-preference for Supervisor.Send.
const ( SendModePeerPrefer SendMode = "peer-prefer" SendModePeerOnly SendMode = "peer-only" SendModeSpawnOnly SendMode = "spawn-only" // SendModeAutoTmux requires the auto-spawn path to land the // agent in a tmux pane; no-tmux → typed ErrTmuxUnavailable. // Useful when the operator wants the live-pane guarantee // (e.g. so they can watch the agent's progress) instead of // the silent spawn-fresh-subprocess fallback. SendModeAutoTmux SendMode = "auto-tmux" )
type SendOptions ¶ added in v0.20.0
type SendOptions struct {
SessionID string // upstream session UUID for resume (claude / codex / opencode)
Model string // vendor-specific model name
Format string // "text" | "json" | "stream-json" — passed through where supported
Cwd string // working directory for the upstream CLI
ExtraArgs []string // raw passthrough argv appended to the upstream command
// Unattended is true when the dispatch is running under
// `clawtool send --unattended` (ADR-023). Each transport
// translates this into its upstream's elevation flag
// (--dangerously-skip-permissions for claude,
// --dangerously-bypass-approvals-and-sandbox for codex,
// --yolo for gemini / opencode, etc.) so the model actually
// gets the permissions the audit log claims it has. Without
// this flag the upstream CLI will still prompt for tool
// approval — defeating the entire feature.
Unattended bool
// Sandbox is the resolved sandbox.Profile to wrap the upstream
// process in (ADR-020). When non-nil, startStreamingExec
// applies the host-native sandbox.Engine.Wrap on the spawned
// cmd before Start. Nil = no sandbox (legacy path, default).
//
// We use the typed Profile rather than the profile name
// string because profile resolution (config lookup, validation,
// per-instance override) is the supervisor's job — transports
// stay platform-agnostic. Caller wires this from
// config.SandboxConfig + sandbox.ParseProfile.
Sandbox *sandbox.Profile
// Env carries secrets-store values the supervisor resolved for
// this instance (audit #205). Merged onto the parent process
// env in startStreamingExecWith so the child sees it as if it
// were inherited. Never overrides parent env keys — caller
// (withSecretsResolved) only fills missing values.
Env map[string]string
}
SendOptions documents the keys Transports look for in the opts map. All keys are optional; transports that don't understand a key silently ignore it (forward-compat).
func ParseOptions ¶ added in v0.20.0
func ParseOptions(opts map[string]any) SendOptions
ParseOptions extracts the well-known keys from a free-form opts map. Unknown keys are tolerated — the caller may surface them per-transport.
type Status ¶
type Status struct {
Adapter string `json:"adapter"`
Detected bool `json:"detected"`
SettingsPath string `json:"settings_path,omitempty"`
Claimed bool `json:"claimed"` // true when our marker file exists and lists tools
DisabledByUs []string `json:"disabled_by_us,omitempty"` // tools we disabled (read from marker)
Notes string `json:"notes,omitempty"` // anything an adapter wants to surface (e.g. "settings file missing")
}
Status is a snapshot of the adapter's current claim state.
type Supervisor ¶ added in v0.20.0
type Supervisor interface {
Agents(ctx context.Context) ([]Agent, error)
Send(ctx context.Context, instance, prompt string, opts map[string]any) (io.ReadCloser, error)
Resolve(ctx context.Context, requested string) (Agent, error)
// SubmitAsync persists the prompt + spawns a background dispatch,
// returning a task_id immediately. Callers poll / wait via the
// BIAM TaskGet / TaskWait surfaces. Errors out when the BIAM
// runner isn't wired (e.g. a test or server boot that skipped
// BIAM init).
SubmitAsync(ctx context.Context, instance, prompt string, opts map[string]any) (string, error)
}
Supervisor is the single dispatch entry point for prompt routing. One per `clawtool` process.
func NewSupervisor ¶ added in v0.20.0
func NewSupervisor() Supervisor
NewSupervisor wires the default supervisor. Tests inject custom loaders / transports.
Round-robin counters and the rate / concurrency limiter are pulled from process-wide singletons (sharedDispatchState) so multiple callers in the same process — MCP tool handlers, the HTTP gateway, the BIAM runner — observe one rotation cursor and one token bucket. Building fresh state per call resets both, which silently disables rate limits and pins round-robin to the first instance.
type Transport ¶ added in v0.20.0
type Transport interface {
Family() string
Send(ctx context.Context, prompt string, opts map[string]any) (io.ReadCloser, error)
}
Transport forwards a prompt to an already-installed upstream CLI (or its bridge / app-server) and returns the streamed response.
The returned reader streams whatever wire format the upstream emits (NDJSON of stream-json events for claude/gemini, JSON-RPC frames for codex app-server, ACP messages for opencode acp, plain text otherwise). Closing the reader cancels the upstream process.
func AiderTransport ¶ added in v0.22.48
func AiderTransport() Transport
AiderTransport returns the Aider transport.
func ClaudeTransport ¶ added in v0.20.0
func ClaudeTransport() Transport
ClaudeTransport returns the Claude Code transport.
func CodexTransport ¶ added in v0.20.0
func CodexTransport() Transport
CodexTransport returns the Codex transport. Exposed as a constructor so the supervisor can wire one in without depending on the unexported type name.
func GeminiTransport ¶ added in v0.20.0
func GeminiTransport() Transport
GeminiTransport returns the Gemini transport.
func HermesTransport ¶ added in v0.20.0
func HermesTransport() Transport
HermesTransport returns the Hermes transport.
func OpencodeTransport ¶ added in v0.20.0
func OpencodeTransport() Transport
OpencodeTransport returns the OpenCode transport.
Source Files
¶
- agents.go
- aider_transport.go
- claude_transport.go
- claudecode.go
- codex_transport.go
- gemini_transport.go
- generic.go
- hermes_transport.go
- limiter.go
- lookup.go
- mcp_host.go
- opencode_transport.go
- peer_lifecycle.go
- peer_route.go
- peer_spawn.go
- policy.go
- sandbox_danger_gate.go
- sandbox_resolve.go
- secrets_resolve.go
- supervisor.go
- transport.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package biam — Unix-socket dispatch server.
|
Package biam — Unix-socket dispatch server. |
|
Package worktree — opt-in git-worktree isolation per dispatch (ADR-014 T5, design from the 2026-04-26 multi-CLI fan-out).
|
Package worktree — opt-in git-worktree isolation per dispatch (ADR-014 T5, design from the 2026-04-26 multi-CLI fan-out). |