harness

package module
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Aug 11, 2026 License: MIT Imports: 18 Imported by: 0

README

resolute-harness-go

The agent harness framework for Go — sessions, durable execution, event-sourced conversations, and HTTP exposure for agents built on resolute-agent-core-go and resolute-llm-go.

Design docs: docs/architecture.md and the ADRs in docs/adr/. Vocabulary: CONTEXT.md.

What this is

resolute-agent-core-go is a single-conversation agent loop: one Agent, one session, per-prompt event streams, in-process only. It deliberately (ADR-0006/0007 in that repo) does not solve multi-session management, durability, persistence beyond JSONL files, or network transport.

resolute-harness-go is that missing outer layer. It is a Go port of the architecture of flue — the TypeScript "Agent Harness Framework" built on @earendil-works/pi-agent-core and @earendil-works/pi-ai, the exact upstream libraries the resolute Go libraries were ported from. Flue is the proven harness for pi; this is the harness for the pi ports.

What the harness adds on top of agent-core:

  • Durable submission engine — idempotent admission, lease-based ownership, attempt tracking, per-session head-of-line serialization, two-phase settlement, crash reconciliation, durability budgets. kill -9 mid-turn resumes cleanly. No external orchestrator (no Temporal, no DBOS) — see ADR-0002.
  • Event-sourced conversation — an append-only log of canonical records, reduced into a parent-linked message tree. Recovery, compaction (re-parenting), branching, and future sub-agent sessions all fall out of one structure. The Agent's in-RAM state is rebuilt from this log; a pi.SessionRepo adapter projects the active leaf path into agent-core unchanged — see ADR-0003.
  • HTTP transportPOST = 202 admission, GET = SSE replay-from-offset then live tail, ?wait=true blocking convenience, POST …/steer and …/followup for mid-run control — see ADR-0004.
  • user vs signal inbound kinds — direct exchanges vs. one participant's activity in a multi-party conversation (a Slack thread, a GitHub issue), in the record schema from day one — see ADR-0005.
  • Structured results — attach a JSON Schema to a prompt (resultSchema) and get validated JSON on the settled record, with corrective-turn retries.
  • Durable subagents — a running agent can spawn another registered definition via the injected task tool; the parent suspends durably (no parked goroutine, no lease held) and resumes with the child's final answer as the tool result — see "Durable subagents" below.
  • Pluggable storage — one narrow adapter contract (Submission + Conversation + Attachment stores), memory and SQLite in-tree, shipped conformance test suite — see ADR-0006 and "Writing a store adapter" below.
  • Observability seams — a typed event Observer plus an execution Interceptor at every operation boundary (attempt, operation, model turn, tool); the future OTel adapter needs no engine changes — see ADR-0008.

Quickstart

go run ./examples/basic

runs keyless (a deterministic local provider stands in for the model) with a SQLite store in ./harness-data. Set GEMINI_API_KEY to run against Gemini instead. Then:

# fire-and-forget: 202 with {submissionId, conversationId}
curl -s localhost:8484/agents/assistant/demo -d '{"kind":"user","body":"what time is it?"}'

# or block until the durable result:
curl -s 'localhost:8484/agents/assistant/demo?wait=true' -d '{"kind":"user","body":"hello there"}'

# watch the conversation as canonical records over SSE (replay + live tail;
# resume from any offset with -H "Last-Event-ID: <record id>"):
curl -N localhost:8484/agents/assistant/demo

# steer an in-flight run:
curl -s localhost:8484/agents/assistant/demo/steer -d '{"body":"answer in French"}'

Durability walkthrough: dispatch a prompt, kill -9 the process before it settles, and start it again. The interrupted submission's lease expires, a fresh attempt reclaims it over the same SQLite file, and the run settles; the SSE replay shows records from both attempts, distinguished by attemptId.

Composition is explicit (ADR-0009) — no discovery, no codegen:

rt, _ := harness.NewRuntime(harness.Config{
    Agents: map[string]harness.AgentDefinition{
        "assistant": {Initialize: func(ctx context.Context, id harness.InstanceID, env harness.Env) (harness.AgentRuntimeConfig, error) {
            provider, err := gemini.New(gemini.Config{APIKey: env.Secret("GEMINI_API_KEY")})
            if err != nil {
                return harness.AgentRuntimeConfig{}, err
            }
            return harness.AgentRuntimeConfig{
                Model:         "gemini/gemini-3.1-pro-preview",
                ContextWindow: 1_000_000,
                Providers:     []llm.LLMProvider{provider},
                SystemPrompt:  promptFor(id), // per-instance setup is first-class
                Tools:         myTools,
            }, nil
        }},
    },
    Store: store, // sqlite.Open(dir) or memory.New()
})
rt.Start(ctx)
http.ListenAndServe(":8484", rt.Handler()) // auth = your middleware

rt.Dispatch / rt.Wait / rt.Steer / rt.FollowUp / rt.Compact expose the same operations in-process.

Durable subagents

A running agent can delegate to another registered definition through the harness-injected task tool (HARNESS-15). The call admits a durable child submission — leased, retried, and settled by the same engine as any dispatch — and the parent suspends at the transcript level: the pending assistant_tool_call is the durable suspension point, no goroutine parked, no lease held. When the child settles, the harness appends the parent's pending tool outcome (the wake) and requeues it; the resumed turn sees the child's final answer as the tool result.

rt, _ := harness.NewRuntime(harness.Config{
    Agents: map[string]harness.AgentDefinition{
        "triage":     {Description: "classify bug reports", Initialize: initTriage},
        "researcher": {Description: "answer lookups", Initialize: initResearcher},
    },
    Store:     store,
    Subagents: harness.SubagentPolicy{"triage": {"researcher"}}, // absent key → no task tool
})

Description is routing metadata shown to the parent model in the task tool's schema. From the model's side a task(agent, prompt) call is an ordinary blocking tool call: the result is the child's final answer (or its validated JSON when the child dispatch carried a resultSchema), and a failed child comes back as isError: true carrying the child's error, error code, and partial output. Parallel task calls in one turn fan out; the parent resumes when its last child settles.

The durability guarantees are the engine's own: a crash while waiting loses nothing — the suspension point and the child row are durable, the child keeps running under its own lease, and the settlement wake (replayed on the recovery path when it was missed) re-drives the parent; the waiting parent holds no lease and is excluded from the interrupted-running reclaim. A parent that settles with live children cascades cancellation to them (OnParentTerminal; v1 offers CancelChildren only).

SubagentLimits bound the fan-out: MaxChildrenPerRun (default 8; excess calls get an immediate error result, never a suspension), MaxDepth (default 1 — children get no task tool, so cycles are impossible by construction), MaxWait (default 0 = unbounded; a lapsed wait lands an error outcome and cancels the children). There is no operator-facing cancel API yet — cancellation is engine-internal (the orphan cascade, wait expiry). See examples/triage for a runnable wiring.

