runtime

package
v0.31.0 Latest Latest
Warning

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

Go to latest
Published: Sep 3, 2026 License: MIT Imports: 84 Imported by: 0

Documentation

Overview

Package runtime: this file is the thin seam to the read-only intelligence Engine (internal/agent/introspect). The commands themselves were carved off the Session god-object into their own leaf package; Session supplies the Engine its state plus the few runtime-internal callbacks it needs and otherwise just delegates.

Package runtime is the agent loop: orient via the context compiler, call Claude, execute the tool calls it proposes (under the permission gate), capture every step as an event, and stop when the model is done. v0 is the minimal organism: state → context → LLM → tools → edit/command → diff → test → event.

Index

Constants

View Source
const (
	KindInstruction = "instruction"
	KindMemory      = "memory"
	KindReference   = "reference"
	KindHistory     = "history"
)

Generic context kinds. Callers classify their material into these; the engine orders by them and knows nothing more.

Variables

This section is empty.

Functions

func CodeQuery added in v0.10.0

func CodeQuery(ctx context.Context, root, query, scope string) (string, bool)

CodeQuery is the deterministic "where does X live" oracle over root — a ranked search with no model loop. Shared by the agent tool (Session.codeQuery) and the standalone `memcode mcp serve` memory server, so an external agent gets the same answer. ok=false marks a usage error (no terms, bad scope, timeout); a no-match result is ok=true with a guidance string.

func ForkSession

func ForkSession(root, srcID string) (string, error)

ForkSession copies srcID's saved transcript (and its checkpoints) to a fresh session id and returns it — the original session is untouched. Fork-from-disk is safe because the transcript persists at every turn boundary and callers refuse to fork mid-turn, so disk equals the live history. The episodic log (events.jsonl/transcript.md) is DELIBERATELY not copied: session search, recall, and the focus reducer scan every session dir, and a duplicated history would double-count what happened — the original keeps the episodic truth, and the fork starts logging its own from here. Checkpoints ride along (best-effort) so /rewind still reaches pre-fork turns in the fork.

func RenderThemeSample

func RenderThemeSample(width int) string

RenderThemeSample renders a tiny fixed diff snippet using the ACTIVE theme — the real diff add/del backgrounds, gutters, and syntax highlighting — so the /theme picker's live preview shows what code review actually looks like, not just chrome. It reads the active theme live (newDiffCtx), so it recolors as the picker previews.

func ResolveSession

func ResolveSession(root, ref string) (string, error)

ResolveSession maps a user reference to a resumable session id: ""/"latest" → the most recently saved transcript; otherwise an exact id or unique prefix ("sess_" optional). Only sessions WITH a saved transcript qualify — older sessions predating transcript persistence are recall-only.

func ResumableSessions

func ResumableSessions(root string) []string

ResumableSessions lists resumable session ids, newest first (for pickers).

Types

type AdminExecutor added in v0.18.0

type AdminExecutor func(ctx context.Context, name string, input json.RawMessage) (string, error)

AdminExecutor performs one admin tool operation (cmd-injected; see cmd/admin_tools.go). It is only invoked after the runtime's gate approves a mutation; read-only calls run directly.

type AgentResult

type AgentResult struct {
	Text      string
	ToolCalls int
	ServedBy  string // which model actually ran it
}

AgentResult is a sub-agent's report-back: its final text plus the telemetry callers surface (the tool-count + served-by on the marker line).

type AgentSpec

type AgentSpec struct {
	Task     string      // the self-contained instruction the sub-agent runs to completion
	ReadOnly bool        // read-only (no edits/mutating bash) vs a full mutating agent
	Scope    string      // optional subsystem/path tag (telemetry + scout focus)
	IterCap  int         // 0 = mode default
	Purpose  llm.Purpose // ledger attribution; also picks the run shape (Explore → scout prompt)
	Effort   wire.Effort // pin the sub-agent's thinking effort ("" = its own per-turn heuristic)
}

AgentSpec is everything that configures a sub-agent. Defaults (zero value) are a mutating, MainLoop-ish agent; callers set what they need.

type ApprovalDecision

type ApprovalDecision struct {
	Allow         bool   // run it
	Remember      bool   // and don't ask again for like commands (persist an approval rule)
	RememberScope string // when Allow: the chosen ApprovalScope.Key ("" = none / plain yes)
	Command       string // when Allow and non-empty: run THIS instead of the original
	Reason        string // when !Allow: why — fed back to the model so it can adjust
	Interrupt     bool   // STOP the whole turn (Esc / "No, stop") — the model does not get another call
	Redirect      bool   // when !Allow with a typed Reason: deny this action and skip its siblings, but let the turn CONTINUE so the model reads the feedback and responds (does NOT terminate)
}

ApprovalDecision is the user's structured answer. It generalizes yes/no into the four outcomes a real agent needs: allow · allow-with-edited-input · deny-with-reason · interrupt (stop going down this path entirely).

func Allowed

func Allowed() ApprovalDecision

Allowed is a plain yes.

func Denied

func Denied(reason string) ApprovalDecision

Denied is a no, optionally with a reason the model will see.

type ApprovalRequest

type ApprovalRequest struct {
	Title    string // the thing being approved — the command, or "edit <path>"
	Label    string // category header, e.g. "Bash command" / "Edit file"
	Detail   string // optional one-line description under the title
	Command  string // the command, when this is a command (so it can be edited/remembered)
	Cwd      string // working directory for a command (for the "don't ask again … in <dir>" scope)
	Editable bool   // whether "allow with edited command" / "don't ask again" apply (commands)
	Risk     string // risk class label
	// RememberScopes replaces the single default don't-ask-again option with one card
	// option per scope (MCP: remember this tool / remember the whole server). When set,
	// the card reads Execute / <scopes…> / Cancel and Cancel is a plain deny.
	RememberScopes []ApprovalScope
}

ApprovalRequest is a structured description of an action awaiting the user's OK. The metadata (Title/Label/Detail) lets a front-end render a rich prompt; Command + Editable enable "allow, but run this instead". This replaces the old bare prompt string — a boolean approver is too crude for a serious agent.

type ApprovalScope

type ApprovalScope struct {
	Key   string // returned in ApprovalDecision.RememberScope when chosen
	Label string // full option text on the card
}

ApprovalScope is one scoped "and don't ask again" option a request offers.

type AskOption

type AskOption = tools.AskOption

AskOption is one candidate answer — a concise Label plus an optional muted Description line. Aliased to the tool-input type so there's ONE shape from the model's JSON through to the rendered card.

type AskRequest

type AskRequest struct {
	Question string
	Options  []AskOption // 2-4 choices (label + optional description); the user may also type their own
}

AskRequest is a clarifying question the agent poses to the user (HITL).

type AskResponse

type AskResponse struct{ Answer string }

AskResponse is the user's answer (a chosen option or free text); empty means the user dismissed it (the agent should then proceed on its best judgment).

type BusyOwner

