agent

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 21, 2026 License: Apache-2.0 Imports: 30 Imported by: 0

Documentation

Overview

Package agent implements the LLM agent run-loop.

Package agent includes support for user-defined sub-agent profiles stored as Markdown files under .ok/agents/. Each file defines a reusable, named sub-agent with an optional model override, tool whitelist, and custom system prompt. The loader is called from boot; the resulting tools are registered alongside built-in tools so the LLM can invoke them by name.

Package agent — file-read tracker for tool-aware context compression.

The fileTracker records which messages in the session carry the content of which files (from read_file and grep results). When the same file appears in a new tool result, the previous occurrence is skeletonized in-place — keeping signatures and structural hints while dropping the body — so the model still knows the file exists and what it exports, but stops paying tokens for stale copies of the implementation.

This implements the core insight from TokenTamer (github.com/borhen68/TokenTamer): track tool_use → file mappings and skeletonize background files while keeping the most recent read intact.

Only read_file and grep results are tracked; writes (write_file, edit_file, multi_edit) are never skeletonized because they represent the authoritative state after a mutation. bash results are also skipped — their content is too varied to map to a single file.

Package agent — modelrouter.go was removed; never wired.

Package agent provides the Planner — a hierarchical task decomposition engine. It takes a high-level goal, decomposes it into sub-tasks with dependency edges, executes them in topological order, and re-plans on failure.

Package agent provides the Reasoner — OK's top-level multi-agent orchestrator that decomposes a goal into a task DAG via LLM, then executes it with Planner's DAG engine using concurrent dispatch.

Package agent — session resilience: mid-turn persistence, crash recovery, graceful context management, and reconnect support.

Package agent — dead code removed; model router was never wired.

Package agent — tool result skeletonizer for context compression.

When the same file is read multiple times in a session (by read_file or grep), older copies are skeletonized — the implementation body is replaced with a structural summary while signatures, imports, and type declarations are preserved. The model still knows what the file exports but stops paying tokens for stale copies of the implementation.

Only the most recent read of each file is kept full; every earlier read is skeletonized in-place in the session message slice.

Index

Constants

View Source
const DefaultPlannerPrompt = `` /* 277-byte string literal not displayed */

DefaultPlannerPrompt steers the planner toward concise plans, not execution.

View Source
const DefaultStoreURL = "https://raw.githubusercontent.com/colbymchenry/ok-agents/main/index.json"
View Source
const DefaultTaskSystemPrompt = `` /* 173-byte string literal not displayed */

DefaultTaskSystemPrompt steers a sub-agent toward focused, terse delivery. Sub-agents have the same capabilities as the main agent: they can read, write, run commands, and spawn further sub-agents. The parent's task tree keeps nesting visible.

Variables

View Source
var ErrContextOverflow = fmt.Errorf("context window exhausted — send /compact to compress, or /new to start fresh")

ErrContextOverflow is returned when the context window is exhausted.

View Source
var NewComputerUse = computeruse.NewComputerUse

Functions

func FilterRegistry

func FilterRegistry(parent *tool.Registry, names []string, exclude ...string) *tool.Registry

FilterRegistry builds a sub-registry from parent: the named whitelist (empty = every parent tool), minus any excluded names. Used to scope what a spawned sub-agent — a `task` sub-agent or a subagent skill — may call.

func FormatUsageLine

func FormatUsageLine(u *provider.Usage, p *provider.Pricing) string

FormatUsageLine renders the per-turn token/cache summary — the key signal for the cache-first design — as a single line (no trailing newline), or "" when usage is unset or empty. Cache is reported as absolute "(N cached / M new)" so a turn that adds a lot of fresh content doesn't read as "cache broke" the way a falling percentage would; the cached prefix is still hitting, the denominator just grew. Reasoning tokens (a subset of completion) show the chain-of-thought cost. Shared by TextSink and the chat TUI so both frontends render the line identically.

func InstallAgent

func InstallAgent(ctx context.Context, projectRoot, name, url string) error

