repl

package
v0.0.0-...-299e031 Latest Latest
Warning

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

Go to latest
Published: Sep 7, 2026 License: MIT Imports: 69 Imported by: 0

Documentation

Overview

Interactive input helpers for the REPL.

The production TTY path uses native_input.go so the terminal's real cursor stays at the editing position; this is required for CJK IME candidate windows to follow the cursor. The Bubble Tea model in this file remains as a fallback for environments where raw native input cannot be started, and as the pure logic home for paste folding / history / slash suggestions.

Responsibilities beyond what bubbles/textinput gives us out of the box:

  1. Up/Down history navigation (seeded from memory.Store).
  2. Reject Enter on whitespace-only / non-printable-only buffers.
  3. Slash-command suggestion panel when the buffer starts with "/".
  4. Bracketed-paste folding: a multi-line or long single-line paste (≥ DefaultPasteFoldMinChars runes, configurable) is replaced inline by a `[Pasted text #N +L lines +C chars]` placeholder token, and the textinput widget treats the whole token as one atomic unit (cursor moves past it, Backspace deletes it whole). On submit, tokens expand back to the original pastes.

Native input owns platform terminal details; no OS-specific imports belong in this Bubble Tea fallback/model file.

Package repl implements the interactive multi-turn loop for codrax.

When the binary is launched with no --request, main.go hands control to this package. Each user line is dispatched as a fresh orchestrator.Run, with prior conversation injected into the request string via memory.Store.BuildContext. Slash commands manipulate the store directly without going through the orchestrator.

Multi-line input is supported: end a line with \ to continue on the next line. The continuation lines are joined with newlines.

Index

Constants

View Source
const (
	DefaultDataTaskMaxRepairRounds                 = 6
	DefaultDataTaskMaxDataRounds                   = 18
	DefaultDataTaskMaxNodeFailures                 = 2
	DefaultDataTaskMaxCustomTransformClassFailures = 3
)
View Source
const (
	// WriteIntentExplicitChange is the only write-intent value that can enter
	// write Auto Pilot. The route enum and operation=code_change are useful
	// hints, but they are too coarse to distinguish "learn/diagnose first"
	// from "modify repository bytes now".
	WriteIntentExplicitChange = "explicit_change"
	WriteIntentAnalysisOnly   = "analysis_only"
	WriteIntentAmbiguous      = "ambiguous"
)
View Source
const DefaultAttachedLogMaxBytes = 512 * 1024 * 1024 // 512 MiB

DefaultAttachedLogMaxBytes is the out-of-the-box 512 MiB cap on every REPL attach surface (/log + /htrace). Consumed by New when Config.AttachedLogMaxBytes is not set; the cmd layer populates Config from codrax.yaml :: log_attach_max_bytes so both CLI and REPL paths honour the same override. Mirrors cmd/root.go :: defaultAttachedLogMaxBytes. DefaultAttachedLogMaxBytes is the REPL-side default cap. Mirrors cmd.defaultAttachedLogMaxBytes — the two constants must agree so a unit test that bypasses initApp sees the same baseline as a real CLI run. Raised from 50 MiB → 256 MiB in 2026-05 and then to 512 MiB in 2026-06 to match systrace / perfetto / large hilog captures while keeping the ingestion hard ceiling in place.

View Source
const DefaultPasteFoldMinChars = 120

DefaultPasteFoldMinChars is the fallback threshold (in Unicode runes, not bytes) above which a single-line paste gets folded into a placeholder. Multi-line pastes fold unconditionally. Kept as a public constant so cmd/root.go can surface the same default in its help text, and so tests stay insensitive to yaml plumbing.

Runes, not bytes, because the user-facing setting is in characters (pay attention, CJK users: "你好" is 2 chars, 6 bytes). Keeping the internal comparison in runes makes the knob's unit match the UI.

Variables

This section is empty.

Functions

func IsConcreteOperationPolicy

func IsConcreteOperationPolicy(p TurnPolicy) bool

IsConcreteOperationPolicy reports whether a guarded TurnPolicy carries enough typed operation surface to start the operation pipeline. It consumes only structured fields: operation kind, side effects, and target surface. Plain `operation=investigate` over repo/log/trace/MCP observations is not concrete operation work and should remain in the analysis pipeline.

func NewDirectLLMTraceAdapter

func NewDirectLLMTraceAdapter(adapter llm.Adapter, renderer *render.Renderer, agent types.AgentName, stage types.PipelineStage) llm.Adapter

NewDirectLLMTraceAdapter wraps direct REPL-side LLM callers so data, operation, chitchat, and other non-BaseAgent planners get the same transparent request/stream UX as the code-analysis agents. The wrapper is render-only: it must never affect routing, gates, evidence, or execution.

func PrintOAuthAuthorizationComplete

func PrintOAuthAuthorizationComplete(out io.Writer, lang string)

PrintOAuthAuthorizationComplete renders the successful end of an OAuth browser authorization flow. It deliberately avoids token/cache details.

func PrintOAuthAuthorizationPrompt

func PrintOAuthAuthorizationPrompt(out io.Writer, lang, url string)

PrintOAuthAuthorizationPrompt renders the pre-REPL OAuth browser prompt with the same subdued prefix language as normal REPL info rows. cmd/root calls this before the REPL object exists; keeping the styling here avoids a second UX dialect in the startup path.

func PrintStartupHeader

func PrintStartupHeader(out io.Writer, version, repoRoot string)

PrintStartupHeader renders the top CODRAX identity row. cmd/root uses this when OAuth needs to prompt before the REPL object exists, so the browser URL still appears under the same visual header the eventual REPL banner uses.

func PrintTopologyDiscoveryComplete

func PrintTopologyDiscoveryComplete(out io.Writer, lang string, count int, elapsed string)

PrintTopologyDiscoveryComplete renders the successful end of startup topology discovery. elapsed should already be formatted for display.

func PrintTopologyDiscoveryError

func PrintTopologyDiscoveryError(out io.Writer, lang string, err error, elapsed string)

PrintTopologyDiscoveryError renders a startup topology discovery failure.

func PrintTopologyDiscoveryStart

func PrintTopologyDiscoveryStart(out io.Writer, lang string)

PrintTopologyDiscoveryStart renders the startup-only workspace topology discovery status in the same visual dialect as the REPL banner rows.

func RunCommandOperationCLI

func RunCommandOperationCLI(ctx context.Context, userLine string, policy TurnPolicy, cfg CommandOperationCLIConfig) (string, error)

RunCommandOperationCLI executes a typed operation turn in single-shot CLI mode. Progress and command output summaries are written to cfg.Progress (normally stderr); the returned string is the final user-facing Markdown answer that the caller should print to stdout.

func RunDataTaskCLI

func RunDataTaskCLI(ctx context.Context, request string, policy TurnPolicy, cfg DataTaskCLIConfig) (answer string, retErr error)

func SetMemoryContextTimeout

func SetMemoryContextTimeout(timeout time.Duration)

SetMemoryContextTimeout updates the REPL foreground memory-context guard. Non-positive durations keep the current value so operators cannot accidentally disable the fail-open boundary.

func SetSingleShotRoutePolicyTimeout

func SetSingleShotRoutePolicyTimeout(timeout time.Duration)

SetSingleShotRoutePolicyTimeout updates the single-shot route-policy wall clock. Contract differs from SetTurnPolicyClassifierTimeout on purpose: zero is a MEANINGFUL value (disable the outer deadline, rely on the adapter-native first-byte/stall/retry protections); only negative values keep the current guard.

func SetTurnPolicyClassifierTimeout

func SetTurnPolicyClassifierTimeout(timeout time.Duration)

