control

package
v1.0.4 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: 35 Imported by: 0

Documentation

Overview

Package control is the transport-agnostic session driver. A Controller owns the agent run loop and session lifecycle, takes commands (Send/Cancel/Approve/ SetPlanMode/Compact/NewSession) and emits everything that happens — reasoning, tool calls, approvals, turn completion — as a typed event stream to a single event.Sink.

The point is one orchestration layer behind every frontend: a terminal TUI, a desktop webview, or an HTTP/SSE server each drive the Controller identically (issue commands, render events) and none of them re-implement turn lifecycle, cancellation, or approval. The Controller depends on no frontend.

See also:

controller_turn.go     — turn lifecycle (runGuarded, Send, Submit, runTurn, Compose)
controller_approval.go — approval/ask/plan-mode (Approve, Ask, requestApproval)
controller_session.go  — session persistence (Snapshot, NewSession, Resume)
controller_mcp.go      — MCP server hot-add/remove
controller_memory.go   — memory quick-add/save
controller_query.go    — read-only accessors (History, Balance, Skills, DST, etc.)
input.go               — Compose, CustomCommand, RunSkill, MCPPrompt
slash.go               — slash-command completion and management notices
refs.go                — @-reference resolution

Index

Constants

View Source
const PlanApprovalTool = "exit_plan_mode"

PlanApprovalTool is the Tool name the controller uses on the ApprovalRequest it emits to gate a proposed plan. Frontends key their plan-approval UI on it.

View Source
const PlanModeMarker = "" /* 448-byte string literal not displayed */

PlanModeMarker is prepended to every user turn while plan mode is on. It rides in the user message (not the system prompt or tools), so the cache-stable prompt prefix is left untouched and the toggle costs nothing in cache hits.

Variables

This section is empty.

Functions

func PlanTodosJSON

func PlanTodosJSON(plan string) (string, error)

PlanTodosJSON parses an approved plan's markdown into todo_write-shaped args JSON ({"todos":[...]}), or ("", nil) when the plan has no list items.

Types

type ApprovalManager

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

ApprovalManager manages the approval lifecycle for writer tools.

func NewApprovalManager

func NewApprovalManager(sink event.Sink) *ApprovalManager

NewApprovalManager creates an ApprovalManager.

func (*ApprovalManager) Answer

func (m *ApprovalManager) Answer(id string, answers []event.AskAnswer)

Answer resolves an outstanding ask by id with the user's answers.

func (*ApprovalManager) Approve

func (m *ApprovalManager) Approve(id string, allow, remember bool)

Approve resolves an outstanding approval by id. When remember is true, the grant is cached for this session so subsequent calls to the same tool+subject auto-approve without prompting.

func (*ApprovalManager) Ask

func (m *ApprovalManager) Ask(ctx context.Context, questions []event.AskQuestion) ([]event.AskAnswer, error)

Ask implements agent.Asker — emits an Ask event and blocks for the answer.

func (*ApprovalManager) Bypass

func (m *ApprovalManager) Bypass() bool

Bypass reports bypass mode state.

func (*ApprovalManager) GateApprover

func (m *ApprovalManager) GateApprover() permission.Approver

GateApprover returns a permission.Approver that routes through this manager.

func (*ApprovalManager) SetAutoApprove

func (m *ApprovalManager) SetAutoApprove(v bool)

SetAutoApprove sets auto-approve mode (used during plan execution).

func (*ApprovalManager) SetBypass

func (m *ApprovalManager) SetBypass(v bool)

SetBypass enables/disables bypass mode.

type ArgData

type ArgData struct {
	Skills       []skill.Skill
	ServerNames  []string
	ModelRefs    []string
	CurrentModel string
}

ArgData supplies the dynamic data SlashArgItems needs, so the completion logic is one shared function both frontends call with their own session data — the chat TUI (controller-free, from its cached lists) and the desktop (from the controller). This keeps the CLI and desktop sub-command hints identical.

type Controller

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

Controller drives one chat session. Construct with New; drive with the command methods; observe through the Sink passed in Options.

Fields are grouped by concern:

[core]     runner, executor, sink, policy, label, systemPrompt, cleanup, baseCtx
[session]  sessionDir, sessionPath
[plugins]  host, reg, pluginCtx
[modules]  commands, skills, hooks, mem
[dst]      dst, workDir, proofChain
[billing]  balanceURL, balanceKey
[jobs]     jobs
[approval] approval, planMode
[turn]     turn, pendingMemory, bgWG, mu, cancel, running

func New

func New(opts Options) *Controller