type BusyOwner int

BusyOwner says who owns the frontend's busy state. OwnerTurn is a scheduler transaction (queueing behind it is normal); OwnerAsync is a non-transaction operation (advisor, /compact) that a new turn must not trample.

const (
	OwnerNone BusyOwner = iota
	OwnerTurn
	OwnerAsync
)

type ChatState

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

ChatState holds the evolving state of an interactive session — the system prompt, the running message history, and the queued follow-ups — so any front-end (the line-REPL below or the TUI) can pump input lines via Submit while the runtime owns routing and the agent loop.

type ContextItem added in v0.13.0

type ContextItem struct {
	Kind    string `json:"kind"`
	Content string `json:"content"`
	Source  string `json:"source,omitempty"` // provenance label for the injected block header
}

ContextItem is one piece of supplemental context handed to the engine by a caller (the agent runtime, an API, CI). The engine stays ignorant of where it came from: Kind is a GENERIC content class, never an orchestration concept like "agent", "user", "channel", or "conversation". Those live above the engine and are flattened into these generic items before an invocation.

type Decision

type Decision struct {
	Kind DecisionKind
	Pos  int          // 1-based queue position (Queued / Coalesced)
	Tx   *Transaction // the started or queued transaction (nil for Steered)
}

Decision is the outcome of accept — for the UI to acknowledge.

type DecisionKind

type DecisionKind int

DecisionKind is what the scheduler did with an accepted line.

const (
	DecisionStarted      DecisionKind = iota // started a transaction now (was idle)
	DecisionQueued                           // queued behind the active transaction
	DecisionCoalesced                        // merged into the previous queued item (rapid paste)
	DecisionSteered                          // folded into the active transaction as a steer
	DecisionAwaitVerdict                     // planning, unclassified — classify, then Accept again with the verdict (nothing mutated)
	DecisionPlanDeferred                     // classified separate — the frontend parks it (nothing mutated)
	DecisionBusyDeclined                     // an async op owns busy while idle — declined (nothing mutated, no ghost tx to cancel)
)

type DrainSink

type DrainSink int

DrainSink is where drained deferred messages go.

const (
	SinkQueueBehind  DrainSink = iota // queue behind what was just accepted (the apply turn)
	SinkStartNow                      // route normally — nothing is ahead
	SinkCarryForward                  // re-park against the new plan session
)

func PlanDrainSink

func PlanDrainSink(exit PlanExit) DrainSink

PlanDrainSink is the ONE policy for replaying parked messages at a plan exit — the three hand-rolled drain loops used to each encode a different slice of it.

type GateInput

type GateInput struct {
	Phase        plan.Phase  // the plan machine's phase at submit/finalize time
	PlanEpoch    int         // epoch the verdict was computed against (0 = n/a)
	CurrentEpoch int         // the machine's epoch NOW (finalize time)
	Verdict      PlanVerdict // the relevance verdict, when one exists
	Busy         BusyOwner   // who owns the frontend's busy state
	// Internal marks a system-generated accept (the plan task itself, the apply
	// instruction, a revise, an already-classified deferred replay): it bypasses the
	// plan gate by construction (Phase zero) AND pre-marks a queued tx classified, so
	// the background follow-up classifier never judges system text against the active
	// task (folding "Begin implementing the approved plan" as a steer broke the apply).
	Internal bool
}

GateInput is the non-scheduler context a frontend supplies with each accept. The zero value is exactly today's plain-chat behavior: chatting, no verdict, not busy.

type GitStat

type GitStat struct {
	Files, Added, Removed                   int // unstaged working tree + untracked
	StagedFiles, StagedAdded, StagedRemoved int // index (git add'd)
}

GitStat is the working-tree summary surfaced in the footer cockpit: how many files are dirty and the +/- line churn, split into unstaged (working tree, incl. untracked) and staged (index). A clean tree is the zero value.

func (GitStat) Clean

func (g GitStat) Clean() bool

Clean reports whether the working tree has nothing uncommitted.

type Orientation

type Orientation struct {
	Repo             string
	Branch           string
	Subsystems       int
	Highlights       []string // top subsystem keys by activity (for a concrete prompt)
	ClaimsCurrent    int
	ClaimsStale      int
	ClaimsConflicted int
	Sources          int
}

Orientation is the "I know where I am and what I know about this repo" summary the TUI shows on the opening screen, so the first moment proves memcode is repo-aware rather than a blank chatbot.

type PlanExit

type PlanExit int

PlanExit names how a plan session ended, for the deferred-message drain policy.

const (
	ExitExecute PlanExit = iota // approved: parked messages run AFTER the apply turn
	ExitCancel                  // cancelled: nothing ahead — parked messages start now
	ExitNewPlan                 // a new plan is starting: carry leftovers into it
)

type PlanOpt

type PlanOpt = plan.Opt

PlanOpt is a functional option for EnterPlan — an alias for plan.Opt so existing callers (WithYolo) compile unchanged.

func WithTask

func WithTask(task string) PlanOpt

WithTask anchors this plan session to the task text that started it — the message the user typed, or the /plan argument. Alias for plan.WithTask.

func WithYolo

func WithYolo() PlanOpt

WithYolo suppresses human-in-the-loop questions during planning and auto-resolves them with the model's recommended choice. The TUI also auto-executes the plan without showing the approval selector. Alias for plan.WithYolo.

type PlanVerdict

type PlanVerdict int

PlanVerdict is the plan-relevance classifier's answer as data.

const (
	VerdictNone     PlanVerdict = iota // not classified (yet)
	VerdictRelated                     // continues/steers the plan being drafted
	VerdictSeparate                    // a separate ask — park it until the plan is done
)

type Result

type Result struct {
	SessionID    string   `json:"session_id"`
	Iterations   int      `json:"iterations"`
	ToolCalls    int      `json:"tool_calls"`
	WrongTurns   int      `json:"wrong_turns"` // failed tool calls
	FilesRead    int      `json:"files_read"`
	FilesChanged []string `json:"files_changed"`
	DiffLines    int      `json:"diff_lines"`
	InputTokens  int      `json:"input_tokens"`
	OutputTokens int      `json:"output_tokens"`
	Verified     bool     `json:"verified"` // a verification command passed
}

Result reports measurable outcomes of a session (used by the A/B harness).

type RouteAction

type RouteAction int

RouteAction is the single vocabulary for what happens to an incoming message.

const (
	ActStartTurn    RouteAction = iota // idle → run it now
	ActSteer                           // fold into the active transaction
	ActQueue                           // run after the active transaction
	ActCoalesce                        // merge into the last queued item (rapid burst)
	ActAwaitVerdict                    // planning, unclassified → classify first, re-enter with the verdict
	ActDeferForPlan                    // classified separate → park until the plan exits
	ActRejectBusy                      // an async op owns busy and nothing is active — decline politely
)

type Scheduler

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