SetTurnPolicyClassifierTimeout updates the REPL route-classifier wall clock. Non-positive durations keep the current value: callers should fail-soft to the code default rather than accidentally disabling the guard.

func SingleShotRoutePolicyTimeout

func SingleShotRoutePolicyTimeout() time.Duration

SingleShotRoutePolicyTimeout reports the single-shot route-policy budget (0 = disabled). An explicit setting is a total deadline; the built-in default applies per actual non-streaming request.

func TurnRouteHintFromPolicy

func TurnRouteHintFromPolicy(p TurnPolicy) types.TurnRouteHint

TurnRouteHintFromPolicy projects the guarded turn-policy result into pipeline-scoped typed metadata. The analyzer may use it to avoid the wrong initial pre-scan, but the hint is not evidence and never replaces emit_analysis.

Types

type ChitchatClassifier

type ChitchatClassifier interface {
	Classify(ctx context.Context, userLine, priorTurnHint string) (isChitchat bool, err error)
}

ChitchatClassifier decides whether a user turn should be routed to the chit-chat responder (bypassing the analysis pipeline) or to the normal pipeline path. A non-nil error means "could not decide" and the REPL's gate treats it as "fall through to pipeline" — the safe default, because over-analyzing a casual greeting wastes cycles but under-analyzing a real code question gives the wrong answer.

priorTurnHint is a compact 1-line summary of the previous turn the REPL provides for multi-turn disambiguation. Empty string means "no prior turn / first turn / hint unavailable" — classifier MUST behave byte-identically to the no-hint case in that scenario. Format (REPL constructs, classifier consumes as opaque text):

kind=<chitchat|pipeline|plan|shell> topic=<oneLine, ≤100 chars>

The hint exists to disambiguate continuation references (e.g. user types "expand 10" after a list_memory listing) that the classifier cannot route correctly from the current line alone.

func NewChitchatClassifier

func NewChitchatClassifier(adapter llm.Adapter) ChitchatClassifier

NewChitchatClassifier builds the default classifier. Nil adapter yields a classifier that errors on every Classify so the gate falls through to the pipeline (fail-safe).

type ChitchatResponder

type ChitchatResponder interface {
	Respond(ctx context.Context, userLine, priorContext string) (reply string, err error)
}

ChitchatResponder generates a conversational reply to a REPL turn that has been routed away from the analysis pipeline. The caller (REPL.chitchatDispatch) treats a non-nil error as a visible failure — it prints a warning and does NOT write the turn to memory, so a failed responder does not pollute future prior-conversation context.

func NewChitchatResponder

func NewChitchatResponder(adapter llm.Adapter) ChitchatResponder

NewChitchatResponder builds the default responder. Callers pass the adapter they want used; a nil adapter yields a responder that errors on every Respond so the REPL can print a clean "not configured" warning instead of panicking.

type CommandOperationAnswerer

type CommandOperationAnswerer interface {
	AnswerCommandOperationResult(ctx context.Context, userLine string, plan operation.CommandOperationPlan, result operation.CommandOperationResult, lang string) (string, error)
}

type CommandOperationCLIConfig

type CommandOperationCLIConfig struct {
	Planner       CommandOperationPlanner
	Policy        operation.CommandPolicy
	RepoRoot      string
	RuntimeAnchor string
	Language      string
	Providers     []operation.ProviderInfo
	Progress      io.Writer
}

CommandOperationCLIConfig is the single-shot companion to the REPL command operation path. It deliberately reuses the same planner, policy, lint, executor, repair, continuation, and answer synthesis contracts so CLI and REPL do not drift.

type CommandOperationContinuation

type CommandOperationContinuation struct {
	Complete bool
	Reason   string
	Request  operation.CommandOperationRequest
}

type CommandOperationContinuationPlanner

type CommandOperationContinuationPlanner interface {
	ContinueCommandOperation(ctx context.Context, userLine, repoRoot string, policy TurnPolicy, snapshot operation.CapabilitySnapshot, records []commandOperationResultRecord) (CommandOperationContinuation, error)
}

type CommandOperationEvaluator

type CommandOperationEvaluator interface {
	EvaluateCommandOperation(ctx context.Context, userLine string, records []commandOperationResultRecord, lang string) (operation.OperationEvaluation, error)
}

type CommandOperationPlanner

type CommandOperationPlanner interface {
	PlanCommandOperation(ctx context.Context, userLine, repoRoot string, policy TurnPolicy) (operation.CommandOperationRequest, error)
}

CommandOperationPlanner turns a typed route=operation turn into a command proposal. It only plans; execution is handled by operation.CommandExecutor after deterministic policy evaluation and user approval.

func NewCommandOperationPlanner

func NewCommandOperationPlanner(adapter llm.Adapter) CommandOperationPlanner

type CommandOperationPlannerWithHandoff

type CommandOperationPlannerWithHandoff interface {
	PlanCommandOperationWithHandoff(ctx context.Context, userLine, repoRoot string, policy TurnPolicy, snapshot operation.CapabilitySnapshot, recentOperationContext string) (operation.CommandOperationRequest, error)
}

type CommandOperationPlannerWithSnapshot

type CommandOperationPlannerWithSnapshot interface {
	PlanCommandOperationWithSnapshot(ctx context.Context, userLine, repoRoot string, policy TurnPolicy, snapshot operation.CapabilitySnapshot) (operation.CommandOperationRequest, error)
}

type CommandOperationRecordsAnswerer

type CommandOperationRecordsAnswerer interface {
	AnswerCommandOperationRecords(ctx context.Context, userLine string, records []commandOperationResultRecord, lang string) (string, error)
}

type CommandOperationReplanner

type CommandOperationReplanner interface {
	ReplanCommandOperation(ctx context.Context, userLine, repoRoot string, policy TurnPolicy, snapshot operation.CapabilitySnapshot, previous operation.CommandOperationPlan, result operation.CommandOperationResult) (operation.CommandOperationRequest, error)
}

type Config