InstallAgent downloads an agent definition from URL and saves to .ok/agents/.

func NestedSink

func NestedSink(ctx context.Context, fallback event.Sink) event.Sink

NestedSink returns a sink that forwards a sub-agent's tool activity to the parent stream, nested under the tool call carried by ctx, so a frontend shows it beneath that call (the same nesting `task` uses). Falls back to the given sink when ctx carries no call context. Used by subagent skills.

func NewSessionPath

func NewSessionPath(dir, model string) string

NewSessionPath returns the path to use for a fresh session, namespaced by the model so the filename hints at what the conversation was with. dir is typically config.SessionDir().

func PublishAgent

func PublishAgent(def AgentDef) string

PublishAgent formats an AgentDef as a shareable Markdown string.

func RunSubAgent

func RunSubAgent(ctx context.Context, prov provider.Provider, reg *tool.Registry, sysPrompt, prompt string, opts Options, sink event.Sink) (string, error)

RunSubAgent runs prompt to completion in a fresh sub-agent session over reg, emitting tool activity to sink, and returns the sub-agent's final assistant answer. It is the shared core behind the `task` tool and subagent skills: a caller supplies the system prompt (the task persona or the skill body), the tool registry (already filtered), and the run Options (model budget, gate).

Types

type Agent

type Agent struct {
	// contains filtered or unexported fields
}

func New

func New(prov provider.Provider, tools tool.ToolRegistry, session *Session, opts Options, sink event.Sink) *Agent

New constructs an Agent. MaxSteps <= 0 means no cap — the run loop continues until the model gives a final answer, the context is canceled, or the provider errors (compaction keeps the context bounded). A nil sink is replaced with event.Discard so the agent can always emit unconditionally.

func (*Agent) AggressiveCompact

func (a *Agent) AggressiveCompact(ctx context.Context) error

func (*Agent) AuditLog

func (a *Agent) AuditLog() []core.AuditRecord

AuditLog returns a copy of the audit chain entries, or nil if auditing is disabled.

func (*Agent) CheckContextOverflow

func (a *Agent) CheckContextOverflow() error

func (*Agent) CompactNow

func (a *Agent) CompactNow(ctx context.Context) error

CompactNow runs one compaction pass immediately, regardless of the usage-ratio threshold maybeCompact normally honors. Used by the chat TUI's `/compact` command so the user can reset the prefix before it naturally fills up.

func (*Agent) ContextUsage

func (a *Agent) ContextUsage() float64

ContextUsage returns the estimated fraction (0.0–1.0) of the context window used by the current session. Returns 0 when the window is unconfigured.

func (*Agent) ContextWindow

func (a *Agent) ContextWindow() int

ContextWindow returns the configured context-window size in tokens. 0 means compaction is disabled for this agent.

func (*Agent) Hooks

func (a *Agent) Hooks() ToolHooks

Hooks returns the currently installed tool hooks, or nil.

func (*Agent) LastUsage

func (a *Agent) LastUsage() *provider.Usage

LastUsage returns the most recent per-turn token telemetry the provider reported (nil if no turn has run yet). The TUI uses it to show a context gauge alongside the prompt; the actual cache decisions still live inside maybeCompact.

func (*Agent) Run

func (a *Agent) Run(ctx context.Context, input string) error

Run appends the user input and drives the tool loop until the model returns a final answer (no tool calls), the context is canceled, or the provider errors.

func (*Agent) Session

func (a *Agent) Session() *Session

Session returns the agent's current conversation, useful for persistence hooks that need to read the message log between turns.

func (*Agent) SessionCache

func (a *Agent) SessionCache() (hit, miss int)

SessionCache returns the cumulative cache hit/miss prompt tokens across every API call this session — the basis for the status line's aggregate hit-rate.

func (*Agent) SetAsker

func (a *Agent) SetAsker(as Asker)

SetAsker installs the asker the `ask` tool uses to question the user. Interactive frontends wire one in; headless runs leave it nil.