Examples

Every example runs keyless with go run (a deterministic local provider stands in for the model) and switches to Gemini when GEMINI_API_KEY is set. Each main.go opens with a copy-paste runbook.

Example Port What it demonstrates
examples/basic 8484 The walking skeleton: one agent, SQLite store, Observer + Interceptor, one tool, 202/?wait=true/SSE, kill-and-restart durability. Start here.
examples/chat 8485 A browser chat client embedded in the binary (no npm): live SSE rendering, durable session switching, and a Steer button that alters a run mid-flight.
examples/triage 8486 Structured results: resultSchema on the dispatch, validated JSON on the settled record, the corrective-turn retry visible in the stream, and retry-budget exhaustion.
examples/github-bot 8487 A channel in the flue sense: verified GitHub webhook ingress → signal dispatches with delivery-id idempotency (replay → same 202, mutated → 409) and a narrow per-issue reply tool.
examples/scheduler 8488 Time-driven agents: one signal dispatch per wall-clock window with deterministic dispatch ids, so restarts (or N replicas on one store) never double-fire a window.
examples/multitenant 8489 The concurrency contract: per-instance tenant prompts from one definition, head-of-line ordering inside a session, parallelism across sessions and tenants (load.sh shows it on a stopwatch).
examples/coder 8490 The built-in tool ecosystem: a coding assistant wired to the four execution tools (read/write/edit/bash) rooted at a workspace directory, with a stdout observer narrating tool activity — including ToolCallUpdatedEvent's streamed partial results from a running bash command.

Writing a store adapter

The store contract (harness.Store = SubmissionStore + ConversationStore + AttachmentStore) is one tier for every backend — no SQL-only extensions. The exported conformance suite is the contract: a third-party adapter (Postgres, Mongo, …) is correct exactly when it passes the same suite the in-tree memory and SQLite backends pass:

import "github.com/dev-resolute/resolute-harness-go/storetest"

func TestConformance(t *testing.T) {
    storetest.Run(t, func(t *testing.T) harness.Store { return myStore(t) })
}

The suite pins every engine-visible invariant — admission idempotency and payload conflict, runnable-head-per-session, claim CAS, attempt markers, lease renew/expiry, two-phase settlement, record ordering and offset reads, digest-keyed attachments — so a subtly wrong adapter fails tests instead of corrupting production.

What this is not (v1)

Channel adapters (Slack, Discord, …), a client SDK, React bindings, a dev console, a CLI, workflows, and sandboxes/shell are all deferred. The seams for each exist in the design; the packages do not. See docs/architecture.md §10.

Layering

resolute-llm-go        LLM providers, streaming        (v0.10.1)
resolute-agent-core-go agent loop, tools, skills       (v0.9.0)
resolute-harness-go    sessions, durability, transport (this repo)

License / lineage

Architecture derived from flue (Apache-2.0, © the flue authors). This is an independent Go implementation, not a source port.

Documentation

Index

Constants

View Source
const (
	DefaultMaxAttempts       = 10
	DefaultSubmissionTimeout = time.Hour
)

Durability budget defaults (architecture.md §4.2 invariant 7).

View Source
const DefaultResultRetries = 2

DefaultResultRetries is the feedback-retry budget when a Prompt requests a structured result and leaves ResultRetries at 0.

Variables

View Source
var (
	// ErrUnknownAgent reports a dispatch to an agent name with no registered
	// definition.
	ErrUnknownAgent = errors.New("unknown agent")
	// ErrRuntimeClosed reports an operation on a closed Runtime.
	ErrRuntimeClosed = errors.New("runtime is closed")
	// ErrNoRunInFlight reports a steer or follow-up aimed at a session with
	// no live run. Steering is live-only in v1 (ADR-0004); nothing is
	// persisted.
	ErrNoRunInFlight = errors.New("no run in flight for the session")
	// ErrSessionBusy reports a Compact aimed at a session whose run is in
	// flight; compaction is an idle-session operation.
	ErrSessionBusy = errors.New("session has a run in flight")
)

Runtime-level sentinel errors.

View Source
var (
	// ErrDispatchConflict reports a re-admission of an existing dispatch id
	// with a different payload. Identical replays are not an error — they
	// return the original submission.
	ErrDispatchConflict = errors.New("dispatch id already admitted with a different payload")
	// ErrSubmissionNotFound reports an unknown submission id.
	ErrSubmissionNotFound = errors.New("submission not found")
	// ErrConversationNotFound reports an unknown conversation.
	ErrConversationNotFound = errors.New("conversation not found")
	// ErrClaimLost reports a state CAS (claim, release, reserve, finalize,
	// lease renewal) that did not apply because the submission was not in the
	// expected state or owned by the expected attempt.
	ErrClaimLost = errors.New("submission claim lost")
	// ErrAttachmentNotFound reports an unknown attachment digest.
	ErrAttachmentNotFound = errors.New("attachment not found")
	// ErrUnsupportedSchema reports a store opened over a persisted schema
	// version this build does not support.
	ErrUnsupportedSchema = errors.New("unsupported store schema version")
)

Store-level sentinel errors. Every implementation returns these (possibly wrapped) so callers can branch with errors.Is.

View Source
var ErrInvalidDispatch = errors.New("invalid dispatch")

ErrInvalidDispatch reports a dispatch rejected at admission; nothing entered the store.

Functions

This section is empty.

Types

type AgentDefinition

type AgentDefinition struct {
	// Description is routing metadata shown to parent models in the task
	// tool schema (HARNESS-15).
	Description string
	Initialize  func(ctx context.Context, id InstanceID, env Env) (AgentRuntimeConfig, error)
}

AgentDefinition is a named initializer registered in Runtime config (ADR-0009). Initialize runs on every claim, so per-instance dynamic setup — tenant prompts, per-user tools — is first-class.

type AgentRuntimeConfig

type AgentRuntimeConfig struct {
	Model         string
	ContextWindow int
	MaxTokens     int
	Providers     []llm.LLMProvider
	SystemPrompt  string
	Tools         []pi.RegisteredTool
	Skills        []pi.Skill
	// ReserveTokens and KeepRecentTokens tune agent-core's compaction cut
	// point; zero values use agent-core's defaults.
	ReserveTokens    int
	KeepRecentTokens int
	// SummarizationRetry configures agent-core's bounded retry of transient
	// summarization failures during Compact (agent-core v0.7.0). The zero
	// value disables retries; enabling it keeps a transient 429/5xx from
	// failing a compaction outright. Retry lifecycle is surfaced to
	// Observers as RecoveryEvents.
	SummarizationRetry pi.SummarizationRetryPolicy
	// MaxAttempts is the durability budget on execution tries, recomputed
	// from durable history on every claim; 0 means DefaultMaxAttempts.
	MaxAttempts int
	// SubmissionTimeout bounds a submission's total lifetime from admission;
	// 0 means DefaultSubmissionTimeout.
	SubmissionTimeout time.Duration
}