type Config struct {
	Runner   Runner
	Store    *memory.Store
	Render   ResultRenderer
	Renderer *render.Renderer
	RepoRoot string
	Branch   string
	In       io.Reader // nil → interactive (bubbletea); non-nil → line-oriented
	Out      io.Writer

	// ReadRunAutoResume arms the read-run snapshot auto-resume lane.
	// DEFAULT OFF (user ruling 2026-07-30: the feature is not stable
	// enough to be ambient — an interrupted session's snapshot must
	// never silently revive a run). codrax.yaml read_run_auto_resume.
	ReadRunAutoResume bool

	// UI customization (used by line-oriented mode).
	Prompt           string // primary prompt, e.g. ">"
	PromptCont       string // continuation prompt, e.g. "."
	Banner           string // printed once at start; empty → default badge
	HeaderPrinted    bool   // true when cmd already rendered the CODRAX startup header
	ModelListLine    string // optional model-list summary shown before model config
	ModelSummaryLine string // optional resolved model summary

	// PasteFoldMinChars is the rune-count threshold above which a
	// single-line paste gets folded into a placeholder. Multi-line
	// pastes always fold regardless of length. Zero or negative →
	// DefaultPasteFoldMinChars. Surfaces
	// codrax.yaml :: repl_paste_fold_min_chars.
	PasteFoldMinChars int

	// Version and BuildTime are the build-stamped identifiers passed
	// from cmd. Empty strings render as "dev" / "unknown" so a bare
	// `go run` still produces a coherent banner / /version line.
	Version   string
	BuildTime string

	// Language toggles banner hint text between zh and en. Mirrors
	// codrax.yaml `lang` / CLI `--lang`. Only "en" (case-insensitive)
	// flips to English; every other value — including empty, "zh",
	// "off", "fr" — renders zh (the codrax.yaml default).
	Language string

	// MarkdownPreview is optional REPL UX: when the final answer markdown
	// transcript is written, this registers the file and prints a browser
	// preview URL below the existing "Markdown saved" line. Nil disables
	// preview hints without affecting output dumping.
	MarkdownPreview MarkdownPreviewer

	// OutputDumpDir and OutputDumpMax mirror the orchestrator's final
	// answer transcript dump settings for REPL-local answer paths
	// (local transform / summarize / translate and /chat). Pipeline
	// runs still write via the orchestrator so BusContext can carry the
	// path; local paths write here because they bypass Runner.Run.
	OutputDumpDir string
	OutputDumpMax int

	// HitraceConvert optionally overrides the /htrace convert executor.
	// Nil uses hitraceconv.ConvertFile.
	HitraceConvert HitraceConvertFunc

	// ChitchatResponder handles the /chat slash command. When nil,
	// /chat prints a "not configured" warning and takes no LLM action
	// — the command is recognised but inert. cmd/root.go constructs
	// the default LLM-backed responder from providers.yaml when
	// codrax.yaml `chitchat_enabled: true` (the default). Exposing
	// this as an interface lets unit tests inject a deterministic stub
	// without needing an LLM.
	ChitchatResponder ChitchatResponder

	// Memory is the read-side handle into the conversation memory
	// store. cmd/root.go wires memory.NewAdapter(store) here so the
	// chitchat tool-use loop can call recall_memory directly. nil
	// disables the tool-use path (chitchat falls back to single-shot
	// Chat with the keyword-injected priorContext only).
	Memory types.MemoryReader

	// EnvSettings forwards codrax.yaml's env_recommend_* knobs into
	// the REPL so /env probe / explain / cache use the right
	// timeouts and strategy filters. Zero value falls through to
	// types.DefaultEnvRecommendSettings via ResolvedEnvRecommendSettings.
	EnvSettings types.EnvRecommendSettings

	// ColorMode controls ANSI escape emission for diff rendering
	// (and any other code blocks added later). Default ColorAuto:
	// on for TTY, off for pipes. NO_COLOR env wins over everything.
	// Surfaces --color={auto,always,never} on the CLI.
	ColorMode render.ColorMode

	// ChitchatClassifier optionally runs a single LLM call before each
	// normal dispatch to decide whether to reroute the turn to the
	// chit-chat path. nil disables the gate; the REPL falls back to
	// explicit /chat only. Requires ChitchatResponder to be non-nil;
	// cmd/root.go ties both wires together via codrax.yaml
	// `chitchat_classifier_enabled: true`. Fail-safe: any classifier
	// error routes to the pipeline unchanged.
	ChitchatClassifier ChitchatClassifier
	// UserMode is the explicit user-facing task lane. Auto preserves the
	// existing classifier-driven route; code/operation/data/write bypass
	// classification for users who know which lane they want.
	UserMode UserMode

	// OperationEnabled is the feature gate for the future independent
	// computer-operation / artifact-generation route. Batch 1 wires only
	// classification safety: when false, route=operation is refused at the
	// REPL surface instead of falling into the source-analysis pipeline.
	OperationEnabled bool
	// OperationProviders are optional side-effect capable providers for the
	// operation route. Empty means plan-only: REPL shows the typed operation
	// plan and stops before execution.
	OperationProviders []operation.ProviderInfo
	// OperationPlanner is an optional LLM-backed command planner for
	// route=operation + operation_kind=computer_operation.
	OperationPlanner CommandOperationPlanner
	// DataTaskPlanner is an optional LLM-backed planner for route=data. It is
	// intentionally independent from the source-analysis pipeline and the
	// command-operation approval loop.
	DataTaskPlanner DataTaskPlanner
	// DataMaterialExtractor is an optional multimodal extractor used only when
	// a data plan declares non-text materials as required and no text evidence
	// is available to the deterministic runner.
	DataMaterialExtractor DataMaterialExtractor
	// DataTaskMaxRepairRounds bounds script-failure repair attempts in the
	// read-only data lane. Zero uses DefaultDataTaskMaxRepairRounds.
	DataTaskMaxRepairRounds int
	// DataTaskMaxDataRounds bounds execute/evaluate/continue batches in the
	// read-only data lane. Zero uses DefaultDataTaskMaxDataRounds.
	DataTaskMaxDataRounds int
	// OperationCommandPolicy controls command-operation approval/execution.
	OperationCommandPolicy operation.CommandPolicy
	// MCPServers is used only for explicitly configured operation providers.
	// Explorer/read-mode MCP exposure continues to live in the agent layer.
	MCPServers *mcp.Registry
	// MCPServerConfigs lets explicitly lazy operation providers start only
	// after a user approves the operation plan. Eager MCP exposure remains
	// owned by cmd/root.go and the agent layer.
	MCPServerConfigs []types.MCPServerConfig
	// OperationSkillConfigs are local manifest-backed operation providers. They
	// are descriptors until typed operation routing plus approval reaches the
	// provider execution path.
	OperationSkillConfigs []types.OperationSkillConfig
	// OperationPendingStore persists one unresolved operation-lane decision so
	// restarts can surface pending approvals/clarifications. It is independent
	// from write-mode PlanStore.
	OperationPendingStore *OperationPendingStore

	// PlanStore persists B0 write-mode ChangePlans for the REPL
	// session. Nil disables the /plan slash command family —
	// useful for tests and for single-shot invocations that never
	// construct a REPL. cmd/root.go wires a real store under
	// <runtime-anchor>/plans when the REPL starts.
	PlanStore *PlanStore

	// PlanGroupStore persists stage II multi-phase PlanGroups
	// for the REPL's /phase slash command family. Nil disables
	// /phase entirely — single-phase plans remain fully usable
	// via /plan / /approve as before. cmd/root.go wires a real
	// store under <runtime-anchor>/plans/groups/ alongside
	// PlanStore.
	PlanGroupStore *PlanGroupStore

	// WriteWorkflowRunStore persists the controller-engine write
	// workflow DAG under <runtime-anchor>/plans/workflows/. Nil
	// disables the write half of /workflow while leaving operation
	// provider workflows unchanged.
	WriteWorkflowRunStore *WriteWorkflowRunStore

	// WriteWorkflowIdentityMint mints the current WFID-1 repo identity for
	// a repo root. cmd/root.go wires it to
	// orchestrator.MintWriteWorkflowRepoIdentityForRepo so the bare
	// /workflow resume form can run the same single-point identity gate
	// (types.MatchWriteWorkflowRepoIdentity) the write turn runs, and so
	// the one-shot resume token records the canonical repo root it was
	// minted in — the REPL never grows a second canonicaliser or git
	// probe. Nil (tests / partial wiring) skips the bare-resume identity
	// verdict and stamps the trimmed raw root.
	WriteWorkflowIdentityMint func(repoRoot string) types.WriteWorkflowRepoIdentity

	// ReadRunSnapshotStore persists typed read-mode execution snapshots
	// under <runtime-anchor>/plans/read_runs/. Nil disables the advanced
	// /read-runs audit command family.
	ReadRunSnapshotStore *ReadRunSnapshotStore

	// RuntimeArtifactStore persists one-shot runtime observations (auto-routed
	// pasted logs/traces) as durable refs under <runtime-anchor>/runtime_artifacts/.
	// The REPL may reattach those bytes on a later typed repo/hybrid continuation
	// without putting the raw payload into the classifier prompt.
	RuntimeArtifactStore *RuntimeArtifactStore

	// FailureTaxonomy is the stage-3 reader interface for
	// /pitfalls inspection. The REPL only reads (list / clear);
	// the orchestrator owns Append. Nil = /pitfalls reports
	// "feature disabled."
	FailureTaxonomy FailureTaxonomyReader

	// AttachedLogMaxBytes caps every REPL attach surface (`/log
	// <path>`, `/log` paste mode, splitPastedLog auto-route) so a
	// runaway paste cannot balloon the REPL process memory. Mirrors
	// cmd's maxAttachedLogBytes — both are driven by
	// codrax.yaml :: log_attach_max_bytes. Zero or negative →
	// DefaultAttachedLogMaxBytes (512 MiB), matching the CLI default.
	AttachedLogMaxBytes int

	// AttachedTraceMaxBytes caps the perf-channel attach surface
	// (`/htrace <path>` and the
	// `/atrace` aliases). Defaults to AttachedLogMaxBytes when zero
	// or negative — a user who only configures the log cap still
	// gets symmetric trace handling. Set independently to override.
	AttachedTraceMaxBytes int

	// SettingsPath is the resolved codrax.yaml the CLI loaded (or "" if
	// none was found). Surfaced verbatim by the L2 gate's reject
	// message so the user knows WHICH file to edit. Optional; empty
	// falls back to a generic "in codrax.yaml" phrasing.
	SettingsPath string

	// WriteEnabled mirrors codrax.yaml :: write_enabled. Gates every
	// REPL transition into a non-read mode (`/mode write|apply|verify`,
	// `/approve`). When false, the REPL refuses the transition with a
	// clear error pointing at the yaml knob — the alternative was the
	// pre-fix silent state where /mode write accepted, the planner
	// dispatched, the analyzer failed in a confusing way ("hypothesis
	// coverage" / "context canceled"), and the user had no idea
	// write_enabled was the cause. cmd/root.go forwards
	// runtime_settings.WriteEnabled.
	WriteEnabled bool

	// WriteApprovalPolicy controls /approve for write ChangePlans.
	// Defaults to auto_safe when empty. It is independent from command
	// operation approval and from write_enabled's capability gate.
	WriteApprovalPolicy writeflow.ApprovalPolicy

	// WriteAutoInitRepo mirrors the resolved auto-init authorization
	// (yaml `write_auto_init_repo` OR CLI `--auto-init-repo`). When
	// true, the REPL's /approve flow skips the interactive y/N
	// consent prompt for bare/headless repos and silently
	// authorizes the orchestrator to scaffold. When false (default),
	// /approve runs DetectRepoState before dispatching and prompts
	// for consent if the target needs init.
	WriteAutoInitRepo bool

	// WriteScaffoldEnabled mirrors the resolved scaffold authorization
	// (yaml `write_scaffold_enabled` OR CLI `--allow-scaffold`). When
	// true, the orchestrator's plan pre-hook tolerates an empty target
	// directory; without it, empty-dir plan/apply dispatches fail-loud
	// with a hint at the two authorization surfaces. Forwarded into
	// the orchestrator on REPL boot so REPL sessions inherit the
	// startup-time authorization without the user having to repeat
	// the flag.
	WriteScaffoldEnabled bool

	// RuntimeAnchor is <CWD>/.codrax/ — used by /worktree gc to
	// locate the worktree base under <RuntimeAnchor>/worktrees/.
	// Empty disables the gc subcommand (the slash dispatcher
	// surfaces a typed warning).
	RuntimeAnchor string

	// WorktreeKeepTTL + WorktreeKeepMaxCount mirror the resolved
	// codrax.yaml knobs so /worktree gc uses the same caps as
	// startup. Zero on either disables that gate.
	WorktreeKeepTTL      time.Duration
	WorktreeKeepMaxCount int

	// Topology is the multi-repo discovery snapshot for RepoRoot.
	// Populated by cmd/root.go::initApp; consumed by /repos. Nil
	// disables the slash command (handler surfaces a typed warning).
	Topology *topology.RepoTopology

	// MultiRepoEnabled mirrors codrax.yaml :: multi_repo_enabled.
	// When false, /repos still works (it shows a hint pointing at
	// the yaml gate) but Phase 4 multigraph routing is bypassed.
	MultiRepoEnabled bool

	// MultiRepoMaxActive mirrors codrax.yaml :: multi_repo_max_active.
	// /repos cap <N> overrides this value session-locally.
	MultiRepoMaxActive int

	// MultiRepoInactivePreviewCount mirrors codrax.yaml ::
	// multi_repo_inactive_preview_count. Threaded through to BusContext
	// so context/builder.go's L0 LLM advisory can decide how many
	// out-of-active sub-repos to surface in the prompt.
	MultiRepoInactivePreviewCount int

	// Multigraph is the cmd/root-supplied carrier handle the REPL
	// reads to render the /repos listing (auto-active row marker
	// + color). Stored as any so the REPL stays free of an
	// internal/tool/repomap/multigraph import. The REPL probes for
	// the ActiveSlugSnapshot() method via type-assert; nil disables
	// the auto-active marker so the listing falls back to the
	// 2-state pinned/inactive view.
	Multigraph any

	// InitialFocusSlugs pre-populates the session focus pin map at
	// REPL boot. Used by the --focus CLI flag (cmd/root.go); REPL
	// users who pin via /repos focus reach the same map through the
	// command handler. Empty / nil = no pin (default REPL boot).
	InitialFocusSlugs []string

	// OnMultiRepoFocusChange is invoked by the REPL when the user
	// runs /repos focus or /repos unfocus. cmd/root.go wires this to
	// app.multigraph.SetFocus so the next Run picks up the change.
	// Nil disables the propagation (focus state stays REPL-local).
	OnMultiRepoFocusChange func(slugs []string)

	// OnMultiRepoCapChange is invoked by /repos cap. cmd/root.go
	// wires this to app.multigraph.SetCap.
	OnMultiRepoCapChange func(n int)

	// OnMultiRepoRefresh is invoked by /repos refresh. cmd/root.go
	// rebuilds app.multigraph from the freshly-discovered topology
	// so the next Run sees the new sub-repo set.
	OnMultiRepoRefresh func(newTopology *topology.RepoTopology)
}