Scheduler is the actor wrapper around schedState: ONE owner goroutine (run) holds the state and serializes every access through a command channel. Neither the TUI intake nor the executor mutates scheduler state directly — they only send commands and read replies — so there is no shared mutable state to race (the design constraint).

Drivers:

  • intake (TUI Update goroutine): Accept / Cancel / Snapshot — fast, synchronous round-trips; they never block on execution.
  • executor (TUI engine goroutine): on a "run" kick, TakeActive() → RunTransaction → Finish; DrainSteers mid-run at the runLoop safe boundary. TakeActive is NON-BLOCKING so the executor can keep selecting on its other inputs (sentinels).

The actor owns the per-transaction context: TakeActive mints context.WithCancel and stores the cancel, so Cancel() is just a command and the executor never owns cancellation state.

func NewScheduler

func NewScheduler(parent context.Context, obs SchedulerObserver, clock func() time.Time) *Scheduler

NewScheduler starts the actor goroutine; it runs until parent is cancelled. clock is injectable for tests — pass time.Now in production.

func (*Scheduler) Accept

func (s *Scheduler) Accept(line string, gate GateInput) Decision

Accept routes one submitted line against the current state (intake; never blocks on execution). gate carries the non-scheduler context (plan phase/epoch, relevance verdict, busy owner) — GateInput{} is plain-chat behavior. Returns what the scheduler did, for the UI to acknowledge; the AwaitVerdict/PlanDeferred/BusyDeclined kinds mutated nothing and expect the frontend to act (classify / park / decline).

func (*Scheduler) Cancel

func (s *Scheduler) Cancel() bool

Cancel aborts the active transaction (Esc/Ctrl-C): marks it cancelling, cancels its context, and DISCARDS the queue — interrupt means STOP, not advance to the next queued item. Reports whether there was an active transaction.

func (*Scheduler) DrainSeparate

func (s *Scheduler) DrainSeparate() (activeText, activeTitle string, items []separateAsk)

DrainSeparate returns and clears the buffered separate-task items, along with the active transaction's raw text and the classifier's synthesized title for it (for seeding a "current task" placeholder todo item the first time this fires — both go through the synthTitle guard, so raw prose never becomes a list title) — called at the runLoop safe boundary.

func (*Scheduler) DrainSteers

func (s *Scheduler) DrainSteers() []string

DrainSteers returns and clears the active transaction's pending steers — called at the runLoop safe boundary to fold `+input` into the active turn.

func (*Scheduler) Finish

func (s *Scheduler) Finish(result TransactionResult) (promoted bool)

Finish marks the active transaction terminal and promotes the next queued one (FIFO). Returns true when a transaction was promoted — the executor should kick itself to run it.

func (*Scheduler) FoldQueued

func (s *Scheduler) FoldQueued(expectActive string, ids []string) []string

FoldQueued promotes the named queued transactions into the active transaction as steers (the classifier's "related" verdict) and returns their folded texts. expectActive is the active-tx id the classifier judged against — the fold is dropped if the active tx changed since (so a refinement of a finished task can't steer an unrelated running turn).

func (*Scheduler) NoteSeparate

func (s *Scheduler) NoteSeparate(items []separateAsk, activeTitle string)

NoteSeparate records items the background follow-up classifier judged SEPARATE from the active task (the "not related" verdict) — they stay queued to run as their own turn later, but this buffers them so the runLoop safe boundary can track them on the todo list and give the model a brief FYI note (see DrainSeparate). activeTitle is the SAME classify call's synthesized title for the CURRENT task (empty when synthesis failed).

func (*Scheduler) PendingClassification

func (s *Scheduler) PendingClassification() (activeID, active string, items []*Transaction)

PendingClassification returns the active task's text and the queued transactions the background follow-up classifier hasn't examined yet (marking them examined). The classifier runs the cheap structured-output model OFF the actor goroutine, then calls FoldQueued with the ids it judged related to the active task.

func (*Scheduler) Snapshot

func (s *Scheduler) Snapshot() []string

Snapshot returns the queued transactions' texts (for a quiet UI indicator).

func (*Scheduler) TakeActive

func (s *Scheduler) TakeActive() (*Transaction, context.Context, bool)

TakeActive hands the active, not-yet-running transaction to the executor with its cancellable context. NON-BLOCKING: returns (nil, nil, false) when nothing is ready, so the executor stays free to handle its other inputs. The executor calls this on a "run" kick (after Accept reported Started, or after Finish promoted the next).

type SchedulerObserver

type SchedulerObserver interface {
	SchedulerChanged(activeID string, queued []string)
}

SchedulerObserver is notified (best-effort) when the active/queued set changes, so a front-end can render a quiet "(N queued)" indicator. The queue is runtime-managed; there is no user-facing queue-editing command in Phase 1.

type Session

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

Session runs one agent task against a project.

func New

func New(st store.Store, runner *llm.Runner, root, model string, mode permissions.Mode, out io.Writer) *Session

New constructs a Session.

func (*Session) AddObjective

func (s *Session) AddObjective(ctx context.Context, title string) (string, error)

AddObjective records a human-authored goal for this project (the `/goal` command). Objectives are never inferred; this is the user asserting one.

func (*Session) AddRedactSecrets

func (s *Session) AddRedactSecrets(values ...string)

AddRedactSecrets registers additional secret values with the session's redactor (e.g. the token /login just received) so they never reach transcripts, traces, or tool output.

func (*Session) Admin added in v0.18.0

func (s *Session) Admin() bool

Admin reports whether this is an admin session (the TUI swaps its slash set).

func (*Session) Answer

func (s *Session) Answer(ctx context.Context, scope, question string) (string, error)

Answer runs a read-only investigation to completion and returns the model's final text. It is the unit of a parallel explorer (a "reader" sub-agent): it orients on a scope, reads/searches under the read-only tool set, and never edits or runs MUTATING commands (it may run read-only shell commands like git log via the gated inspect shell) — so any number can run concurrently with no serialized-writer contention. Output is suppressed (the orchestrator owns presentation); every tool call is still recorded as an event.

func (*Session) ArchDoc

func (s *Session) ArchDoc() string

ArchDoc renders the repo's architecture diagrams verbatim from its docs (deterministic).

func (*Session) AskAdvisor

func (s *Session) AskAdvisor(ctx context.Context, question, effort string) (string, bool)

AskAdvisor sends a question (plus light session context) to the gateway's second-opinion advisor — a DIFFERENT vendor from the coding lanes; the gateway owns which model serves it — and returns its advice. effort is the reasoning depth (low|medium|high; "" → high). This is a deliberate user action (/advisor, or "Ask an advisor" in plan mode), not part of the coding-inference path. Returns (text, ok).

func (*Session) BrowserEnabled

func (s *Session) BrowserEnabled() bool

BrowserEnabled reports whether --chrome is active (browser tools are advertised and a Chrome session may be launched). Used by the TUI's /dispatch to forward the capability to spawned sub-agents.

func (*Session) CacheStats