New builds a Controller. A nil Sink is replaced with event.Discard.

func (*Controller) AddMCPServer

func (c *Controller) AddMCPServer(e config.PluginEntry) (int, error)

AddMCPServer connects an MCP server live and persists it to the config file. Its tools are registered immediately and become available on the next turn (the agent reads the registry per turn). Returns the number of tools the server exposed. A save failure after a successful connect is reported but non-fatal.

func (*Controller) Answer

func (c *Controller) Answer(id string, answers []event.AskAnswer)

Answer replies to an outstanding ask prompt.

func (*Controller) AnswerQuestion

func (c *Controller) AnswerQuestion(id string, answers []event.AskAnswer)

AnswerQuestion is an alias for Answer; used by some frontends.

func (*Controller) Approve

func (c *Controller) Approve(id string, allow, session bool)

Approve answers an outstanding approval prompt. id is the Approval.ID from the event; session=true persists the grant for this session. The reply is consumed by the turn that issued the request; unknown ids are silently ignored.

func (*Controller) Ask

func (c *Controller) Ask(ctx context.Context, questions []event.AskQuestion) ([]event.AskAnswer, error)

Ask implements agent.Asker. It emits an Ask event and blocks for the answer.

func (*Controller) AuditLog

func (c *Controller) AuditLog() []core.AuditRecord

AuditLog returns the complete audit trail.

func (*Controller) AuditLogJSON

func (c *Controller) AuditLogJSON() (string, error)

AuditLogJSON returns the audit trail as a JSON string.

func (*Controller) Balance

func (c *Controller) Balance(ctx context.Context) (*billing.Balance, error)

Balance queries the active provider's wallet balance, or (nil, nil) when the provider declares no balance_url.

func (*Controller) Bypass

func (c *Controller) Bypass() bool

Bypass reports whether YOLO/bypass mode is on, for the status-bar indicator.

func (*Controller) Cancel

func (c *Controller) Cancel()

Cancel ends the current turn (if any) without changing state.

func (*Controller) Close

func (c *Controller) Close()

Close stops plugin subprocesses and releases resources.

func (*Controller) Commands

func (c *Controller) Commands() []command.Command

Commands returns the loaded custom slash commands.

func (*Controller) Compact

func (c *Controller) Compact(ctx context.Context) error

Compact triggers an immediate context compaction on the executor.

func (*Controller) Compose

func (c *Controller) Compose(text string) string

Compose applies the plan-mode marker to a turn's text when plan mode is on, returning the message to actually send to the model. The frontend keeps showing the raw text as the user bubble.

func (*Controller) ContextSnapshot

func (c *Controller) ContextSnapshot() (int, int)

ContextSnapshot returns (promptTokens, contextWindow) from the most recent turn.

func (*Controller) CustomCommand

func (c *Controller) CustomCommand(input string) (sent string, found bool)

CustomCommand resolves a "/name args…" line against the loaded custom slash commands, returning the rendered prompt to send (found=false when no command matches). It does not apply the plan-mode marker — call Compose for that.

func (*Controller) DSTBrain

func (c *Controller) DSTBrain() *dstsetup.DSTRunner

DSTBrain returns the single DST facade (nil when DST is unavailable).

func (*Controller) DSTEnabled

func (c *Controller) DSTEnabled() bool

DSTEnabled reports whether DST per-step verification is active.

func (*Controller) ECPEngine

func (c *Controller) ECPEngine() *evolution.Engine

ECPEngine returns the evolution engine for ECP federation (may be nil).

func (*Controller) ECPSharedSecret

func (c *Controller) ECPSharedSecret() string

ECPSharedSecret returns the HMAC shared secret for ECP peer authentication.

func (*Controller) EnableInteractiveApproval

func (c *Controller) EnableInteractiveApproval()

EnableInteractiveApproval wires the Controller as the Asker (for `ask` tool) and installs an interactive permission gate (for approval prompts). Without this, headless runs resolve "ask" to allow. Deny rules remain active.

func (*Controller) HasRefs

func (c *Controller) HasRefs(line string) bool

HasRefs reports whether a line contains any resolvable @references, so a frontend can decide to resolve off its event loop only when needed.

func (*Controller) History

func (c *Controller) History() []provider.Message

History returns the executor's current message log.

func (*Controller) HookRunner

func (c *Controller) HookRunner() *hook.Runner

HookRunner returns the session's hook runner (nil-safe; may hold zero hooks).

func (*Controller) Host

func (c *Controller) Host() *plugin.Host