Config holds all dependencies for constructing a REPL. Using a struct keeps the constructor readable as the field count grows and lets tests inject an io.Reader for scripted input.

type DataMaterialExtractor

type DataMaterialExtractor interface {
	ExtractDataMaterials(ctx context.Context, repoRoot, outputRoot string, materials []dataquery.NonTextRequiredMaterial) ([]dataquery.MaterialExtraction, error)
}

func NewDataMaterialExtractor

func NewDataMaterialExtractor(adapter llm.Adapter) DataMaterialExtractor

type DataTaskCLIConfig

type DataTaskCLIConfig struct {
	Planner         DataTaskPlanner
	RepoRoot        string
	RuntimeAnchor   string
	Language        string
	MaxRepairRounds int
	MaxDataRounds   int
	Progress        io.Writer
	ResumePath      string
}

DataTaskCLIConfig is the single-shot companion to the REPL data workflow. It intentionally shares the same planner interfaces and deterministic runner as the REPL path so CLI and REPL do not drift into different data-lane semantics.

type DataTaskContinuationPlanner

type DataTaskContinuationPlanner interface {
	ContinueDataTask(ctx context.Context, userLine, repoRoot string, policy TurnPolicy, candidates []dataquery.CandidateFile, records []dataTaskWorkflowRecord) (dataquery.TaskPlan, error)
}

type DataTaskContinuationPlannerWithDeferred

type DataTaskContinuationPlannerWithDeferred interface {
	ContinueDataTaskWithDeferred(ctx context.Context, userLine, repoRoot string, policy TurnPolicy, candidates []dataquery.CandidateFile, records []dataTaskWorkflowRecord, deferred dataquery.TaskPlan) (dataquery.TaskPlan, error)
}