func (s *Session) CacheStats() (read, write int)

CacheStats returns cumulative prompt-cache token counts (read ≈ 10% cost; write = tokens written to the cache). Shown under /debug.

func (*Session) Chat

func (s *Session) Chat(ctx context.Context) error

Chat runs the line-REPL interactive session: streamed stdin with time-based coalescing and routing, multimodal attachments, persisted events. The TUI drives the same StartChat/Submit/EndChat seams without owning stdin.

func (*Session) Checkpoints

func (s *Session) Checkpoints() []checkpoint.Manifest

Checkpoints lists this session's rewind points (turns that edited files), oldest first.

func (*Session) ClassifyFollowups

func (s *Session) ClassifyFollowups(ctx context.Context, active string, items []string) (verdicts map[int]followupVerdict, activeTitle string, ok bool)

ClassifyFollowups asks the cheap structured-output classifier which queued follow-ups refine the active task. Returns one verdict per index: Related marks a fold, and Title — present only for a SEPARATE verdict — is the classifier's concise, synthesized todo-list title (never the raw text). It ALSO returns activeTitle: the SAME classifier call's synthesized title for the CURRENT task itself (same call, no extra cost) — used to seed the placeholder todo item for the active task instead of clipping its raw text. ok=false means NO classification happened (error / timeout / missing context): the caller must treat the batch as unjudged — retry or leave it queued — never as a set of "separate" verdicts (an empty map read through Go's zero value is what used to turn every classify hiccup into untitled "separate tasks" pasted verbatim onto the todo list). Safe to call concurrently with the active turn (the metered Runner is concurrency-safe).

func (*Session) ClassifyPlanIntent

func (s *Session) ClassifyPlanIntent(ctx context.Context, text string) bool

ClassifyPlanIntent resolves the AMBIGUOUS middle of plan-request detection — the front-end's deterministic heuristic handles clear yes/no and only escalates here when "plan" is present but the intent is unclear. Lives on Session (not the introspect Engine) so it rides the shared judge plumbing (traced, failure-counted). Best-effort: any error/timeout/parse miss → false, so ambiguity never hijacks an ordinary turn into plan mode.

func (*Session) ClassifyPlanMessage

func (s *Session) ClassifyPlanMessage(ctx context.Context, text, task, draft string) (related bool, title string)

ClassifyPlanMessage asks the cheap structured-output classifier whether text continues the plan anchored at task (with draft — the pinned LastPlan, possibly empty — as context), or is a separate request that should be parked. task/draft are passed in, not read from s.planCtl, so the call is safe off-goroutine: take them from PlanGateSnapshot. Returns related=true (fold-in, the safe default) when there is no task anchor to judge against (e.g. a test that sets planCtl.Active directly without going through EnterPlan), and on ANY classify error/timeout/parse miss — an infra hiccup must never silently swallow a genuine follow-up by mistaking it for something separate. title is populated only for a related=false verdict and is the classifier's synthesized title, never raw text.

func (*Session) ClearCredentials

func (s *Session) ClearCredentials()

ClearCredentials disconnects the provider (the /logout path).

func (*Session) CloseBrowser

func (s *Session) CloseBrowser()

CloseBrowser tears down the Chrome process if one was launched. Safe to call when no browser session exists (nil-safe). Called at session end.

func (*Session) CommitGateChoice

func (s *Session) CommitGateChoice(ctx context.Context, commitFirst bool)

CommitGateChoice is the TUI's entry point when the PLAN SELECTOR carries the commit decision ("Commit first, then execute" / "Execute without committing") instead of a second card: it performs the commit when asked and marks the gate resolved so the apply turn doesn't re-ask.

func (*Session) CommitGateNeeded

func (s *Session) CommitGateNeeded(ctx context.Context) bool

CommitGateNeeded reports whether the plan selector should offer the commit choice: interactive, a git repo, a dirty tree, and no remembered preference (a remembered "commit"/"skip" is honored silently by commitGateOK).

func (*Session) Compact

func (s *Session) Compact(ctx context.Context, st *ChatState) string

Compact is the manual /compact entry point: force a compaction now and return a one-line result for the front-end to print (success line, or why it was a no-op).

func (*Session) Connected

func (s *Session) Connected() bool

Connected reports whether the session's provider has a usable backend — hosted gateway credentials OR a configured custom endpoint (the Phase C widening; the seam is provider.Connector). Providers without the Connector capability (test fakes, futures) count as connected — the mandatory-login gate only applies to the real lazy client.

func (*Session) ContextTokens

func (s *Session) ContextTokens() int

ContextTokens returns the input size of the latest MAIN conversation call — an estimate of how full the context window currently is (footer "ctx N%"). Read under dispMu (snapshot): the engine writes s.served mid-turn while View reads this every frame.

func (*Session) ContextWindow

func (s *Session) ContextWindow() int

ContextWindow returns the input window (tokens) the meter measures against. It prefers the serving backend's REAL window when the gateway reported one (the lane's max_model_len — often far smaller than the model-name default), so the meter warns before a self-hosted overflow instead of reading a comfortable 14%. Falls back to the model's nominal window (Anthropic turns report none).

func (*Session) DeferWhilePlanning

func (s *Session) DeferWhilePlanning(text, title string)