AgentRuntimeConfig is the result of an AgentDefinition initializer: the complete, catalog-free declaration of how one agent instance runs (ADR-0007). Model is a "provider/model" ref resolved against Providers by name; ContextWindow is required and drives compaction thresholds.

type AssistantMessageCompletedPayload

type AssistantMessageCompletedPayload struct {
	Message MessagePayload `json:"message"`
}

AssistantMessageCompletedPayload is the payload of an assistant_message_completed record: the final message as agent-core appended it to the transcript.

type AssistantMessageStartedPayload

type AssistantMessageStartedPayload struct {
	Model       string `json:"model"`
	MessageType string `json:"messageType"`
}

AssistantMessageStartedPayload is the payload of an assistant_message_started record, announcing an assistant message of the given type from the given model.

type AssistantToolCallPayload

type AssistantToolCallPayload struct {
	CallID           string          `json:"callId"`
	ToolName         string          `json:"toolName"`
	Args             json.RawMessage `json:"args,omitempty"`
	ThoughtSignature []byte          `json:"thoughtSignature,omitempty"`
}

AssistantToolCallPayload is the payload of an assistant_tool_call record. ThoughtSignature is the provider's opaque per-call signature (Gemini 3); turn recovery must replay it with the call or the provider rejects the recovered turn (HARNESS-11). Additive: absent for providers without one.

type Attachment

type Attachment struct {
	Ref  AttachmentRef
	Data []byte
}

Attachment is one out-of-line blob plus its ref. Records carry only the ref; the bytes live in the AttachmentStore keyed by content digest.

type AttachmentRef

type AttachmentRef struct {
	Digest    string `json:"digest"`
	MediaType string `json:"mediaType"`
	Size      int64  `json:"size"`
}

AttachmentRef points at bytes stored out-of-line in the AttachmentStore. It is part of the record schema from day one (ADR-0006) even though v1 has no ingestion path.

type AttachmentStore

type AttachmentStore interface {
	// PutAttachment stores data and returns its ref. The digest is
	// "sha256:<hex>" over the raw bytes; putting identical bytes twice is
	// idempotent and returns the same ref.
	PutAttachment(ctx context.Context, mediaType string, data []byte) (AttachmentRef, error)
	// GetAttachment returns the attachment by digest, or
	// ErrAttachmentNotFound.
	GetAttachment(ctx context.Context, digest string) (Attachment, error)
}

AttachmentStore is the digest-keyed blob half of the store contract. It is in the schema from day one (ADR-0006) so vision later is a feature, not a migration; v1 ships no ingestion path.

type Attempt

type Attempt struct {
	ID           string    `json:"id"`
	SubmissionID string    `json:"submissionId"`
	OwnerID      string    `json:"ownerId"`
	StartedAt    time.Time `json:"startedAt"`
}

Attempt is the durable marker of one execution try. The marker is written before any work happens, so reconciliation can distinguish "started then died" from "never started"; budgets are recomputed from the marker history.

type AttemptStartedEvent

type AttemptStartedEvent struct{ Correlation }

AttemptStartedEvent reports the durable attempt marker landing.

type CompactRequest

type CompactRequest struct {
	Agent    string
	Instance InstanceID
	Session  string // empty means "default"
}

CompactRequest addresses a session for the manual Compact operation.

type CompactionEvent

type CompactionEvent struct {
	Correlation
	Reason string // "manual" | "overflow"
}

CompactionEvent reports a compaction landing (manual or recovery-driven).

type CompactionPayload

type CompactionPayload struct {
	Summary          string `json:"summary"`
	FirstKeptEntryID string `json:"firstKeptEntryId,omitempty"`
	StartIdx         int    `json:"startIdx"`
	EndIdx           int    `json:"endIdx"`
	// Usage is the summarization usage from the agent's BranchSummary; nil
	// when the provider reported none (AGENT-20 follow-through).
	Usage *pi.Usage `json:"usage,omitempty"`
}

CompactionPayload is the payload of a compaction record: the summary text plus the re-parent point. StartIdx/EndIdx mirror agent-core's BranchSummary range so LoadBranchSummaries can round-trip it.

type Config

type Config struct {
	Agents map[string]AgentDefinition
	Store  Store
	// Env is passed to every AgentDefinition initializer; nil means OSEnv().
	Env Env
	// Logger receives engine diagnostics; nil means slog.Default().
	Logger *slog.Logger
	// ClaimInterval is the coordinator's poll cadence between wake nudges;
	// 0 means 250ms.
	ClaimInterval time.Duration
	// LeaseDuration bounds attempt ownership; heartbeats renew at a third of
	// it. 0 means 30s.
	LeaseDuration time.Duration
	// DeltaFlushBytes flushes a pending delta batch once it reaches this
	// size; 0 means 1024. Message boundaries always flush regardless.
	DeltaFlushBytes int
	// DeltaFlushInterval flushes a pending delta batch once its oldest
	// fragment is this stale; 0 means 200ms.
	DeltaFlushInterval time.Duration
	// Observers receive ephemeral HarnessEvents synchronously (ADR-0008).
	Observers []Observer
	// Interceptors wrap every operation boundary in registration order
	// (first is outermost).
	Interceptors []Interceptor
	// Subagents maps an agent definition name to the definitions it may
	// spawn (HARNESS-15); an absent key means no task tool.
	Subagents SubagentPolicy
	// SubagentLimits bound durable subagent fan-out; zero values resolve to
	// documented defaults at NewRuntime.
	SubagentLimits SubagentLimits
}

Config carries everything NewRuntime needs: the named agent definitions, the store, and optional environment, logging, and engine-timing seams.

type Conversation

type Conversation struct {
	ID        string     `json:"id"`
	Key       SessionKey `json:"key"`
	CreatedAt time.Time  `json:"createdAt"`
}

Conversation is the stored identity of one session's conversation log.

type ConversationCreatedPayload

type ConversationCreatedPayload struct {
	Agent    string     `json:"agent"`
	Instance InstanceID `json:"instance"`
	Session  string     `json:"session"`
	// ParentRef links a child conversation to the task_spawned record that
	// created it (HARNESS-15); nil for root conversations.
	ParentRef *ParentRef `json:"parentRef,omitempty"`
}

ConversationCreatedPayload is the payload of a conversation_created record.

type ConversationStore

