Documentation
¶
Overview ¶
Package session is the agent harness's session runner: it runs an agent's session for an assignment on a turn executor, in a git worktree of its own, with the session gates installed and every event logged under the run's trace.
The runner is a library. It owns no process: the binary that mounts it grants the process capability every git, gh and Claude Code child starts through, names the state directory and supplies the command Claude Code calls for each gate.
Index ¶
- Constants
- Variables
- func AppendRuling(stateDirectory string, ruling Ruling) error
- func InstallOperatorGates(home string, gateCommand []string) ([]string, error)
- func LastFencedBlock(text string, tag string) (body string, found bool)
- func LastReply(records []Record) string
- func OperatorVocabulary(stateDirectory string) (*terms.Vocabulary, error)
- func ProviderUnavailable(proposal *model.Proposal[claudecode.Event], err error) (string, bool)
- func PushRemoteURL(ctx context.Context, launcher proc.ILauncher, worktree string) (string, error)
- func ReadRecipe(directory string) (*pb.AgentAssignmentRecipe, error)
- func ResearchCheckSkeleton(term terms.Term) string
- func RunDirectory(stateDirectory string, assignmentID string) string
- func RunTrace(state *RunState) *telemetryv1.TraceContext
- func VerdictSkeleton() string
- func VerifierAssignmentID(author string, head string) string
- func VerifierRecipe(record SliceRecord, author *pb.AgentAssignmentRecipe) (*pb.AgentAssignmentRecipe, error)
- func WriteRulingsView(writer io.Writer, rulings []Ruling) error
- func WriteRunState(directory string, state *RunState) error
- type AgentSessionRunner
- func (runner *AgentSessionRunner) Open(ctx context.Context, recipe *pb.AgentAssignmentRecipe, environment ...string) (*OpenSession, error)
- func (runner *AgentSessionRunner) Reopen(ctx context.Context, recipe *pb.AgentAssignmentRecipe, environment ...string) (*OpenSession, error)
- func (runner *AgentSessionRunner) Resume(ctx context.Context, recipe *pb.AgentAssignmentRecipe, prompt string) (*pb.AgentAssignmentReceipt, error)
- func (runner *AgentSessionRunner) Run(ctx context.Context, recipe *pb.AgentAssignmentRecipe) (*pb.AgentAssignmentReceipt, error)
- func (runner *AgentSessionRunner) StateDirectory() string
- type AgentSessionRunnerOption
- func WithClaudeExecutable(executable string) AgentSessionRunnerOption
- func WithContainerSessions(containers ISessionContainers, settings ContainerSettings) AgentSessionRunnerOption
- func WithCopilotExecutable(executable string) AgentSessionRunnerOption
- func WithGateCommand(arguments ...string) AgentSessionRunnerOption
- func WithLauncher(launcher proc.ILauncher) AgentSessionRunnerOption
- func WithMCPServer(url string) AgentSessionRunnerOption
- func WithOpenTurnExecutors(factory OpenTurnExecutorFactory) AgentSessionRunnerOption
- func WithProvider(router *provider.Router) AgentSessionRunnerOption
- func WithRouter(router IRouter) AgentSessionRunnerOption
- func WithSandbox(manager *sandbox.Manager, dockerUpstream string) AgentSessionRunnerOption
- func WithStateDirectory(directory string) AgentSessionRunnerOption
- func WithTurnExecutors(factory TurnExecutorFactory) AgentSessionRunnerOption
- type BackgroundTask
- type ContainerSettings
- type ContentBlock
- type Credential
- type Decision
- type Defect
- type EventLog
- func (log *EventLog) Close() error
- func (log *EventLog) Context(ctx context.Context) (context.Context, error)
- func (log *EventLog) Failure(ctx context.Context, session string, turn int, eventType string, ...)
- func (log *EventLog) Logger() *slog.Logger
- func (log *EventLog) Record(ctx context.Context, session string, turn int, eventType string, ...)
- type Executor
- type IOpenTurnExecutor
- type IRouter
- type ISessionContainers
- type ITurnExecutor
- type OpenSession
- func (open *OpenSession) BackgroundResult(ctx context.Context, notification claudecode.TaskNotification) error
- func (open *OpenSession) BackgroundResults() <-chan claudecode.TaskNotification
- func (open *OpenSession) Close(ctx context.Context) error
- func (open *OpenSession) Directory() string
- func (open *OpenSession) FirstPrompt() string
- func (open *OpenSession) Interrupt(ctx context.Context) error
- func (open *OpenSession) Plan() *pb.AgentAssignmentPlan
- func (open *OpenSession) Resume(ctx context.Context, suspended time.Duration) error
- func (open *OpenSession) State() RunState
- func (open *OpenSession) Suspend(ctx context.Context, idle time.Duration) error
- func (open *OpenSession) Suspended() bool
- func (open *OpenSession) Turn(ctx context.Context, prompt string, options ...TurnOption) (*pb.AgentAssignmentReceipt, error)
- type OpenTurnExecutorFactory
- type OperatorCorrection
- type QuestionVerdict
- type Record
- type ResearchCheck
- type RouteRequest
- type Ruling
- type RulingCoverage
- type RunState
- type SliceDiff
- type SliceRecord
- type StreamMessage
- type ToolUse
- type TurnExecutorFactory
- type TurnExecutorSpec
- type TurnOption
- type Usage
- type Verdict
Constants ¶
const ( // KeyAffect is the reading, an affect.Reading object. KeyAffect = "affect" // KeyTargetTurn is the turn whose reply the message answers; 0 when the // message opens the session. KeyTargetTurn = "target_turn" )
The keys of an operator affect record beyond the common ones.
const ( // ContainerUser is the user every session container runs as. ContainerUser = "1000:1000" // ContainerNetwork is the Docker network mode a session container joins: // the default bridge, which reaches the internet and the tailnet. ContainerNetwork = "default" // ContainerExecutor is the executor's name on the session image's PATH. ContainerExecutor = "claude" // ContainerCopilotExecutor is the Copilot CLI's name on the session // image's PATH, for a recipe that chooses Copilot. It authenticates with // gh's credential, which every session container mounts. ContainerCopilotExecutor = "copilot" // DefaultDockerExecutable attaches the executor to its container. DefaultDockerExecutable = "docker" // SessionImageFile is where a repository pins its session image. SessionImageFile = "bazel/session_image.txt" // DefaultSessionImage is the fallback for a worktree that pins no image: // the tag bazel/session_image/build.sh gives the last image it built on // this host. DefaultSessionImage = "csf-session:latest" // LabelAssignment is the label every session container carries: its // assignment, which is its run directory's name. LabelAssignment = "csf.assignment" )
Container sessions: each session's executor runs inside one container of the session image, started through the container capability and removed when the session closes.
const ( // EventTypeContainerStarted records the session container and how long it // took to start. EventTypeContainerStarted = "harness_container_started" // EventTypeLaunchReceipt records, when a session closes, how it was // launched (KeyLaunch) and, for a container session, what its container // spent. A host session is one the operator chose explicitly. EventTypeLaunchReceipt = "harness_launch_receipt" // KeyLaunch names how a session was launched: LaunchContainer or // LaunchHost. KeyLaunch = "launch" LaunchContainer = "container" LaunchHost = "host" )
The launch receipt and the container's start, as the event log records them.
const ( DefaultContainerMemoryBytes int64 = 9 << 30 DefaultContainerNanoCPUs int64 = 28_000_000_000 DefaultContainerPidsLimit int64 = 2048 )
The per-session container limits, derived on 2026-10-05 from measured session peaks on the 32-core, 67 GB build host. Inputs: a 45-minute sample of all 46 host sessions every 5 s (executor process tree plus the cgroups of the Bazel containers mounting its run directory), and the receipts of three container sessions (a cold in-container Bazel test plus go test -race: 5.49 GB and 5.98 GB peaks; a real gofmt slice: 1.23 GB).
- memory: largest peak 5.98 GB = 5.57 GiB, x1.5 headroom = 8.36, rounded up to 9 GiB (the sampled median is 0.25 GiB, so peaks rarely coincide);
- cpu: largest 5 s rate 25.0 cores, rounded up to a multiple of 4: 28, leaving 4 of the 32 cores to the host;
- pids: largest task count 838 (threads included, which pids.max counts), x2 rounded up to a power of two: 2048.
const ( // EventTypeSessionSuspended records that an open session was suspended: its // turn executor closed, the session open on its recorded conversation. EventTypeSessionSuspended = "harness_session_suspended" // EventTypeSessionResumed records that a suspended session was resumed: its // turn executor opened again on the recorded conversation for the next turn. EventTypeSessionResumed = "harness_session_resumed" // EventTypeTurnResumed records, after the first turn on a resumed session, // what the resume cost: the time to the turn's first token. EventTypeTurnResumed = "harness_turn_resumed" // KeyIdleSeconds is how long the session had been between turns when its // session was suspended. KeyIdleSeconds = "idle_seconds" // KeySuspendedSeconds is how long the session was suspended before it resumed. KeySuspendedSeconds = "suspended_seconds" // KeyTimeToFirstToken is the resumed turn's time to its first assistant // event, in milliseconds. KeyTimeToFirstToken = "time_to_first_token_ms" )
The session hypervisor's suspend and resume records. The router reads none of them as a close of the conversation: a suspended session still holds it and resumes on it.
const ( // DefaultDockerUpstream is the host's Docker Engine socket the per-session // proxy forwards allowed calls to. DefaultDockerUpstream = "/var/run/docker.sock" // EventTypeSandboxReceipt records a confined session's cgroup usage when it // closes, so the ops view can show what it spent. EventTypeSandboxReceipt = "harness_sandbox_receipt" )
Sandbox files under a run directory and the receipt event.
const ( KeyTraceID = "trace_id" KeySpanID = "span_id" KeyParentSpanID = "parent_span_id" KeySessionID = "session_id" KeyTurn = "turn" KeySequence = "sequence" KeyEventType = "event_type" KeyElapsed = "elapsed_ms" KeyError = "error" // KeyExecutor is the turn executor a run started on, on its run started // record. KeyExecutor = "executor" )
The keys every event log record carries. The turn executor's own records (ipc/model/claudecode) use the same spellings, so one log reads as one trace.
const ( EventTypeRunStarted = "harness_run_started" EventTypeWorktreeReady = "harness_worktree_ready" EventTypeBuildContainerReady = "harness_build_container_ready" EventTypeTurnRequested = "harness_turn_requested" EventTypeRunFinished = "harness_run_finished" EventTypeGateDecision = "session_gate_decision" EventTypeReceiptWritten = "harness_receipt" // EventTypeControlAction records one control-plane operation on the // session — submit, send, cancel, ready, merge_started or merge — whichever client called // it: the CLI, an MCP client or the Workbench page. EventTypeControlAction = "harness_control_action" // EventTypeGitHubCall records one GitHub operation the session called // through CSF's typed GitHub tools, with its outcome. EventTypeGitHubCall = "github_call" )
Event types the harness itself writes; the turn executor writes the stream-json types of the events it forwards.
const ( KeyAction = "action" KeyOperatorAuthored = "operator_authored" // KeyQuestionWanted marks a send by which the operator overrides the // question gate: the operator wanted the question it last refused. KeyQuestionWanted = "question_wanted" KeyTurnID = "turn_id" KeyPullRequestURL = keyPullRequestURL ActionSubmit = "submit" ActionSend = "send" ActionCancel = "cancel" ActionReady = "ready" ActionMerge = "merge" // ActionMergeStarted is written when a merge begins, before the merge // path runs, so a reader of the log knows one is in flight until the // merge record that ends it. ActionMergeStarted = "merge_started" )
The fields and values of a control action record.
const ( KeyTaskID = "task_id" KeyToolUseID = "tool_use_id" KeyTaskStatus = "status" KeyTaskSummary = "summary" KeyTaskElapsed = "task_elapsed_ms" KeyTaskExitCode = "exit_code" KeyTaskOutputEnd = "output_tail" // OutputTailBytes bounds the output a background result carries: the end // of the task's output, where a command's verdict and exit status are. It // is a size bound for the record, not a measured threshold. OutputTailBytes = 2048 )
The fields of a background result record.
const ( // EventTypeProviderApplied records the provider a session was launched // through and the spelling its model resolved to, with no credential. EventTypeProviderApplied = "harness_provider_applied" // EventTypeProviderFallback records a turn the provider's first spelling // could not serve, retried on the next one. EventTypeProviderFallback = "harness_provider_fallback" // KeyProviderBaseURL is the provider's endpoint. KeyProviderBaseURL = "provider_base_url" // KeyProviderModel is the provider spelling the session's model resolved // to, and KeyProviderNextModel the spelling a fallback moved to. KeyProviderModel = "provider_model" KeyProviderNextModel = "provider_next_model" // KeyProviderReason is why the provider could not serve the spelling, as // the executor reported it. KeyProviderReason = "provider_reason" // KeyModel is the CSF model name the recipe asked for. KeyModel = "model" )
The inference provider's records on a session's event log.
const ( EventTypeUser = "user" EventTypeAssistant = "assistant" EventTypeResult = "result" EventTypeSystem = "system" DirectionIn = "in" DirectionOut = "out" BlockText = "text" BlockToolUse = "tool_use" BlockToolResult = "tool_result" // MaxRecordBytes bounds one event log line read back; a tool input can // carry a whole file. MaxRecordBytes = 16 << 20 )
The stream-json event types, directions and content block types the harness reads back from its own event log, spelled as Claude Code spells them.
const ( // MCPServerName is the name sessions reach the host's own MCP server by, // so its tools are spelled mcp__csf__<tool> in a recipe's allowed tools. MCPServerName = "csf" // AssignmentHeader carries a session's assignment on every request it // makes to the host's MCP server, so an operation names its actor. AssignmentHeader = "X-CSF-Assignment" )
Executables and arguments of the children the runner starts.
const ( HookPreToolUse = "PreToolUse" HookPostToolUse = "PostToolUse" // HookStop is the turn executor's end-of-turn event: the reply gate's. HookStop = "Stop" // ToolBash is the tool the two tool gates watch. ToolBash = "Bash" )
The Claude Code hook events the harness installs gates on, spelled as Claude Code's settings and hook input spell them.
const ( // ToolAskUserQuestion is the turn executor's tool for putting a question // to the operator; the question gate judges it before it is shown. ToolAskUserQuestion = "AskUserQuestion" // ReadyToolName is the CSF GitHub tool that marks a draft ready, and // ToolReadyPullRequest its name as a session's turn executor calls it. ReadyToolName = "MarkPullRequestReady" ToolReadyPullRequest = "mcp__" + MCPServerName + "__" + ReadyToolName // ToolGrep and ToolGlob are the turn executor's search tools; a chain of // them is the search gate's. ToolGrep = "Grep" ToolGlob = "Glob" )
const ( // RunStateFile records the run for its gates and for a resumed turn. RunStateFile = "run.json" // SettingsFile is the Claude Code settings the session starts with. SettingsFile = "settings.json" // EventsFile is the run's event log, one JSON object per line. EventsFile = "events.jsonl" // WorktreeDirectory is the git worktree the session works in. WorktreeDirectory = "worktree" )
The files of one run directory, <state directory>/<assignment id>.
const ( // ResearchCheckFence tags the fenced block holding one research check as // a JSON object, or several as a JSON array. ResearchCheckFence = "research-check" FitsGoalYes = "yes" FitsGoalNo = "no" FitsGoalPartly = "partly" LikelySourceAgentOutput = "agent output" LikelySourcePaper = "paper" LikelySourcePerson = "person" LikelySourceUnknown = "unknown" )
The research check protocol between the message and the reply gate: what a reply carries for each unvetted term before acting on it.
const ( VerdictFieldPass = "pass" VerdictFieldDefects = "defects" )
The verdict's field names, as Incomplete reports a missing one.
const EventTypeBackgroundResult = "harness_background_result"
EventTypeBackgroundResult records a turn the turn executor started on its own, between turns, because a task it ran in the background ended: the harness counts it as the session's next turn, and the record carries what woke it.
const EventTypeOperatorAffect = "operator_affect"
EventTypeOperatorAffect records, for a turn whose message is the operator's own words, the message's operator affect as pkg/affect reads it: strain, kind, whether it is an operator correction, whether the operator reports strain, and the features. It is the label of the agent turn the message answers, which the record names as its target turn.
const EventTypeRulings = "rulings"
EventTypeRulings records, at the start of every turn, the rulings in force when the turn was sent: the question gate checks the turn's questions and the alternatives they offer against them.
const EventTypeRunResumed = "harness_run_resumed"
EventTypeRunResumed records that a run was reopened on its recorded conversation, after the harness restarted.
const EventTypeSessionClosed = "harness_session_closed"
EventTypeSessionClosed records that an open session's turn executor was closed.
const EventTypeUnvettedTerms = "unvetted_terms"
EventTypeUnvettedTerms records, for a turn whose message is the operator's own words, the operator's unvetted terms: the terms of the message that appear in none of the operator's earlier messages. The vocabulary of earlier messages is the union of every such record under the state directory, since every term is unvetted exactly once, at its first appearance.
const KeyRulings = "rulings"
KeyRulings is the rulings a rulings record carries.
const (
// KeyTerms is the unvetted terms, as the operator spelled them.
KeyTerms = "terms"
)
The keys of an unvetted terms record beyond the common ones.
const PushRemote = "origin"
PushRemote is the remote the harness pushes a work branch to and opens its pull request on.
const RecipeFile = "recipe.json"
RecipeFile holds the run's frozen recipe, so a restarted harness can reopen the run without being handed it again.
const RulingsFile = "rulings.jsonl"
RulingsFile holds every ruling the operator recorded, one JSON object per line, in the order recorded, under the harness's state directory.
const StopReasonEndTurn = "end_turn"
StopReasonEndTurn is the stop reason of the message that ends a reply.
const SubtypeBackgroundTasksChanged = "background_tasks_changed"
SubtypeBackgroundTasksChanged is the system event the turn executor writes with the whole list of its background tasks whenever one starts or ends.
const Unenforced = "UNENFORCED"
Unenforced is how a ruling no gate enforces is flagged wherever rulings are shown.
const VerdictFence = "verdict"
VerdictFence tags the fenced block holding a verifier station's typed verdict: the final message of a session that read only a slice record and its diff (#420).
const (
VerifierAgentID = "slice-verifier"
)
The verifier station's recipe: one agent, a branch per verified head, and the one tool that reads the diff.
Variables ¶
var ( // ErrInvalidOption reports a nil option or an option value the runner // cannot use. ErrInvalidOption = errors.New("harness session: invalid option") // ErrNoLauncher reports a runner built without the process capability. ErrNoLauncher = errors.New("harness session: a process launcher is required") // ErrNoStateDirectory reports a runner built without a state directory. ErrNoStateDirectory = errors.New("harness session: an absolute state directory is required") // ErrNoGateCommand reports a runner built without the gate command. ErrNoGateCommand = errors.New("harness session: a gate command is required") // ErrInvalidRecipe reports a recipe the harness cannot run. ErrInvalidRecipe = errors.New("harness session: invalid recipe") // ErrSessionExists reports a run already started for the assignment; a // follow-up turn is a Resume. ErrSessionExists = errors.New("harness session: the assignment already has a session; resume it") // ErrWorktreeCollision reports a worktree path already occupied by // something the harness did not record. ErrWorktreeCollision = errors.New("harness session: the worktree path is already occupied") // ErrWorktree reports a worktree git could not create. ErrWorktree = errors.New("harness session: could not create the worktree") // ErrRunMismatch reports a recorded run that belongs to another recipe. ErrRunMismatch = errors.New("harness session: the recorded run belongs to a different recipe") // ErrNoPrompt reports a resumed turn with nothing to say. ErrNoPrompt = errors.New("harness session: a resumed turn needs a prompt") // ErrTurnFailed reports a turn the turn executor did not complete. ErrTurnFailed = errors.New("harness session: the turn failed") // ErrPushRemote reports a worktree whose push remote git could not name. ErrPushRemote = errors.New("harness session: could not read the push remote's URL") )
var ( FitsGoalValues = []string{FitsGoalYes, FitsGoalNo, FitsGoalPartly} LikelySourceValues = []string{LikelySourceAgentOutput, LikelySourcePaper, LikelySourcePerson, LikelySourceUnknown} )
The values the enumerated fields of a research check may hold.
var ErrCopilotInjectUnsupported = errors.New("session: the copilot turn executor cannot inject a message mid-turn")
copilotTurnExecutor adapts the Copilot CLI turn executor to the runner's turn shape: the runner hands every executor one stream-json user message, and Copilot takes the prompt it carries. Each turn is one Copilot process on the session, so the same executor serves a Run, a Resume and an open session. ErrCopilotInjectUnsupported means the Copilot CLI turn executor runs one process per turn and cannot take a message in the middle of one; queued messages arrive with the next turn instead.
var ErrNoRunState = errors.New("harness session: no run is recorded in this directory")
ErrNoRunState reports a run directory with no recorded run.
ErrProviderUnavailable reports a turn the provider could not serve on the spelling it was asked for: the fallback retries it on the next spelling, and a turn with none left fails with this error.
var ErrSessionClosed = errors.New("harness session: the session is closed")
ErrSessionClosed reports a turn on an open session after Close.
var ErrSuspended = errors.New("harness session: the session is suspended; resume it before the turn")
ErrSuspended reports a turn asked of a suspended session; the caller resumes it first.
var ErrUserSettings = errors.New("harness session: the user settings cannot take the gate hook")
ErrUserSettings reports a user settings file the harness cannot merge its hook into: not a JSON object, or hooks not an object of event arrays.
Functions ¶
func AppendRuling ¶ added in v0.3.0
AppendRuling records ruling after every earlier one under stateDirectory.
func InstallOperatorGates ¶ added in v0.3.0
InstallOperatorGates hooks the operator's own Claude Code and Copilot sessions under home up to the wait gate, so a shell command that waits on nothing is refused there as it is in a harness session. Claude Code's user settings keep every other setting and hook, and a second install changes nothing; Copilot's hooks file is the harness's own. It returns the files it wrote.
func LastFencedBlock ¶ added in v0.3.0
LastFencedBlock is the body of the last fenced block a text tags with tag, trimmed; found is false when it has none.
func LastReply ¶ added in v0.3.0
LastReply is the text of the last assistant message in records that said anything, its text blocks joined by a blank line: a session's final message so far.
func OperatorVocabulary ¶ added in v0.3.0
func OperatorVocabulary(stateDirectory string) (*terms.Vocabulary, error)
OperatorVocabulary is every term recorded as unvetted in any run under stateDirectory: the operator's vocabulary so far. A run whose log cannot be read contributes nothing.
func ProviderUnavailable ¶ added in v0.3.0
ProviderUnavailable reports whether the provider could not serve the spelling the turn asked for, which is what a fallback retries, and the reason the executor gave. The executor reports it either way: as the result event of a proposal it completed, or inside the claudecode.TurnError of one it did not. A turn that failed for any other cause is not a fallback: the model answered and the session keeps its turn.
func PushRemoteURL ¶
PushRemoteURL is the URL of the worktree's push remote, as git records it: the repository gh is told to act on, so a clone whose gh default is another remote still gets its pull request where the branch was pushed.
func ReadRecipe ¶
func ReadRecipe(directory string) (*pb.AgentAssignmentRecipe, error)
ReadRecipe reads the recipe a run recorded when it opened.
func ResearchCheckSkeleton ¶ added in v0.3.0
ResearchCheckSkeleton is the check for term with its answers blank and its enumerations spelled out, as the message shows it to the agent.
func RunDirectory ¶
RunDirectory is the deterministic directory of an assignment's run.
func RunTrace ¶
func RunTrace(state *RunState) *telemetryv1.TraceContext
RunTrace is the run's root span recorded in state.
func VerdictSkeleton ¶ added in v0.3.0
func VerdictSkeleton() string
VerdictSkeleton is a verdict with every field shown, as the verifier's task shows it.
func VerifierAssignmentID ¶ added in v0.3.0
VerifierAssignmentID is the verifier station's assignment for one author run at one head.
func VerifierRecipe ¶ added in v0.3.0
func VerifierRecipe(record SliceRecord, author *pb.AgentAssignmentRecipe) (*pb.AgentAssignmentRecipe, error)
VerifierRecipe is the verifier station's recipe for a slice record: a session on the author's model and repository whose task is the record and whose one tool reads its diff, in a worktree at the record's head.
func WriteRulingsView ¶ added in v0.3.0
WriteRulingsView writes the human-readable view of the rulings in force, generated from the records: the coverage first, then each ruling under its identifier with its gate or the UNENFORCED flag, its statement, the operator's words quoted and its facts.
func WriteRunState ¶
WriteRunState replaces the run state atomically, so a gate never reads a half-written file. Gates and hooks use this to persist state changes like push times.
Types ¶
type AgentSessionRunner ¶
type AgentSessionRunner struct {
// contains filtered or unexported fields
}
AgentSessionRunner runs agent sessions for prepared assignments.
func NewAgentSessionRunner ¶
func NewAgentSessionRunner(options ...AgentSessionRunnerOption) (*AgentSessionRunner, error)
NewAgentSessionRunner validates the whole option set before building the runner.
func (*AgentSessionRunner) Open ¶
func (runner *AgentSessionRunner) Open(ctx context.Context, recipe *pb.AgentAssignmentRecipe, environment ...string) (*OpenSession, error)
Open prepares the assignment's session exactly as Run does, up to the first turn: the recipe is frozen, the worktree created, the gates installed and the turn executor opened on the session. The first turn is the caller's to run, with OpenSession.FirstPrompt. The worktree creation is bounded by ctx; the executor's process is bounded by the session, which Close ends.
func (*AgentSessionRunner) Reopen ¶
func (runner *AgentSessionRunner) Reopen(ctx context.Context, recipe *pb.AgentAssignmentRecipe, environment ...string) (*OpenSession, error)
Reopen reopens a run a previous harness process left open: the same assignment, worktree, branch and trace, and the turn executor opened on the recorded conversation, resumed when a turn has run on it. No worktree is created and nothing is replayed.
func (*AgentSessionRunner) Resume ¶
func (runner *AgentSessionRunner) Resume(ctx context.Context, recipe *pb.AgentAssignmentRecipe, prompt string) (*pb.AgentAssignmentReceipt, error)
Resume runs a follow-up turn on the assignment's existing session, in the same worktree and under the same trace.
func (*AgentSessionRunner) Run ¶
func (runner *AgentSessionRunner) Run(ctx context.Context, recipe *pb.AgentAssignmentRecipe) (*pb.AgentAssignmentReceipt, error)
Run starts the assignment's session: it prepares the recipe, creates the worktree, installs the gates and runs the first turn on the task. The receipt is returned whenever the session was started, also when the turn failed, so its links are never lost with the error.
func (*AgentSessionRunner) StateDirectory ¶
func (runner *AgentSessionRunner) StateDirectory() string
StateDirectory is the directory every run lives under, as <assignment id>/.
type AgentSessionRunnerOption ¶
type AgentSessionRunnerOption func(runner *AgentSessionRunner) error
AgentSessionRunnerOption configures an AgentSessionRunner.
func WithClaudeExecutable ¶
func WithClaudeExecutable(executable string) AgentSessionRunnerOption
WithClaudeExecutable runs Claude Code from executable instead of the name claude on PATH.
func WithContainerSessions ¶ added in v0.3.0
func WithContainerSessions(containers ISessionContainers, settings ContainerSettings) AgentSessionRunnerOption
WithContainerSessions runs every session the runner opens inside its own container of the session image, with no Docker socket.
func WithCopilotExecutable ¶ added in v0.3.0
func WithCopilotExecutable(executable string) AgentSessionRunnerOption
WithCopilotExecutable runs the Copilot CLI from executable instead of the name copilot on PATH, for a recipe that chooses Copilot.
func WithGateCommand ¶
func WithGateCommand(arguments ...string) AgentSessionRunnerOption
WithGateCommand is the command Claude Code calls for every gated hook, with the hook event and the run directory appended. Required.
func WithLauncher ¶
func WithLauncher(launcher proc.ILauncher) AgentSessionRunnerOption
WithLauncher grants the process capability git, gh and the turn executor start through. Required.
func WithMCPServer ¶ added in v0.3.0
func WithMCPServer(url string) AgentSessionRunnerOption
WithMCPServer gives every session the host's MCP server at url, under the name MCPServerName. A recipe still allows each tool it may call.
func WithOpenTurnExecutors ¶
func WithOpenTurnExecutors(factory OpenTurnExecutorFactory) AgentSessionRunnerOption
WithOpenTurnExecutors replaces the open Claude Code turn executor.
func WithProvider ¶ added in v0.3.0
func WithProvider(router *provider.Router) AgentSessionRunnerOption
WithProvider launches every session through router, the host's inference provider: each turn executor is given the provider's environment, its --model is the provider's spelling of the recipe's CSF model, and a turn the first spelling cannot serve is retried on the next.
func WithRouter ¶
func WithRouter(router IRouter) AgentSessionRunnerOption
WithRouter routes every opened session: without one, each session opens a new conversation.
func WithSandbox ¶ added in v0.3.0
func WithSandbox(manager *sandbox.Manager, dockerUpstream string) AgentSessionRunnerOption
WithSandbox confines every session the runner opens: it is granted the sandbox capability and the Docker Engine socket the per-session proxy forwards to. Without it, sessions run unsandboxed, which is the default until the sandbox acceptance passes on a host.
func WithStateDirectory ¶
func WithStateDirectory(directory string) AgentSessionRunnerOption
WithStateDirectory keeps each run under directory/<assignment id>. Required; the directory must be absolute.
func WithTurnExecutors ¶
func WithTurnExecutors(factory TurnExecutorFactory) AgentSessionRunnerOption
WithTurnExecutors replaces the Claude Code turn executor.
type BackgroundTask ¶ added in v0.3.0
type BackgroundTask struct {
TaskID string `json:"task_id"`
TaskType string `json:"task_type"`
Description string `json:"description"`
}
BackgroundTask is one task the turn executor runs in the background, as its background_tasks_changed event lists it.
func LiveBackgroundTasks ¶ added in v0.3.0
func LiveBackgroundTasks(records []Record) []BackgroundTask
LiveBackgroundTasks is the background tasks the turn executor last reported running in records: the list of its latest background_tasks_changed event. A closed or resumed executor runs none until it reports again.
type ContainerSettings ¶ added in v0.3.0
type ContainerSettings struct {
// Image is the session image for a worktree that pins none in
// [SessionImageFile].
Image string
// Docker is the docker CLI the executor is attached through with
// `docker exec -i`; the harness keeps the socket, the session never sees
// it.
Docker string
// MemoryBytes, NanoCPUs and PidsLimit are the per-session limits; zero
// takes the derived default.
MemoryBytes int64
NanoCPUs int64
PidsLimit int64
// Credentials are the executor's and gh's, mounted read-only.
Credentials []Credential
// ConversationStore is the host directory the executor saves its
// conversations under, one directory per working directory (such as
// ~/.claude/projects). The worktree's own directory is mounted read-write
// into the session's home, so a container session resumes a conversation
// a host session saved, and the other way round. Empty mounts none.
ConversationStore string
}
ContainerSettings is how every session container is built.
type ContentBlock ¶ added in v0.3.0
type ContentBlock struct {
Type string `json:"type"`
Text string `json:"text"`
ID string `json:"id"`
Name string `json:"name"`
Input json.RawMessage `json:"input"`
Content json.RawMessage `json:"content"`
ToolUseID string `json:"tool_use_id"`
IsError bool `json:"is_error"`
}
ContentBlock is one block of a stream-json message's content: ID names a tool_use block's call, and ToolUseID and IsError are a tool_result block's call and outcome.
func (*ContentBlock) ResultText ¶ added in v0.3.0
func (block *ContentBlock) ResultText() string
ResultText is a tool_result block's content as text: a string content, or its text blocks joined by a newline.
type Credential ¶ added in v0.3.0
type Credential struct {
// Source is the host path.
Source string
// Home is the path under the session's home it is mounted at, such as
// .config/gh.
Home string
}
Credential is one host credential mounted read-only into the session's home.
type Defect ¶ added in v0.3.0
type Defect struct {
Statement string `json:"statement"`
Field string `json:"field"`
Evidence []string `json:"evidence"`
}
Defect is one thing the verifier found wrong: the statement it contradicts (an acceptance item, a decision, the proof), where in the record that statement is, and the evidence, each a diff location (path:line) or a record field.
type EventLog ¶
type EventLog struct {
// contains filtered or unexported fields
}
EventLog appends one run's records to its events.jsonl. The runner and every gate process open it for appending; each record is one write of one line, so records from the session and its hooks interleave whole.
func OpenEventLog ¶
func OpenEventLog(directory string, trace *telemetryv1.TraceContext) (*EventLog, error)
OpenEventLog opens the event log of the run in directory for appending, under trace, the run's root span.
func (*EventLog) Context ¶
Context is ctx carrying the run's trace, so spans derived from it, the turn executor's included, belong to the run.
func (*EventLog) Failure ¶
func (log *EventLog) Failure(ctx context.Context, session string, turn int, eventType string, message string, err error, attributes ...slog.Attr)
Failure writes one error record of eventType under a new child span.
type Executor ¶ added in v0.3.0
type Executor string
Executor names the turn executor a session runs on, as a recipe's executor field spells it.
The turn executors a recipe can choose. An empty executor field is Claude Code.
func ExecutorOf ¶ added in v0.3.0
func ExecutorOf(recipe *pb.AgentAssignmentRecipe) Executor
ExecutorOf is the turn executor the recipe chooses.
type IOpenTurnExecutor ¶
type IOpenTurnExecutor interface {
ITurnExecutor
Interrupt(ctx context.Context) error
Close(ctx context.Context) error
}
IOpenTurnExecutor is a turn executor kept open across turns: the Claude Code process whose stream-json input stays open in production, a double in specs. Interrupt asks it to end the running turn at its next safepoint; Inject writes a message to the executor's input; Close ends it and joins what it started.
type IRouter ¶
type IRouter interface {
Route(ctx context.Context, request RouteRequest) (string, error)
}
IRouter decides which real session, a Claude Code conversation, a virtual session runs on: an existing one it fits well in, which it resumes, or request.Session, a new one.
type ISessionContainers ¶ added in v0.3.0
type ISessionContainers interface {
StartDetached(ctx context.Context, spec docker.SandboxSpec) (docker.StartedContainer, error)
ContainerUsage(started docker.StartedContainer) (sandbox.Usage, error)
RemoveContainer(ctx context.Context, id string) error
}
ISessionContainers is the container capability a container session needs: *docker.ContainerHost in production, a double in specs.
type ITurnExecutor ¶
type ITurnExecutor interface {
Propose(ctx context.Context, turn *claudecode.Turn) (*model.Proposal[claudecode.Event], error)
Inject(ctx context.Context, message string) error
}
ITurnExecutor carries out one turn of a session: the Claude Code turn executor in production, a double in specs.
type OpenSession ¶
type OpenSession struct {
// contains filtered or unexported fields
}
OpenSession is one assignment's session held open: its run directory, event log and worktree exist, its turn executor is open, and no turn has run until the caller asks for one. One goroutine drives it; turns never overlap.
func (*OpenSession) BackgroundResult ¶ added in v0.3.0
func (open *OpenSession) BackgroundResult(ctx context.Context, notification claudecode.TaskNotification) error
BackgroundResult counts the turn the executor started on notification as the session's next turn and records it as a typed background result: the task, how it ended, its exit code when its output names one, how long it ran and the bounded tail of its output. Nothing is sent to the executor; the turn is already running.
func (*OpenSession) BackgroundResults ¶ added in v0.3.0
func (open *OpenSession) BackgroundResults() <-chan claudecode.TaskNotification
BackgroundResults delivers the task notifications the turn executor read between turns, each of which started a turn of the executor's own. The goroutine driving the session records each with OpenSession.BackgroundResult.
func (*OpenSession) Close ¶
func (open *OpenSession) Close(ctx context.Context) error
Close ends the turn executor, bounded by ctx, and closes the event log. It is idempotent.
func (*OpenSession) Directory ¶
func (open *OpenSession) Directory() string
Directory is the run directory: events.jsonl, run.json, settings.json and the worktree live there.
func (*OpenSession) FirstPrompt ¶
func (open *OpenSession) FirstPrompt() string
FirstPrompt is the task as the agent first reads it.
func (*OpenSession) Interrupt ¶
func (open *OpenSession) Interrupt(ctx context.Context) error
Interrupt asks the running turn to end at the executor's next safepoint. An suspended session runs no turn, so there is nothing to interrupt.
func (*OpenSession) Plan ¶
func (open *OpenSession) Plan() *pb.AgentAssignmentPlan
Plan is the frozen recipe the session runs.
func (*OpenSession) Resume ¶ added in v0.3.0
Resume is the session hypervisor's resume: it opens the turn executor again on the recorded conversation after a suspend, on the spec it was first opened with and resumed, exactly as a restarted harness reopens a run. suspended is how long the session was suspended; the next turn's receipt carries it with the time to first token. A session whose executor is open is left as it is.
func (*OpenSession) State ¶
func (open *OpenSession) State() RunState
State is a copy of the run's record as last written.
func (*OpenSession) Suspend ¶ added in v0.3.0
Suspend is the session hypervisor's suspend: it ends the turn executor's process, bounded by ctx, and keeps the session open on its recorded conversation: the event log stays open, the worktree and run record stay, and OpenSession.Resume opens the executor again. idle is how long the session had been between turns. A suspended session is left as it is.
func (*OpenSession) Suspended ¶ added in v0.3.0
func (open *OpenSession) Suspended() bool
Suspended reports whether the session is suspended: its turn executor closed.
func (*OpenSession) Turn ¶
func (open *OpenSession) Turn(ctx context.Context, prompt string, options ...TurnOption) (*pb.AgentAssignmentReceipt, error)
Turn runs prompt as the session's next turn on the open executor and returns the receipt. ctx bounds the wait for the turn's result; the executor's process outlives it.
type OpenTurnExecutorFactory ¶
type OpenTurnExecutorFactory func(ctx context.Context, spec TurnExecutorSpec) (IOpenTurnExecutor, error)
OpenTurnExecutorFactory opens the turn executor an OpenSession holds.
func ClaudeCodeOpenTurnExecutors ¶
func ClaudeCodeOpenTurnExecutors(launcher proc.ILauncher, executable string) OpenTurnExecutorFactory
ClaudeCodeOpenTurnExecutors opens one Claude Code process per session through launcher and keeps it across turns.
func OpenTurnExecutorsByRecipe ¶ added in v0.3.0
func OpenTurnExecutorsByRecipe(launcher proc.ILauncher, claude string, copilot string) OpenTurnExecutorFactory
OpenTurnExecutorsByRecipe opens the turn executor each recipe chooses: Claude Code from claude, or the Copilot CLI from copilot.
type OperatorCorrection ¶ added in v0.3.0
OperatorCorrection is one operator correction a run recorded: the run, the turn whose reply it answers, and when the operator sent it.
func OperatorCorrections ¶ added in v0.3.0
func OperatorCorrections(stateDirectory string) ([]OperatorCorrection, error)
OperatorCorrections is every operator correction recorded in any run under stateDirectory, oldest first. A run whose log cannot be read contributes nothing.
type QuestionVerdict ¶ added in v0.3.0
QuestionVerdict is one question an agent put to the operator, the class the question gate placed it in and whether the gate refused it.
type Record ¶ added in v0.3.0
type Record struct {
Time time.Time `json:"time"`
Message string `json:"msg"`
EventType string `json:"event_type"`
Turn int `json:"turn"`
Direction string `json:"direction"`
Event json.RawMessage `json:"event"`
// Provider is the turn executor that reported Event.
Provider string `json:"provider"`
Gate string `json:"gate"`
Decision string `json:"decision"`
Reason string `json:"reason"`
Error string `json:"error"`
// Action is a control action record's action: submit, send, cancel,
// ready, merge_started or merge.
Action string `json:"action"`
Terms []terms.Term `json:"terms"`
Rulings []Ruling `json:"rulings"`
// Questions is a question gate decision's verdicts and QuestionWanted a
// send that overrides the gate.
Questions []QuestionVerdict `json:"questions"`
QuestionWanted bool `json:"question_wanted"`
// Affect is an operator affect record's reading; nil on every other kind.
Affect *affect.Reading `json:"affect"`
}
Record is one event log line as the harness reads it back: the keys every record carries and the keys of the record kinds the harness acts on.
func ReadRecords ¶ added in v0.3.0
ReadRecords reads the event log of the run in directory and returns the records keep accepts, in order. A line that is not a record is skipped.
func ReadTurnRecords ¶ added in v0.3.0
ReadTurnRecords returns the records of the turn running: the last turn start, a turn request or a background result, and every record after it. No turn started is no records.
func TurnRecords ¶ added in v0.3.0
TurnRecords is the part of a run's records that belongs to the turn running, as ReadTurnRecords reads it.
func (*Record) Assistant ¶ added in v0.3.0
Assistant is what the assistant said in the record, its text blocks joined by a blank line, and the tools it called, in order. A record that is not an assistant event has neither.
func (*Record) Stream ¶ added in v0.3.0
func (record *Record) Stream() (message StreamMessage, ok bool)
Stream decodes the executor event the record carries; ok is false for a record that carries none.
type ResearchCheck ¶ added in v0.3.0
type ResearchCheck struct {
Term string `json:"term"`
WhatItIs string `json:"what_it_is"`
WhatItDoesNotDo string `json:"what_it_does_not_do"`
// FitsGoal is one of FitsGoalValues: whether the term fits the
// operator's stated goal.
FitsGoal string `json:"fits_goal"`
Why string `json:"why"`
// LikelySource is one of LikelySourceValues: where the term most likely
// came from.
LikelySource string `json:"likely_source"`
}
ResearchCheck is the typed record a reply carries for each unvetted term.
func (ResearchCheck) Incomplete ¶ added in v0.3.0
func (check ResearchCheck) Incomplete() []string
Incomplete names the fields a check leaves blank or outside their values; a complete check has none.
type RouteRequest ¶
type RouteRequest struct {
Virtual string
// Session is the conversation the virtual session opens when no
// existing one fits.
Session string
Model string
// Agent is the agent's identity: its id and a hash of its instructions.
// A resumed conversation keeps the system prompt it first recorded, so
// only a conversation with the same identity may be attached.
Agent string
// Key is the topic key: the assignment's ticket.
Key string
// Summary is the assignment in a line: its pull request title.
Summary string
}
RouteRequest is what a router knows about a virtual session (one assignment) before it opens.
type Ruling ¶ added in v0.3.0
type Ruling struct {
ID string `json:"ruling_id"`
Statement string `json:"statement"`
Excludes []string `json:"excludes"`
Supersedes string `json:"supersedes,omitempty"`
Quote string `json:"quote,omitempty"`
RuledOn string `json:"ruled_on,omitempty"`
Scope string `json:"scope,omitempty"`
Why string `json:"why,omitempty"`
EnforcedBy string `json:"enforced_by,omitempty"`
PendingGate string `json:"pending_gate,omitempty"`
RecordedAt time.Time `json:"recorded_at"`
}
Ruling is one recorded operator ruling: what it decides, the operator's own words, the day, whom it binds, why, the gate that enforces it or the one that will, the alternatives it rules out, and the ruling it replaces. An agent question, or an alternative it offers, that names an excluded alternative re-litigates the ruling.
func InForce ¶ added in v0.3.0
InForce folds the content of a rulings file into the rulings in force, in the order first recorded: the latest record of each identifier, without the ones a later ruling supersedes.
func RulingsInForce ¶ added in v0.3.0
RulingsInForce returns the rulings in force under stateDirectory, in the order first recorded. No rulings file is no rulings.
func (Ruling) Enforced ¶ added in v0.3.0
Enforced reports whether a gate enforces the ruling. A ruling whose gate is only pending is not enforced.
func (Ruling) Enforcement ¶ added in v0.3.0
Enforcement is the ruling's gate, or the UNENFORCED flag with the gate pending when one is named.
type RulingCoverage ¶ added in v0.3.0
RulingCoverage is how many of the rulings in force a gate enforces, out of all of them.
func CoverageOf ¶ added in v0.3.0
func CoverageOf(rulings []Ruling) RulingCoverage
CoverageOf counts the rulings a gate enforces.
func (RulingCoverage) Unenforced ¶ added in v0.3.0
func (coverage RulingCoverage) Unenforced() int
Unenforced is the rulings in force that no gate enforces.
type RunState ¶
type RunState struct {
AssignmentID string `json:"assignment_id"`
AgentID string `json:"agent_id"`
TicketURL string `json:"ticket_url"`
SessionID string `json:"session_id"`
TraceID string `json:"trace_id"`
SpanID string `json:"span_id"`
Repository string `json:"repository"`
Worktree string `json:"worktree"`
Branch string `json:"branch"`
BaseBranch string `json:"base_branch"`
PullRequestTitle string `json:"pull_request_title"`
// WorkspaceMode is the workspace configuration: "normal" or "patch".
WorkspaceMode string `json:"workspace_mode"`
// Turns counts the turns started on the session, so a gate attributes its
// decisions to the turn that is running.
Turns int `json:"turns"`
// LastPushTime records the Unix timestamp of the most recent push,
// initialized when the session starts and updated by the commit gate.
LastPushTime int64 `json:"last_push_time,omitempty"`
// Model is the LLM model the session runs on, needed by the commit hook.
Model string `json:"model,omitempty"`
// Executor is the turn executor the session runs on. A run recorded
// before executors were chosen has none and ran on Claude Code.
Executor Executor `json:"executor,omitempty"`
// BuildContainerID is the ID of the long-lived build container for this session.
BuildContainerID string `json:"build_container_id,omitempty"`
}
RunState is what the harness records about a run so its session gates and a later resumed turn act on the same session, trace and branch. Gates read it; only the runner writes it.
func ReadRunState ¶
ReadRunState reads the run recorded in directory.
func (*RunState) TurnExecutor ¶ added in v0.3.0
TurnExecutor is the turn executor the run is on: the recorded one, or Claude Code for a run recorded before executors were chosen.
type SliceDiff ¶ added in v0.3.0
type SliceDiff struct {
Base string `json:"base"`
Head string `json:"head"`
Files []string `json:"files"`
}
SliceDiff is the change a slice makes: its merge base with the base branch, its head, and every path it touches.
type SliceRecord ¶ added in v0.3.0
type SliceRecord struct {
AssignmentID string `json:"assignment_id"`
TicketURL string `json:"ticket_url"`
Branch string `json:"branch"`
Diff SliceDiff `json:"diff"`
Decisions []Decision `json:"decisions"`
OpenQuestions []string `json:"open_questions"`
Verdict *Verdict `json:"verdict,omitempty"`
}
SliceRecord is the slice IR (#420): the typed record every station of a slice reads and writes. The author's run gives the ticket, the branch and the diff; the author states its decisions and open questions; the verifier station adds its verdict.
func ReadSliceRecord ¶ added in v0.3.0
func ReadSliceRecord(ctx context.Context, launcher proc.ILauncher, stateDirectory string, assignment string) (SliceRecord, error)
ReadSliceRecord reads the slice record of the author run assignment in stateDirectory: its run state, its diff through git, and the verdict of the verifier station for its head when one has answered.
type StreamMessage ¶ added in v0.3.0
type StreamMessage struct {
Type string `json:"type"`
Subtype string `json:"subtype"`
// Model is the model a session's init event names.
Model string `json:"model"`
Message struct {
Content json.RawMessage `json:"content"`
Model string `json:"model"`
// StopReason is end_turn on the message that ends a turn's reply.
StopReason string `json:"stop_reason"`
Usage *Usage `json:"usage"`
} `json:"message"`
Result string `json:"result"`
IsError bool `json:"is_error"`
DurationMS float64 `json:"duration_ms"`
// TotalCostUSD is a result's cost so far, cumulative within one executor
// process; nil when the executor reports none, as Copilot does not.
TotalCostUSD *float64 `json:"total_cost_usd"`
Usage *Usage `json:"usage"`
// Tasks is the background tasks running, on a background_tasks_changed
// system event.
Tasks []BackgroundTask `json:"tasks"`
}
StreamMessage is the stream-json event an executor record carries.
func (*StreamMessage) Blocks ¶ added in v0.3.0
func (message *StreamMessage) Blocks() []ContentBlock
Blocks is the message's content as blocks: a string content is one text block, and content of neither shape is no block.
type ToolUse ¶ added in v0.3.0
type ToolUse struct {
// ID is the call's tool_use_id.
ID string
Name string
Input json.RawMessage
}
ToolUse is one tool call an assistant message made.
type TurnExecutorFactory ¶
type TurnExecutorFactory func(spec TurnExecutorSpec) (ITurnExecutor, error)
TurnExecutorFactory builds the turn executor for one turn.
func ClaudeCodeTurnExecutors ¶
func ClaudeCodeTurnExecutors(launcher proc.ILauncher, executable string) TurnExecutorFactory
ClaudeCodeTurnExecutors builds Claude Code turn executors that start executable through launcher.
func TurnExecutorsByRecipe ¶ added in v0.3.0
func TurnExecutorsByRecipe(launcher proc.ILauncher, claude string, copilot string) TurnExecutorFactory
TurnExecutorsByRecipe builds the turn executor each recipe chooses: Claude Code from claude, or the Copilot CLI from copilot.
type TurnExecutorSpec ¶
type TurnExecutorSpec struct {
Session uuid.UUID
Directory string
// Arguments are the turn executor's extra arguments: the settings with
// the gates, the model, the permission mode and the allowed tools.
Arguments []string
Logger *slog.Logger
// Resume is true when the session already exists.
Resume bool
// Environment is appended, as NAME=value, to the environment the turn
// executor's process inherits: the Bazel cache locations, for one.
Environment []string
// LaunchPrefix, when set, wraps the turn executor: the process started is
// LaunchPrefix[0] with LaunchPrefix[1:] ahead of the executor and its
// arguments. The sandbox puts its launcher here.
LaunchPrefix []string
// TaskNotifications, when set, receives the task notifications an open
// turn executor reads between turns: the background completions that wake
// the session. It must not block.
TaskNotifications func(notification claudecode.TaskNotification)
// Executor is the turn executor the recipe chose.
Executor Executor
// RunDirectory is the run's directory, which holds the worktree.
RunDirectory string
}
TurnExecutorSpec is what the runner asks of a turn executor.
type TurnOption ¶ added in v0.3.0
type TurnOption func(input *turnInput)
TurnOption describes the message a turn carries.
func OperatorAuthored ¶ added in v0.3.0
func OperatorAuthored() TurnOption
OperatorAuthored marks the turn's message as the operator's own words: a chat turn the operator sent, or a relayed message that quotes the operator verbatim. The harness computes the message's unvetted terms against the operator's vocabulary, records them and puts them into the turn, and the reply gate holds the reply to them. It records the message's operator affect too, and when the strain reads high it says so in the turn and the reply gate bounds the reply's length.
type Usage ¶ added in v0.3.0
type Usage struct {
InputTokens int64 `json:"input_tokens"`
OutputTokens int64 `json:"output_tokens"`
CacheRead int64 `json:"cache_read_input_tokens"`
CacheCreation int64 `json:"cache_creation_input_tokens"`
}
Usage is the tokens a message or a result counted.
type Verdict ¶ added in v0.3.0
Verdict is what the verifier station returns: whether the slice record and its diff hold, and every defect it found. Pass is true exactly when there is no defect.
func ParseVerdict ¶ added in v0.3.0
ParseVerdict reads the last verdict block of a reply; found is false when the reply carries none, and err is set when the block is not one JSON object.
func (Verdict) Incomplete ¶ added in v0.3.0
Incomplete names the fields a verdict leaves missing or inconsistent; a complete verdict has none.