Host returns the running MCP host (nil when no plugins), for frontends that list servers / resolve MCP prompts.

func (*Controller) IsDSTAvailable

func (c *Controller) IsDSTAvailable() bool

IsDSTAvailable reports whether the DST brain is initialised and available.

func (*Controller) Jobs

func (c *Controller) Jobs() []jobs.View

Jobs returns the still-running background jobs for the status bar.

func (*Controller) Label

func (c *Controller) Label() string

Label returns the human-readable model label, e.g. "deepseek-flash".

func (*Controller) LastUsage

func (c *Controller) LastUsage() *provider.Usage

LastUsage returns the most recent turn's token telemetry (nil before the first turn).

func (*Controller) MCPPrompt

func (c *Controller) MCPPrompt(ctx context.Context, input string) (sent string, found bool, err error)

MCPPrompt resolves a "/mcp__server__prompt args…" line: it maps the positional args onto the prompt's declared arguments and fetches the rendered prompt from the MCP server (an async prompts/get). found is false when no such prompt exists; err carries a fetch failure. Honors ctx.

func (*Controller) Memory

func (c *Controller) Memory() *memory.Set

Memory returns the loaded memory snapshot (nil when memory is disabled), for frontends that surface a memory panel or the /memory command. The returned *Set is immutable — mutations go through QuickAdd / SaveDoc.

func (*Controller) NewSession

func (c *Controller) NewSession() error

NewSession rotates to a fresh session file, archiving the old one, and creates a new in-memory conversation so the next turn starts from scratch.

func (*Controller) PlanMode

func (c *Controller) PlanMode() bool

PlanMode reports whether plan mode is on.

func (*Controller) QuickAdd

func (c *Controller) QuickAdd(scope memory.Scope, note string) (string, error)

QuickAdd appends a one-line note to the doc-memory file for scope (project OK.md by default) — the write side of "#<note>". Returns the file written. Protected by a cap (256 entries) to prevent unbounded growth when notes are added without a corresponding turn to drain them.

func (*Controller) RemoveMCPServer

func (c *Controller) RemoveMCPServer(name string) (disconnected bool, err error)

RemoveMCPServer disconnects a live MCP server — its tools vanish from the next turn — and removes it from the config file. A server declared in .mcp.json disconnects for this session but returns on the next start.

func (*Controller) ResolveRefs

func (c *Controller) ResolveRefs(ctx context.Context, line string) (block string, errs []string)

ResolveRefs resolves the @references in a line into a single tagged context block (file/dir contents, MCP resource bodies), plus per-reference error strings for any that failed. An empty block means no references resolved. Safe to call off a frontend's event loop; honors ctx for the resource reads.

func (*Controller) Resume

func (c *Controller) Resume(sess *agent.Session, path string)

Resume loads a saved session into the executor.

func (*Controller) Run

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