func (*Agent) SetGate

func (a *Agent) SetGate(g Gate)

SetGate installs the per-call permission gate. Used by `ok chat` to swap the headless gate built in setup for an interactive one that prompts the user; nil disables gating. Safe to call before the run loop starts.

func (*Agent) SetHooks

func (a *Agent) SetHooks(h ToolHooks)

SetHooks installs tool hooks. Nil disables.

func (*Agent) SetMsgBus

func (a *Agent) SetMsgBus(b *bus.Bus)

SetMsgBus wires an optional message bus for publishing execution events. nil disables publishing; all publish calls are nil-safe.

func (*Agent) SetOnTurnComplete

func (a *Agent) SetOnTurnComplete(fn func(context.Context, string, string))

SetOnTurnComplete registers a callback fired after each turn completes.

func (*Agent) SetPipe

func (a *Agent) SetPipe(p eventpipe.Sink)

SetPipe installs a typed event pipeline sink. When set, the agent emits typed events directly to the pipeline instead of going through the old event.Sink. This is the migration path from old-style to typed events. Must be called before Run starts, or protected by pipeMu.

func (*Agent) SetPlanMode

func (a *Agent) SetPlanMode(v bool)

non-ReadOnly tool the model calls and returns a "blocked" result instead of running it. The cache-friendly bits — system prompt, tools schema, message history — are left untouched, so the toggle costs nothing in cache hits.

func (*Agent) SetSession

func (a *Agent) SetSession(s *Session)

SetSession replaces the agent's conversation wholesale. Used by `ok chat --resume` to load a saved JSONL transcript before the first turn, so the model picks up exactly where it left off.

func (*Agent) SetSessionPath

func (a *Agent) SetSessionPath(path string)

SetSessionPath sets the path for mid-batch auto-save during tool chains. Called by the controller after NewSession or Resume.

type AgentDef

type AgentDef struct {
	Name              string   // derived from filename (no .md)
	Description       string   // first H1 heading or "Sub-agent: <name>"
	Model             string   // optional model override (empty = inherit parent)
	Tools             []string // tool whitelist (empty = all parent tools)
	PermissionMode    string   // "plan" | "normal" | "yolo" | "" (inherit)
	AllowedMCPServers []string // MCP servers this agent can access; empty = all
	SystemPrompt      string   // raw Markdown body
	FilePath          string   // source file path
}