type ConversationStore interface {
	// EnsureConversation returns the conversation for key, creating it with
	// the supplied candidate (ID, CreatedAt) when absent. The bool reports
	// whether this call created it.
	EnsureConversation(ctx context.Context, candidate Conversation) (Conversation, bool, error)
	// GetConversation returns the conversation for key, or
	// ErrConversationNotFound.
	GetConversation(ctx context.Context, key SessionKey) (Conversation, error)
	// AppendRecords appends records to the conversation log in order.
	AppendRecords(ctx context.Context, conversationID string, recs []Record) error
	// ReadRecords returns records with IDs strictly greater than afterID in
	// append order; afterID "" reads from the start.
	ReadRecords(ctx context.Context, conversationID string, afterID string) ([]Record, error)
}

ConversationStore is the conversation-log half of the store contract: an append-only record log per conversation plus the session-key mapping.

type ConversationTree

type ConversationTree struct {
	// Entries holds every reduced entry in log order. Nothing is ever
	// removed: compaction re-parents, it never deletes.
	Entries []ReducedEntry
	// LeafID is the ID of the active leaf entry; "" for an empty tree.
	LeafID string
}

ConversationTree is the reduced, parent-linked projection of a conversation log. It is derived exclusively by Reduce and never stored.

func Reduce

func Reduce(records []Record) ConversationTree

Reduce is the pure projection from a record log to a conversation tree. It is deterministic, and prefix-consistent: Reduce(log[:n]) contains exactly the first n records in order, and an entry's parent only ever changes when a later compaction record re-parents it.

Non-compaction records chain onto the current leaf. A compaction record carrying FirstKeptEntryID becomes a summary node at the root of the active branch: the kept entry re-parents onto it, and everything the summary covered remains reachable as an abandoned branch — history is never rewritten, only re-rooted.

func (ConversationTree) ActiveLeafPath

func (t ConversationTree) ActiveLeafPath() []Record

ActiveLeafPath returns the records on the path from the active branch's root to the leaf, in order. This is what the projection adapter serves to agent-core.

type Correlation

type Correlation struct {
	SessionKey     SessionKey
	ConversationID string
	SubmissionID   string
	AttemptID      string
	TurnID         string
}

Correlation carries the same ids as record envelopes, so observer output lines up with the durable record of what happened.

type DeltaEvent

type DeltaEvent struct {
	Correlation
	Kind RecordKind // assistant_text_delta or assistant_thinking_delta
	Text string
}

DeltaEvent reports one streamed fragment (unbatched — observers see what the model emitted, not the flush policy).

type Dispatch

type Dispatch struct {
	Agent      string
	Instance   InstanceID
	Session    string // empty means "default"
	DispatchID string
	Message    DispatchMessage
	// Parent, when set, marks the dispatch as a spawned child run
	// (HARNESS-15); admission links the conversation and submission back to
	// the parent run. The link is partially honored when the target session
	// key already has a conversation (EnsureConversation's not-created path,
	// e.g. an idempotent re-drive): no new conversation_created is written,
	// so ParentRef is not re-asserted, while the submission still records
	// ParentSubmissionID/ParentCallID/Depth.
	Parent *SpawnParent
}

Dispatch is an inbound request to run work: admission, not execution. DispatchID is the idempotency key; when empty a fresh one is generated, which opts the caller out of idempotent replay.

type DispatchMessage

type DispatchMessage struct {
	Kind        InboundKind     `json:"kind"`
	Body        string          `json:"body"`
	Attachments []AttachmentRef `json:"attachments,omitempty"`
	Signal      *SignalMeta     `json:"signal,omitempty"`
	// ResultSchema, when set, is a JSON Schema the run's final answer must
	// validate against; the validated JSON rides the submission_settled
	// record.
	ResultSchema json.RawMessage `json:"resultSchema,omitempty"`
	// ResultRetries bounds the validate→feedback→retry loop; 0 means
	// DefaultResultRetries.
	ResultRetries int `json:"resultRetries,omitempty"`
}

DispatchMessage is the inbound payload of a dispatch: a discriminated user-or-signal union, plus the optional structured-result request. It is stored durably on the submission, so re-attempts and idempotency comparisons see the schema too.

func SignalMessage

func SignalMessage(body string, meta SignalMeta) DispatchMessage

SignalMessage builds a signal-kind DispatchMessage.

func UserMessage

func UserMessage(body string) DispatchMessage

UserMessage builds a user-kind DispatchMessage.

func (DispatchMessage) Validate

func (m DispatchMessage) Validate() error

Validate checks the structural rules of the inbound union. It is called at admission; a failing message never enters the store.

type DispatchResult

type DispatchResult struct {
	SubmissionID   string `json:"submissionId"`
	ConversationID string `json:"conversationId"`
}

DispatchResult is the admission receipt: the durable submission created (or replayed) for the dispatch, and the conversation it targets.

type Env

type Env interface {
	// Secret returns the named secret, or "" when absent.
	Secret(name string) string
}

Env is the injection seam for secrets and config lookup inside AgentDefinition initializers, keeping definitions unit-testable.

func OSEnv

func OSEnv() Env

OSEnv returns an Env backed by the process environment. It is the default when Config.Env is nil.

type HarnessEvent

type HarnessEvent interface {
	// contains filtered or unexported methods
}

HarnessEvent is the sealed union of ephemeral engine events delivered to Observers (ADR-0008). Distinct from canonical records (durable) and from agent-core's per-prompt events (which the engine consumes).

type InboundKind

type InboundKind string

InboundKind discriminates the two DispatchMessage kinds (ADR-0005).

const (
	InboundUser   InboundKind = "user"
	InboundSignal InboundKind = "signal"
)

The two inbound kinds. User is a direct 1:1 exchange with the agent's principal; Signal is one participant's activity in a multi-party conversation the agent participates in.

type InstanceID

type InstanceID string

InstanceID identifies one durable agent instance, addressed /agents/{name}/{id}. Instances are materialized on first dispatch and exist only in the stores.

type Interceptor

type Interceptor func(ctx context.Context, op OpInfo, next func(context.Context) error) error

Interceptor is onion middleware wrapped around every operation boundary — submission attempt, session operation, model turn, tool execution — so trace context propagates through context.Context natively. Interceptors compose in registration order (the first registered is outermost); an interceptor that returns without calling next aborts the operation and the error is accounted like any attempt failure. This pair of seams is the whole observability surface: the future OTel adapter is built on Observer + Interceptor with no engine changes (ADR-0008).

type LeaseRenewal

type LeaseRenewal struct {
	SubmissionID   string
	AttemptID      string
	LeaseExpiresAt time.Time
}

LeaseRenewal carries the parameters of a heartbeat: the owning attempt and its new lease expiry.

type MessagePayload

type MessagePayload struct {
	Role string          `json:"role"`
	Type string          `json:"type"`
	Body json.RawMessage `json:"body,omitempty"`
}

MessagePayload is the harness wire form of an agent-core message. The record schema owns its JSON shape; conversion to and from pi.Message happens at the projection boundary.

func (MessagePayload) ToPi

func (m MessagePayload) ToPi() pi.Message

ToPi converts the wire form back into an agent-core message.