type DataTaskEvaluator

type DataTaskEvaluator interface {
	EvaluateDataTask(ctx context.Context, userLine, repoRoot string, records []dataTaskWorkflowRecord, lang string) (dataquery.Evaluation, error)
}

type DataTaskEvaluatorWithDeferred

type DataTaskEvaluatorWithDeferred interface {
	EvaluateDataTaskWithDeferred(ctx context.Context, userLine, repoRoot string, records []dataTaskWorkflowRecord, deferred dataquery.TaskPlan, lang string) (dataquery.Evaluation, error)
}

type DataTaskPlanner

type DataTaskPlanner interface {
	PlanDataTask(ctx context.Context, userLine, repoRoot string, policy TurnPolicy, candidates []dataquery.CandidateFile) (dataquery.TaskPlan, error)
}

func NewDataTaskPlanner

func NewDataTaskPlanner(adapter llm.Adapter) DataTaskPlanner

type DataTaskRepairPlanner

type DataTaskRepairPlanner interface {
	RepairDataTask(ctx context.Context, userLine, repoRoot string, policy TurnPolicy, candidates []dataquery.CandidateFile, previous dataquery.TaskPlan, executionError string) (dataquery.TaskPlan, error)
}

type DataTaskResultPatchPlanner

type DataTaskResultPatchPlanner interface {
	ProposeDataResultPatch(ctx context.Context, userLine, repoRoot string, previous dataquery.TaskPlan, partial dataquery.Result, violations []dataquery.DataTaskViolation, records []dataTaskWorkflowRecord, lang string) (dataquery.DataResultPatchPlan, error)
}

type DataTaskTypedRepairPlanner

type DataTaskTypedRepairPlanner interface {
	RepairDataTaskWithViolation(ctx context.Context, userLine, repoRoot string, policy TurnPolicy, candidates []dataquery.CandidateFile, previous dataquery.TaskPlan, executionError string, violation dataquery.DataTaskViolation) (dataquery.TaskPlan, error)
}

type ErrUnsettledPlanExists

type ErrUnsettledPlanExists struct {
	Existing PlanInfo
}

Save writes plan as JSON to <planDir>/<plan.ID>.json. The parent directory is MkdirAll-ed. Plan content is re-marshalled with indent for operator readability. Returns the written absolute path or an error with context.

Idempotency: a second Save with the same plan.ID OVERWRITES the prior file. Plans are immutable-once-applied conceptually but the REPL's /plan clear workflow sometimes regenerates to the same ID; a stricter "no-overwrite" rule would force the caller to double-bookkeep. ErrUnsettledPlanExists is returned by PlanStore.Save when the project already has an unsettled plan and the caller is trying to create a new one. The single-pending-plan invariant says only merged / rejected / applied_failed are terminal; anything else blocks new plan creation. The error carries the offending plan's PlanInfo so the caller can render a user-actionable message.

func (*ErrUnsettledPlanExists) Error

func (e *ErrUnsettledPlanExists) Error() string

type FailureTaxonomyReader

type FailureTaxonomyReader interface {
	// All returns a snapshot of every persisted pattern,
	// newest LastSeen first. Empty when the cache is fresh
	// or the feature is disabled.
	All() []types.FailurePattern

	// Clear wipes the cache file + zeroes the in-memory
	// copy. Idempotent — missing file is success.
	Clear() error
}

FailureTaxonomyReader is the REPL-side interface to the stage-3 Failure Taxonomy cache. The REPL only inspects (list / show / clear); the orchestrator owns Append at reflector-emit time. internal/orchestrator's FailureTaxonomyStore satisfies this interface.

type GroupInfo

type GroupInfo struct {
	ID        string
	Path      string
	Status    string
	Decision  string
	Phases    int
	ActiveIdx int
	ModTime   int64
}

GroupInfo is the lightweight metadata List returns. Mirrors PlanInfo's shape — same idea (avoid loading full JSON when the caller only needs to enumerate).

type HitraceConvertFunc

type HitraceConvertFunc func(context.Context, hitraceconv.Options) (hitraceconv.Result, error)

HitraceConvertFunc is the REPL's narrow conversion seam. Production uses hitraceconv.ConvertFile; tests inject a stub to assert slash-command option plumbing without fabricating a full binary trace container.

type LocalResponder

type LocalResponder interface {
	RespondLocal(ctx context.Context, userLine, priorContext, lastAnswer, presentationDirective string) (string, error)
}

LocalResponder is the optional extension interface the dispatcher prefers when route=RouteLocal and the wired ChitchatResponder satisfies it. The contract:

  • userLine is the trimmed current message,
  • priorContext is the BuildContext-assembled prior conversation,
  • lastAnswer is the full text of the most recent assistant response (may be multi-paragraph),
  • presentationDirective is the directive the classifier emitted (may be empty).

Implementations MUST NOT claim to read the repository, invent file paths / line numbers, or introduce evidence not present in lastAnswer / priorContext / userLine. The constraint is enforced in the system prompt rather than schema because the local path is free-form prose, not a tool call.

type MarkdownPreviewer

type MarkdownPreviewer interface {
	RegisterMarkdown(path string) (string, error)
}

MarkdownPreviewer registers the markdown transcript written by the orchestrator and returns a browser URL for the current REPL process. Kept as a small interface so tests can stub it and the REPL does not depend on the concrete HTTP server package.

type MemoryReader

type MemoryReader = memoryReaderAlias

MemoryReader re-exports types.MemoryReader so chitchat callers do not have to import internal/types just to satisfy the optional interface above. Pure type alias — same value, no wrapping.

type OperationPendingStore

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

OperationPendingStore persists the single unresolved operation-lane decision for a REPL workspace. It is intentionally separate from PlanStore: ChangePlan approval changes source bytes in a worktree, while operation approval controls computer-operation/provider execution and must not be mixed with source-plan state.

func NewOperationPendingStore

func NewOperationPendingStore(dir string) *OperationPendingStore

NewOperationPendingStore constructs a store rooted at dir. The directory is created lazily only when a pending operation actually needs persistence.

func (*OperationPendingStore) Clear

func (s *OperationPendingStore) Clear() error

func (*OperationPendingStore) Load

func (s *OperationPendingStore) Load() (operationPendingSnapshot, bool, error)

func (*OperationPendingStore) Path

func (s *OperationPendingStore) Path() string

func (*OperationPendingStore) SaveCommandClarification

func (s *OperationPendingStore) SaveCommandClarification(pending pendingCommandClarification) error

func (*OperationPendingStore) SaveCommandPlan

func (s *OperationPendingStore) SaveCommandPlan(plan operation.CommandOperationPlan) error

func (*OperationPendingStore) SaveProviderOperation

func (s *OperationPendingStore) SaveProviderOperation(pending pendingProviderOperation, workflow *operation.WorkflowInstance) error

type PlanGroupStore

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

PlanGroupStore manages on-disk PlanGroup files (stage II multi-phase write-mode containers). Mirrors PlanStore's shape; lives at <runtimeAnchor>/plans/groups/.

Single-phase plans never create a group, so this store is empty in pre-stage-II flows (back-compat).

Thread-safety: a per-store mutex guards every disk op so concurrent /phase show / .Save / .Clear calls from the REPL (interactive event handler + REPL Loop slash dispatch) don't race. Cross-process safety is whatever the underlying filesystem provides — atomic rename guarantees readers never see a half-written file, but two processes saving the same group ID simultaneously would race on the rename target. Not a concern in practice (group IDs embed PID and unix-nano).

func NewPlanGroupStore

func NewPlanGroupStore(planDir string) *PlanGroupStore