AgentDef is a user-defined sub-agent profile loaded from .ok/agents/*.md.

func LoadAgentDefs

func LoadAgentDefs(projectRoot string) []AgentDef

LoadAgentDefs scans .ok/agents/ (under projectRoot) for .md files and parses each one into an AgentDef. Non-existent directory is not an error; permission or I/O errors are logged to stderr so the operator can diagnose.

func (AgentDef) Describe

func (d AgentDef) Describe() string

Describe returns a one-line tool description for registration.

func (AgentDef) MakeAgentTool

func (d AgentDef) MakeAgentTool(
	prov provider.Provider,
	parentReg *tool.Registry,
	pricing *provider.Pricing,
	gate Gate,
	hooks ToolHooks,
	jm *jobs.Manager,
	opts Options,
	resolveModel func(string) (provider.Provider, *provider.Pricing, int, error),
) tool.Tool

MakeAgentTool returns a tool.Tool that executes this agent definition via RunSubAgent, using the given provider, registry, pricing, gate, hooks, jobs, and options. The caller supplies a function to resolve a model name to a provider (for model overrides) and a function to filter the registry.

type AskTool

type AskTool struct{}

AskTool lets the model put a structured multiple-choice question (or a few) to the user mid-task and get the answer back — for genuine forks the model can't resolve from the request or the code (which library, which approach, …) rather than guessing or asking in prose. The frontend renders selectable options, the user picks, and the choices come back as the tool result. It reaches the user through the Asker carried on the call context (CallContext); with no asker (headless runs) it returns a "decide for yourself" result so an autonomous run never blocks.

func NewAskTool

func NewAskTool() *AskTool

func (*AskTool) Description

func (*AskTool) Description() string

func (*AskTool) Execute

func (*AskTool) Execute(ctx context.Context, args json.RawMessage) (string, error)

func (*AskTool) Name

func (*AskTool) Name() string

func (*AskTool) ReadOnly

func (*AskTool) ReadOnly() bool

ReadOnly is true: asking has no host side effects, so it never needs approval and stays available in plan mode (clarifying scope while planning is fine).

func (*AskTool) Schema

func (*AskTool) Schema() json.RawMessage

type Asker

type Asker interface {
	Ask(context.Context, []event.AskQuestion) ([]event.AskAnswer, error)
}

func CallContext

func CallContext(ctx context.Context) (string, event.Sink, Asker, bool)

type ComputerAction

type ComputerAction = computeruse.ComputerAction

type ComputerUse

type ComputerUse = computeruse.ComputerUse

type Coordinator

type Coordinator struct {
	// contains filtered or unexported fields
}

Coordinator runs two models in separate sessions to keep each one's prompt prefix cache-stable: a low-frequency planner proposes an approach, then the executor (a Runner — typically a DSTRunner wrapping an Agent) carries it out. The sessions never mix, so neither model's prefix is disturbed by the other's turns.

func NewCoordinator

func NewCoordinator(planner provider.Provider, plannerSession *Session, plannerPricing *provider.Pricing, executor Runner, execName string, temperature float64, sink event.Sink) *Coordinator

NewCoordinator wires a planner provider (with its own session) to an executor Runner (typically a DSTRunner wrapping an Agent). sink receives the planner's phase/text/usage events; the executor emits its own events to its own sink (the CLI wires the same sink into both). A nil sink is replaced with event.Discard.

func (*Coordinator) Run

func (c *Coordinator) Run(ctx context.Context, input string) error

Run plans with the planner model, then hands the plan to the executor.

type Gate

type Gate interface {
	Check(context.Context, string, json.RawMessage, bool) (bool, string, error)
}

type MemMsg

type MemMsg struct {
	Path  string
	Scope string
	Note  string
}

MemMsg is published on the bus for memory events.

type Options

type Options struct {
	MaxSteps    int
	Temperature float64
	Pricing     *provider.Pricing // optional, for per-turn cost display

	// Gate is the per-call permission gate. nil disables gating.
	Gate Gate

	// Hooks fires PreToolUse / PostToolUse shell hooks around tool calls. nil
	// disables hook firing.
	Hooks ToolHooks

	// Jobs is the session's background-job manager (nil disables background tools).
	Jobs *jobs.Manager

	// Context management. ContextWindow <= 0 disables compaction. CompactRatio
	// and RecentKeep fall back to defaults when unset.
	ContextWindow int
	CompactRatio  float64
	RecentKeep    int
	ArchiveDir    string

	// AuditChain is the tamper-evident audit log for tool execution.
	// nil disables auditing.
	AuditChain *core.AuditChain

	// OnTurnComplete, when set, is called after each complete turn (model returns
	// a final answer without tool calls). It receives the user's input and the
	// model's final text for episodic memory capture. The callee should not block.
	OnTurnComplete func(ctx context.Context, input, answer string)

	// SessionPath, when non-empty, enables mid-batch auto-save so crash recovery
	// loses at most 5 tool-call rounds per long tool chain.
	SessionPath string
}

Options configures an Agent.

type Plan

type Plan struct {
	Goal      string     `json:"goal"`
	Tasks     []PlanTask `json:"tasks"`
	CreatedAt time.Time  `json:"created_at"`
	// contains filtered or unexported fields
}

Plan is a hierarchical task decomposition.

func NewPlan

func NewPlan(goal string) *Plan

NewPlan creates an empty plan for the given goal.

func (*Plan) AddTask

func (p *Plan) AddTask(id, desc string, dependsOn ...string)

AddTask appends a task to the plan.

func (*Plan) GetReadyTasks

func (p *Plan) GetReadyTasks() []*PlanTask

GetReadyTasks returns tasks whose dependencies are all done and that are pending.

func (*Plan) MarkBlocked

func (p *Plan) MarkBlocked()

MarkBlocked marks pending tasks with failed dependencies as blocked, and unblocks blocked tasks whose dependencies are no longer failed (e.g. after a retry resets a failed dependency to pending).

func (*Plan) SetStatus

func (p *Plan) SetStatus(id string, status PlanTaskStatus, result, errMsg string)

SetStatus updates a task's status.

func (*Plan) Summary

func (p *Plan) Summary() string

Summary returns a human-readable status of the plan.

type PlanTask

type PlanTask struct {
	ID          string         `json:"id"`
	Description string         `json:"description"`
	DependsOn   []string       `json:"depends_on,omitempty"`
	Status      PlanTaskStatus `json:"status"`
	Result      string         `json:"result,omitempty"`
	Error       string         `json:"error,omitempty"`
	Attempts    int            `json:"attempts,omitempty"` // retry count
	CreatedAt   time.Time      `json:"created_at"`
	CompletedAt time.Time      `json:"completed_at,omitempty"`
}

PlanTask is one atomic unit of work in a plan.

type PlanTaskStatus

type PlanTaskStatus string

PlanTaskStatus tracks each sub-task's state.

const (
	TaskPending PlanTaskStatus = "pending"
	TaskRunning PlanTaskStatus = "running"
	TaskDone    PlanTaskStatus = "done"
	TaskFailed  PlanTaskStatus = "failed"
	TaskBlocked PlanTaskStatus = "blocked" // dependency failed, won't run
)
const TaskInProgress PlanTaskStatus = "in_progress"

TaskInProgress is deprecated; use TaskRunning. Kept for backward compatibility with existing serialized plans.

type Planner

type Planner struct {
	// contains filtered or unexported fields
}

Planner orchestrates hierarchical task execution with dependency ordering. Decomposition is handled by Reasoner.decompose() (LLM-based) or by the plan tool's explicit steps mode — Planner only manages execution and retry.

func NewPlanner

func NewPlanner() *Planner

NewPlanner creates a planner with default settings.

func (*Planner) Execute

func (p *Planner) Execute(ctx context.Context, plan *Plan, runner PlannerRunner, maxConcurrent int) (string, error)

Execute runs a plan's tasks respecting dependency order. It runs ready tasks concurrently up to maxConcurrent, and re-plans on failure.

type PlannerRunner

type PlannerRunner func(ctx context.Context, task PlanTask) (string, error)

PlannerRunner executes tasks in a plan. The actual execution is delegated to a function so the tool layer can wire in the agent's run loop.

type ProofRecorder

type ProofRecorder interface {
	AppendWithPath(atomID, proposition, evidence, parentID, path string)
}

ProofRecorder records verification results into the proof chain.

type Reasoner

type Reasoner struct {
	// contains filtered or unexported fields
}

Reasoner decomposes a goal into a task DAG via an LLM planner, then executes it with Planner's topological concurrency. It satisfies the Runner interface so it slots in as a drop-in replacement for Agent or Coordinator.

The dispatch function executes individual PlanTasks — typically via RunSubAgent with the executor's provider/registry, or via a Team's orchestrator. The caller (boot) wires it.

func NewReasoner

func NewReasoner(
	plannerProv provider.Provider,
	plannerSess *Session,
	plannerPricing *provider.Pricing,
	dispatch PlannerRunner,
	maxConcurrent int,
	temperature float64,
	sink event.Sink,
) *Reasoner

NewReasoner creates a Reasoner. plannerProv and plannerSess are the decomposition LLM (kept in its own session for cache-stable prefix). dispatch is called for each ready PlanTask; maxConcurrent caps parallel dispatches. sink receives phase/text/usage events from the decomposer.

func (*Reasoner) Reason

func (r *Reasoner) Reason(ctx context.Context, goal string) (string, error)

Reason decomposes and executes like Run, but returns the aggregated result string alongside any error — see the multi-agent protocol in REASONIX.md.

func (*Reasoner) Run

func (r *Reasoner) Run(ctx context.Context, goal string) error

Run decomposes the goal into a Plan DAG, then executes it with Planner. Falls back to a single-task plan if decomposition fails, so the executor still gets a chance to handle the goal. The aggregated result is captured and available via Reason().

type Renderer

type Renderer interface{ Render(string) string }

type Runner

type Runner interface {
	Run(ctx context.Context, input string) error
}

Runner carries out one task turn. Both Agent (single model) and Coordinator (two-model) satisfy it, so the CLI stays agnostic to which is in use.

type Session

type Session struct {
	Messages []provider.Message
	// contains filtered or unexported fields
}

Session holds the conversation history for one task. Uses sync.RWMutex so concurrent reads (Snapshot) don't serialize. A generation counter avoids copying the message slice on repeated Snapshot calls within the same mutation epoch — the cached snapshot is returned until the next Add or Replace.

func LoadSession

func LoadSession(path string) (*Session, error)

LoadSession reads a JSONL file written by Save into a fresh Session value. Missing files surface as os.IsNotExist so callers can fall through to a new session.

func NewSession

func NewSession(system string) *Session

NewSession initializes a session with an optional system prompt.

func (*Session) Add

func (s *Session) Add(m provider.Message)

Add appends a message. Safe for concurrent use.

func (*Session) Gen

func (s *Session) Gen() uint64

Gen returns the current generation counter. It increments on every Add, Replace, and ReplaceIfUnchanged call. Used for detecting concurrent changes.

func (*Session) Len

func (s *Session) Len() int

Len returns the number of messages. Safe for concurrent use.

func (*Session) Replace

func (s *Session) Replace(msgs []provider.Message)

Replace atomically swaps the message history. Used by compaction. A nil input is rejected to prevent accidental data loss.

func (*Session) ReplaceIfUnchanged

func (s *Session) ReplaceIfUnchanged(msgs []provider.Message, expectedGen uint64) bool

ReplaceIfUnchanged atomically swaps the message history only if gen still equals expectedGen. Used by compaction to detect concurrent Add calls that would otherwise be lost. Returns false when gen has changed (caller should retry or skip).

func (*Session) Save

func (s *Session) Save(path string) error

Save writes the session's messages to path in JSONL — one provider.Message per line — so a user can resume the conversation later. The file is rewritten in full on every save: chat sessions are small (kilobytes), and append-only would have to be reconciled with the compaction pass that mutates the middle of session.Messages. Safe for concurrent use.

func (*Session) SkeletonizeAt

func (s *Session) SkeletonizeAt(indices []int, toolName string)

SkeletonizeAt skeletonizes tool messages at the given indices, replacing their Content with a compressed version that preserves structural info. Indices that are out of range or point to non-tool messages are silently skipped. This is called after a file re-read to compress stale copies.

func (*Session) Snapshot

func (s *Session) Snapshot() []provider.Message

Snapshot returns the current message slice for safe iteration. The returned slice shares a backing array with the session — callers MUST NOT mutate any element or append to the slice. A generation counter ensures stale cached snapshots are refreshed after Add or Replace.

type SessionInfo

type SessionInfo struct {
	Path    string
	ModTime time.Time
	Preview string
	Turns   int
}

SessionInfo summarizes a saved session for the --resume picker: where it is on disk, when it was last touched, the first user message as a preview, and a rough turn count.

func ListSessions

func ListSessions(dir string) ([]SessionInfo, error)

ListSessions returns every *.jsonl session under dir, newest first, each with a preview line so the picker can show something the user recognizes. A missing directory is not an error — it just means there's nothing to resume yet.

type Snapshotter

type Snapshotter interface {
	CaptureDir(dir string) error
	Rollback() error
	Clear()
}

Snapshotter captures and rolls back file state. Each call to Execute should get a fresh instance via the factory to keep layers isolated.

type Specialist

type Specialist struct {
	Name          string
	Description   string
	Model         string
	Prov          provider.Provider
	Pricing       *provider.Pricing
	ContextWindow int
	Tools         []string       // tool names for display / filtering
	Reg           *tool.Registry // pre-filtered registry; nil means empty
	Prompt        string
}

Specialist describes one agent in a team.

type SpecialistConfig

type SpecialistConfig struct {
	Name        string   `toml:"name"`
	Model       string   `toml:"model"`
	Description string   `toml:"description"`
	Prompt      string   `toml:"prompt"`
	Tools       []string `toml:"tools"`
}

type SpecialistRunner

type SpecialistRunner struct {
	Specialist
	// contains filtered or unexported fields
}

SpecialistRunner wraps a specialist with its own registry and session.

type StoreEntry

type StoreEntry struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	Author      string `json:"author"`
	URL         string `json:"url"`
	Tools       string `json:"tools"`
}

StoreEntry is one agent listing in the community store index.

func ListAgents

func ListAgents(ctx context.Context, storeURL string) ([]StoreEntry, error)

ListAgents fetches the agent store index.

type Task

type Task = PlanTask

Task is the public alias for PlanTask — see the multi-agent protocol in REASONIX.md. See PlanTask for the full definition.

type TaskResult

type TaskResult struct {
	TaskID  string
	Summary string
	Output  string
	Error   string
}

TaskResult is the output of a completed task — see the multi-agent protocol in REASONIX.md.

type TaskTool

type TaskTool struct {
	// contains filtered or unexported fields
}

func NewTaskTool

func NewTaskTool(prov provider.Provider, pricing *provider.Pricing, parentReg *tool.Registry,
	maxSteps, contextWindow int, temperature float64, archiveDir, sysPrompt string, gate Gate, workDir string,
	newSnap func() Snapshotter, proofRecorder ProofRecorder, maxConcurrent int) *TaskTool

NewTaskTool is the backward-compatible 13-arg constructor (hooks/jm/lang default to nil).

func NewTaskToolFull

func NewTaskToolFull(prov provider.Provider, pricing *provider.Pricing, parentReg *tool.Registry,
	maxSteps, contextWindow int, temperature float64, archiveDir, sysPrompt string, gate Gate, workDir string,
	newSnap func() Snapshotter, proofRecorder ProofRecorder, maxConcurrent int,
	hooks ToolHooks, jm *jobs.Manager, subPromptSuffix string) *TaskTool

NewTaskToolFull creates a TaskTool with sub-agent hooks, jobs manager, and prompt suffix.

func (*TaskTool) Active

func (t *TaskTool) Active() int

Active reports the number of currently-running sub-agents.

func (*TaskTool) Description

func (t *TaskTool) Description() string

func (*TaskTool) Execute

func (t *TaskTool) Execute(ctx context.Context, args json.RawMessage) (string, error)

func (*TaskTool) Name

func (t *TaskTool) Name() string

func (*TaskTool) ReadOnly

func (t *TaskTool) ReadOnly() bool

ReadOnly returns true. The sub-agent spawned by task is confined to read-only tools by buildSubReg (which excludes bash, write_file, etc.), so plan mode can safely allow task calls. This also lets task parallelise with other read-only tools. An explicit p.Tools whitelist can still grant writers to the sub-agent, but that is opt-in.

func (*TaskTool) Schema

func (t *TaskTool) Schema() json.RawMessage

type Team

type Team struct {
	Orchestrator Runner
	// contains filtered or unexported fields
}

Team runs a multi-model agent team with an orchestrator + specialists.

func BuildTeam

func BuildTeam(
	cfg TeamConfig,
	resolveModel func(string) (provider.Provider, *provider.Pricing, int, error),
	sink event.Sink,
	baseReg *tool.Registry,
	gate Gate,
	hooks ToolHooks,
	opts Options,
) (*Team, error)

BuildTeam constructs a Team from config, creating both the orchestrator Agent and all specialists. The orchestrator gets delegate_<name> tools AND a delegate_all tool for parallel dispatch.

func NewTeam

func NewTeam(orchestrator Runner, specialists []Specialist, sink event.Sink) *Team

NewTeam builds a Team. The orchestrator gets a delegate tool to call specialists.

func (*Team) Run

func (t *Team) Run(ctx context.Context, input string) error

func (*Team) RunParallel

func (t *Team) RunParallel(ctx context.Context, task string, names []string) (map[string]string, error)

RunParallel dispatches a task to multiple specialists concurrently and aggregates their results. All specialists receive the same task; the orchestrator receives each result labeled by specialist name.

type TeamConfig

type TeamConfig struct {
	Orchestrator string             `toml:"orchestrator"`
	Specialists  []SpecialistConfig `toml:"specialists"`
}

type TextSink

type TextSink struct {
	// contains filtered or unexported fields
}

TextSink renders a turn's event stream to ANSI text on an io.Writer. It is the reference terminal frontend: a headless `ok run` writes to stdout, and during the cache-first migration the chat TUI is fed through it too. The output is byte-for-byte what the agent used to print directly, now driven by typed events instead of inline Fprint calls.

renderer, when non-nil, replaces the streamed raw answer text with styled markdown once the text stream completes (a Message event). termWidth is the column count used to count how many rows the raw stream occupied before the redraw moves the cursor back. A nil renderer keeps the raw stream — correct for piped output and for the chat TUI, which renders markdown itself.

func NewTextSink

func NewTextSink(out io.Writer, renderer Renderer, termWidth int) *TextSink

NewTextSink builds a TextSink writing to out. renderer/termWidth drive the post-stream markdown redraw; pass a nil renderer to keep the raw stream.

func (*TextSink) Emit

func (s *TextSink) Emit(e *event.Event)

Emit renders one event. Called serially by the run loop.

type ToolHooks

type ToolHooks interface {
	PreToolUse(context.Context, string, json.RawMessage) (bool, string)
	PostToolUse(context.Context, string, json.RawMessage, string)
	ConsumeRollback() (string, string, bool)
}

type ToolMsg

type ToolMsg struct {
	Name     string
	Args     string
	Result   string
	Err      string
	Duration time.Duration
}

ToolMsg is published on the bus for tool execution events.

type UsageTracker

type UsageTracker struct {
	// contains filtered or unexported fields
}

UsageTracker records per-turn and session-aggregate token telemetry. It was extracted from Agent to keep the Agent struct focused on the run loop rather than owning every concern directly. It is safe for concurrent use: a status-bar goroutine may read LastUsage / SessionCache while the run-loop goroutine calls Record.

func NewUsageTracker

func NewUsageTracker() *UsageTracker

NewUsageTracker returns an initialized UsageTracker.

func (*UsageTracker) LastUsage

func (t *UsageTracker) LastUsage() *provider.Usage

LastUsage returns the most recent per-turn telemetry, or nil when no turn has completed.

func (*UsageTracker) Record

func (t *UsageTracker) Record(u provider.Usage)

Record stores the latest per-turn usage and accumulates session cache tokens. Call from the run-loop goroutine on each ChunkUsage event.

func (*UsageTracker) SessionCache

func (t *UsageTracker) SessionCache() (hit, miss int)

SessionCache returns the cumulative cache hit/miss prompt tokens across every API call this session, so frontends can show the aggregate hit-rate.

func (*UsageTracker) Snapshot

func (t *UsageTracker) Snapshot() (last *provider.Usage, hit, miss int)

Snapshot returns a copy of the current state for use in events.

Directories

Path Synopsis
Package agent provides the ComputerUse orchestrator — a screenshot→analyze→act→verify loop that lets the agent control the desktop GUI.
Package agent provides the ComputerUse orchestrator — a screenshot→analyze→act→verify loop that lets the agent control the desktop GUI.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL