agentruntime

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Aug 13, 2026 License: MIT Imports: 23 Imported by: 0

Documentation

Overview

Package agentruntime is the shared session-construction layer behind `yottacode run` (internal/oneshot), the TUI (internal/tui), and the ACP server (internal/acp). All three build the same "world" — registry, permissions, adapter, router, LSP manager, skills, subagents, MCP, compaction — and previously did so via two independently-drifting ~370/~850 line inline blocks. Builder.Build is the single place that construction happens now; callers layer their own presentation-specific state (Bubbletea Model, stdout streaming, checkpoints, recall) on top of the returned Runtime.

Index

Constants

This section is empty.

Variables

View Source
var EffortLevels = []string{"", "low", "medium", "high"}

EffortLevels is the closed set RebuildAdapterForEffort accepts, mirroring internal/tui/effort_picker.go's effortEntries — "" means provider default (no reasoning override injected).

Functions

func IsValidEffortLevel

func IsValidEffortLevel(level string) bool

IsValidEffortLevel reports whether level is one of EffortLevels.

func RebuildAdapterForEffort

func RebuildAdapterForEffort(rt *Runtime, level string) error

RebuildAdapterForEffort updates rt.ChatOptions.ReasoningEffort and reconstructs every adapter that bakes reasoning effort in at construction time — rt.Adapter and, when a router pair is configured for this session, rt.RouterAdapters — using exactly Build's own adapter-selection logic (RoutingEnabled → the freshly-rebuilt advisor adapter; otherwise a plain adapter.NewWithConfig), so a mid-session effort change can never silently knock a routed session off its advisor model the way an independent "just call NewWithConfig" rebuild could. Session-only: nothing is persisted to config.toml. No preflight re-probe — matches internal/tui/effort_picker.go's commitEffortChoice, which also skips it (the provider connection already worked at session start).

func RecomposeSystemPrompt

func RecomposeSystemPrompt(sess *session.Session, content string, headBytes int)

RecomposeSystemPrompt rewrites the session's system message. headBytes marks the stable cache prefix (the static base prompt ahead of the memory tail) — see adapter.Message.CacheHeadBytes. Exported because callers with a live prompt at construction time (oneshot) re-score memory against it after Build returns and need to overwrite what Build injected with the prompt-unaware memory.SystemPrompt.

func SetAdvisorRouting

func SetAdvisorRouting(rt *Runtime, enabled bool) error

SetAdvisorRouting toggles whether subagent dispatch and summarization route through rt.RouterAdapters' implementer/advisor pair. Mirrors internal/tui/cmd_router.go's applyRoutingOn/applyRoutingOff exactly — session-only (nothing persisted to config.toml), and deliberately does NOT touch rt.Adapter/rt.Cfg.Adapter: which model serves the main conversation is decided once, at Build time, by fileCfg.Router.RoutingEnabled() (see RebuildAdapterForEffort), not by this toggle. Returns an error when enabling is requested but no advisor/implementer pair is configured for this session — mirrors the TUI's own "/advisor on" guidance to configure one first.

Types

type Builder

type Builder struct{}