DeferWhilePlanning parks a message the plan-intake classifier judged SEPARATE from the plan: it never reaches the scheduler while planning (so it can't corrupt the draft), and is instead tracked on the todo list — same synthTitle guard noteSeparateRequests uses, so raw prose never lands on the list even when the classifier gave no title — and stays visible instead of silently vanishing until DrainPlanDeferred replays it. Unlike noteSeparateRequests, there is no live turn's *messages to append an FYI note into — this fires OUTSIDE any turn, off the composer's intake path.

func (*Session) DelegatedModel added in v0.31.0

func (s *Session) DelegatedModel() string

DelegatedModel reports the model delegated work runs on, resolved through policy. It exists for display (/status, /policy) — decision points resolve the target they care about rather than asking a general question.

func (*Session) DisplayModel

func (s *Session) DisplayModel() string

DisplayModel is what the cockpit (footer/banner//status) shows as THE model: the /model pin when one is set — the user picked it, so that's the identity, even before the first pinned turn serves (ServingModel would still show the pre-pin lane) — else the served/default model. The ⇄ line keeps reporting per-turn serving reality (absorbs included).

func (*Session) DrainPlanDeferred

func (s *Session) DrainPlanDeferred() []string

DrainPlanDeferred pops and clears every message parked by DeferWhilePlanning, FIFO — called on ExitPlan (Execute or Cancel) so parked messages run once the plan work that pushed them aside is actually done, exactly as the user asked.

func (*Session) DrainShellReportBacks

func (s *Session) DrainShellReportBacks() []ShellReportBack

DrainShellReportBacks returns the promoted commands that finished since the last call, each once, with their result redacted for hand-back to the model. Polled by the UI.

func (*Session) EffortOverride

func (s *Session) EffortOverride() string

func (*Session) Emit

func (s *Session) Emit(ctx context.Context, kind events.Kind, payload map[string]any)

Emit is the public alias of emit — the plan.Controller's Session interface needs a callable emitter, and interface methods must be exported. Same behavior as emit.

func (*Session) EndChat

func (s *Session) EndChat(ctx context.Context)

EndChat records the session-finished event and closes the episodic log. Idempotent enough for one call.

func (*Session) Endpoint

func (s *Session) Endpoint() (provider.Endpoint, bool)

func (*Session) EnterPlan

func (s *Session) EnterPlan(ctx context.Context, opts ...PlanOpt)

EnterPlan switches the session into plan mode: research-only tools, the reasoning model, and the planning system prompt. Records plan_started. Delegates to the plan.Controller machine; already planning/applying → no-op.

func (*Session) ExitPlan

func (s *Session) ExitPlan(ctx context.Context, approved bool)

ExitPlan leaves plan mode, restoring the prior model. approved distinguishes an /execute handoff (Approve: pins the contract, arms the apply turn) from a /cancel (Cancel: abandon); either way the lifecycle event is recorded.

func (*Session) ExtraMile

func (s *Session) ExtraMile() bool

func (*Session) GitStat

func (s *Session) GitStat(ctx context.Context) GitStat

GitStat computes the current working-tree diffstat. It shells out to git, so the TUI must call it OFF the render path (a tea.Cmd), cache the result, and re-run it on a throttle / at turn end — never per frame.

func (*Session) Intelligence

func (s *Session) Intelligence(ctx context.Context, command, arg string) (string, bool)

Intelligence runs a read-only memcode command for the TUI's "orient me" shortcuts.

func (*Session) InvalidateModels

func (s *Session) InvalidateModels()

InvalidateModels drops the Runner's control-plane snapshot (roles, byok coverage, credits state) so the next model call refetches — the hook for /login, /apikeys mutations, and anything else that changes selection inputs.

func (*Session) JobsRender

func (s *Session) JobsRender() string

JobsRender lists the running shells (by slot) for /jobs. Finished/failed attempts aren't shown — they live in the session log, not the user's working set.

func (*Session) KillAllJobs

func (s *Session) KillAllJobs()

KillAllJobs reaps every running job (session end — nothing orphans).

func (*Session) KillJobArg

func (s *Session) KillJobArg(arg string) string

KillJobArg powers /kill (and the agent's jobs-kill). The arg is a SHELL SLOT: empty stops the lone running shell (lists when several); a slot that isn't running shows what IS running instead of a dead-end.

func (*Session) Lanes added in v0.26.0

func (s *Session) Lanes() []provider.LaneInfo

Endpoint reports the ACTIVE custom endpoint when the session runs against an arbitrary OpenAI-compat backend instead of the memcode gateway (one-wire Phase C). ok=false on the hosted gateway, signed out, and providers without the Endpointer capability (test fakes). The TUI keys the /model picker, cost display, and login-card copy on this; the runtime keys tool gating on it. Lanes reports the attached family lanes (subscriptions + own keys), empty off the Laner seam (exclusive endpoint mode, test fakes).

func (*Session) LastError

func (s *Session) LastError() error

LastError returns the terminal error of the most recent turn (nil on success). Meaningful on the synchronous one-shot path (agent -c / resumed one-shot), where a non-nil result must surface as a non-zero exit code for scripting/CI.

func (*Session) LastText

func (s *Session) LastText() string

LastText returns the most recent assistant message — e.g. the just-proposed plan — so the front-end can hand it to the (stateless) advisor for review. The advisor has no session access, so without this the plan would never reach it.

func (*Session) Mode

func (s *Session) Mode() permissions.Mode

Mode returns the active permission mode.

func (*Session) Model

func (s *Session) Model() string

Model returns the active model id.

func (*Session) Orientation

func (s *Session) Orientation(ctx context.Context) Orientation

Orientation gathers the deterministic repo summary (no model call).

func (*Session) Personality

func (s *Session) Personality() string

Personality returns the chosen voice ("" = default).

func (*Session) PersonalityBlurb

func (s *Session) PersonalityBlurb(ctx context.Context) string

PersonalityBlurb returns a one-line greeting in the currently-set voice (best-effort).

func (*Session) PersonalityResolved

func (s *Session) PersonalityResolved() string

PersonalityResolved returns the concrete voice in effect ("" = default): the session's random roll when personality is "random", else the chosen voice. Display uses this; persistence keeps the literal choice.

func (*Session) Pin

func (s *Session) Pin() string

Pin returns the pinned model label ("" = Automatic).

func (*Session) PlanGateSnapshot

func (s *Session) PlanGateSnapshot() (task, draft string)

PlanGateSnapshot returns the plan anchor task + pinned draft atomically — the machine's own lock covers the read (Controller.Snapshot), so it is safe from the TUI's classify goroutine while the turn goroutine pins the contract at synthesis. Callers snapshot first, then pass the copies into ClassifyPlanMessage.

func (*Session) PlanPhaseEpoch

func (s *Session) PlanPhaseEpoch() (plan.Phase, int)

PlanPhaseEpoch returns the plan machine's phase and session epoch atomically — the pair the intake gate stamps on a submission so an async relevance verdict can be staleness-checked when it lands.

func (*Session) PlanPolicyOverride added in v0.31.0

func (s *Session) PlanPolicyOverride(target string) map[string]any

PlanPolicyOverride reports policy the user attached to the plan currently in flight, nil when none. It lives on the plan controller and dies with the plan.

func (*Session) PlanPresentable

func (s *Session) PlanPresentable() bool

PlanPresentable reports whether the most recent plan turn actually rendered a plan to approve. The TUI gates the "Plan ready" approval selector on this so an interrupted plan turn (Ctrl-C on a clarifying question) never raises a selector with no plan behind it.

func (*Session) PlanYolo

func (s *Session) PlanYolo() bool

PlanYolo reports whether the current plan was started with /yolo — suppress HITL questions during planning and auto-execute the plan without showing the selector.

func (*Session) Planning

func (s *Session) Planning() bool

Planning reports whether the session is currently in plan mode.

func (*Session) Policy added in v0.31.0

func (s *Session) Policy() *policy.Resolver

Policy exposes the resolver so callers at a decision point can read it, and so the /policy view and the policy tool can show and mutate the session layer.

func (*Session) PolicySummary added in v0.31.0

func (s *Session) PolicySummary() string

policySummary answers "how have I customized memcode?" — every target, its effective value, and WHERE each value came from. Per-field resolution across four layers is only comprehensible if the source is visible.

func (*Session) ReasoningDisplay

func (s *Session) ReasoningDisplay() string

ReasoningDisplay returns the HONEST reasoning-depth tier the SERVING model is actually using this turn ("high"/"max"/"medium"/"low"), or "" when the model exposes no thinking — for the status line. It is lane-aware (reasoningTier): a hybrid open model (GLM etc.) reasons at HIGH on ordinary turns and MAX on hard ones, matching the gateway's reasoning_effort policy; an Anthropic adaptive model shows its effort tier; anything else shows nothing. This replaces the old label that always read "effort: off" — which LIED on the cheap lane (GLM was actually at high/max).

func (*Session) Restricted added in v0.28.0

func (s *Session) Restricted() bool

Restricted reports whether the session is a restricted management console (admin): a limited slash whitelist, no repo/coding tools.

func (*Session) ReviewPlanWith added in v0.29.0

func (s *Session) ReviewPlanWith(ctx context.Context, plan, model string) (string, string)

ReviewPlanWith runs a ONE-SHOT plan critique on a model the USER named on the approval card ("Review with another model").

This is deliberately its own category: not utility inference (it is not plumbing, and the user chose it), and not routing (nothing selected it on the user's behalf). It is an explicit, user-directed second opinion, scoped to this one operation.

The chosen model runs on an ephemeral fork. The session pin, the workspace store, and the user store are untouched, the footer keeps showing the session model, and any revision the critique prompts runs back on the pinned model. Returns the critique prose and the model that produced it.

func (*Session) Rewind

func (s *Session) Rewind(seq int) ([]string, error)

Rewind restores every file edited from checkpoint seq onward to its state before that turn ran. Returns the restored paths.

func (*Session) Room

func (s *Session) Room() room.State

Room returns the current assessed room state (for the TUI badge / tests).

func (*Session) Root

func (s *Session) Root() string

Root returns the project root.

func (*Session) Run

func (s *Session) Run(ctx context.Context, task string) (Result, error)

Run executes a task end-to-end and returns its metrics.

func (*Session) RunFollowupClassifier

func (s *Session) RunFollowupClassifier(ctx context.Context, sched *Scheduler, kick <-chan struct{})

RunFollowupClassifier is the background loop (one goroutine per session) that drives the batched classify. kick is signaled by the front-end on each mid-turn queue submit; the loop debounces, then within followupMaxWait classifies the whole pending batch against the active task and folds the related ones into it as steers. Runs until ctx is done. The classify call runs here, OFF the scheduler's actor goroutine, so it never stalls intake.

func (*Session) RunPlan

func (s *Session) RunPlan(ctx context.Context, task string) (string, error)

RunPlan produces a single, detailed plan for task in plan mode — research-only tools, the reasoning model, the planning prompt — and returns the plan text. It makes NO changes; the interactive TUI is where a plan is iterated on and then executed.

func (*Session) RunShellLine

func (s *Session) RunShellLine(ctx context.Context, line string) bool

RunShellLine runs a `$`/`>` shell-lane line IMMEDIATELY, outside the turn scheduler — it's a LOCAL capture action (a command the user typed by hand), not an agent turn, so it must never queue behind an active turn. Reports whether the line was actually a shell route (false → the caller routes it normally through the scheduler). Safe to call concurrently with a running turn: the front-end owns its own busy/spinner state (the observer's Busy is a no-op) and command output is marshalled onto the UI thread, so a hand-run `$ git status` interleaves cleanly mid-turn.

func (*Session) RunTransaction

func (s *Session) RunTransaction(ctx context.Context, st *ChatState, tx *Transaction)

RunTransaction executes one scheduled transaction as a turn — the TUI executor's entry point. The scheduler already routed (this is an active, promoted transaction), so this just scores and runs. A `$`-prefixed transaction takes the direct-shell lane. Steers submitted into this transaction while it runs are folded in by runLoop (SetSteerDrain).

func (*Session) RunningJobs

func (s *Session) RunningJobs() int

RunningJobs is the count of live shells — the footer's "N shells".

func (*Session) RunningShells

func (s *Session) RunningShells() []jobs.View

RunningShells snapshots this session's background shells that are still running — the live surface behind the footer "N shells" segment and the idle-row indicator. In-memory and mutex-cheap, so the TUI may call it on the render path (unlike the agent count, which polls the filesystem).

func (*Session) ServedBy

func (s *Session) ServedBy() string

ServedBy returns a compact label for WHO served the last call — the cheap-lane model's short name (e.g. "glm-5p1") when the cheap lane served it, else the model short name ("sonnet"/"haiku"). Used to tag a scout's Explore marker so you can see, per fan-out, whether it hit the cheap lane or fell back to Anthropic. "" if nothing served yet.

func (*Session) ServedByok

func (s *Session) ServedByok() bool

ServedByok reports whether the LAST main call served on the user's own provider key — the footer's per-turn byok segment. Strictly last-turn state: recordServed writes it unconditionally, so a non-BYOK turn clears it.

func (*Session) ServingModel

func (s *Session) ServingModel() string

ServingModel returns the model that ACTUALLY served the last main call — which differs from Model() when a turn escalates (e.g. an apply turn runs on Opus while the session default is Sonnet). The footer uses this so it reflects reality, not the static default.

func (*Session) SessionID

func (s *Session) SessionID() string

SessionID returns the current session id (set by Run/StartChat).

func (*Session) SetAdmin added in v0.18.0

func (s *Session) SetAdmin(exec AdminExecutor)

SetBrowserEnabled enables the browser tools (--chrome). When enabled, the browser toolset is advertised to the model and a persistent Chrome session is lazily launched on the first browser tool call — headed (a visible window, ephemeral profile) for interactive sessions, headless for gateway children (SetBrowserHeadless). The Chrome process is torn down on CloseBrowser (called at session end). SetAdmin switches this session into admin mode: the settings assistant for the gateway and agents. Admin tools only, no shell, no editor.

func (*Session) SetApprover

func (s *Session) SetApprover(fn func(context.Context, ApprovalRequest) ApprovalDecision)

SetApprover replaces the approval callback. The TUI routes approvals through its own input instead of a separate os.Stdin reader. The callback receives a structured ApprovalRequest and returns a structured ApprovalDecision (allow / allow-with-edited-command / deny-with-reason / interrupt).

func (*Session) SetAsker

func (s *Session) SetAsker(fn func(context.Context, AskRequest) AskResponse)

SetAsker replaces the human-in-the-loop question callback. The TUI routes ask_user questions through its own selector instead of stdin.

func (*Session) SetBrowserEnabled

func (s *Session) SetBrowserEnabled(enabled bool)

func (*Session) SetBrowserHeadless added in v0.24.0

func (s *Session) SetBrowserHeadless(on bool)

SetBrowserHeadless makes the lazily-launched Chrome run headless — required for gateway/service children with no desktop session.

func (*Session) SetContext added in v0.13.0

func (s *Session) SetContext(items []ContextItem)

SetContext supplies caller-provided supplemental context for the run (agent runtime, API, CI). Injected every turn after project/user context, in the engine's fixed Kind order. The CLI and Desktop never call this, so their context is unchanged. Supplemental context is input for this run only — it does not write project memory.

func (*Session) SetCredentials

func (s *Session) SetCredentials(url, token string)

SetCredentials swaps fresh gateway credentials into the provider (the /login success path — no restart). No-op without the Connector capability.

func (*Session) SetEffortOverride

func (s *Session) SetEffortOverride(level string)

SetEffortOverride forces the per-turn thinking effort from the /effort command: "off", "medium", or "high" pin it every turn; "auto" (or anything else) clears the override and returns to the per-turn heuristic (effortForTurn). EffortOverride reports the current setting.

func (*Session) SetExtraMCPServers added in v0.28.0

func (s *Session) SetExtraMCPServers(servers map[string]mcp.ServerConfig)

SetExtraMCPServers adds server configs that were NOT discovered from .mcp.json (project/user/local config) — currently used for one thing: handing this run its own chrome-devtools-mcp connection to the user's existing Chrome, after the caller has already acquired a broker lease. The caller is responsible for that lease; this only wires the resulting MCP server into the session like any other.

func (*Session) SetExtraMile

func (s *Session) SetExtraMile(on bool)

SetExtraMile toggles "extra mile" mode: when on, a fact rides every turn and the gateway injects an above-and-beyond rule (edge cases + feature completeness) for planner/executor modes. ExtraMile reports the current state.

func (*Session) SetMode

func (s *Session) SetMode(m permissions.Mode)

SetMode changes the permission mode mid-session (e.g. via a `/mode` command).

func (*Session) SetModel

func (s *Session) SetModel(model string)

SetModel changes the model mid-session (e.g. via a `/model` command).

func (*Session) SetNoApprover added in v0.24.0

func (s *Session) SetNoApprover(on bool)

SetNoApprover marks this session as having no human to answer approval prompts (a detached job child). Tools whose every use would be denied (e.g. browser_eval at Dangerous outside allow-all) are then not advertised at all — a tool that can never run must not be offered.

func (*Session) SetNoContext

func (s *Session) SetNoContext(v bool)

SetNoContext puts the session in cold mode (no ContextPack) — for A/B eval.

func (*Session) SetObserver

func (s *Session) SetObserver(o UIObserver)

SetObserver attaches a UIObserver (see above). Pass nil to detach.

func (*Session) SetOutput

func (s *Session) SetOutput(w io.Writer)

SetOutput redirects the session's streamed output to w. The TUI points this at a writer that forwards bytes to the Bubble Tea program.

func (*Session) SetPersonality

func (s *Session) SetPersonality(p string)

SetPersonality sets the agent's voice (a built-in key or free-text custom voice); empty clears it back to the default. Tone only — the gateway guards it from affecting behavior. "random" rolls a concrete voice ONCE per session, here — so the ready line can name what the roll selected and the session speaks with one voice (a fresh roll each launch keeps the chaos; a per-request re-roll made every reply a different character and nothing displayable). Re-picking random re-rolls.

func (*Session) SetPin

func (s *Session) SetPin(label string, window int)

SetPin pins a concrete model for the session (the /model picker's choice): the gateway serves this model for every real request; invisible plumbing (classify/ compact) stays on the utility lanes. label is the gateway's sanitized short name ("sonnet", "glm-5p2"); "" = Automatic. window is the pin's context window from the picker list (0 = unknown → the SDK catalog sizes the meter). A pin change invalidates the learned lane budget/window — the next serve re-teaches them.

A pin change also drops thinking/redacted_thinking blocks from the live chat history (if a ChatState is attached via StartChat): those blocks are provider-specific — Anthropic validates signatures it issued, and a different model (even another Anthropic tier) can't vouch for them. Replaying foreign thinking produces "thinking blocks in the latest assistant message cannot be modified" (a hard 400). Text and tool blocks are provider-neutral and stay.

func (*Session) SetPolicy added in v0.31.0

func (s *Session) SetPolicy(r *policy.Resolver)

SetPolicy installs the resolved policy layers for this session. Called once at the cmd boundary, where the workspace and user files are loaded.

func (*Session) SetResume

func (s *Session) SetResume(id string)

SetResume asks the NEXT StartChat to re-enter session id with its saved history instead of minting a fresh session. Set it from the cmd layer (or a slash command) before the front-end starts the chat; StartChat consumes it.

func (*Session) SetSeparateDrain

func (s *Session) SetSeparateDrain(f func() (activeText string, activeTitle string, items []separateAsk))

SetSeparateDrain wires the interactive separate-task source (the transaction scheduler). runLoop calls it between tool iterations to track genuinely disparate follow-ups on the todo list instead of leaving them invisible in the queue.

func (*Session) SetServingDefault

func (s *Session) SetServingDefault(model string)

SetServingDefault records the gateway's everyday (cheap-lane) model so the footer/banner show what will actually serve — instead of the CLI's bootstrap identity — before any turn has run. Set once at startup (after asking the gateway /v1/models); read under dispMu.

func (*Session) SetSessionID added in v0.12.0

func (s *Session) SetSessionID(id string)

setSessionID assigns this Session's id AND ties the metered Runner to it, so every model call carries the session on the wire (the compat `user` field) for serving affinity + telemetry. Use this everywhere instead of writing s.sessionID directly. SetSessionID pins the session id used by the next StartChat, so a caller can control continuity itself: the gateway derives a stable id per conversation and pins it, and StartChat then does resume-or-create under that id instead of minting a fresh one. (Distinct from a leftover sessionID between two chats on one Session, which must still mint a new id.)

func (*Session) SetSkillRoots added in v0.13.0

func (s *Session) SetSkillRoots(roots []string)

SetSkillRoots supplies caller-provided EXTRA skill discovery roots (e.g. a gateway agent's own skills dir). They rank between repo-local and user-global skills, so an agent can carry capabilities without editing the project or the user's global skill set. Empty for the CLI and Desktop.

func (*Session) SetSteerDrain

func (s *Session) SetSteerDrain(f func() []string)

SetSteerDrain wires the interactive steer source (the transaction scheduler). runLoop calls it between tool iterations to fold mid-turn `+steer` input into the active turn.

func (*Session) SetTaskAttachments added in v0.15.0

func (s *Session) SetTaskAttachments(atts []input.Attachment)

SetTaskAttachments supplies caller-resolved attachments for the NEXT submitted turn (a gateway job carrying channel media — a photo texted to the bot, a PDF emailed to it). They merge into that turn's bundle and ride the normal attachment path (caps, downscaling, wire blocks), then clear.

func (*Session) SetToolNotify added in v0.27.0

func (s *Session) SetToolNotify(fn func(label string))

SetToolNotify installs a callback invoked once per tool call with a short human label ("bash(go test ./...)", "read_file(internal/sync.go)"). It is a lightweight activity tap for frontends that surface what a detached agent is doing right now (the job heartbeat); unlike toolLine it fires for quiet tools too. Nil detaches. Set before the session runs; not for mid-turn swaps.

func (*Session) SetToolPolicy added in v0.23.0

func (s *Session) SetToolPolicy(allow, deny []string) (unknown []string)

SetToolPolicy restricts the session to the given toolsets/tools (allow; empty = all) minus disabled ones (deny wins) — the gateway applies an agent's configured policy here. Unknown entries are reported, not silently dropped.

func (*Session) SetWidth

func (s *Session) SetWidth(w int)

SetWidth records the terminal width so rendered diffs can fill the full row with a background. The TUI calls this on resize.

func (*Session) Spend

func (s *Session) Spend() (in, out, cacheRead, cacheWrite int, usd float64)

Spend returns the session's token breakdown and estimated cost in USD (priced per response under each call's model; rates are approximate). Powers /cost.

func (*Session) SpendByBackend

func (s *Session) SpendByBackend() []ledger.BackendSpend

SpendByBackend returns per-backend usage, busiest first. One entry ("anthropic") in a classic session; two once the hybrid router is live.

func (*Session) SpendByPurpose

func (s *Session) SpendByPurpose() []ledger.PurposeSpend

SpendByPurpose returns per-purpose usage, most expensive first — so /cost can show where the money actually went (e.g. explore scouts vs the main loop vs synthesis).

func (*Session) StartChat

func (s *Session) StartChat(ctx context.Context) *ChatState

StartChat begins an interactive session: assigns a session id, loads remembered approvals, records the start event, and builds the system prompt. Pump input with Submit; finish with EndChat. When SetResume was called, the saved transcript is re-entered instead of minting a fresh session — the episodic log appends to the same session dir, so memory stays one thread.

func (*Session) Submit

func (s *Session) Submit(ctx context.Context, st *ChatState, line string)

Submit routes one raw input line and runs the resulting turn(s) to completion, draining any queued follow-ups. Output streams to the session writer; approvals go through the session approver. Blank lines are ignored.

func (*Session) Sync

func (s *Session) Sync(ctx context.Context, targets []config.SyncTarget) (string, error)

Sync regenerates the project overview and writes it to the selected AI-editor context files. It mirrors the `memcode sync` CLI pipeline (cmd/sync.go) but runs against the live session's store/runner/root — so the interactive /sync command needs no separate process. targets is the user's pick from the picker (empty + Everything=false means "nothing configured"); the overview is synthesized deterministically from repo facts, so this works without a model call.

func (*Session) SyncDetect

func (s *Session) SyncDetect() []memsync.DetectedTarget

SyncDetect scans the project root for known AI-editor context files (CLAUDE.md, AGENTS.md, .github/copilot-instructions.md, …) and reports which exist on disk and which are already managed by memcode. The /sync picker calls this to show the grid of toggleable targets before the user picks.

func (*Session) TailJobArg

func (s *Session) TailJobArg(arg string) string

TailJobArg powers /tail: the arg is a shell slot; empty tails the lone running shell.

func (*Session) ThinkingEffort

func (s *Session) ThinkingEffort() string

ThinkingEffort returns the current turn's thinking-effort label for the status line ("low"/"medium"/"high"/…), or "" when thinking is off (so the spinner shows it only while the model is actually thinking).

func (*Session) Tokens

func (s *Session) Tokens() (in, out int)

Tokens returns cumulative session token flow: total input the model saw (uncached + cache reads) and total output. Footer "↑in ↓out".

type ShellReportBack

type ShellReportBack struct {
	Command string
	Exit    int
	Failed  bool
	Output  string
}

ShellReportBack is a promoted foreground command that has finished and owes its result back to the model (redacted, ready to inject as a new turn). Identified by command, not shell slot — a finished job holds no slot.

func (ShellReportBack) ReportPrompt

func (b ShellReportBack) ReportPrompt() string

ReportPrompt renders a finished background shell (started or promoted) as the turn text handed to the model.

type SteerEvent

type SteerEvent struct {
	Text string
	At   time.Time
}

SteerEvent is a `+input` the user submitted while a transaction was active: recorded immediately, then folded into the SAME transaction at the next safe boundary in runLoop (after tool_results, before the next model call). It cannot affect an in-flight model call — that is what cancel is for.

type Transaction

type Transaction struct {
	ID        string // "tx_<n>" — unique within a session, deterministic for tests
	Text      string // the user intent (paste-expanded, routing marker stripped)
	State     TxState
	CreatedAt time.Time
	StartedAt time.Time
	EndedAt   time.Time

	Result TransactionResult
	// contains filtered or unexported fields
}

Transaction is the unit the scheduler manages. steers is unexported because it is mutated only through schedState (actor-serialized); everything else is read-only once the transaction is created, except the lifecycle fields the scheduler advances.

type TransactionResult

type TransactionResult struct {
	State TxState
	Err   string
}

TransactionResult is a transaction's outcome. v1 records only the terminal state and any error; the commented fields are the deliberate seam for Phase-3 undo (revert a completed transaction's edits via git/patch) — shaped now so it attaches cleanly later.

type TxState

type TxState string

TxState is the explicit lifecycle of a Transaction. Transitions are owned by the scheduler state core (schedState) and serialized by the actor; nothing mutates a transaction's state from two goroutines.

const (
	TxQueued     TxState = "queued"     // accepted while another was active; inert (no ChatState yet)
	TxRunning    TxState = "running"    // the active transaction; the only writer of ChatState
	TxCancelling TxState = "cancelling" // Esc/Ctrl-C requested; unwinding
	TxCompleted  TxState = "completed"  // finished normally (terminal)
	TxFailed     TxState = "failed"     // errored (terminal)
	TxCancelled  TxState = "cancelled"  // aborted before completion (terminal)
)

type UIObserver

type UIObserver interface {
	// Routed reports how a submitted line was classified.
	Routed(route input.Route, reason string)
	// QueueChanged reports the current queued-task texts (most-recent last).
	QueueChanged(items []string)
	// Busy reports whether a turn is actively running.
	Busy(busy bool)
	// Mood reports the running interaction-friction reading (for the TUI gauge).
	Mood(r mood.Reading)
	// Room reports the assessed room state (mode/intent/policy) for the TUI badge.
	Room(s room.State)
	// Todos reports the agent's current work-tracker checklist (the live region).
	Todos(list todos.List)
	// Tokens reports the running output-token count for the current turn (the ↓
	// counter): a live estimate between the API's usage events, snapped to the
	// authoritative value when it arrives.
	Tokens(output int)
	// Raw prints a block VERBATIM into scrollback — no markdown/prose processing,
	// internal whitespace and blank lines preserved exactly. Used by the `$`
	// direct-shell lane so command output is high-fidelity terminal output, not an
	// assistant-rendered artifact.
	Raw(text string)
}

UIObserver lets a front-end (the TUI) observe the interactive session's routing and lifecycle without scraping stdout. Every method is optional — the runtime nil-checks before calling — so a headless caller can ignore it. This is a tap on the existing event points in Submit, NOT a change to the agent loop itself.

Jump to

Keyboard shortcuts

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