NewPlanGroupStore constructs a store rooted at <planDir>/groups/. Mirror of NewPlanStore — caller passes the same plan dir so /plan list and /phase show coexist in the same anchor.

func (*PlanGroupStore) Clear

func (s *PlanGroupStore) Clear(id string) error

Clear deletes a group's on-disk file. Idempotent — missing file returns nil. Used by /phase rollback's terminal path (group rolled back) and by tests.

func (*PlanGroupStore) FindActiveGroup

func (s *PlanGroupStore) FindActiveGroup() (*types.PlanGroup, error)

FindActiveGroup returns the most recent non-terminal group, or (nil, nil) when no group is in flight. /phase show uses this when invoked without an explicit group ID — the typical case is the operator just had write_analyzer emit a sequential proposal and wants to see what's queued.

func (*PlanGroupStore) GroupDir

func (s *PlanGroupStore) GroupDir() string

GroupDir returns the absolute on-disk directory the store uses. Exposed for /phase show banner rendering and tests.

func (*PlanGroupStore) List

func (s *PlanGroupStore) List() ([]GroupInfo, error)

List enumerates every group file under groupDir, newest first. A missing dir returns (nil, nil) — fresh user has no groups yet, which isn't an error.

func (*PlanGroupStore) Load

func (s *PlanGroupStore) Load(id string) (*types.PlanGroup, error)

Load reads a group by ID. Returns (nil, nil) when the file doesn't exist (caller treats as "no such group").

func (*PlanGroupStore) Save

func (s *PlanGroupStore) Save(g *types.PlanGroup) (string, error)

Save writes the group to <groupDir>/<group-id>.json atomically. Returns the absolute path so callers can stash a pendingGroupPath the way they do pendingPlanPath.

type PlanInfo

type PlanInfo struct {
	ID      string // plan-<nano>-<pid>
	Path    string // absolute file path
	SizeB   int64  // file size in bytes
	ModTime int64  // last modification unix-nano
	Status  string // PlanStatus* from types/change_plan.go; empty when JSON
	// was unreadable (List logs + continues so one corrupt file
	// doesn't break enumeration).
	UnvalidatedCount int    // number of static-check stages skipped
	HasCritique      bool   // plan_critic produced a non-empty review
	PhaseGroupID     string // non-empty when this plan is one phase of a multi-phase group
	PhaseIndex       int    // 0-based phase index within the group; meaningful only when PhaseGroupID != ""
	// WorktreeMissing fires when the plan's persisted
	// WorktreePath has been deleted out-of-band (e.g., user
	// rm -rf'd .codrax/worktrees/...). Populated by the
	// banner-detection path so unsettledBanner can tag the
	// plan as "(orphaned worktree)" instead of pretending it's
	// still fully actionable. Commit 42 P1.
	WorktreeMissing bool
}

PlanInfo is the lightweight metadata PlanStore.List returns for each plan. Avoids loading full plan JSON when the caller only needs to enumerate (e.g. /plan ls output).

type PlanStore

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

PlanStore is the REPL's persistent index of ChangePlans. Single- shot mode writes plan JSONs via cmd/root.go's writePlanFile and then exits — no index needed. REPL mode needs persistence so:

  • Users can inspect their last plan via `/plan show` without re-running the planner.
  • `/plan clear` removes stale pending state without wiping files (the files stay on disk as an audit trail).
  • Multiple plans from one session coexist (e.g. user refined the request and emitted a new plan; PlanStore keeps both).

Thread safety: REPL is single-goroutine per process but the store's methods use a mutex anyway because the same directory may be shared with a single-shot invocation running in parallel (two shells, two processes) and file operations are not atomic. Each plan lives in its own <id>.json so writes do not conflict, but the mutex serialises the metadata cache.

Format: plans live as JSON under <planDir>/<id>.json. plan.ID embeds unix-nano + pid (format: plan-<nano>-<pid>) so no two files ever collide. The directory is created on first Save; a fresh user with an empty baseDir sees no residue.

B0 minimum: Load, Save, List, Clear. B1 will add richer metadata indexing (per-session filter, pending-vs-applied filter, trigger-turn lookup) once the write-mode workflows stabilise.

func NewPlanStore

func NewPlanStore(planDir string) *PlanStore

NewPlanStore constructs a PlanStore rooted at planDir. The directory is NOT created eagerly — Save does it on first write so read-only inspection of a fresh install doesn't leak an empty directory. Absolute paths pass through; relative paths stay relative (caller is responsible for absolutising upstream, which cmd/root.go does).

func (*PlanStore) Clear

func (s *PlanStore) Clear(id string) error

Clear removes <id>.json from the plan directory. Returns nil when the file is already absent (idempotent). Used by /plan clear to discard a plan the user has decided against.

Side effects: removes the JSON file. Does NOT remove any git worktree artifacts — those are managed by the worktree package's Discard / PruneDeadSessions path independent of plan files.

func (*PlanStore) List

func (s *PlanStore) List() ([]PlanInfo, error)

List enumerates every <id>.json file under planDir and returns their metadata sorted by ModTime descending (newest first). A missing planDir returns (nil, nil) — fresh user has no plans and that's not an error.

Errors during per-file stat are logged (via fmt.Fprintf to stderr) but do not abort — one corrupt entry should not block the /plan show command.

func (*PlanStore) Load

func (s *PlanStore) Load(id string) (*types.ChangePlan, error)

Load reads <planDir>/<id>.json and decodes the ChangePlan. Returns (nil, err) when the file is missing, unreadable, or the JSON does not match the ChangePlan schema. Callers typically use this in /plan show and /approve workflows; the REPL treats a load error as "plan was deleted by hand" and prompts the user.

func (*PlanStore) PlanDir

func (s *PlanStore) PlanDir() string

PlanDir returns the directory this store manages. Used by the REPL banner / /plan output to show users where their plans live.

func (*PlanStore) Save

func (s *PlanStore) Save(plan *types.ChangePlan) (string, error)

func (*PlanStore) SaveForTest

func (s *PlanStore) SaveForTest(plan *types.ChangePlan) (string, error)

SaveForTest writes a plan JSON to disk WITHOUT enforcing the single-pending-plan invariant. Tests use this to construct multi-plan fixtures (multiple statuses, recovery scenarios) that would otherwise be impossible to reach through normal user flows — at runtime, Save's invariant guarantees no two unsettled plans can coexist, but tests legitimately need to seed those states.

Production code MUST use Save. This helper has no callers outside _test.go files.

func (*PlanStore) Settle

func (s *PlanStore) Settle(planID, newStatus, reason string) error

Settle is the public funnel for transitioning a plan to a terminal status. /merge → PlanStatusMerged. /reject → PlanStatusRejected. /plan clear deletes the file outright and does NOT call Settle.

Reason is recorded as RejectionReason on the plan when newStatus is PlanStatusRejected; ignored otherwise. The MergedAt / RejectedAt timestamps are stamped here so callers don't have to.

Returns os.ErrNotExist when the plan file is missing; the caller surfaces a clear "plan not found" message.

func (*PlanStore) UpdateStatus

func (s *PlanStore) UpdateStatus(id, status string, appliedAt *time.Time) error

UpdateStatus reads the plan JSON, mutates the Status (and optionally AppliedCommitSHA / WorktreePath / AppliedAt when the caller passes non-zero values), and rewrites the file. Used by the apply stage hook + the verify stage hook to track plan lifecycle so /plan list can show which plans were applied vs failed.

Idempotent: calling with the same status re-serialises without behavioural change. Missing plan file → error with the path; the caller typically logs and proceeds because failure to persist status doesn't break the apply / verify flow itself.