Builder constructs a Runtime from a SessionSpec. Stateless — safe to share across concurrent Build calls (each call only touches its own local state and the spec/session it's given).

func NewBuilder

func NewBuilder() *Builder

NewBuilder returns a Builder. A constructor exists (rather than a bare struct literal at call sites) so adding builder-level config later (e.g. injected clocks for tests) doesn't churn every caller.

func (*Builder) Build

func (b *Builder) Build(ctx context.Context, spec SessionSpec) (*Runtime, error)

Build constructs the full session world: session, memory, adapter, router, permissions, registry + tools, MCP, subagents, and the LoopConfig ready to hand to agent.Turn. It never calls os.Getwd()/os.Chdir() — every constructor here takes spec.Cwd explicitly, which is what makes this safe to call concurrently for N different sessions with different working directories (see SessionSpec.Cwd's doc comment).

type Runtime

type Runtime struct {
	Session *session.Session
	Fresh   bool

	Cfg      agent.LoopConfig
	Registry *agent.Registry

	Permissions   *permissions.Permissions
	AgentTool     *agent.AgentTool
	SubagentTasks *subagents.Registry
	MCPManager    *mcppkg.Manager

	Skills    []skills.Skill
	SkillTool *agent.SkillTool

	LSPManager      *lsp.Manager
	CodeMapProvider codemap.Provider

	Adapter        adapter.Client
	RouterAdapters *cli.RouterAdapters

	// ChatOptions is the live, as-constructed options snapshot Adapter
	// (and RouterAdapters) were built from — mirrors internal/tui's own
	// m.opts. Build seeds it (including any advisor-model override
	// applied to spec.ChatOptions.Model below); RebuildAdapterForEffort
	// mutates ChatOptions.ReasoningEffort and reconstructs every adapter
	// that bakes reasoning effort in at construction time. Session-only —
	// never written back to config.toml.
	ChatOptions cli.ChatOptions

	// RoutingAuto is the session-live mirror of fileCfg.Router.RoutingAuto()
	// at construction — whether subagent dispatch/summarization currently
	// route through RouterAdapters' implementer/advisor pair. Distinct from
	// FileCfg.Router.Mode (the on-disk setting Build read once): this field
	// is what SetAdvisorRouting toggles, session-only, matching
	// internal/tui/cmd_router.go's own m.routerMode/RouterModeAuto split
	// between "what's persisted" and "what's live."
	RoutingAuto bool

	// GHClient is the typed GitHub client backing the pr_*/issue_*/
	// git_push composite tools (see Build). Lazy and side-effect-free
	// to construct — the real auth/network cost is paid on first tool
	// call, not here — so every caller gets it unconditionally, same as
	// MCPManager. Exposed on Runtime because TUI's status bar also
	// reads it directly (resolveCurrentPRCmd) for a display-only
	// current-PR lookup that has no ACP equivalent.
	GHClient githubapi.Interface

	// RecallIndex backs the model-callable session_recall tool (see
	// Build) — a full-text/semantic index over every saved session at
	// ~/.yottacode/index.sqlite, shared across every caller (it's one
	// file on disk, not per-session state; SQLite's WAL mode is what
	// makes concurrent handles from oneshot/tui/acp safe at once). Nil
	// when recall.Open fails (non-fatal — see rt.Warnings); callers
	// must Close it themselves when the session ends (TUI's own
	// teardown does this already; CloseSession/Shutdown do it for ACP).
	// Backfilling the corpus (session.List + re-index) stays a
	// TUI-only background job — see internal/tui/run.go — since it's a
	// startup-time catch-up most valuable for the interactive,
	// long-running case; oneshot/ACP sessions still search whatever a
	// prior TUI/backfill run already indexed, they just don't trigger
	// a fresh corpus-wide catch-up themselves.
	RecallIndex *recall.Index

	// Model is the effective model name after Build's own resolution —
	// specifically, when advisor routing is enabled and overrides the
	// requested model, this is the *overridden* name. spec.Model (i.e.
	// spec.ChatOptions.Model) stays whatever the caller originally
	// passed in; callers that display or persist the active model (the
	// TUI's status bar, session bookkeeping) must read this field, not
	// spec.Model, or they'll show a stale name under routing.
	Model string

	PlanMode    *agent.PlanModeState
	AutoMode    *agent.AutoModeState
	YoloMode    *agent.YoloModeState
	LoopControl *agent.LoopControlState

	PlanStore *agent.PlanStore
	CwdRef    *agent.CwdRef

	// CmdSandbox is the optional session-scoped command sandbox backing
	// run_bash and worktree/dispatch sandbox inheritance. Nil preserves
	// HostSandbox behavior.
	CmdSandbox agent.Sandbox

	FileCfg         config.Config
	ExperimentalSet *experimental.Set
	Mem             memory.Loaded
	EmbedClient     *memory.EmbedClient

	// BaseSystemPrompt is the pre-memory composed prompt (profile framing
	// + skills section, before memory injection). Callers that know the
	// live user prompt at construction time (oneshot) can re-score memory
	// against it and overwrite the session's system message via
	// RecomposeSystemPrompt; callers that don't (TUI, ACP — the prompt
	// arrives later, via session/prompt) just keep what Build already
	// injected with the prompt-unaware memory.SystemPrompt.
	BaseSystemPrompt string

	// RawSystemPrompt is the base system prompt *before*
	// composeSystemPrompt/appendSkillsSection are applied — opts.
	// SystemPrompt (or defaultSystemPrompt) plus the dispatch addendum,
	// nothing else. TUI needs this raw form, not BaseSystemPrompt: its
	// own per-turn/reload paths (rebuildSystemPromptForTurn,
	// reloadMemoryNow, recomposeSystemPromptWithSkills) call
	// composeSystemPrompt/appendSkillsSection themselves, so handing them
	// the already-composed BaseSystemPrompt would double the profile
	// framing and skills section on every turn.
	RawSystemPrompt string

	// Warnings collects non-fatal construction issues (router degrade,
	// embedding model unreachable, MCP server start failures, unknown
	// experimental flags, skills/subagents load warnings) as pre-formatted
	// lines. The caller decides how to surface them — TUI startup
	// notices, oneshot stderr, an ACP diagnostic message.
	Warnings []string
}

Runtime is everything Builder.Build constructs for one session. Callers layer their own presentation-specific state on top: the Bubbletea Model/Config (TUI), stdout streaming (oneshot), or ACP protocol plumbing (internal/acp). Deliberately excluded (stay caller-specific): checkpoints, recall/FTS index, workspace trust, sensitivity posture, and the GitHub PR/Issue tool suite — all TUI-interactive or ghClient-coupled concerns with no ACP v1 equivalent yet.

func (*Runtime) Close

func (rt *Runtime) Close(ctx context.Context)

Close releases every process/goroutine resource owned by this Runtime. It is intentionally safe to call more than once: callers use it from both normal session-close paths and error/defer cleanup paths. Persistence stays caller-owned because TUI, oneshot, and ACP each have different save gates.

type SessionSpec

type SessionSpec struct {
	cli.ChatOptions

	// Cwd is the session's working directory. ChatOptions carries no cwd
	// field — oneshot/tui both derive it from os.Getwd() today. ACP must
	// derive it from session/new's cwd parameter instead, and Build must
	// never call os.Getwd()/os.Chdir() itself: it hosts N concurrent
	// sessions with potentially different cwds in one process.
	Cwd string

	// MCPServers is session-scoped and additive on top of the global
	// fileCfg.MCPServers Build loads from config.toml. Empty for
	// oneshot/tui (they only ever use the global set); ACP's session/new
	// carries this from the client's mcpServers param. On name collision
	// a session-scoped entry overrides the global one of the same name.
	MCPServers []config.MCPServer

	// DisableWorktreeTools excludes EnterWorktreeTool/ExitWorktreeTool
	// from the registry. Both tools call process-global os.Chdir() in
	// addition to updating the session's CwdRef (see
	// EnterWorktreeTool.swapCwd) — safe when one process hosts one
	// session at a time (oneshot, TUI), unsafe when one process hosts N
	// concurrent sessions with different cwds (ACP). oneshot/tui pass
	// false; acp passes true.
	DisableWorktreeTools bool

	// SupportsBackgroundDispatch controls AgentTool.AllowBackground and
	// DispatchTool.SupportsBackground. oneshot passes false (no
	// long-lived session to host async completions — its own rationale,
	// documented at the oneshot call site historically). tui and acp
	// both pass true: both are long-lived processes that can host
	// detached background work and surface its completion later.
	SupportsBackgroundDispatch bool

	// PreCompact is the optional pre-compaction snapshot hook threaded
	// into LoopConfig.Compaction.PreCompact. tui supplies its snapshot
	// writer (writePreSummarySnapshot); oneshot and acp leave this nil,
	// which agent.CompactionConfig treats as "no snapshot taken."
	PreCompact func(history []adapter.Message) (string, error)

	// DeferMCPStart skips starting the MCP manager and registering its
	// tools inside Build. Build still constructs Runtime.MCPManager (via
	// mcp.NewManager) so the caller has something to Start() later — it
	// just doesn't block Build on it. TUI sets this: it deliberately
	// starts MCP servers asynchronously via its own Bubbletea tea.Cmd
	// (internal/tui/cmd_mcp.go) so slow/hanging npx-based servers don't
	// delay first paint, and that existing machinery already knows how
	// to Start() the manager and register tools once it has a
	// *mcp.Manager and *agent.Registry to work with — nothing new is
	// needed there. oneshot and acp both leave this false: neither has a
	// progressive-UI concept, so Build's synchronous start-and-register
	// is exactly what they want (acp's session/new must return only
	// once the session is actually ready).
	DeferMCPStart bool
}

SessionSpec is the input to Builder.Build. It wraps cli.ChatOptions (the struct both current callers already resolve from flags/env/config via cli.Resolve) rather than re-deriving a hand-picked subset, plus the handful of fields ChatOptions has no room for.

Jump to

Keyboard shortcuts

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