Run sends input directly to the runner (bypassing Submit's slash dispatch and @-ref expansion). Used by headless/ACP callers that compose their own input.

func (*Controller) RunSkill

func (c *Controller) RunSkill(input string) (sent string, found bool)

RunSkill resolves a "/<name> args…" line against the loaded skills, returning the skill's rendered body to send as a turn (found=false when no skill matches). Invoking a skill by slash always inlines its body — the model reads and follows the playbook in the main loop; a subagent skill's isolation is only engaged when the model calls it via run_skill / the dedicated tool. The caller applies Compose for plan-mode/memory framing.

func (*Controller) Running

func (c *Controller) Running() bool

Running reports whether a turn is in flight.

func (*Controller) SaveDoc

func (c *Controller) SaveDoc(path, body string) (string, error)

SaveDoc overwrites a recognized memory doc with body — the save side of the desktop panel's in-place editor. Returns the file written.

func (*Controller) Send

func (c *Controller) Send(input string)

Send starts a turn with an already-composed message (the caller applied any plan-mode marker and @-ref expansion). Used by the chat TUI.

func (*Controller) SessionCache

func (c *Controller) SessionCache() (hit, miss int)

SessionCache returns cumulative cache hit/miss prompt tokens for the session.

func (*Controller) SessionDir

func (c *Controller) SessionDir() string

SessionDir returns the directory where session files are persisted ("" when persistence is disabled).

func (*Controller) SessionPath

func (c *Controller) SessionPath() string

SessionPath returns the full path to the current session file ("" when persistence is disabled).

func (*Controller) SetBaseContext

func (c *Controller) SetBaseContext(ctx context.Context)

SetBaseContext replaces the parent context used by runGuarded to spawn turn contexts. Set it before the first turn so cancellation propagates from the caller (e.g. server shutdown) into running turns. Safe to call concurrently with running turns — only the next spawned turn picks up the new context.

func (*Controller) SetBypass

func (c *Controller) SetBypass(on bool)

SetBypass turns YOLO/bypass mode on or off for the session: while on, every approval prompt is auto-allowed (writers and bash run without asking). Deny rules still block. Runtime-only — never written to config.

func (*Controller) SetDSTEnabled

func (c *Controller) SetDSTEnabled(v bool)

SetDSTEnabled turns per-step DST verification on or off.

func (*Controller) SetPlanMode

func (c *Controller) SetPlanMode(v bool)

SetPlanMode toggles plan mode on the controller and the underlying executor.

func (*Controller) SetSessionPath

func (c *Controller) SetSessionPath(path string)

SetSessionPath sets the path for this session's auto-save file.

func (*Controller) Skills

func (c *Controller) Skills() []skill.Skill

Skills returns the discoverable skills.

func (*Controller) Snapshot

func (c *Controller) Snapshot() error

Snapshot saves the current session to disk.

func (*Controller) Submit

func (c *Controller) Submit(input string)

Submith is the one-call entry for a simple frontend: it takes raw user input and does everything — slash-command dispatch, @-reference expansion, plan-mode composition — emitting all output as events.

type Options

type Options struct {
	Runner       agent.Runner
	Executor     *agent.Agent
	Sink         event.Sink
	Policy       permission.Policy
	Label        string
	SystemPrompt string
	SessionDir   string
	SessionPath  string
	WorkDir      string // project root for DST compile/test checks
	Host         *plugin.Host
	Commands     []command.Command
	Skills       []skill.Skill
	Hooks        *hook.Runner
	Memory       *memory.Set
	Cleanup      func()
	// BalanceURL/BalanceKey wire the active provider's optional wallet-balance
	// endpoint and bearer key; empty when the provider declares no balance_url.
	BalanceURL string
	BalanceKey string
	// MsgBus is the session-scoped message bus for pub-sub communication.
	MsgBus *bus.Bus
	// Jobs is the session-scoped background-job manager (nil disables background jobs).
	Jobs *jobs.Manager
	// Registry is the executor's live tool set, and PluginCtx the session-scoped
	// context; both are needed for hot-adding MCP servers via AddMCPServer.
	Registry  *tool.Registry
	PluginCtx context.Context
	// DSTBrain is the compile/test guard (nil when DST is unavailable).
	DSTBrain *dstsetup.DSTRunner
	// ProofChain accumulates per-turn verification results (compile/test/file checks)
	// and exposes a ProofSummary() for injection into turn context.
	ProofChain *core.ProofChain
	// AuditChain is the tamper-evident audit log for tool execution.
	// nil disables auditing.
	AuditChain *core.AuditChain
	// OnRemember is called when the user picks "always allow" — persists the rule.
	OnRemember func(rule string)
	// EnvDiagnosis is a cached environment+boot diagnostic string injected into
	// the user message every 5 turns (not into the system prompt) so the
	// cache-stable prefix stays warm. Empty disables injection.
	EnvDiagnosis string
	// Kernel carries civilization primitives (identity, recall, trust, learn).
	Kernel *kernel.Kernel
	// EvolEngine is the self-evolution engine for ECP federation endpoints (optional).
	EvolEngine *evolution.Engine
	// EvolSecret is the shared HMAC secret for ECP peer authentication.
	EvolSecret string
}

Options carries the already-built pieces setup assembles. Lifecycle metadata lets the controller mint and rotate session files; Host/Commands are surfaced to frontends that resolve MCP prompts and slash commands.

type SlashItem

type SlashItem struct {
	Label   string `json:"label"`
	Insert  string `json:"insert"`
	Hint    string `json:"hint"`
	Descend bool   `json:"descend"`
}

SlashItem is one slash-completion suggestion. Insert is the token text placed at the current argument position (callers replace from the token's start, see SlashArgItems' returned offset); Descend hints the menu to re-open one level deeper after accepting (e.g. "/mcp " → "/mcp add ").

func SlashArgItems

func SlashArgItems(line string, d ArgData) ([]SlashItem, int)

SlashArgItems completes the arguments of a management slash command (everything after the command word). It returns the suggestions filtered by the token being typed and the byte offset where that token begins, so a caller replaces just that token. Only structured commands participate (/mcp /model /skill /hooks); others yield nil. Single source of truth for CLI + desktop.

Jump to

Keyboard shortcuts

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