type Observer

type Observer func(HarnessEvent)

Observer receives HarnessEvents synchronously. Observers are read-only and cheap; panics are logged and never affect execution (ADR-0008).

type OpInfo

type OpInfo struct {
	Kind OpKind
	// Operation is "prompt" or "compact" at the OpOperation boundary.
	Operation string
	Correlation
	// ToolName and CallID are set at the OpTool boundary.
	ToolName string
	CallID   string
}

OpInfo describes the boundary an Interceptor wraps, with the same correlation ids as record envelopes.

type OpKind

type OpKind string

OpKind names an interceptor boundary.

const (
	OpAttempt   OpKind = "attempt"
	OpOperation OpKind = "operation"
	OpTurn      OpKind = "turn"
	OpTool      OpKind = "tool"
)

The four operation boundaries wrapped by interceptors.

type OperationEndedEvent

type OperationEndedEvent struct {
	Correlation
	Operation string
	Err       string
}

OperationEndedEvent closes an OperationStartedEvent; Err is "" on success.

type OperationStartedEvent

type OperationStartedEvent struct {
	Correlation
	Operation string
}

OperationStartedEvent and OperationEndedEvent bound one session operation ("prompt" or "compact").

type OrphanPolicy added in v0.6.0

type OrphanPolicy int

OrphanPolicy selects what happens to live children when a parent settles terminally (v1 offers only CancelChildren).

const (
	// CancelChildren cancels a parent's live children when it settles.
	CancelChildren OrphanPolicy = iota
)

type ParentRef added in v0.6.0

type ParentRef struct {
	ConversationID string `json:"conversationId"`
	SpawnRecordID  string `json:"spawnRecordId"`
}

ParentRef is the upward link on a child conversation.

type Record

type Record struct {
	RecordEnvelope
	Payload json.RawMessage `json:"payload,omitempty"`
}

Record is one append-only entry in the durable conversation log. Payload holds the kind-specific body as opaque JSON; use the typed payload accessors to decode it. The JSON encoding of Record is the SSE wire format.

func (Record) DecodePayload

func (r Record) DecodePayload(dst interface{ payloadKind() RecordKind }) error

DecodePayload unmarshals the record payload into dst, which must be a pointer to the payload type matching the record kind.

type RecordEnvelope

type RecordEnvelope struct {
	ID             string     `json:"id"`
	Kind           RecordKind `json:"kind"`
	ConversationID string     `json:"conversationId"`
	Session        string     `json:"session"`
	SubmissionID   string     `json:"submissionId,omitempty"`
	TurnID         string     `json:"turnId,omitempty"`
	AttemptID      string     `json:"attemptId,omitempty"`
	Time           time.Time  `json:"time"`
}

RecordEnvelope is the correlation header every canonical record carries. The record ID is a ULID and doubles as the SSE stream offset.

type RecordKind

type RecordKind string

RecordKind identifies the type of a canonical record. The SSE wire format uses the kind as the event name.

const (
	KindConversationCreated       RecordKind = "conversation_created"
	KindUserMessage               RecordKind = "user_message"
	KindSignal                    RecordKind = "signal"
	KindAssistantMessageStarted   RecordKind = "assistant_message_started"
	KindAssistantTextDelta        RecordKind = "assistant_text_delta"
	KindAssistantThinkingDelta    RecordKind = "assistant_thinking_delta"
	KindAssistantToolCall         RecordKind = "assistant_tool_call"
	KindToolOutcome               RecordKind = "tool_outcome"
	KindAssistantMessageCompleted RecordKind = "assistant_message_completed"
	KindCompaction                RecordKind = "compaction"
	KindSubmissionSettled         RecordKind = "submission_settled"
	KindTaskSpawned               RecordKind = "task_spawned"
)

The full v1 record kind set. Later slices author the delta, signal, and compaction kinds; declaring them now pins the schema (ADR-0005/0006).

type RecoveryEvent

type RecoveryEvent struct {
	Correlation
	// Decision is "overflow_compact_retry", "transient_backoff",
	// "dangling_tool_call_reconciled", or one of the "summarization_retry_*"
	// lifecycle decisions (scheduled / attempt_start / finished) relayed from
	// agent-core's OnSummarizationRetry hook.
	Decision string
	Detail   string
}

RecoveryEvent reports an engine recovery decision.

type ReducedEntry

type ReducedEntry struct {
	Record   Record
	ParentID string
}

ReducedEntry is one node of the reduced conversation tree: a canonical record plus its parent link. ParentID is "" for a root entry.

type Runtime

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

Runtime is the composed harness: agent definitions, store, coordinator, and transport. Construct with NewRuntime, then Start; mount Handler wherever the app wants it.

func NewRuntime

func NewRuntime(cfg Config) (*Runtime, error)

NewRuntime validates cfg and builds a Runtime. The Runtime is inert until Start.

func (*Runtime) Close

func (rt *Runtime) Close() error

Close stops the coordinator and waits for in-flight work to wind down, bounded by a shutdown timeout. Idempotent.

func (*Runtime) Compact

func (rt *Runtime) Compact(ctx context.Context, req CompactRequest) error

Compact runs the manual compaction operation on an idle session: the conversation is summarized via the agent's model and a compaction record lands in the log; the reducer re-parents subsequent prompts onto it.

func (*Runtime) Dispatch

func (rt *Runtime) Dispatch(ctx context.Context, d Dispatch) (DispatchResult, error)

Dispatch admits one unit of work in-process — the same admission path the HTTP transport uses. It returns as soon as the submission is durable.

func (*Runtime) FollowUp

func (rt *Runtime) FollowUp(ctx context.Context, req SteerRequest) error

FollowUp enqueues a message for after the in-flight prompt's current exchange, producing the follow-up exchange within the same submission. Live-only, like Steer.

func (*Runtime) Handler

func (rt *Runtime) Handler() http.Handler

Handler returns the HTTP transport (ADR-0004). Auth and other middleware are the app's concern; mount this wherever the app wants.

func (*Runtime) Records

func (rt *Runtime) Records(ctx context.Context, conversationID string, afterID string) ([]Record, error)

Records reads the conversation log from afterID (exclusive; "" reads from the start) — the same replay the SSE transport serves.

func (*Runtime) Start

func (rt *Runtime) Start(ctx context.Context) error

Start launches the coordinator. The supplied ctx bounds the coordinator's lifetime alongside Close.

func (*Runtime) Steer

func (rt *Runtime) Steer(ctx context.Context, req SteerRequest) error

Steer injects a message into the session's in-flight run at agent-core's next safe point (post-tool-batch). Live-only passthrough in v1: with no run in flight it returns ErrNoRunInFlight and persists nothing.

func (*Runtime) Wait

func (rt *Runtime) Wait(ctx context.Context, submissionID string) (SettledPayload, error)