Thread-safe against concurrent reads via s.mu but NOT against concurrent writers; the REPL is single-goroutine so this is fine for codrax's current usage model.

type ProviderOperationAnswerer

type ProviderOperationAnswerer interface {
	AnswerProviderOperationResult(ctx context.Context, userLine string, records []providerOperationResultRecord, lang string) (string, error)
}

type ProviderOperationEvaluator

type ProviderOperationEvaluator interface {
	EvaluateProviderOperation(ctx context.Context, userLine string, records []providerOperationResultRecord, lang string) (operation.OperationEvaluation, error)
}

type REPL

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

REPL drives the interactive prompt.

func New

func New(cfg Config) *REPL

New constructs a REPL from a Config.

func (*REPL) Loop

func (r *REPL) Loop() error

func (*REPL) MultiRepoActiveCap

func (r *REPL) MultiRepoActiveCap() int

MultiRepoActiveCap returns the effective LRU cap (session override if set, else the Config value). Phase 4 consumes this when sizing MultiGraph.

func (*REPL) MultiRepoFocusSnapshot

func (r *REPL) MultiRepoFocusSnapshot() map[string]bool

MultiRepoFocusSnapshot returns a copy of the session-pinned slug set. Safe to call from any goroutine; the returned map is owned by the caller.

func (*REPL) Topology

func (r *REPL) Topology() *topology.RepoTopology

Topology returns the current topology snapshot pointer (may be nil). Snapshot is treated as immutable; refresh swaps the pointer.

type ReadRunSnapshotInfo

type ReadRunSnapshotInfo struct {
	ID               string
	Path             string
	Request          string
	TaskGraphHash    string
	TaskNodeCount    int
	NodeStatusCount  int
	ReadFileCount    int
	AcceptedEvidence int
	ModTime          int64
}

type ReadRunSnapshotStore

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

func NewReadRunSnapshotStore

func NewReadRunSnapshotStore(planDir string) *ReadRunSnapshotStore

func (*ReadRunSnapshotStore) Clear

func (s *ReadRunSnapshotStore) Clear(id string) error

func (*ReadRunSnapshotStore) ClearAll

func (s *ReadRunSnapshotStore) ClearAll() (int, error)

