Documentation
¶
Overview ¶
Package tools provides built-in tool implementations for the octo agentic loop. Each tool implements agent.ToolExecutor and exposes a Definition() method that returns the agent.ToolDefinition the LLM sees.
Index ¶
- Constants
- Variables
- func ActiveMCPRegistry() *mcp.Registry
- func ArtifactContentType(path string) (ctype string, ok bool)
- func BgCommand(id string) (string, bool)
- func BrowserHealerSet() bool
- func BrowserSkillGeneratorSet() bool
- func BrowserSkillsDir() string
- func BrowserVisionEnabled() bool
- func CleanSpillFiles()
- func CloseSessionBackgroundManager(id string)
- func CloseSessionSubAgentManager(id string)
- func CloseSessionWorkflowManager(id string)
- func DefaultTools() []agent.ToolDefinition
- func DefaultToolsFor(model string) []agent.ToolDefinition
- func DetectToolchain() (present, missing []string)
- func FormatBgNote(e BgExit) string
- func FormatBgNoteWithSummary(mgr *BackgroundManager, e BgExit) string
- func FormatSubAgentNote(ev SubAgentNotification) string
- func FormatTaskList(items []tasks.Task) string
- func FormatWorkflowNote(ev WorkflowNotification) string
- func HasActiveSubAgentSync() bool
- func HasActiveSync() bool
- func IsSubAgent(ctx context.Context) bool
- func KillAllBackground()
- func KillAllSessionSubAgents()
- func KillDefaultWorkflows()
- func LoopExpired(start time.Time) bool
- func MaybeSpillOutput(id, body string) string
- func NetworkAllowed() bool
- func PromoteCurrentSubAgentSync()
- func PromoteCurrentSync()
- func ResetBrowserSession()
- func SendFileToolDef() agent.ToolDefinition
- func SetAsker(a Asker)
- func SetBackgroundOnExit(fn func(BgExit))
- func SetBrowserHealer(h browser.Healer)
- func SetBrowserSession(b *browser.Browser, p *browser.Page)
- func SetBrowserSkillGenerator(g browser.SkillGenerator)
- func SetBrowserVision(on bool)
- func SetDefaultSubAgentManager(m *SubAgentManager)
- func SetDefaultWorkflowOnDone(fn func(WorkflowNotification))
- func SetDefaultWorkflowOnEvent(fn func(WorkflowEvent))
- func SetMCPRegistry(r *mcp.Registry)
- func SetRestarter(f func(reason string))
- func SetSandbox(p *sandbox.Policy)
- func SetSkills(r *skills.Registry)
- func SetSpawner(s Spawner)
- func SetTaskStore(s TaskStore)
- func SetToolSearchConfig(c ToolSearchConfig)
- func SetWakerSupported(v bool)
- func ShellEnvNote() string
- func SubAgentEventSink(ctx context.Context) func(SubAgentEvent)
- func ToolCallTarget(name string, input map[string]any) (realName string, realInput map[string]any, ok bool)
- func ToolchainNote() string
- func WithAsker(ctx context.Context, a Asker) context.Context
- func WithBackgroundManager(ctx context.Context, mgr *BackgroundManager) context.Context
- func WithChannelSender(ctx context.Context, s ChannelFileSender) context.Context
- func WithSubAgentEventSink(ctx context.Context, sink func(SubAgentEvent)) context.Context
- func WithSubAgentManager(ctx context.Context, mgr *SubAgentManager) context.Context
- func WithSubAgentMarker(ctx context.Context) context.Context
- func WithTaskStore(ctx context.Context, store TaskStore) context.Context
- func WithWaker(ctx context.Context, w Waker) context.Context
- func WithWorkflowManager(ctx context.Context, mgr *WorkflowManager) context.Context
- func WithWorkingDir(ctx context.Context, dir string) context.Context
- func WorkingDir(ctx context.Context) string
- type AgentKillTool
- type AgentSendTool
- type AgentStatusTool
- type AgentTool
- type AskRequest
- type AskResponse
- type AskUserQuestionTool
- type Asker
- type BackgroundManager
- func (m *BackgroundManager) BeginSync() *SyncSession
- func (m *BackgroundManager) Command(id string) (string, bool)
- func (m *BackgroundManager) EndSync()
- func (m *BackgroundManager) FireExitHook(e BgExit)
- func (m *BackgroundManager) HasSync() bool
- func (m *BackgroundManager) Kill(id string) bool
- func (m *BackgroundManager) KillAll()
- func (m *BackgroundManager) KillWithSignal(id string, sigName string) bool
- func (m *BackgroundManager) List() []BgInfo
- func (m *BackgroundManager) ListRunning() []BgInfo
- func (m *BackgroundManager) Mode(id string) (BackgroundMode, bool)
- func (m *BackgroundManager) Promote(id string) bool
- func (m *BackgroundManager) PromoteSync()
- func (m *BackgroundManager) Read(id string) (output, status string, found bool, blocked bool, mode BackgroundMode)
- func (m *BackgroundManager) Remove(id string)
- func (m *BackgroundManager) SetOnExit(fn func(BgExit))
- func (m *BackgroundManager) Start(command string, mode BackgroundMode, opts ...StartOption) (string, error)
- func (m *BackgroundManager) Tail(id string, lines int) (output, status string, found bool, blocked bool, mode BackgroundMode)
- func (m *BackgroundManager) WriteStdin(id string, input string) error
- func (m *BackgroundManager) WriteStdinAndClose(id string, input string) error
- type BackgroundMode
- type BgExit
- type BgInfo
- type BrowserTool
- type ChannelFileSender
- type DefaultRegistry
- type EditFileTool
- type GlobTool
- type GrepTool
- type KillShellTool
- type ReadFileTool
- type ReadTracker
- type RestartServerTool
- type ScheduleWakeupTool
- type SendFileTool
- type ShowArtifactTool
- type SkillTool
- type SpawnRequest
- type SpawnResult
- type Spawner
- type StartOption
- type SubAgentEvent
- type SubAgentInfo
- type SubAgentManager
- func (m *SubAgentManager) BeginSync() *SyncSession
- func (m *SubAgentManager) ContinueSync(ctx context.Context, agentID, message string) (SpawnResult, error)
- func (m *SubAgentManager) EndSync(s *SyncSession)
- func (m *SubAgentManager) HasSync() bool
- func (m *SubAgentManager) Kill(id string) bool
- func (m *SubAgentManager) KillAll()
- func (m *SubAgentManager) ListRunning() []SubAgentInfo
- func (m *SubAgentManager) PromoteSync()
- func (m *SubAgentManager) Read(id string) (result, status string, found bool)
- func (m *SubAgentManager) RunSync(ctx context.Context, req SpawnRequest) (SpawnResult, error)
- func (m *SubAgentManager) Send(agentID, message string) error
- func (m *SubAgentManager) SetOnEvent(fn func(SubAgentEvent))
- func (m *SubAgentManager) SetOnExit(fn func(SubAgentNotification))
- func (m *SubAgentManager) SetSynchronous(v bool)
- func (m *SubAgentManager) Start(req SpawnRequest) (string, error)
- func (m *SubAgentManager) Synchronous() bool
- type SubAgentNotification
- type SyncSession
- type TaskCreateTool
- type TaskListTool
- type TaskStore
- type TaskUpdateTool
- type TerminalInputTool
- type TerminalOutputTool
- type TerminalTool
- type ToolSearchConfig
- type ToolSearchMode
- type Waker
- type WebFetchTool
- type WebSearchResponse
- type WebSearchResult
- type WebSearchTool
- type WorkflowEvent
- type WorkflowKillTool
- type WorkflowManager
- func (m *WorkflowManager) Kill(id string) (found, wasRunning bool)
- func (m *WorkflowManager) KillAll()
- func (m *WorkflowManager) List() []WorkflowRunSnapshot
- func (m *WorkflowManager) Read(id string) (WorkflowRunSnapshot, bool)
- func (m *WorkflowManager) SetOnDone(fn func(WorkflowNotification))
- func (m *WorkflowManager) SetOnEvent(fn func(WorkflowEvent))
- func (m *WorkflowManager) Start(req WorkflowRunRequest) (string, error)
- type WorkflowNotification
- type WorkflowRunRequest
- type WorkflowRunSnapshot
- type WorkflowSaveTool
- type WorkflowStatusTool
- type WorkflowTool
- type WriteFileTool
Constants ¶
const AsyncModeNotice = "" /* 396-byte string literal not displayed */
AsyncModeNotice is the model-facing instruction appended to an async background-launch tool result. Wrapped in <system-reminder> so StripRemindersForDisplay strips it from UI cards (TUI and web).
const GlobMaxResults = 200
GlobMaxResults caps the number of paths a single glob call returns. The LLM rarely needs more than this, and a missing cap could surface tens of thousands of paths on large repos (node_modules, vendor/) and blow up context.
const GrepMaxLines = 200
GrepMaxLines caps how many output lines a single grep call returns. Without it a broad pattern (e.g. `func`) on a large repo floods the LLM context with thousands of hits. Past the cap the output is truncated with a marker naming the total, so the model narrows the pattern instead of assuming it saw everything. Mirrors GlobMaxResults' role for glob.
const InteractiveModeNotice = "" /* 359-byte string literal not displayed */
InteractiveModeNotice is the model-facing instruction appended to an interactive background-launch tool result. Wrapped in <system-reminder> so UI card renderers strip it automatically.
const JinaReaderHost = "https://r.jina.ai/"
JinaReaderHost is the public Jina AI Reader endpoint. Sending a URL via `https://r.jina.ai/<URL>` returns the page rendered to Markdown — handles JavaScript-rendered pages, paywalls (where Jina has access), and most of the noisy chrome a raw curl would dump on the LLM.
const MaxLoopLifetime = 12 * time.Hour
MaxLoopLifetime bounds how long an in-session loop may keep ticking, measured from its first wakeup. A runaway loop — the model re-arming forever, or an interval loop nobody stops — must not tick indefinitely, especially on the server (web/IM) where no one watches it spend tokens. All three surfaces (TUI, web, IM) enforce this same bound via LoopExpired: once a loop has run this long it stops instead of re-arming. For a schedule that must outlive this, use a persistent cron task (cron-task-creator) instead.
const ReadFileImageMaxBytes = 5 * 1024 * 1024 // 5 MB
ReadFileImageMaxBytes is the maximum image file size read_file will embed for multimodal models. Past this the user gets an error suggesting they resize or compress the image.
const ReadFileMaxLines = 2000
ReadFileMaxLines caps how many lines a single read_file call returns. Past this, the caller must paginate via offset/limit. Matches the cap Claude Code's Read tool uses (helps keep LLM context sane).
const TerminalSpillBytes = 16 * 1024
TerminalSpillBytes is the size past which terminal output is written to a temp file instead of being returned to the LLM inline. ~16 KB (~4k tokens) is comfortably "too long" for a single tool result; below it the output is handed back unchanged.
const WebFetchInlineBytes = 64 * 1024
WebFetchInlineBytes is the size up to which a fetched body is returned inline. Larger responses (up to WebFetchMaxBytes) are written to a temp file and summarised with an outline + head preview, so a big page never floods the model's context while its full content stays one read_file away.
const WebFetchMaxBytes = 5 * 1024 * 1024 // 5 MB
WebFetchMaxBytes is the absolute ceiling on a single fetched body, whether returned inline or spilled to a temp file. It bounds memory and disk for a pathological response; past it the body is truncated with a clear marker. Set well above any real page so the spilled file holds the full content in practice.
const WebSearchDefaultMax = 5
WebSearchDefaultMax is the default number of results returned per search. Most tasks need ≤10; capped at 20 to keep responses small.
const WebSearchHardMax = 20
WebSearchHardMax is the upper bound for max_results regardless of what the caller asks for.
Variables ¶
var TerminalTimeout = 120 * time.Second
TerminalTimeout is the maximum time a single terminal command may run synchronously before it is automatically promoted to a background process.
Functions ¶
func ActiveMCPRegistry ¶
ActiveMCPRegistry returns the registered registry or nil if MCP is off.
func ArtifactContentType ¶
ArtifactContentType returns the Content-Type for a previewable artifact path, or ok=false when the extension isn't previewable.
func BgCommand ¶
BgCommand returns the original command string for a background process id on the default manager, or ("", false) if unknown.
func BrowserHealerSet ¶
func BrowserHealerSet() bool
BrowserHealerSet / BrowserSkillGeneratorSet report whether the LLM-backed browser helpers are wired (for tests/diagnostics).
func BrowserSkillGeneratorSet ¶
func BrowserSkillGeneratorSet() bool
func BrowserSkillsDir ¶
func BrowserSkillsDir() string
BrowserSkillsDir is where recorded browser skills live (editable YAML).
func BrowserVisionEnabled ¶
func BrowserVisionEnabled() bool
BrowserVisionEnabled reports the current setting (for tests/diagnostics).
func CleanSpillFiles ¶
func CleanSpillFiles()
CleanSpillFiles removes this process's spill files. Wire it into session shutdown next to KillAllBackground so a normal exit leaves no leftovers.
func CloseSessionBackgroundManager ¶
func CloseSessionBackgroundManager(id string)
CloseSessionBackgroundManager kills every process tracked for a session and drops its manager. Call when a session is deleted so its background daemons don't leak until daemon shutdown. No-op for an unknown id.
func CloseSessionSubAgentManager ¶
func CloseSessionSubAgentManager(id string)
CloseSessionSubAgentManager kills every sub-agent tracked for a session and drops its manager. No-op for an unknown id.
func CloseSessionWorkflowManager ¶
func CloseSessionWorkflowManager(id string)
CloseSessionWorkflowManager cancels every workflow tracked for a session and drops its manager. No-op for an unknown id.
func DefaultTools ¶
func DefaultTools() []agent.ToolDefinition
DefaultTools returns the tool list with no model context — equivalent to DefaultToolsFor(""). MCP schemas are always uploaded in full (the Tool Search bridge needs the model to evaluate its auto threshold), so unmigrated callers keep the original behaviour.
func DefaultToolsFor ¶
func DefaultToolsFor(model string) []agent.ToolDefinition
DefaultToolsFor returns the slice of ToolDefinitions sent to the LLM when `--tools` is on, for the given model. Order matches allTools. Each capability-gated tool is withheld unless the corresponding registration call has been made — SkillTool needs SetSkills, sub-agent tools need a SubAgentManager. Advertising a tool that can only error wastes a slot and confuses the model.
MCP surfaces ride alongside the built-ins. When Tool Search is active for this model (see toolSearchActive) the full per-tool catalog is replaced by the three search/describe/call bridge tools, so the model's tools array carries three small schemas instead of every MCP tool's schema every turn.
func DetectToolchain ¶
func DetectToolchain() (present, missing []string)
DetectToolchain reports which curated developer tools resolve on the current PATH. Presence-only via exec.LookPath — a filesystem lookup, not a subprocess — so it is cheap enough to call on every context build, including the server's per-turn recompose. Versions are deliberately not probed; the agent runs `<tool> --version` on demand when it actually needs one. On Windows LookPath honours PATHEXT, so npm.cmd / npx.cmd resolve as expected.
func FormatBgNote ¶
FormatBgNote renders a background-process completion as a <system-reminder> block. It rides the steer path of whichever frontend wires it (CLI/TUI: Inbox.Enqueue; server: Inbox or steer queue; IM: the session agent's Inbox), so the model reads it as an environment event rather than user speech. Wrapping in <system-reminder> matches octo's convention for injected, non-user context — UIs strip these spans from user-visible text.
func FormatBgNoteWithSummary ¶
func FormatBgNoteWithSummary(mgr *BackgroundManager, e BgExit) string
FormatBgNoteWithSummary renders a completion notice plus a summary of other background processes still running. The summary helps the model track in-flight async/interactive work without a dedicated process-list tool. Pass nil for mgr to omit the summary (e.g. unit tests that only verify the basic note format).
func FormatSubAgentNote ¶
func FormatSubAgentNote(ev SubAgentNotification) string
FormatSubAgentNote renders a sub-agent completion notification as a <system-reminder> block. It rides the existing steer path (Agent.Steer → folded into the next tool_result, or prepended to the next turn — see turncore.go), so the model reads it as an environment event rather than user speech.
func FormatTaskList ¶
FormatTaskList renders a slice of tasks for display. Used both by the task_list tool's Execute and by the REPL's /tasks slash command.
Layout: a one-line header followed by one row per task, status-prefixed so the user can scan at a glance:
Tasks: 1 in progress, 2 pending ▶ #3 Migrating auth middleware ○ #1 Add migration test ○ #4 Update README
func FormatWorkflowNote ¶
func FormatWorkflowNote(ev WorkflowNotification) string
FormatWorkflowNote renders a background workflow completion as a <system-reminder> note for the model, mirroring FormatSubAgentNote. It lets the transport nudge the model when a detached run finishes instead of relying on the model to poll workflow_status.
func HasActiveSubAgentSync ¶
func HasActiveSubAgentSync() bool
HasActiveSubAgentSync reports whether the default manager has a synchronous sub-agent running. Used by the TUI to conditionally show the Ctrl+B hint.
func HasActiveSync ¶
func HasActiveSync() bool
HasActiveSync reports whether the default manager has a sync terminal polling. Used by the TUI to conditionally show the Ctrl+B hint.
func IsSubAgent ¶
IsSubAgent reports whether ctx is currently inside a sub-agent's run.
func KillAllBackground ¶
func KillAllBackground()
KillAllBackground terminates every tracked background process — the default manager AND every per-session manager. Wire it into session/REPL/daemon shutdown to avoid orphans regardless of which session launched a process.
func KillAllSessionSubAgents ¶
func KillAllSessionSubAgents()
KillAllSessionSubAgents terminates every sub-agent across all sessions. Called on daemon shutdown, mirroring KillAllBackground.
func KillDefaultWorkflows ¶
func KillDefaultWorkflows()
KillDefaultWorkflows cancels every background workflow on the process-global manager — called on CLI/TUI exit so a detached run doesn't linger.
func LoopExpired ¶
LoopExpired reports whether a loop that first armed at start has run past the anti-leak lifetime and must stop. Shared by every surface so the bound is identical everywhere; a zero start (no loop yet) is never expired.
func MaybeSpillOutput ¶
MaybeSpillOutput returns body unchanged when it is small enough to give the LLM directly. When body exceeds TerminalSpillBytes — and has more lines than the preview would show — it writes the full body to a temp file and returns a head+tail preview plus the file path and a one-line read hint, so the agent decides how to read the rest (read_file with offset/limit, or grep) instead of having the whole blob flood its context.
id names the source (e.g. a background process id) and is woven into the temp filename. On any write failure it degrades to returning body unchanged: losing context is worse than a missing file.
func NetworkAllowed ¶
func NetworkAllowed() bool
NetworkAllowed reports whether the active sandbox policy permits network access. When no sandbox is active (nil policy) network is allowed.
func PromoteCurrentSubAgentSync ¶
func PromoteCurrentSubAgentSync()
PromoteCurrentSubAgentSync signals the default manager's synchronous sub-agent to promote. Called by the TUI Ctrl+B handler.
func PromoteCurrentSync ¶
func PromoteCurrentSync()
PromoteCurrentSync signals the default manager's sync terminal to promote. Called by the TUI Ctrl+B handler.
func ResetBrowserSession ¶
func ResetBrowserSession()
ResetBrowserSession closes and clears the active browser session.
func SendFileToolDef ¶
func SendFileToolDef() agent.ToolDefinition
SendFileToolDef is the definition runChannelTurns appends to the IM tool list. Exported so the server can advertise the tool without re-deriving it.
func SetAsker ¶
func SetAsker(a Asker)
SetAsker registers the asker the ask_user_question tool delegates to. Pass nil to disable (the tool then doesn't appear in DefaultTools).
func SetBackgroundOnExit ¶
func SetBackgroundOnExit(fn func(BgExit))
SetBackgroundOnExit registers the completion hook on the default manager (the one the built-in terminal tool uses). The REPL wires this to push a "background finished" notice into the conversation + UI. Pass nil to clear.
func SetBrowserHealer ¶
SetBrowserHealer injects the LLM-backed step healer used by run_skill.
func SetBrowserSession ¶
SetBrowserSession injects a pre-connected session (tests).
func SetBrowserSkillGenerator ¶
func SetBrowserSkillGenerator(g browser.SkillGenerator)
SetBrowserSkillGenerator injects the LLM-backed skill distiller used by record_stop (nil falls back to deterministic compilation).
func SetBrowserVision ¶
func SetBrowserVision(on bool)
SetBrowserVision enables/disables handing images to the model.
func SetDefaultSubAgentManager ¶
func SetDefaultSubAgentManager(m *SubAgentManager)
SetDefaultSubAgentManager registers the global manager used by AgentTool when no local manager is set.
func SetDefaultWorkflowOnDone ¶
func SetDefaultWorkflowOnDone(fn func(WorkflowNotification))
SetDefaultWorkflowOnDone wires the completion hook on the process-global workflow manager so a finished background run reaches the CLI/TUI agent (parity with SetBackgroundOnExit / SubAgentManager.SetOnExit). Pass nil to clear.
func SetDefaultWorkflowOnEvent ¶
func SetDefaultWorkflowOnEvent(fn func(WorkflowEvent))
SetDefaultWorkflowOnEvent wires the live-progress hook on the process-global workflow manager so the CLI/TUI can show a running-workflow panel. Pass nil to clear.
func SetMCPRegistry ¶
SetMCPRegistry installs the active registry for this session. Passing nil clears it (used by defer in cmd/octo so the next session starts clean). Idempotent.
func SetRestarter ¶
func SetRestarter(f func(reason string))
SetRestarter registers the function the restart_server tool delegates to. Pass nil to disable (the tool then doesn't appear in DefaultToolsFor). Mirrors SetAsker: process-global, set once at server start.
func SetSandbox ¶
SetSandbox enables OS-level command confinement for the terminal tools. Pass nil to disable. cmd/octo calls this when --sandbox is requested.
func SetSkills ¶
SetSkills registers the skills the `skill` tool serves and that DefaultTools uses to decide whether to advertise the tool. cmd/octo calls this at session start. Pass nil (or an empty registry) to disable.
func SetSpawner ¶
func SetSpawner(s Spawner)
SetSpawner registers the function the Agent tool delegates to. Pass nil to disable (the tool then doesn't appear in DefaultTools).
func SetTaskStore ¶
func SetTaskStore(s TaskStore)
SetTaskStore registers the store the task_* tools delegate to. Pass nil to disable; the three tools then drop out of DefaultTools.
func SetToolSearchConfig ¶
func SetToolSearchConfig(c ToolSearchConfig)
SetToolSearchConfig installs the active tool_search configuration. Fields left at their zero value fall back to the documented defaults so a partial config block behaves sensibly.
func SetWakerSupported ¶
func SetWakerSupported(v bool)
SetWakerSupported toggles whether schedule_wakeup is advertised. Pass true from the TUI and server entry points; leave it false (the default) for the headless one-shot.
func ShellEnvNote ¶
func ShellEnvNote() string
ShellEnvNote returns platform-shell guidance for the session environment context, or "" on platforms where the default POSIX assumptions hold.
func SubAgentEventSink ¶
func SubAgentEventSink(ctx context.Context) func(SubAgentEvent)
SubAgentEventSink returns the sink stamped by WithSubAgentEventSink, or nil when none is set (headless, tests, or a manager with no onEvent hook).
func ToolCallTarget ¶
func ToolCallTarget(name string, input map[string]any) (realName string, realInput map[string]any, ok bool)
ToolCallTarget unwraps an mcp_call bridge invocation into the real MCP tool name and its arguments, so permission checks and hooks key on the real tool rather than the "mcp_call" wrapper. ok is false when name isn't an mcp_call (or carries no inner tool name), in which case the caller uses name/input unchanged.
func ToolchainNote ¶
func ToolchainNote() string
ToolchainNote renders the detected/missing toolchain as a session-context block, pairing with ShellEnvNote (which says how to install on this platform) so the model knows both what is missing and how to add it. Returns "" only in the degenerate case where nothing was probed.
func WithAsker ¶
WithAsker stamps a turn-scoped asker that takes precedence over the process-global one for the duration of this turn.
func WithBackgroundManager ¶
func WithBackgroundManager(ctx context.Context, mgr *BackgroundManager) context.Context
WithBackgroundManager returns ctx carrying mgr as the manager the terminal tools dispatch to for this turn.
func WithChannelSender ¶
func WithChannelSender(ctx context.Context, s ChannelFileSender) context.Context
WithChannelSender stamps the file sender for the duration of an IM turn.
func WithSubAgentEventSink ¶
func WithSubAgentEventSink(ctx context.Context, sink func(SubAgentEvent)) context.Context
WithSubAgentEventSink returns a context carrying sink, which the execution layer (the agentSpawner) pulls out to forward a child's runtime events. The SubAgentManager stamps this in before calling Spawn/Continue, so the Spawner interface itself stays unchanged and the non-TUI path (no sink) emits nothing.
func WithSubAgentManager ¶
func WithSubAgentManager(ctx context.Context, mgr *SubAgentManager) context.Context
WithSubAgentManager returns ctx carrying mgr for the sub-agent tools to find.
func WithSubAgentMarker ¶
WithSubAgentMarker stamps ctx so descendants are detectable as sub-agent work. The Spawner implementation calls this when invoking the child loop.
func WithTaskStore ¶
WithTaskStore returns ctx carrying store for the task_* tools to find.
func WithWorkflowManager ¶
func WithWorkflowManager(ctx context.Context, mgr *WorkflowManager) context.Context
WithWorkflowManager stamps ctx with the per-session workflow manager the workflow tools dispatch to (web/IM). CLI/TUI leave it unset and fall back to defaultWorkflowMgr.
func WithWorkingDir ¶
WithWorkingDir returns a context that roots terminal commands at dir. An empty dir is a no-op (returns ctx unchanged).
func WorkingDir ¶
WorkingDir returns the working directory stamped into ctx, or "" if none.
Types ¶
type AgentKillTool ¶
type AgentKillTool struct{}
AgentKillTool terminates an async sub-agent.
func (AgentKillTool) Definition ¶
func (AgentKillTool) Definition() agent.ToolDefinition
type AgentSendTool ¶
type AgentSendTool struct{}
AgentSendTool delivers a follow-up message to an existing sub-agent.
func (AgentSendTool) Definition ¶
func (AgentSendTool) Definition() agent.ToolDefinition
type AgentStatusTool ¶
type AgentStatusTool struct{}
AgentStatusTool reports one sub-agent's state or lists the running set.
func (AgentStatusTool) Definition ¶
func (AgentStatusTool) Definition() agent.ToolDefinition
type AgentTool ¶
type AgentTool struct{}
AgentTool is the unified sub-agent tool. It replaces the previous explore_agent / plan_agent / general_agent / code_review_agent split with a single tool controlled by parameters.
Parameters:
- description: short label for UI/logging
- prompt: the task (self-contained — the child can't see this conversation)
- subagent_type: optional agent type (explore, plan, general, code-review). Omit to fork yourself — the child inherits your full conversation context.
- run_in_background: when true the agent runs async and you are notified on completion. When false (default) it blocks and returns the result.
- model: optional model override
- tools: optional tool-name allowlist for the child
The tool is advertised only when a SubAgentManager is registered.
func (AgentTool) Definition ¶
func (AgentTool) Definition() agent.ToolDefinition
type AskRequest ¶
type AskRequest struct {
// Question is the prompt shown to the user, complete with punctuation.
Question string
// Options are the mutually-exclusive (or, when MultiSelect, jointly
// selectable) labels. Empty means "free text answer" (the asker prompts
// for an open-ended response with no list).
Options []string
// MultiSelect lets the user pick more than one option. The asker is
// responsible for parsing a comma-separated selection and returning all
// chosen labels.
MultiSelect bool
// Header is an optional short tag (≤12 chars) shown in the prompt UI.
// Helpful when several questions share visual real estate.
Header string
}
AskRequest is the structured question, parsed from the tool input.
type AskResponse ¶
type AskResponse struct {
// Choices are the labels the user selected from Options. Empty when
// the user picked "Other" (then Custom carries their free text) or
// when the question had no Options (free text only).
Choices []string
// Custom is the free-text answer for "Other" picks or option-less
// questions. Empty for plain selections.
Custom string
// Cancelled reports user dismissal. The tool surfaces this to the LLM
// as a non-error result so the model can decide what to do next.
Cancelled bool
}
AskResponse is what the user provided. Cancelled is true when the user dismissed the prompt (Ctrl-C, empty enter on a forced-pick, etc.); in that case Choices and Custom are empty.
type AskUserQuestionTool ¶
type AskUserQuestionTool struct{}
AskUserQuestionTool lets the model ask the user a single structured clarifying question. The point isn't to chat with the user — it's to resolve a branch where the model genuinely doesn't have enough information to pick a default and asking via free-form prose would produce a sloppy, hard-to-parse answer.
The declared schema mirrors Claude Code's AskUserQuestion natively — a `questions` array of {question, header, multiSelect, options:[{label, description}]} — so the shape the model was trained to emit and the shape we advertise are the same. That alignment is the point: a flat snake_case schema drifted from the model's prior, and it would intermittently revert to the CC shape, fail validation, and the prompt would never reach the user (the "web modal didn't pop up" bug). We still cap at ONE question per call (maxItems:1, per the M11-prep design — simpler REPL/web/IM UX; the model fires multiple calls when it needs multiple), and Execute still tolerates the old flat shape as a fallback. See normalizeAskInput.
func (AskUserQuestionTool) Definition ¶
func (AskUserQuestionTool) Definition() agent.ToolDefinition
type Asker ¶
type Asker interface {
Ask(ctx context.Context, q AskRequest) (AskResponse, error)
}
Asker presents a structured question to the user and waits for their answer. Implementations live in cmd/octo (the REPL prompt reader); tests substitute fakes. Like Spawner, this interface stays free of the stdin/terminal mechanics so the tools package doesn't depend on cmd/octo.
type BackgroundManager ¶
type BackgroundManager struct {
// contains filtered or unexported fields
}
BackgroundManager owns the set of detached background processes for a session. Methods are safe for concurrent use.
func DefaultBackgroundManager ¶
func DefaultBackgroundManager() *BackgroundManager
DefaultBackgroundManager returns the process-wide background manager used by the CLI/TUI and by tools when no per-session manager is injected.
func NewBackgroundManager ¶
func NewBackgroundManager() *BackgroundManager
NewBackgroundManager returns an empty manager.
func SessionBackgroundManager ¶
func SessionBackgroundManager(id string) *BackgroundManager
SessionBackgroundManager returns the per-session manager for id, creating and registering it on first use.
func (*BackgroundManager) BeginSync ¶
func (m *BackgroundManager) BeginSync() *SyncSession
BeginSync registers a new SyncSession for the current synchronous terminal command. Call EndSync (via defer) when the command finishes or is promoted. At most one sync terminal runs per manager at a time — the agent loop is serial — so there is only one slot.
func (*BackgroundManager) Command ¶
func (m *BackgroundManager) Command(id string) (string, bool)
Command returns the original command string for a background process id, or ("", false) if the id is unknown or has been removed.
func (*BackgroundManager) EndSync ¶
func (m *BackgroundManager) EndSync()
EndSync clears the current SyncSession.
func (*BackgroundManager) FireExitHook ¶
func (m *BackgroundManager) FireExitHook(e BgExit)
FireExitHook calls the onExit hook directly with e, as if a tracked process had just exited. No-op when no hook is registered. Used in tests to trigger the notification path without starting a real shell process.
func (*BackgroundManager) HasSync ¶
func (m *BackgroundManager) HasSync() bool
HasSync reports whether a sync terminal is currently polling.
func (*BackgroundManager) Kill ¶
func (m *BackgroundManager) Kill(id string) bool
Kill terminates the process for id with SIGKILL. Returns false when id is unknown.
func (*BackgroundManager) KillAll ¶
func (m *BackgroundManager) KillAll()
KillAll terminates every tracked process. Called on session shutdown so no background command is orphaned.
func (*BackgroundManager) KillWithSignal ¶
func (m *BackgroundManager) KillWithSignal(id string, sigName string) bool
KillWithSignal terminates the process for id with the named signal. Supported signals: SIGKILL, SIGTERM, SIGINT. Returns false when id is unknown.
func (*BackgroundManager) List ¶
func (m *BackgroundManager) List() []BgInfo
List returns every visible tracked process — running AND exited-but-not-yet reaped — oldest first, so the TUI panel and completion summaries can see what has finished. Invisible (sync, pre-timeout) processes are excluded.
func (*BackgroundManager) ListRunning ¶
func (m *BackgroundManager) ListRunning() []BgInfo
ListRunning returns the visible processes that haven't exited yet, oldest first. Processes started invisibly (e.g. sync mode) are excluded until promoted.
func (*BackgroundManager) Mode ¶
func (m *BackgroundManager) Mode(id string) (BackgroundMode, bool)
Mode returns the background mode for id, or ("", false) if unknown.
func (*BackgroundManager) Promote ¶
func (m *BackgroundManager) Promote(id string) bool
Promote makes a background process visible in ListRunning. Used when a sync-started process times out and becomes a true background task.
func (*BackgroundManager) PromoteSync ¶
func (m *BackgroundManager) PromoteSync()
PromoteSync signals the current sync terminal to promote itself to a visible background process. No-op if no sync terminal is running.
func (*BackgroundManager) Read ¶
func (m *BackgroundManager) Read(id string) (output, status string, found bool, blocked bool, mode BackgroundMode)
Read returns output produced since the last call for id, plus a status string. found is false when id is unknown. blocked is true when the caller has polled too many times without new output while the process is still running — this forces the LLM to stop polling. mode reports the process's background mode (empty when not found).
func (*BackgroundManager) Remove ¶
func (m *BackgroundManager) Remove(id string)
Remove drops a process from the tracking map, releasing its retained output buffer. Used by the synchronous terminal path to reap a hidden command once it has exited and its output has been returned to the caller — otherwise every synchronous command would leak a bgProcess (up to maxBgOutputBytes each) for the life of the session. Visible background tasks are NOT reaped this way: their output stays readable via terminal_output after they exit.
func (*BackgroundManager) SetOnExit ¶
func (m *BackgroundManager) SetOnExit(fn func(BgExit))
SetOnExit registers a completion hook fired once per process when it exits, carrying its final status and any output not yet read. Pass nil to clear. The hook runs on the process's waiter goroutine (not under the manager lock), so it may call back into the manager (e.g. Read) without deadlocking. The CLI uses it to push a "background finished" notice into the conversation + UI; the default (nil) keeps the original poll-only behaviour.
func (*BackgroundManager) Start ¶
func (m *BackgroundManager) Start(command string, mode BackgroundMode, opts ...StartOption) (string, error)
Start launches command detached (via `sh -c`), with no timeout, and returns its background id. Output streams into a capped buffer; the process is killed if its context is cancelled (Kill / KillAll). The mode argument determines whether the process is an async one-shot task or an interactive service/REPL.
func (*BackgroundManager) Tail ¶
func (m *BackgroundManager) Tail(id string, lines int) (output, status string, found bool, blocked bool, mode BackgroundMode)
Tail returns a non-destructive snapshot of the last `lines` lines of a process's output (lines <= 0 = all retained), plus its status. found is false when id is unknown. Unlike Read it does not advance the cursor — it's for on-demand progress peeks (the terminal_output tool). repeated calls return the same bytes, but repeated empty snapshots of a running process are counted as polling; once blocked is true, callers should tell the model to stop. mode reports the process's background mode (empty when not found).
func (*BackgroundManager) WriteStdin ¶
func (m *BackgroundManager) WriteStdin(id string, input string) error
WriteStdin sends text to the stdin of a running background process. Returns an error if the process is unknown or has already exited.
func (*BackgroundManager) WriteStdinAndClose ¶
func (m *BackgroundManager) WriteStdinAndClose(id string, input string) error
WriteStdinAndClose sends text to the process's stdin and then closes it, signalling EOF. Use for one-shot initial stdin (e.g. piping a PR body through --body-file - instead of embedding it in the shell command where backticks and quotes would be interpreted by the shell).
type BackgroundMode ¶
type BackgroundMode string
BackgroundMode distinguishes the two kinds of tracked background processes the terminal tool can launch. Async processes run a one-shot task and notify the model on completion; interactive processes are long-running services or REPLs that the model may observe or feed via terminal_output / terminal_input.
const ( // BgModeAsync is for one-shot tasks (tests, builds, installs). The model // must not poll them; completion is pushed automatically. BgModeAsync BackgroundMode = "async" // BgModeInteractive is for long-running services and REPLs (rails c, // octo serve). The model may use terminal_output and terminal_input. BgModeInteractive BackgroundMode = "interactive" )
type BgExit ¶
type BgExit struct {
ID string
Command string
Status string // "exited: 0" / "exited: <err>" — same shape readNew returns
NewOutput string
}
BgExit is delivered to a BackgroundManager's onExit hook when a detached process finishes. NewOutput is whatever hadn't been consumed by Read yet at exit (so a push notification and a later terminal_output poll don't double-report the same bytes — the readNew cursor advances either way).
type BgInfo ¶
type BgInfo struct {
ID string
Command string
Mode BackgroundMode
Start time.Time
End time.Time // zero value while running; set when the process exits
Status string // "running" / "exited: …"
}
BgInfo is a snapshot of a tracked background process — used by the TUI's live "background (N running)" panel and by background-completion summaries.
func RunningBackground ¶
func RunningBackground() []BgInfo
RunningBackground lists the still-running processes on the default manager, for the TUI's live "background (N running)" panel.
type BrowserTool ¶
type BrowserTool struct{}
BrowserTool drives a real Chrome over CDP: navigate, click, type, wait, capture downloads, etc. One tool, action-multiplexed, like computer-use.
func (BrowserTool) Definition ¶
func (BrowserTool) Definition() agent.ToolDefinition
type ChannelFileSender ¶
type ChannelFileSender interface {
// SendFile uploads path to the bound chat under the display name name
// (empty name falls back to the file's basename). The adapter picks the
// wire type (image / video / file) from the extension.
SendFile(path, name string) error
}
ChannelFileSender delivers a local file to the IM chat the current turn is bound to. It is implemented by the server (wrapping the platform adapter's SendFile) and stamped into the turn ctx via WithChannelSender — the same ctx-scoping pattern as Asker, since only IM turns have a chat to push to.
type DefaultRegistry ¶
type DefaultRegistry struct {
// contains filtered or unexported fields
}
DefaultRegistry is the agent.ToolExecutor used when `octo --tools` is enabled. It dispatches each tool call by name to the matching entry in allTools, returning a clean error for unknown names.
When tracker is non-nil it enforces read-before-write: write_file / edit_file calls to an existing file are refused unless the file was read (and is unchanged) this session. The zero value (DefaultRegistry{}) has a nil tracker and so enforces nothing — preserved for tests and callers that don't want the discipline. Use NewDefaultRegistry for the enforced variant.
func NewDefaultRegistry ¶
func NewDefaultRegistry() DefaultRegistry
NewDefaultRegistry returns a registry with read-before-write enforcement backed by a fresh per-session ReadTracker.
type EditFileTool ¶
type EditFileTool struct{}
EditFileTool replaces an exact substring inside an existing file. The match must be unique unless replace_all is true. Refuses to create the file if it doesn't exist — use write_file for that.
func (EditFileTool) Definition ¶
func (EditFileTool) Definition() agent.ToolDefinition
type GlobTool ¶
type GlobTool struct{}
GlobTool returns paths matching a glob pattern, sorted by modification time descending so the most recently touched files appear first.
File enumeration is delegated to ripgrep (`rg --files`), which respects .gitignore / .ignore and prunes ignored subtrees efficiently — the same engine grep uses, so the two tools agree on what's "in" the project. The glob pattern itself is then matched in-process against each path so the semantics below are exactly preserved regardless of ripgrep's own glob rules.
Supports `**` for "any directory depth" (e.g. `src/**/*.go`). Other segments use the standard path.Match semantics: `*` matches any run of non-separator characters, `?` matches a single character, character classes via `[…]`.
func (GlobTool) Definition ¶
func (GlobTool) Definition() agent.ToolDefinition
type GrepTool ¶
type GrepTool struct{}
GrepTool is a thin wrapper over `ripgrep` (`rg`). It accepts a regex pattern and one of three output modes: full content lines, file paths only, or per-file match counts.
We deliberately depend on a working `rg` binary rather than reimplement in pure Go. Ripgrep handles .gitignore, binary detection, and parallel I/O — duplicating those is months of work, and `rg` is a near-universal install on developer machines.
func (GrepTool) Definition ¶
func (GrepTool) Definition() agent.ToolDefinition
type KillShellTool ¶
type KillShellTool struct {
// contains filtered or unexported fields
}
KillShellTool terminates a background process started by TerminalTool with run_in_background:"async" or "interactive" and returns its final output — the counterpart to terminal_output, which only reads. Split out from terminal_output's old kill:true flag so "stop this process" is a first-class, obvious action.
func (KillShellTool) Definition ¶
func (KillShellTool) Definition() agent.ToolDefinition
Definition describes the required "id".
func (KillShellTool) Execute ¶
func (t KillShellTool) Execute(ctx context.Context, _ string, input map[string]any) (agent.ToolResult, error)
Execute kills the process, then returns its final remaining output. An unknown id is an error (Kill reports it); an already-exited process is a no-op kill and still returns its last output.
type ReadFileTool ¶
type ReadFileTool struct{}
ReadFileTool reads a UTF-8 text file, optionally a window of it, and returns its content with each line prefixed by its 1-based line number (the `cat -n` format the LLM is already familiar with).
For image files (.png, .jpg, .jpeg, .gif, .webp, .bmp, .tiff, .heic, .ico) the tool reads the raw bytes and returns them as an image content block for multimodal model consumption alongside a short text description.
func (ReadFileTool) Definition ¶
func (ReadFileTool) Definition() agent.ToolDefinition
type ReadTracker ¶
type ReadTracker struct {
// contains filtered or unexported fields
}
ReadTracker enforces the read-before-write discipline within a session: the agent may only write to (or edit) a file it has already read, and only while its on-disk mtime still matches what was seen at read time. This stops the LLM from blindly overwriting a file it half-remembers, or clobbering an edit made out-of-band since it last looked.
State is per-session (one tracker per Registry). All methods are safe for concurrent use, though the agent loop dispatches tools sequentially.
func (*ReadTracker) CheckWritable ¶
func (rt *ReadTracker) CheckWritable(absPath string) error
CheckWritable reports whether absPath may be written/edited right now.
Rules:
- A path that does NOT exist on disk is always writable (creating a new file needs no prior read — you can't read what isn't there).
- An existing path must have been read this session, else the LLM is writing blind → refuse.
- An existing, previously-read path whose mtime advanced since the read was changed out-of-band → refuse and force a re-read.
The returned error text mirrors Claude Code's wording so the LLM reacts the way it's been trained to (re-read, then retry).
func (*ReadTracker) RecordRead ¶
func (rt *ReadTracker) RecordRead(absPath string)
RecordRead notes that absPath was read (or written) and stamps it with the file's current mtime. A failed stat is silently ignored — if we can't tell the file's mtime there's nothing to enforce against later, and recording a zero time would wrongly trip the "modified since read" guard.
type RestartServerTool ¶
type RestartServerTool struct{}
RestartServerTool lets the model restart the octo server process — after a binary upgrade or a config change that is only read at startup. The restart is scheduled, not immediate: the server drains in-flight turns (including the one executing this tool), so the model's final reply still reaches the user before the process exits and the supervisor respawns it.
func (RestartServerTool) Definition ¶
func (RestartServerTool) Definition() agent.ToolDefinition
type ScheduleWakeupTool ¶
type ScheduleWakeupTool struct{}
ScheduleWakeupTool lets the model schedule its own next turn — the mechanism behind the loop skill. The model calls it at the end of a turn to come back later (dynamic, self-paced mode) or to keep a fixed cadence (repeat = interval mode); NOT calling it ends the loop. It only works inside a live session that can be re-entered — the interactive TUI or a server-managed web/IM session — so it is withheld from the headless one-shot (wakerEnabled gates it in DefaultToolsFor) and the per-session Waker is injected into the turn ctx.
func (ScheduleWakeupTool) Definition ¶
func (ScheduleWakeupTool) Definition() agent.ToolDefinition
type SendFileTool ¶
type SendFileTool struct{}
SendFileTool delivers a local file to the user through the active IM channel (WeChat, Telegram, Discord, …). It is the only way the model can put a file in front of an IM user: a chat reply carries text, so a generated image, chart, document, or any other artifact must be pushed as a file.
The tool lives in allTools so DefaultRegistry can dispatch it, but it is withheld from the advertised tool list everywhere except IM turns — see DefaultToolsFor, which always skips it, and runChannelTurns, which appends its definition and injects the sender. On a non-IM turn channelSenderFrom returns nil and the tool reports a clean, model-readable error.
func (SendFileTool) Definition ¶
func (SendFileTool) Definition() agent.ToolDefinition
type ShowArtifactTool ¶
type ShowArtifactTool struct{}
ShowArtifactTool surfaces an existing file to the user as a previewable artifact. write_file/edit_file payloads already feed the web Artifacts panel automatically; this tool covers files produced any other way — build scripts (e.g. web-artifacts-builder's bundle.html), generators, downloads. Its tool_use block also lands in the session transcript, which is what authorizes the artifact endpoint to serve the path.
func (ShowArtifactTool) Definition ¶
func (ShowArtifactTool) Definition() agent.ToolDefinition
type SkillTool ¶
type SkillTool struct{}
SkillTool loads a skill's full SKILL.md body on demand. The model calls it after spotting a matching skill in the system-prompt "Available skills" manifest; the body returns as a tool_result, landing in history rather than the frozen system prefix. The zero value reads from the package-level registry set by SetSkills.
func (SkillTool) Definition ¶
func (SkillTool) Definition() agent.ToolDefinition
type SpawnRequest ¶
type SpawnRequest struct {
// Description is a short human-readable label for logging / progress UI.
Description string
// AgentType is the subagent_type the caller selected (e.g. "explore",
// "general"), shown alongside Description in the live panels. Empty for an
// untyped fork.
AgentType string
// Prompt is the sub-agent's user message. It carries the task.
Prompt string
// ForkConversation, when true, seeds the child's history with the parent's
// conversation so far (a true fork), rather than starting fresh. The
// spawner trims the in-flight tool_use turn that spawned the child so the
// copied history ends cleanly. Set by the sub_agent tool when no
// subagent_type is given; workflow agents leave it false.
ForkConversation bool
// Tools, when non-empty, restricts the child to this subset of the
// parent's tool list. nil/empty means "inherit all of parent's tools
// except Agent itself" — the spawn implementation handles the
// recursion filter, not the tool.
Tools []string
// DisallowedTools is subtracted from the child's inherited tool set
// (frontmatter `disallowed_tools`). Applied on top of Tools/ReadOnly.
DisallowedTools []string
// Model, when non-empty, overrides the parent's model for this child.
Model string
// SystemSuffix, when non-empty, is appended to the child's system prompt
// (after the shared parent System) to give a preset agent its persona.
SystemSuffix string
// ReadOnly, when true, strips the mutating tools (write_file, edit_file)
// from the child's toolbelt on top of the always-dropped Agent tool.
ReadOnly bool
// LeanContext, when true, runs the child on the parent's lite model and
// seeds it with the parent's lean system prompt (skills manifest + memory
// dropped). Set for cheap read-only presets (explore/plan). Falls back to
// the parent's model/system when no lite model / lean system is configured.
LeanContext bool
// Schema, when non-empty, is a JSON Schema (as a JSON string) the child's
// reply must satisfy. The spawner instructs the child to emit only matching
// JSON, strips any markdown fences, and re-prompts once if the reply isn't
// valid JSON. The returned Reply is the cleaned JSON text.
Schema string
// Isolation, when "worktree", runs the child in a fresh git worktree so its
// file/terminal changes don't touch the main checkout. Changes are left on a
// dedicated branch for the caller to review; an unchanged run is cleaned up.
// Requires a git repository.
Isolation string
// SessionDir, when non-empty, tells the spawner to persist the sub-agent's
// full conversation transcript to <SessionDir>/<agent-id>.jsonl so it can
// be inspected after a failure.
SessionDir string
}
SpawnRequest is the LLM-supplied Agent tool payload, parsed.
type SpawnResult ¶
type SpawnResult struct {
// AgentID addresses the sub-agent for a later Continue. Spawn returns
// a non-empty id when the implementation keeps the child alive.
AgentID string
Reply string
InputTokens int
OutputTokens int
// Turns is the number of provider round-trips the sub-agent executed.
Turns int
// StopReason carries why the sub-agent stopped. Empty for normal
// completion; "max_turns" when the loop budget was exhausted.
StopReason string
}
SpawnResult is the sub-agent's final output, plus its token usage so the parent can roll it into the session total.
type Spawner ¶
type Spawner interface {
Spawn(ctx context.Context, req SpawnRequest) (SpawnResult, error)
// Continue re-runs a still-alive sub-agent (one a prior Spawn kept in the
// implementation's live registry) with a new message and returns its next
// reply. The sub-agent's prior history carries over. An unknown or
// already-evicted agentID returns an error whose text tells the model to
// launch a fresh sub-agent instead. Concurrent Continue calls on the SAME
// agentID must be serialized by the implementation — a sub-agent's history
// can't take two interleaved turns.
Continue(ctx context.Context, agentID, message string) (SpawnResult, error)
}
Spawner runs one sub-agent task to completion and returns the final reply. Implementations live outside the tools package (cmd/octo wires the real one, tests substitute fakes) so this package stays free of agent-construction concerns. Multiple Spawner calls may run concurrently from one parent tool_use batch (see parallel dispatch in agent.dispatchTools), so the implementation MUST be safe for concurrent invocation on distinct requests.
func ActiveSpawner ¶
func ActiveSpawner() Spawner
ActiveSpawner returns the currently registered Spawner, or nil if none.
type StartOption ¶
type StartOption func(*bgProcess)
StartOption is a functional option for BackgroundManager.Start.
func WithOnLine ¶
func WithOnLine(fn func(string)) StartOption
WithOnLine registers a callback that receives each line of output as it is produced. Used by the synchronous path to forward output to the progress callback in real time.
func WithVisible ¶
func WithVisible(v bool) StartOption
WithVisible sets the process visibility in ListRunning. Sync-started processes start hidden (visible=false) and are promoted to visible=true when they time out.
type SubAgentEvent ¶
type SubAgentEvent struct {
AgentID string // manager handle, e.g. "agent_1"
Description string // human-readable label from sub_agent
AgentType string // subagent_type, e.g. "explore" (empty for an untyped fork)
// Kind is one of:
// "started" — the sub-agent began a task (or a Continue round)
// "tool" — it dispatched a tool (ToolName set)
// "tool_error" — a tool returned an error (ToolName set)
// "done" — the round finished (sync return or async completion);
// live panels drop the entry on this
Kind string
ToolName string
ToolInput map[string]any // optional: the tool's input arguments for UI display
}
SubAgentEvent is a runtime progress event from a sub-agent, forwarded to a SubAgentManager's onEvent hook for live display. It is distinct from SubAgentNotification, which is the one-shot completion record: events stream while the sub-agent works, the notification fires once when it finishes.
Only tool-level activity is carried — not per-token text — so a live panel can show each sub-agent's tool-call chain without the event volume of N concurrent sub-agents streaming their prose.
type SubAgentInfo ¶
SubAgentInfo is a snapshot of a sub-agent for listing.
type SubAgentManager ¶
type SubAgentManager struct {
// contains filtered or unexported fields
}
SubAgentManager owns the set of async sub-agents for a session. Methods are safe for concurrent use.
func NewSubAgentManager ¶
func NewSubAgentManager(spawner Spawner) *SubAgentManager
NewSubAgentManager returns an empty manager.
func SessionSubAgentManager ¶
func SessionSubAgentManager(id string, mkSpawner func() Spawner) *SubAgentManager
SessionSubAgentManager returns the per-session sub-agent manager for id, creating it via mkSpawner on first use. Subsequent calls reuse the existing manager (and its spawner), so mkSpawner is only invoked once per session. If mkSpawner is nil and no manager exists for id, nil is returned — callers that only need to signal an already-running sync sub-agent (e.g. a WebSocket promote message) can use this safely without constructing a manager.
func (*SubAgentManager) BeginSync ¶
func (m *SubAgentManager) BeginSync() *SyncSession
BeginSync registers a new SyncSession for the current synchronous sub-agent run. Call EndSync with the returned session (via defer) when the run finishes or is promoted. At most one synchronous sub-agent runs per manager at a time — the agent loop is serial — so there is only one slot.
func (*SubAgentManager) ContinueSync ¶
func (m *SubAgentManager) ContinueSync(ctx context.Context, agentID, message string) (SpawnResult, error)
ContinueSync re-runs a still-alive sub-agent (addressed by its spawner-side id) with a new message and blocks until it replies. The synchronous counterpart of Send.
func (*SubAgentManager) EndSync ¶
func (m *SubAgentManager) EndSync(s *SyncSession)
EndSync clears the current SyncSession only if it is still the one returned by BeginSync. This protects against a deferred EndSync racing with a later synchronous run in tests or if the manager is ever used concurrently.
func (*SubAgentManager) HasSync ¶
func (m *SubAgentManager) HasSync() bool
HasSync reports whether a synchronous sub-agent is currently running.
func (*SubAgentManager) Kill ¶
func (m *SubAgentManager) Kill(id string) bool
Kill terminates the sub-agent for id. Returns false when id is unknown.
func (*SubAgentManager) KillAll ¶
func (m *SubAgentManager) KillAll()
KillAll terminates every tracked sub-agent. Called on session shutdown.
func (*SubAgentManager) ListRunning ¶
func (m *SubAgentManager) ListRunning() []SubAgentInfo
ListRunning returns the agents that haven't exited yet, oldest first.
func (*SubAgentManager) PromoteSync ¶
func (m *SubAgentManager) PromoteSync()
PromoteSync signals the current synchronous sub-agent to promote itself to a background agent. No-op if no synchronous sub-agent is running.
func (*SubAgentManager) Read ¶
func (m *SubAgentManager) Read(id string) (result, status string, found bool)
Read returns the latest result and status for an agent. found is false when id is unknown.
func (*SubAgentManager) RunSync ¶
func (m *SubAgentManager) RunSync(ctx context.Context, req SpawnRequest) (SpawnResult, error)
RunSync spawns a sub-agent and blocks until it completes, returning its reply. Used by the synchronous sub_agent path; the spawner stamps the sub-agent marker and keeps the child resumable for a later ContinueSync. When an onEvent hook is registered the child's tool-level activity is streamed the same way async sub-agents are, so live panels work for both sync and async modes.
A synchronous run can be manually promoted to a background agent while it is running (TUI Ctrl+B, Web "Background" button). The same agent_N id is kept, the goroutine continues, and the result arrives via the onExit hook.
func (*SubAgentManager) Send ¶
func (m *SubAgentManager) Send(agentID, message string) error
Send delivers a message to an existing sub-agent asynchronously. Returns immediately; reply arrives via onExit.
func (*SubAgentManager) SetOnEvent ¶
func (m *SubAgentManager) SetOnEvent(fn func(SubAgentEvent))
SetOnEvent registers a runtime-event hook fired as a sub-agent works (started + per-tool activity), for live display. Pass nil to clear. Distinct from onExit, which fires once on completion.
func (*SubAgentManager) SetOnExit ¶
func (m *SubAgentManager) SetOnExit(fn func(SubAgentNotification))
SetOnExit registers a completion hook fired once per sub-agent when it finishes processing. Pass nil to clear.
func (*SubAgentManager) SetSynchronous ¶
func (m *SubAgentManager) SetSynchronous(v bool)
SetSynchronous selects the sub_agent dispatch model. The default (false) is the interactive async path: Start returns immediately and the reply arrives via onExit, which the REPL/TUI re-injects as a follow-up turn. A request/response transport (HTTP server, IM bridge) has no follow-up-turn channel, so it sets this true: sub_agent then blocks the turn on RunSync and returns the child's reply directly as the tool_result. Set once at startup, before any turn runs.
func (*SubAgentManager) Start ¶
func (m *SubAgentManager) Start(req SpawnRequest) (string, error)
Start creates a new sub-agent and runs it asynchronously. Returns the agent_id immediately; result arrives via onExit.
func (*SubAgentManager) Synchronous ¶
func (m *SubAgentManager) Synchronous() bool
Synchronous reports whether the manager runs sub-agents inline (see SetSynchronous).
type SubAgentNotification ¶
type SubAgentNotification struct {
AgentID string // e.g. "agent_1"
Description string // human-readable label from sub_agent
Kind string // "spawn_done" | "message_reply"
Result string // final reply text
InputTokens int
OutputTokens int
// StopReason is empty for normal completion, "max_turns" when the sub-agent
// hit its loop budget. The parent uses this to decide whether to continue.
StopReason string
}
SubAgentNotification is delivered to the onExit hook when a sub-agent finishes a task (spawn) or replies to a message (continue).
type SyncSession ¶
type SyncSession struct {
// contains filtered or unexported fields
}
SyncSession is the promote handle for one in-flight synchronous terminal command. Closing the channel (via Signal) unblocks the polling loop so the process is promoted to a visible background task before the timer fires. safe for concurrent calls (sync.Once).
func (*SyncSession) C ¶
func (s *SyncSession) C() <-chan struct{}
C returns the receive-only channel the terminal polling loop selects on.
func (*SyncSession) Signal ¶
func (s *SyncSession) Signal()
Signal closes the channel, unblocking any select waiting on C(). Safe to call multiple times — only the first call takes effect.
type TaskCreateTool ¶
type TaskCreateTool struct{}
TaskCreateTool adds a new pending task to the session's task list.
Tool name: task_create.
func (TaskCreateTool) Definition ¶
func (TaskCreateTool) Definition() agent.ToolDefinition
type TaskListTool ¶
type TaskListTool struct{}
TaskListTool returns the current task list, grouped by status.
Tool name: task_list.
func (TaskListTool) Definition ¶
func (TaskListTool) Definition() agent.ToolDefinition
type TaskStore ¶
type TaskStore interface {
Create(subject, description, activeForm string) (int, error)
Update(id int, u tasks.UpdateField) (tasks.Task, error)
List() []tasks.Task
Get(id int) (tasks.Task, bool)
Summary() string
}
TaskStore is the surface the task_* tools use to talk to the underlying store. internal/tasks.Store satisfies this directly; tests can substitute fakes without dragging in the tasks package.
func ActiveTaskStore ¶
func ActiveTaskStore() TaskStore
ActiveTaskStore returns the currently registered store, or nil. Used by cmd/octo's /tasks REPL command (and the post-tool summary line) without going through a LLM-driven tool call.
type TaskUpdateTool ¶
type TaskUpdateTool struct{}
TaskUpdateTool mutates an existing task. Most commonly the model uses it to move tasks through the pending → in_progress → completed lifecycle.
Tool name: task_update.
func (TaskUpdateTool) Definition ¶
func (TaskUpdateTool) Definition() agent.ToolDefinition
type TerminalInputTool ¶
type TerminalInputTool struct {
// contains filtered or unexported fields
}
TerminalInputTool sends text to the stdin of a running INTERACTIVE background process launched with terminal run_in_background:"interactive". Use to interact with long-running interactive applications (REPLs, configuration wizards, servers that accept commands via stdin).
func (TerminalInputTool) Definition ¶
func (TerminalInputTool) Definition() agent.ToolDefinition
Definition describes the required "id" and "input".
func (TerminalInputTool) Execute ¶
func (t TerminalInputTool) Execute(ctx context.Context, _ string, input map[string]any) (agent.ToolResult, error)
Execute writes input to the process's stdin. Unknown or exited id is an error. Reject async processes: terminal_input is only meaningful for interactive background tasks.
type TerminalOutputTool ¶
type TerminalOutputTool struct {
// contains filtered or unexported fields
}
TerminalOutputTool reads new output (and status) from an INTERACTIVE background process launched with terminal run_in_background:"interactive".
func (TerminalOutputTool) Definition ¶
func (TerminalOutputTool) Definition() agent.ToolDefinition
Definition describes the required "id". Reading is non-destructive; to stop a process use the kill_shell tool.
func (TerminalOutputTool) Execute ¶
func (t TerminalOutputTool) Execute(ctx context.Context, _ string, input map[string]any) (agent.ToolResult, error)
Execute returns a snapshot of the process's last N lines plus a status line. Read-only and non-advancing — it never terminates the process (that's kill_shell) and never moves a read cursor, so it can't "block" and there is no polling incentive.
type TerminalTool ¶
type TerminalTool struct {
// contains filtered or unexported fields
}
TerminalTool is an agent.ToolExecutor that runs shell commands through the system shell (`sh -c` on macOS/Linux, PowerShell on Windows; see shellCommand). Stdout and stderr are combined and returned as the tool result. Non-zero exit codes are reported as extra metadata in the result text rather than as a tool error, so the LLM can see the failure output and adapt.
The LLM-facing tool name is "terminal" — calling it "bash" would imply a hard /bin/bash dependency, but the executor shells out via the platform shell (the model is told which one via the environment context).
mgr, when non-nil, is the BackgroundManager used for run_in_background launches; nil falls back to the process-wide default manager. The field exists so tests can inject an isolated manager.
func (TerminalTool) Definition ¶
func (TerminalTool) Definition() agent.ToolDefinition
Definition returns the agent.ToolDefinition the LLM receives in the tools list. The JSON Schema describes a required "command" string and an optional "run_in_background" enum.
func (TerminalTool) Execute ¶
func (t TerminalTool) Execute(ctx context.Context, name string, input map[string]any) (agent.ToolResult, error)
Execute runs the command and returns combined output. A non-zero exit code is appended to the output as `[exit: <error>]` rather than being surfaced as an error, giving the LLM visibility into what went wrong.
Internally this delegates to ExecuteStream with a nil progress callback so both code paths share the same exec/scanner pipeline — only the streaming behavior changes.
func (TerminalTool) ExecuteStream ¶
func (t TerminalTool) ExecuteStream( ctx context.Context, _ string, input map[string]any, progress func(chunk string), ) (agent.ToolResult, error)
ExecuteStream runs the command and forwards each output line to progress as it arrives, returning the full aggregated stdout+stderr at the end. progress may be nil — in that case the behaviour is identical to Execute.
stdout and stderr are merged into a single stream so the LLM sees them in chronological order (the same way they'd appear in an interactive terminal). Scanner buffer cap is 1 MiB per line — commands that emit a single 10MB- long line will get their final line truncated, but the more usual case of many short lines is unaffected.
Timeout promotion: if the command exceeds TerminalTimeout (120 s) the original process continues running in the background (no restart). The caller receives the output produced so far plus a background id and a clear instruction to wait for the completion notification.
type ToolSearchConfig ¶
type ToolSearchConfig struct {
Mode ToolSearchMode
ThresholdPct int // auto-mode activation threshold, percent of context window
SearchLimit int // default number of hits tool_search returns
MaxSearchLimit int // upper bound on the caller-supplied limit
}
ToolSearchConfig is the tools-package view of the user's tool_search config. cmd/octo maps the ~/.octo/config.yml block onto this and installs it via SetToolSearchConfig, mirroring SetSandbox / SetMCPRegistry.
type ToolSearchMode ¶
type ToolSearchMode int
ToolSearchMode selects when the bridge replaces full MCP schema upload.
const ( // ToolSearchAuto activates the bridge only when deferred MCP schemas would // occupy at least ThresholdPct% of the model's context window. ToolSearchAuto ToolSearchMode = iota // ToolSearchOn activates the bridge whenever any MCP tool is present. ToolSearchOn // ToolSearchOff never activates the bridge — MCP schemas upload in full. ToolSearchOff )
type Waker ¶
type Waker interface {
// ScheduleWakeup arranges for prompt to run as a fresh user turn in the
// current session after delay. When repeat is true the wakeup re-arms on
// the same cadence (interval mode); otherwise it fires once and the model
// re-arms by calling the tool again (dynamic mode). reason is a short
// human-facing note. Any wakeup already pending for the session is
// replaced — a session has at most one armed wakeup at a time.
ScheduleWakeup(delay time.Duration, prompt, reason string, repeat bool) error
// CancelWakeup stops any pending wakeup for the session — the explicit way
// to end an interval loop (the dynamic mode ends by simply not re-arming).
// No-op when nothing is armed.
CancelWakeup() error
}
Waker schedules a delayed re-entry into the current interactive session: a prompt delivered as a fresh user turn after a delay. It backs the loop skill's two modes — dynamic (the model re-arms each turn) and interval (the wakeup re-arms itself) — and is stamped into the turn ctx per surface. The TUI (cmd/octo) and the HTTP/IM server (internal/server) each implement it differently, the same ctx-scoping the Asker and ChannelFileSender use. A headless one-shot turn stamps none, so schedule_wakeup reports a clean error there rather than pretending to loop in a process that exits after one turn.
type WebFetchTool ¶
type WebFetchTool struct{}
WebFetchTool fetches a URL and returns its body as Markdown. It prefers the Jina AI Reader proxy for JS-rendered pages and clean HTML-to-Markdown conversion, but falls back to a direct HTTP fetch when the proxy fails.
func (WebFetchTool) Definition ¶
func (WebFetchTool) Definition() agent.ToolDefinition
type WebSearchResponse ¶
type WebSearchResponse struct {
Query string `json:"query"`
Results []WebSearchResult `json:"results"`
Count int `json:"count"`
Provider string `json:"provider"`
Error string `json:"error,omitempty"`
}
WebSearchResponse is what gets serialised back to the LLM. Provider records which backend actually produced the results, so the LLM knows whether to trust the corpus (Brave/Google index vs. DDG/Bing HTML scrape).
type WebSearchResult ¶
type WebSearchResult struct {
Title string `json:"title"`
URL string `json:"url"`
Snippet string `json:"snippet"`
}
WebSearchResult is a single normalised search hit. All five backends flatten their wire-format response into this shape so the LLM sees the same contract regardless of which backend produced the result.
type WebSearchTool ¶
type WebSearchTool struct{}
WebSearchTool searches the web. Backend priority (descending):
- Brave Search API (env BRAVE_SEARCH_API_KEY)
- Tavily API (env TAVILY_API_KEY)
- Serper.dev (env SERPER_API_KEY)
- DuckDuckGo HTML (zero key, default)
- Bing HTML (zero key, fallback when DDG returns nothing)
The tool never panics — every backend failure becomes an Error field in the returned response, and the next backend is tried.
func (WebSearchTool) Definition ¶
func (WebSearchTool) Definition() agent.ToolDefinition
type WorkflowEvent ¶
type WorkflowEvent struct {
RunID string
Description string
Kind string
Line string // the progress/log line for Kind=="progress"
Status string // "running" | "done" | "error" for Kind=="done"
}
WorkflowEvent is emitted as a background run progresses, for live display (the web panel). Kind is "started" | "progress" | "done".
type WorkflowKillTool ¶
type WorkflowKillTool struct{}
WorkflowKillTool cancels a running background workflow by id — for a run that has stalled (workflow_status shows a large, growing "last activity" gap) or is no longer wanted.
func (WorkflowKillTool) Definition ¶
func (WorkflowKillTool) Definition() agent.ToolDefinition
type WorkflowManager ¶
type WorkflowManager struct {
// contains filtered or unexported fields
}
WorkflowManager owns background workflow runs for one scope (process-global for CLI/TUI, or per-session for web/IM). It mirrors SubAgentManager: runs outlive the turn that launched them under a detached context.
func NewWorkflowManager ¶
func NewWorkflowManager() *WorkflowManager
NewWorkflowManager returns an empty manager.
func SessionWorkflowManager ¶
func SessionWorkflowManager(id string) *WorkflowManager
SessionWorkflowManager returns the per-session workflow manager for id, creating it on first use.
func (*WorkflowManager) Kill ¶
func (m *WorkflowManager) Kill(id string) (found, wasRunning bool)
Kill cancels one running workflow by id. Returns (found, wasRunning): found is false for an unknown id; wasRunning is false when the run had already finished (a no-op cancel). The run's detached context is cancelled, which propagates to its in-flight sub-agents and unwinds the script.
func (*WorkflowManager) KillAll ¶
func (m *WorkflowManager) KillAll()
KillAll cancels every running workflow this manager owns. Called on session close so a detached run doesn't outlive its conversation.
func (*WorkflowManager) List ¶
func (m *WorkflowManager) List() []WorkflowRunSnapshot
List returns snapshots of every run this manager knows, oldest first.
func (*WorkflowManager) Read ¶
func (m *WorkflowManager) Read(id string) (WorkflowRunSnapshot, bool)
Read returns a snapshot of one run.
func (*WorkflowManager) SetOnDone ¶
func (m *WorkflowManager) SetOnDone(fn func(WorkflowNotification))
SetOnDone registers the completion hook (next-turn notification). nil disables it.
func (*WorkflowManager) SetOnEvent ¶
func (m *WorkflowManager) SetOnEvent(fn func(WorkflowEvent))
SetOnEvent registers the live-progress sink (web panel). nil disables it.
func (*WorkflowManager) Start ¶
func (m *WorkflowManager) Start(req WorkflowRunRequest) (string, error)
Start launches req in the background and returns its handle id (e.g. "wf_1"). The run executes under a detached context so it survives the turn.
type WorkflowNotification ¶
type WorkflowNotification struct {
RunID string
Description string
Status string // "done" | "error"
Result string // final output, or the error message
JournalRunID string // the resume_from handle, when journaling was available
}
WorkflowNotification is delivered to the onDone hook when a background run finishes, so the transport can nudge the model on its next turn.
type WorkflowRunRequest ¶
type WorkflowRunRequest struct {
Description string
Script string
Args string // the run's input value as a JSON string ("" = none)
Agent workflow.AgentFunc
MaxConcurrent int
ResumeFrom string
}
WorkflowRunRequest starts one background run. The Agent func (spawner-backed) and limits are supplied by the caller so the manager stays decoupled from the tools.Spawner concrete type.
type WorkflowRunSnapshot ¶
type WorkflowRunSnapshot struct {
ID string
Description string
Status string // "running" | "done" | "error"
Output string
ErrMsg string
Logs []string
JournalRunID string
Start time.Time
End time.Time
// LastActivity is when the run last emitted progress (a log line or an
// agent start/finish). A running run whose LastActivity is far in the past
// is likely stuck — the gap, not the total elapsed, is the liveness signal.
LastActivity time.Time
}
WorkflowRunSnapshot is a point-in-time view of a background run for listing and status reads.
type WorkflowSaveTool ¶
type WorkflowSaveTool struct{}
WorkflowSaveTool persists a Ruby workflow script to the registry so it can be re-run by name with the workflow tool. Like the workflow tool, it is advertised only when a Spawner is configured.
func (WorkflowSaveTool) Definition ¶
func (WorkflowSaveTool) Definition() agent.ToolDefinition
type WorkflowStatusTool ¶
type WorkflowStatusTool struct{}
WorkflowStatusTool reports on background workflow runs: a list with no argument, or one run's full status + result when given a run id.
func (WorkflowStatusTool) Definition ¶
func (WorkflowStatusTool) Definition() agent.ToolDefinition
type WorkflowTool ¶
type WorkflowTool struct{}
WorkflowTool runs a Ruby (mruby) orchestration script in an embedded wasm interpreter. The script drives sub-agents through the agent() / parallel() / pipeline() primitives; each agent() call delegates to the same Spawner that backs sub_agent. The tool is advertised only when a Spawner is registered.
Like sub_agent, it refuses to run inside a sub-agent — workflow agents are themselves marked as sub-agents, so a child can't recursively launch another workflow.
func (WorkflowTool) Definition ¶
func (WorkflowTool) Definition() agent.ToolDefinition
type WriteFileTool ¶
type WriteFileTool struct{}
WriteFileTool writes (or overwrites) a file with the given content. Parent directories are created with mkdir -p semantics. File permissions default to 0644.
func (WriteFileTool) Definition ¶
func (WriteFileTool) Definition() agent.ToolDefinition
Source Files
¶
- agent.go
- agent_followup.go
- agent_presets.go
- agents.go
- args.go
- artifact.go
- ask_user_question.go
- background.go
- bgnotify.go
- browser.go
- detached.go
- edit_file.go
- envnote.go
- glob.go
- grep.go
- mcp.go
- read_file.go
- read_tracker.go
- registry.go
- restart.go
- sandbox.go
- schedule_wakeup.go
- send_file.go
- session_managers.go
- skill.go
- spawner.go
- spill.go
- subagent_event.go
- subagent_manager.go
- subagent_notify.go
- tasks.go
- terminal.go
- terminal_kill.go
- tool_search.go
- toolchain.go
- uipayload.go
- web_fetch.go
- web_search.go
- workdir.go
- workflow.go
- workflow_manager.go
- workflow_notify.go
- workflow_registry.go
- workflow_save.go
- write_file.go
- write_guard.go