Wait blocks until the submission settles and returns the terminal payload. It waits on the durable submission_settled record, not an in-process future, so it survives a Runtime restart over the same store.

type SessionKey

type SessionKey struct {
	Agent    string     `json:"agent"`
	Instance InstanceID `json:"instance"`
	Session  string     `json:"session"`
}

SessionKey addresses one session of one agent instance: agent name / instance id / session name.

func (SessionKey) String

func (k SessionKey) String() string

String renders the key as "agent/instance/session".

type SettledErrorCode

type SettledErrorCode string

SettledErrorCode classifies why a submission settled as failed, stable for programmatic branching; Error carries the human-readable detail.

const (
	// SettledErrRunFailed is a terminal error from the agent run itself.
	SettledErrRunFailed SettledErrorCode = "run_failed"
	// SettledErrAttemptBudget means the max-attempts durability budget was
	// exhausted.
	SettledErrAttemptBudget SettledErrorCode = "attempt_budget_exhausted"
	// SettledErrTimeout means the submission outlived its durability timeout.
	SettledErrTimeout SettledErrorCode = "timeout_exceeded"
	// SettledErrIndeterminate means a crash interrupted settlement before the
	// terminal record landed; the run's outcome is unknown.
	SettledErrIndeterminate SettledErrorCode = "settlement_indeterminate"
	// SettledErrResultInvalid means the structured result never validated
	// against the requested schema within the feedback budget.
	SettledErrResultInvalid SettledErrorCode = "result_schema_invalid"
	// SettledErrCancelled means the submission was cancelled by the orphan
	// cascade: its parent settled terminally (HARNESS-15).
	SettledErrCancelled SettledErrorCode = "cancelled_by_parent"
)

Failure classifications carried on submission_settled records.

type SettledPayload

type SettledPayload struct {
	Status    SettledStatus    `json:"status"`
	Error     string           `json:"error,omitempty"`
	ErrorCode SettledErrorCode `json:"errorCode,omitempty"`
	Result    json.RawMessage  `json:"result,omitempty"`
}

SettledPayload is the payload of a submission_settled record. Result is present only when the prompt requested a structured result.

type SettledStatus

type SettledStatus string

SettledStatus is the terminal outcome carried on a submission_settled record.

const (
	SettledSucceeded SettledStatus = "succeeded"
	SettledFailed    SettledStatus = "failed"
)

Terminal outcomes of a submission.

type SignalMeta

type SignalMeta struct {
	Type   string            `json:"type"`
	Sender map[string]string `json:"sender,omitempty"`
	Tag    string            `json:"tag,omitempty"`
}

SignalMeta carries the signal-specific fields of a DispatchMessage: what kind of activity it was, who sent it, and an optional correlation tag.

type SignalPayload

type SignalPayload struct {
	Type   string            `json:"type"`
	Body   string            `json:"body"`
	Sender map[string]string `json:"sender,omitempty"`
	Tag    string            `json:"tag,omitempty"`
}

SignalPayload is the payload of a signal record: one participant's activity in a multi-party conversation the agent participates in (ADR-0005). The sender attributes keep the participant distinguishable from the agent's principal.

type SpawnParent added in v0.6.0

type SpawnParent struct {
	SubmissionID   string
	CallID         string
	ConversationID string
	// SpawnRecordID is pre-generated by the task tool: it mints the
	// spawn record's ULID before dispatching the child, and the
	// task_spawned record it appends afterward reuses that same ID.
	SpawnRecordID string
	Depth         int
}

SpawnParent identifies the parent run of a spawned dispatch (HARNESS-15). Set by the task tool; direct callers rarely need it.

type SteerRequest

type SteerRequest struct {
	Agent    string
	Instance InstanceID
	Session  string // empty means "default"
	Body     string
}

SteerRequest addresses a live run for Steer and FollowUp: the session key fields plus the message body.

type Store

Store is the single narrow persistence contract every backend implements (ADR-0006): one tier, no SQL-only extensions. Every implementation must pass the exported conformance suite in package storetest.

type SubagentLimits added in v0.6.0

type SubagentLimits struct {
	MaxChildrenPerRun int           // default 8; excess task calls → immediate error result
	MaxDepth          int           // default 1; the feature is off for agents with no SubagentPolicy entry
	MaxWait           time.Duration // default 0 = unbounded wait
	OnParentTerminal  OrphanPolicy  // default CancelChildren
}

SubagentLimits bound durable subagent fan-out. Zero values resolve to documented defaults at NewRuntime.

type SubagentPolicy added in v0.6.0

type SubagentPolicy map[string][]string

SubagentPolicy maps an agent definition name to the set of definitions it may spawn via the injected task tool (HARNESS-15). Absent key: no task tool.

type Submission

type Submission struct {
	ID             string           `json:"id"`
	SessionKey     SessionKey       `json:"sessionKey"`
	ConversationID string           `json:"conversationId"`
	Status         SubmissionStatus `json:"status"`
	Input          DispatchMessage  `json:"input"`
	AttemptCount   int              `json:"attemptCount"`
	AttemptID      string           `json:"attemptId,omitempty"`
	OwnerID        string           `json:"ownerId,omitempty"`
	LeaseExpiresAt time.Time        `json:"leaseExpiresAt,omitzero"`
	// LastError is the most recent run error recorded when an attempt was
	// released for retry. It survives re-claims so a budget-exhaustion
	// settlement can name the underlying failure (HARNESS-12).
	LastError string    `json:"lastError,omitempty"`
	CreatedAt time.Time `json:"createdAt"`
	// ParentSubmissionID/ParentCallID link a child submission to the task
	// call that spawned it (HARNESS-15); empty for root dispatches.
	ParentSubmissionID string `json:"parentSubmissionId,omitempty"`
	ParentCallID       string `json:"parentCallId,omitempty"`
	// Depth is the spawn depth (0 for root dispatches; child = parent+1).
	Depth int `json:"depth,omitempty"`
	// PendingResume marks a submission parked in waiting whose next drive
	// must Resume (not Prompt) — set by WaitSubmission, kept by
	// ResumeSubmission, consumed by the claim that re-drives it (the claimed
	// row carries the flag; the stored row clears it).
	PendingResume bool `json:"pendingResume,omitempty"`
	// WaitUntil bounds the wait when SubagentLimits.MaxWait is set; zero = unbounded.
	WaitUntil time.Time `json:"waitUntil,omitzero"`
	// CancelRequested asks the owning coordinator to cancel the attempt at
	// the next turn boundary (orphan cascade).
	CancelRequested bool `json:"cancelRequested,omitempty"`
}

Submission is the durable record of one admitted dispatch — the unit of leasing, attempts, and settlement. Its ID is the dispatch id and therefore the idempotency key.

type SubmissionAdmittedEvent

type SubmissionAdmittedEvent struct {
	Correlation
	Input DispatchMessage
}