ClearAll removes every persisted read-run snapshot (the /clear verb's snapshot arm — user ruling 2026-07-30: after /clear no stale run may auto-revive). Returns the number of removed snapshots.

func (*ReadRunSnapshotStore) List

func (*ReadRunSnapshotStore) Load

func (*ReadRunSnapshotStore) LoadAutoResumeCandidate

func (s *ReadRunSnapshotStore) LoadAutoResumeCandidate(requestHash, repoRoot string) (*types.ReadRunSnapshot, error)

func (*ReadRunSnapshotStore) LoadComparablePrior

func (s *ReadRunSnapshotStore) LoadComparablePrior(snapshot types.ReadRunSnapshot) (*types.ReadRunSnapshot, error)

func (*ReadRunSnapshotStore) RunDir

func (s *ReadRunSnapshotStore) RunDir() string

func (*ReadRunSnapshotStore) Save

func (s *ReadRunSnapshotStore) Save(snapshot *types.ReadRunSnapshot) (string, error)

type ResultRenderer

type ResultRenderer func(*types.BusContext) string

ResultRenderer turns a finished BusContext into the user-facing response text. main.go owns the canonical implementation.

type Runner

type Runner interface {
	Run(request, repoRoot, branch string) (*types.BusContext, error)
}

Runner is the orchestrator-shaped surface the REPL needs. Defined here as an interface so tests can stub it without pulling in the full pipeline.

type RuntimeArtifactRef

type RuntimeArtifactRef struct {
	SchemaVersion int       `json:"schema_version"`
	ID            string    `json:"id"`
	Kind          string    `json:"kind"`
	Path          string    `json:"path"`
	Bytes         int       `json:"bytes"`
	SHA256        string    `json:"sha256"`
	Source        string    `json:"source,omitempty"`
	CreatedAt     time.Time `json:"created_at"`
}

func (RuntimeArtifactRef) Valid

func (r RuntimeArtifactRef) Valid() bool

type RuntimeArtifactSnapshot

type RuntimeArtifactSnapshot struct {
	SchemaVersion int                `json:"schema_version"`
	Log           RuntimeArtifactRef `json:"log,omitempty"`
	Trace         RuntimeArtifactRef `json:"trace,omitempty"`
	UpdatedAt     time.Time          `json:"updated_at"`
}

type RuntimeArtifactStore

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

func NewRuntimeArtifactStore

func NewRuntimeArtifactStore(root string) *RuntimeArtifactStore

func (*RuntimeArtifactStore) Load

func (s *RuntimeArtifactStore) Load(ref RuntimeArtifactRef, maxBytes int) (payload string, err error)

func (*RuntimeArtifactStore) LoadLatest

func (s *RuntimeArtifactStore) LoadLatest() (snapshot RuntimeArtifactSnapshot, err error)

func (*RuntimeArtifactStore) Put

func (s *RuntimeArtifactStore) Put(kind, payload, source string) (RuntimeArtifactRef, error)

func (*RuntimeArtifactStore) SaveLatest

func (s *RuntimeArtifactStore) SaveLatest(snapshot RuntimeArtifactSnapshot) error

type SingleShotTurnPolicyClassifier

type SingleShotTurnPolicyClassifier interface {
	ClassifyPolicySingleShot(ctx context.Context, userLine, priorTurnHint string, hasPriorAnswer bool) (TurnPolicy, error)
}

SingleShotTurnPolicyClassifier is the single-shot (CLI, non-REPL) lane of the route-policy classifier. Implementations MUST apply at most ONE outer wall clock — singleShotRoutePolicyTimeout — and MUST NOT re-apply the interactive turnPolicyClassifierTimeout anywhere on this path, so the adapter's own retry ladder stays effective for the one unretryable classification of the process. cmd/root.go prefers this lane when the wired classifier implements it and falls back to ClassifyPolicy only for stub classifiers.

type TurnPolicy

type TurnPolicy struct {
	Route                     TurnRoute
	NeedsRepoAccess           bool
	NeedsOperationAccess      bool
	NeedsDataAccess           bool
	Operation                 string // chat | transform | summarize | translate | elaborate | investigate | computer_operation | artifact_generation | ...
	OperationKind             string // optional more precise operation capability kind
	DataTaskKind              string // optional data lane kind, e.g. data_cleaning | data_join | data_aggregation
	WriteIntent               string // explicit_change | analysis_only | ambiguous
	Source                    string // current_message | last_answer | prior_context | repo | mixed
	CurrentSourceEvidenceMode types.TurnRouteCurrentSourceEvidenceMode
	RiskLevel                 string // none | low | medium | high
	SideEffects               []string
	TargetSurface             string // desktop | browser | file_artifact | office_doc | spreadsheet | slides | external_system | unknown
	RequiresConfirmation      bool
	Confidence                float64 // 0..1; <0.4 demotes to repo
	Reason                    string
	PresentationDirective     string // free-form, e.g. "mermaid", "markdown table", "brief 3-bullet"
	RequiresDiagram           bool   // precise current-turn hard visual authority; never inferred from directive text
}

TurnPolicy is the structured classification result. All fields optional for stub implementations; the dispatcher applies ApplyTurnPolicyGuards before acting on any TurnPolicy so missing or self-contradictory fields cannot drive a wrong route.

func ApplyTurnPolicyGuards

func ApplyTurnPolicyGuards(p TurnPolicy, hasPriorAnswer, hasAttachment bool) TurnPolicy

ApplyTurnPolicyGuards patches obvious self-contradictions in a TurnPolicy before the dispatcher acts on it. The guards are deterministic and structural — no keyword matching. Each guard covers a SPECIFIC failure mode the LLM may produce; documented inline so future maintainers can audit each rule against the production trace it was added for.

hasAttachment is the structural fact that a runtime log / perf trace is sticky on the REPL session this turn (sticky /log, sticky /htrace, or splitPastedLog auto-route). The guard pair for it lives at the bottom: an unread attachment + no prior answer + route=local is structurally impossible to fulfill (the local responder cannot consume the attachment), so the route is demoted to repo.

func (TurnPolicy) PresentationAuthority

func (p TurnPolicy) PresentationAuthority() types.PresentationAuthority

bindTurnPresentationAuthority prevents an LLM-generated display suggestion from becoming current-turn user authority. A non-empty directive must be a contiguous verbatim span of the current message. This is exact provenance validation, not a keyword/semantic scan: Codrax neither decides whether a word means "diagram" nor upgrades an explicit false. The classifier-owned typed boolean still supplies the modality decision, while the byte-backed span proves that the associated request actually came from the user. PresentationAuthority is the single typed current-turn presentation carrier the classifier hands to a runner: the byte-anchored directive span and the precise hard-visual bit travel together (§40.54 fold-in). Dispatch arms copy this value whole — never one field without the other — so a runner can never see a bool-only or directive-only shape the classifier did not emit.

type TurnPolicyClassifier

type TurnPolicyClassifier interface {
	ClassifyPolicy(ctx context.Context, userLine, priorTurnHint string, hasPriorAnswer bool) (TurnPolicy, error)
}

TurnPolicyClassifier is the optional extension interface the REPL dispatcher prefers when it is satisfied by the wired ChitchatClassifier. Returning a non-nil error MUST be treated as "fall through to pipeline" by the caller (matching the legacy Classify contract).

type TurnRoute

type TurnRoute string

TurnRoute is the discrete handler the REPL picks per user turn.

const (
	// RouteLocal — the answer can be produced from the user's
	// current message + previous answer + conversation context.
	// No repository read. Dispatched to the local responder.
	RouteLocal TurnRoute = "local"

	// RouteRepo — the answer requires reading repository files.
	// Dispatched to the existing analysis pipeline unchanged.
	RouteRepo TurnRoute = "repo"

	// RouteHybrid — the answer requires both: re-read the
	// repository AND apply a transformation/presentation that
	// came from the previous answer or the user's framing. RouteRepo
	// may also carry presentation_directive when the current fresh
	// investigation itself asks for a specific view. In both cases
	// the dispatcher carries presentation_directive as typed pipeline
	// metadata; the prompt builder renders it separately from the user
	// request body.
	RouteHybrid TurnRoute = "hybrid"

	// RouteClarify — the user's message references state that
	// does not exist (e.g. "上面那条" with no prior answer). The
	// dispatcher prints a clarify message; no LLM call, no
	// pipeline.
	RouteClarify TurnRoute = "clarify"

	// RouteOperation — the turn asks Codrax to perform a computer
	// operation or generate an external artifact (slides, documents,
	// browser/desktop workflow, etc.). This is deliberately separate
	// from RouteRepo: operation tasks may have side effects and
	// artifact verification requirements, so they must not be routed
	// through the source-evidence pipeline by accident.
	RouteOperation TurnRoute = "operation"

	// RouteData — the turn asks for read-only data cleaning, joining,
	// aggregation, filtering, calculation, transformation, or strict
	// data-shaped output over local structured/semi-structured materials. This
	// is intentionally separate from RouteRepo (no source-code evidence gates)
	// and RouteOperation (no ordinary computer-operation approval loop for pure
	// read-only data math).
	RouteData TurnRoute = "data"

	// RouteWrite — the turn asks to change repository files. The
	// dispatcher enters write Auto Pilot: controller may explore,
	// plan, apply inside an isolated worktree, verify, and replan.
	// Main-repo merge still requires an explicit write action; apply
	// remains guarded by write_enabled, risk, approval, and worktree
	// gates.
	RouteWrite TurnRoute = "write"
)

type UserMode

type UserMode string

UserMode is the user-facing task lane selector. It is deliberately separate from types.PipelineMode, which remains the internal write phase enum used by the orchestrator.

const (
	UserModeAuto      UserMode = "auto"
	UserModeCode      UserMode = "code"
	UserModeOperation UserMode = "operation"
	UserModeData      UserMode = "data"
	UserModeWrite     UserMode = "write"
)

func ParseUserMode

func ParseUserMode(value string) (UserMode, error)

func (UserMode) IsValid

func (m UserMode) IsValid() bool

func (UserMode) Normalize

func (m UserMode) Normalize() UserMode

func (UserMode) TurnPolicy

func (m UserMode) TurnPolicy() (TurnPolicy, bool)

func (UserMode) UsesClassifier

func (m UserMode) UsesClassifier() bool

type WorkflowRunInfo

type WorkflowRunInfo struct {
	ID                string
	Path              string
	Status            string
	ActiveBatchID     string
	ActiveBatchStatus string
	ActiveSliceID     string
	ActiveSliceStatus string
	CompletedSlices   int
	TotalSlices       int
	Batches           int
	ContextPacks      int
	NextState         string
	NextAction        string
	RequiresUser      bool
	ModTime           int64
}

type WriteWorkflowRunStore

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

WriteWorkflowRunStore persists outer write-controller runs under <planDir>/workflows/. It mirrors PlanGroupStore's atomic-write shape while keeping controller metadata separate from ChangePlan files.

func NewWriteWorkflowRunStore

func NewWriteWorkflowRunStore(planDir string) *WriteWorkflowRunStore

func (*WriteWorkflowRunStore) Clear

func (s *WriteWorkflowRunStore) Clear(id string) error

func (*WriteWorkflowRunStore) ClearResumeAuthorizationsExcept

func (s *WriteWorkflowRunStore) ClearResumeAuthorizationsExcept(exceptRunID string) (int, error)

ClearResumeAuthorizationsExcept implements the orchestrator's WriteWorkflowResumeAuthorizationSweeper capability: after any successful write-turn loadOrSeed, residual one-shot explicit-resume tokens on every run except the one consumed this turn are cleared and persisted, making "one-shot" literal — a stamped token survives at most until the next write turn, whichever run and whichever lane (identity match, adoption, fresh seed, --plan-file import) that turn takes. Returns the number of runs whose token was cleared.

func (*WriteWorkflowRunStore) FindActiveRun

func (s *WriteWorkflowRunStore) FindActiveRun() (*types.WriteWorkflowRun, error)

func (*WriteWorkflowRunStore) FindActiveRunMatching

FindActiveRunMatching is the WFID-1 identity-aware finder used by the write controller's auto-resume lane. It walks the same ModTime-ordered candidate list as FindActiveRun but returns a run only when the single-point identity gate (types.MatchWriteWorkflowRepoIdentity) matches the current context, or when the run carries a one-shot explicit-resume authorization stamped by /workflow resume that is valid for the current context (root-bound: a token minted in another repo context never authorizes here — the run falls through to the identity gate and surfaces as a typed skip instead). Mismatched active runs come back as typed skips so the caller can fail closed with a message naming them. FindActiveRun keeps its legacy semantics for the explicit REPL surfaces (/workflow show|resume|clear and plan binding), which act on "the saved active run" by user request rather than auto-resuming it.

func (*WriteWorkflowRunStore) List

func (*WriteWorkflowRunStore) Load

func (*WriteWorkflowRunStore) Save

func (*WriteWorkflowRunStore) WorkflowDir

func (s *WriteWorkflowRunStore) WorkflowDir() string

Jump to

Keyboard shortcuts

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