SubmissionAdmittedEvent reports a durably admitted submission.

type SubmissionClaim

type SubmissionClaim struct {
	SubmissionID   string
	AttemptID      string
	OwnerID        string
	LeaseExpiresAt time.Time
}

SubmissionClaim carries the parameters of a claim CAS: the submission to move queued→running and the attempt taking ownership.

type SubmissionClaimedEvent

type SubmissionClaimedEvent struct {
	Correlation
	OwnerID      string
	AttemptCount int
}

SubmissionClaimedEvent reports a claim CAS that took ownership.

type SubmissionRelease

type SubmissionRelease struct {
	SubmissionID string
	AttemptID    string
	LastError    string
}

SubmissionRelease carries the parameters of a release CAS: the owning attempt giving the submission back to the queue, and optionally the run error that caused it. A non-empty LastError overwrites the submission's stored last error; empty preserves it (a shutdown or lease-reclaim release is not a model failure and must not erase the real one).

type SubmissionResumedEvent added in v0.6.0

type SubmissionResumedEvent struct{ Correlation }

SubmissionResumedEvent reports a waiting submission requeued by a wake.

type SubmissionSettledEvent

type SubmissionSettledEvent struct {
	Correlation
	Payload SettledPayload
}

SubmissionSettledEvent reports terminal settlement.

type SubmissionSpawnedEvent added in v0.6.0

type SubmissionSpawnedEvent struct {
	Correlation         // the PARENT's correlation
	ChildSubmissionID   string
	ChildConversationID string
	Agent               string
	CallID              string
}

SubmissionSpawnedEvent reports a durable child admission (HARNESS-15). May fire once per attempt for the same call (replay); dedupe on ChildSubmissionID.

type SubmissionStatus

type SubmissionStatus string

SubmissionStatus is the durable lifecycle state of a submission: queued → running → waiting → running → terminalizing → settled.

const (
	StatusQueued        SubmissionStatus = "queued"
	StatusRunning       SubmissionStatus = "running"
	StatusWaiting       SubmissionStatus = "waiting" // suspended on child submissions; no lease held
	StatusTerminalizing SubmissionStatus = "terminalizing"
	StatusSettled       SubmissionStatus = "settled"
)

Submission lifecycle states.

type SubmissionStore

type SubmissionStore interface {
	// AdmitSubmission durably admits sub. Re-admitting the same ID with an
	// identical Input returns the previously stored submission; a different
	// Input returns ErrDispatchConflict.
	AdmitSubmission(ctx context.Context, sub Submission) (Submission, error)
	// GetSubmission returns the submission by id, or ErrSubmissionNotFound.
	GetSubmission(ctx context.Context, id string) (Submission, error)
	// ListRunnable returns, per session key, the oldest unsettled submission
	// — and only when it is claimable (queued). A session whose head is
	// running, terminalizing, or reserved is busy and contributes nothing.
	ListRunnable(ctx context.Context) ([]Submission, error)
	// ListByStatus returns every submission in the given status, in
	// admission order. Reconciliation uses it to find interrupted work.
	ListByStatus(ctx context.Context, status SubmissionStatus) ([]Submission, error)
	// ClaimSubmission atomically moves the submission queued→running,
	// recording the attempt id, owner, and lease expiry, and incrementing
	// AttemptCount. It returns ErrClaimLost when the submission is not
	// queued. The claim consumes PendingResume: the returned row carries it
	// (the drive branches Resume vs Prompt on it) while the stored row
	// clears it. A resume claim (PendingResume set) does NOT increment
	// AttemptCount: a resume re-drives a parked parent, not a failed
	// attempt, so it never consumes the failure-attempt budget (and
	// transientBackoff, which keys off AttemptCount, only ever reflects
	// real failures).
	ClaimSubmission(ctx context.Context, claim SubmissionClaim) (Submission, error)
	// StartAttempt durably records the attempt marker. It is written after a
	// successful claim and before any work.
	StartAttempt(ctx context.Context, attempt Attempt) error
	// ListAttempts returns the attempt markers of a submission in start
	// order.
	ListAttempts(ctx context.Context, submissionID string) ([]Attempt, error)
	// RenewLease extends the lease of a running submission. It returns
	// ErrClaimLost when the submission is not running or is owned by a
	// different attempt.
	RenewLease(ctx context.Context, renewal LeaseRenewal) error
	// ListExpiredLeases returns running submissions whose lease expired at or
	// before now.
	ListExpiredLeases(ctx context.Context, now time.Time) ([]Submission, error)
	// ReleaseSubmission moves the submission running→queued so a fresh
	// attempt can claim it, recording release.LastError when non-empty (see
	// SubmissionRelease). It returns ErrClaimLost when the submission is
	// not running or is owned by a different attempt.
	ReleaseSubmission(ctx context.Context, release SubmissionRelease) error
	// WaitSubmission CAS-transitions running→waiting, releasing the lease.
	// ErrClaimLost when the attempt no longer owns the submission.
	WaitSubmission(ctx context.Context, wait SubmissionWait) error
	// ResumeSubmission CAS-transitions waiting→queued (a wake landed).
	// PendingResume survives the requeue; the claim that re-drives the
	// submission consumes it.
	ResumeSubmission(ctx context.Context, submissionID string) error
	// ListChildSubmissions returns submissions spawned by parentSubmissionID.
	ListChildSubmissions(ctx context.Context, parentSubmissionID string) ([]Submission, error)
	// ListExpiredWaits returns waiting submissions with WaitUntil before now.
	ListExpiredWaits(ctx context.Context, now time.Time) ([]Submission, error)
	// CancelSubmission transitions a queued or waiting submission straight to
	// settled (no attempt ever completes it), recording reason into
	// LastError, and reports wasRunning=false. A running submission instead
	// gets CancelRequested=true and wasRunning=true with no status change —
	// the owning coordinator cancels the run context and settles it. Cancel
	// against a terminalizing or settled submission returns ErrClaimLost
	// (already terminal).
	CancelSubmission(ctx context.Context, submissionID, reason string) (wasRunning bool, err error)
	// ReserveSettlement atomically moves the submission
	// running→terminalizing — phase one of settlement. It returns
	// ErrClaimLost when the submission is not running or is owned by a
	// different attempt.
	ReserveSettlement(ctx context.Context, submissionID, attemptID string) error
	// FinalizeSettlement moves the submission terminalizing→settled — phase
	// two. It is idempotent: finalizing an already-settled submission is a
	// no-op, so a crash between the phases resolves cleanly on retry.
	FinalizeSettlement(ctx context.Context, submissionID string) error
}

SubmissionStore is the durable submission half of the store contract. Implementations must make AdmitSubmission idempotent by submission ID and every state transition an atomic CAS on the expected prior state.

type SubmissionWait added in v0.6.0

type SubmissionWait struct {
	SubmissionID string
	AttemptID    string
	WaitUntil    time.Time // zero = unbounded
}

SubmissionWait carries the parameters of a wait CAS: the submission to move running→waiting (HARNESS-15 suspension) and the optional wait bound.

type SubmissionWaitingEvent added in v0.6.0

type SubmissionWaitingEvent struct{ Correlation }

SubmissionWaitingEvent reports a submission suspending into waiting.

type TaskSpawnedPayload added in v0.6.0

type TaskSpawnedPayload struct {
	CallID              string `json:"callId"`
	Agent               string `json:"agent"`
	ChildInstance       string `json:"childInstance"`
	ChildConversationID string `json:"childConversationId"`
	ChildSubmissionID   string `json:"childSubmissionId"`
	Prompt              string `json:"prompt"`
}

TaskSpawnedPayload is the payload of a task_spawned record: a parent run admitted a durable child submission for the named task call (HARNESS-15).

type TextDeltaPayload

type TextDeltaPayload struct {
	Text string `json:"text"`
}

TextDeltaPayload is the payload of an assistant_text_delta record: one batched fragment of streamed assistant text.

type ThinkingDeltaPayload

type ThinkingDeltaPayload struct {
	Text string `json:"text"`
}

ThinkingDeltaPayload is the payload of an assistant_thinking_delta record: one batched fragment of streamed assistant thinking.

type ToolCallEndedEvent

type ToolCallEndedEvent struct {
	Correlation
	CallID   string
	ToolName string
	IsError  bool
}

ToolCallEndedEvent closes a ToolCallStartedEvent.

type ToolCallStartedEvent

type ToolCallStartedEvent struct {
	Correlation
	CallID   string
	ToolName string
}

ToolCallStartedEvent and ToolCallEndedEvent bound one tool execution.

type ToolCallUpdatedEvent added in v0.3.0

type ToolCallUpdatedEvent struct {
	Correlation
	CallID   string
	ToolName string
	Result   pi.ToolResult
}

ToolCallUpdatedEvent reports a partial-result snapshot from a running tool. Ephemeral: observer-only, never recorded.

type ToolOutcomePayload

type ToolOutcomePayload struct {
	CallID   string          `json:"callId"`
	ToolName string          `json:"toolName"`
	Content  string          `json:"content,omitempty"`
	Data     json.RawMessage `json:"data,omitempty"`
	IsError  bool            `json:"isError,omitempty"`
}

ToolOutcomePayload is the payload of a tool_outcome record. It carries the full pi.ToolResult in wire form, correlated to its assistant_tool_call by CallID.

type TurnEndedEvent

type TurnEndedEvent struct {
	Correlation
	Turn int
}

TurnEndedEvent closes a TurnStartedEvent.

type TurnStartedEvent

type TurnStartedEvent struct {
	Correlation
	Turn int
}

TurnStartedEvent and TurnEndedEvent bound one pi.Agent LLM round-trip.

type UserMessagePayload

type UserMessagePayload struct {
	Body        string          `json:"body"`
	Attachments []AttachmentRef `json:"attachments,omitempty"`
}

UserMessagePayload is the payload of a user_message record.

Directories

Path Synopsis
examples
basic command
Command basic is the runnable end-to-end example for resolute-harness-go: one agent definition with per-instance setup, a SQLite store, a logging Observer, a timing Interceptor, and one real tool, served over HTTP.
Command basic is the runnable end-to-end example for resolute-harness-go: one agent definition with per-instance setup, a SQLite store, a logging Observer, a timing Interceptor, and one real tool, served over HTTP.
chat command
Command chat is the browser example for resolute-harness-go: a single Go binary serving an embedded HTML chat page over the harness's own HTTP surface — no npm, no build step, no external assets.
Command chat is the browser example for resolute-harness-go: a single Go binary serving an embedded HTML chat page over the harness's own HTTP surface — no npm, no build step, no external assets.
coder command
Command coder is the coding-assistant example for resolute-harness-go: one agent wired to the four built-in execution tools (read, write, edit, bash) from resolute-agent-core-go's tools package, rooted at a workspace directory, with a stdout observer that narrates tool activity — including a running bash command's partial output — as it happens.
Command coder is the coding-assistant example for resolute-harness-go: one agent wired to the four built-in execution tools (read, write, edit, bash) from resolute-agent-core-go's tools package, rooted at a workspace directory, with a stdout observer that narrates tool activity — including a running bash command's partial output — as it happens.
github-bot command
Command github-bot is the channel example for resolute-harness-go: verified GitHub webhook ingress translated into signal dispatches, with delivery-id idempotency and one narrow application-owned tool that posts the agent's reply back to the issue.
Command github-bot is the channel example for resolute-harness-go: verified GitHub webhook ingress translated into signal dispatches, with delivery-id idempotency and one narrow application-owned tool that posts the agent's reply back to the issue.
multitenant command
Command multitenant is the concurrency-model example for resolute-harness-go: one agent definition serving many tenants, where the instance id picks the tenant (per-instance system prompts via Initialize) and sessions inside an instance give independent, durably ordered conversations.
Command multitenant is the concurrency-model example for resolute-harness-go: one agent definition serving many tenants, where the instance id picks the tenant (per-instance system prompts via Initialize) and sessions inside an instance give independent, durably ordered conversations.
scheduler command
Command scheduler is the time-driven example for resolute-harness-go: an in-process ticker fires scheduled signal dispatches into a durable conversation, and deterministic per-window dispatch ids make the schedule idempotent — a killed-and-restarted process re-fires the current window's dispatch and the store deduplicates it, so nothing runs twice.
Command scheduler is the time-driven example for resolute-harness-go: an in-process ticker fires scheduled signal dispatches into a durable conversation, and deterministic per-window dispatch ids make the schedule idempotent — a killed-and-restarted process re-fires the current window's dispatch and the store deduplicates it, so nothing runs twice.
triage command
Command triage is the structured-results example for resolute-harness-go: a bug-triage endpoint where the dispatch carries a resultSchema, the harness validates the run's final answer against it, and the corrective retry loop is visible in the record stream.
Command triage is the structured-results example for resolute-harness-go: a bug-triage endpoint where the dispatch carries a resultSchema, the harness validates the run's final answer against it, and the corrective retry loop is visible in the record stream.
Package memory provides the in-memory Store used by tests and embedded runs.
Package memory provides the in-memory Store used by tests and embedded runs.
Package sqlite provides the batteries-included durable Store (modernc.org/sqlite — pure Go, no cgo, per ADR-0006).
Package sqlite provides the batteries-included durable Store (modernc.org/sqlite — pure Go, no cgo, per ADR-0006).
Package storetest is the exported conformance suite for the harness store contract (ADR-0006).
Package storetest is the exported conformance suite for the harness store contract (ADR-0006).

Jump to

Keyboard shortcuts

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