agentwire

package module
v0.3.2 Latest Latest
Warning

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

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

README

agentwire

Drive vendor coding-agent CLIs as child processes over their structured protocols, behind one Go event model.

agentwire spawns the vendor binary that is already on the machine and speaks the vendor's own wire protocol to it. It normalizes the conversation into one event stream: text, tool calls, permission requests, usage, results and exit. Five protocols are covered:

  • Claude stream-json over stdin and stdout.
  • Codex app-server JSON-RPC over stdin and stdout.
  • OpenCode serve REST plus an SSE event stream, for 1.x and for the 2.x beta.
  • ACP over stdin and stdout, for Copilot, Cursor and Gemini.
  • pi RPC (NDJSON) over stdin and stdout.

The wire is Go-native. The official vendor SDKs are the reference implementation and the fixture oracle at build and test time only. They are never a runtime dependency. There is no Node, no sidecar and no vendored SDK.

The library knows vendors. It does not know consumers. Vendor flags, environment variables, config formats, protocol quirks and version floors live here. Homes such as ~/.trebi/harness, lifecycle hooks, memory, secret environment, job semantics and UI text belong to the caller.

No global state

Every cache, server pool, version record and crash ledger hangs off one Runtime value that the caller creates. Two runtimes in one process share nothing. No package-level mutable state exists.

Harnesses

Harness Constant Binary Protocol
Claude Code agentwire.Claude claude stream-json (NDJSON) over stdin and stdout
Codex agentwire.Codex codex app-server JSON-RPC over stdin and stdout
OpenCode agentwire.OpenCode opencode serve REST plus SSE
OpenCode 2 (beta) agentwire.OpenCode2 opencode2 /api REST plus SSE
pi agentwire.Pi pi pi RPC (NDJSON) over stdin and stdout
Copilot agentwire.Copilot copilot ACP over stdin and stdout
Cursor agentwire.Cursor cursor-agent ACP over stdin and stdout
Gemini agentwire.Gemini gemini ACP over stdin and stdout
fake agentwire.Fake shell script agentwire NDJSON, for tests
native agentwire.Native none (in process) the native module: one provider stream per turn, registered with Runtime.SetDriver

Runtime.Detect reports whether a binary is installed, its version, whether the version floor is met, and a best-effort login state. native has no binary: its driver implements agentwire.Detector, and the detection reports the credential state.

The native module

native is a separate Go module (github.com/trebi-ai/agent-wire/native). It holds an in-process loop: it folds one provider stream per turn into message parts, runs tools, approvals, skills, an MCP session, and a summary compactor, and keeps one message log per session in a FileStore. Two provider modules give it a model: native/provider/anthropic and native/provider/openai (OpenAI-compatible endpoints, so Ollama, OpenRouter, vLLM, Groq, and LM Studio work through base_url). The root module stays provider-free.

A consumer builds a native.Config, gets a driver with native.NewDriver, and registers it with Runtime.SetDriver(agentwire.Native, drv). Config.Credentials resolves the key per session, Config.ToolSetFuncs opens consumer tools per session, and Config.BeforeCall/AfterCall carry quota bookkeeping.

Getting started

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/trebi-ai/agent-wire"
)

func main() {
	ctx := context.Background()

	rt := agentwire.New(agentwire.Options{
		ClientName:    "myapp",
		ClientVersion: "1.0.0",
	})
	defer rt.Close(ctx)

	sess, err := rt.Start(ctx, agentwire.StartRequest{
		Harness:      agentwire.Claude,
		WorkingDir:   "/path/to/project",
		Instructions: "Read AGENTS.md before you edit anything.",
		Permissions:  agentwire.PermissionPolicy{Mode: agentwire.PermissionAsk},
		MCPServers: []agentwire.MCPServer{{
			Name:    "myapp",
			Command: "myapp",
			Args:    []string{"mcp"},
		}},
	})
	if err != nil {
		log.Fatalf("start: %v", err)
	}
	defer sess.Close(ctx)

	if err := sess.Prompt(ctx, agentwire.Prompt{Text: "Summarize this project."}); err != nil {
		log.Fatalf("prompt: %v", err)
	}

	for ev := range sess.Events() {
		switch ev.Type {
		case agentwire.EventAssistant:
			fmt.Print(ev.Text)
		case agentwire.EventPermission:
			// The policy answered everything it could. A human answers the rest.
			if err := sess.AnswerPermission(ctx, ev.Permission.ID, agentwire.Decision{Allow: true}); err != nil {
				log.Printf("answer: %v", err)
			}
		case agentwire.EventExit:
			fmt.Printf("\nsession ended: %s\n", ev.Error)
		}
	}
}

Runtime.Resume reopens a known session. Runtime.Reconcile kills children that a previous process left behind. Runtime.Close stops every session and shared server.

Session rule

A consumer MUST drain Session.Events() until the channel closes, or call Session.Close. EventExit is always the last event, and it is delivered even after Close. A consumer that stops reading without closing leaks the framing goroutine.

Extension points

StartRequest carries everything the caller wants for one session. The library builds the command.

Field Effect
MCPServers Injects MCP servers for this session only. stdio (Command, Args, Env) or HTTP (URL, Headers). Injection adds to the user's own servers.
Skills Makes extra skill directories visible: natively, through an overlay config dir, or as an index in the instructions.
Instructions Adds per-session system instructions. The rendering is per harness.
Permissions One policy (ask, auto_edit, auto) mapped onto each vendor's own mechanism. Unanswered requests arrive as EventPermission.
Home Isolates the harness config home. Empty uses the user's own configuration and login.
Raw Per-harness overrides the typed fields do not cover, for example strict_mcp (Claude) or approvalPolicy and sandbox (Codex).

Other escape hatches: ExtraArgs appends consumer flags verbatim, BaseCommand skips the launch builder, LogPath writes every frame in and out as NDJSON, and FS routes ACP file reads and writes to the caller so it can see every edit.

Vendor binaries and logins

The library spawns the vendor binary that is on the machine and uses the machine's own login. It never reads, copies or stores vendor tokens. It does not install or update vendor binaries, and it does not require vendor SDKs at run time.

Development

go test ./...
scripts/test.live.sh claude          # real binaries and real logins

The live tier needs the build tag live, a named harness in AGENTWIRE_LIVE, and a shared login. See docs/compat.md for the vendor versions this library was verified against.

Documentation

Overview

Package agentwire drives vendor coding-agent CLIs as child processes over their structured protocols: Claude stream-json, Codex app-server, OpenCode serve, ACP (Copilot, Cursor, Gemini) and pi RPC.

One event model covers every harness. The library knows vendors; it does not know consumers. Vendor flags, env vars, config formats, protocol quirks and version floors live here. Homes such as ~/.trebi/harness, hooks, memory, secret env, job semantics and UI text belong to the caller.

There is no global state: every cache, pool and ledger hangs off one Runtime, and two runtimes in one process share nothing.

The wire is Go-native. Official vendor SDKs are the reference implementation and the fixture oracle at build and test time only; they are never a runtime dependency.

Index

Constants

View Source
const (
	EventInit       = wire.EventInit
	EventAssistant  = wire.EventAssistant
	EventThought    = wire.EventThought
	EventUser       = wire.EventUser
	EventTool       = wire.EventTool
	EventPermission = wire.EventPermission
	EventResult     = wire.EventResult
	EventError      = wire.EventError
	EventUsage      = wire.EventUsage
	EventStatus     = wire.EventStatus
	EventExit       = wire.EventExit
)

Event types.

View Source
const (
	StatusUnknown = wire.StatusUnknown
	StatusRunning = wire.StatusRunning
	StatusWaiting = wire.StatusWaiting
	StatusIdle    = wire.StatusIdle
)

Session statuses.

View Source
const (
	ToolRead   = wire.ToolRead
	ToolEdit   = wire.ToolEdit
	ToolExec   = wire.ToolExec
	ToolMCP    = wire.ToolMCP
	ToolSearch = wire.ToolSearch
	ToolOther  = wire.ToolOther
)

Tool kinds.

View Source
const DefaultFakeScript = `` /* 126-byte string literal not displayed */

DefaultFakeScript emits a tiny assistant turn and a success result.

View Source
const FakeScriptEnv = "AGENTWIRE_FAKE_SCRIPT"

FakeScriptEnv overrides the fake driver script from the environment.

View Source
const StallThreshold = 5 * time.Minute

StallThreshold is the "running but no events and no in-flight tools" window that raises a stall. It is observability only; the consumer's max duration is the killer.

Variables

View Source
var ErrResumeUnsupported = errors.New("agentwire: session resume not supported")

ErrResumeUnsupported reports a driver that cannot resume a session (the vendor no longer knows it, or the protocol has no resume).

View Source
var ErrUnsupported = wire.ErrUnsupported

ErrUnsupported reports a capability the harness does not have.

Functions

func ClaudeMCPConfig added in v0.3.1

func ClaudeMCPConfig(servers []MCPServer) map[string]any

ClaudeMCPConfig renders the Claude/pi JSON shape for injected servers: the value of the top-level "mcpServers" key.

func CodexMCPArgs added in v0.3.1

func CodexMCPArgs(servers []MCPServer) []string

CodexMCPArgs renders the `-c mcp_servers.<name>...` overrides for injected servers. It never renders a secret value: a stdio server lists its env keys in env_vars, and a url server maps each header to an env var in env_http_headers. The process must carry CodexMCPEnv.

func CodexMCPEnv added in v0.3.2

func CodexMCPEnv(servers []MCPServer) (map[string]string, error)

CodexMCPEnv returns the env that the codex process must carry for the servers of CodexMCPArgs. Codex has one process env, so two servers that set one key to different values return an error.

func OpenCodeConfigContent added in v0.3.1

func OpenCodeConfigContent(servers []MCPServer) (string, error)

OpenCodeConfigContent renders the OpenCode config fragment that carries the injected servers: the value of OPENCODE_CONFIG_CONTENT.

func SplitArgs

func SplitArgs(command string) []string

SplitArgs splits a prepared command line into arguments.

The rules are the POSIX shell's word splitting, minus expansion: unquoted whitespace separates words, single quotes are literal, double quotes keep whitespace and honor a backslash before " \ $ ` and newline, and a backslash outside quotes escapes the next character. A consumer that pins a path with spaces or a literal backslash therefore gets the argument it wrote.

Types

type Attachment

type Attachment = wire.Attachment

Attachment is one out-of-band prompt payload (images first).

type AuthState

type AuthState string

AuthState is the best-effort login view of a harness.

const (
	AuthUnknown AuthState = "unknown"
	AuthOK      AuthState = "ok"
	AuthMissing AuthState = "missing"
)

type Classification

type Classification struct {
	Class   FailureClass
	Code    string
	Message string
}

Classification is the family plus a stable machine code.

func ClassifyError

func ClassifyError(text string, status int) Classification

ClassifyError inspects error-shaped text and returns the family and code.

text MUST be error-shaped: a result error field, a stderr tail, an HTTP status field. Free-form assistant prose or tool output yields false positives, because vendors quote ordinary words such as "capacity" and "credentials" in normal text. status is an optional HTTP status from the wire (0 = unknown).

func ClassifyFor

func ClassifyFor(h Harness, text string, status int) Classification

ClassifyFor is ClassifyError with the harness recorded in the code, so a consumer maps "claude.limit.rate_limited" and "codex.limit.rate_limited" to different text.

func ClassifyResult

func ClassifyResult(r *Result) Classification

ClassifyResult classifies an error result frame.

func ClassifyResultFor

func ClassifyResultFor(h Harness, r *Result) Classification

ClassifyResultFor is ClassifyResult with the harness recorded in the code.

func (Classification) EndReason

func (c Classification) EndReason() string

EndReason maps a class to the run end-reason vocabulary (auth|limit|failed|launch).

type Credentials added in v0.3.0

type Credentials struct {
	// Format names the provider wire: "anthropic" or "openai".
	Format string
	// BaseURL overrides the provider endpoint. Empty uses the provider
	// default; an http(s) URL turns the SDK into a gateway client.
	BaseURL string
	// APIKey is the bearer credential.
	APIKey string
	// Headers are extra HTTP headers for every provider call.
	Headers map[string]string
}

Credentials are the provider credentials a native session runs with.

type Decision

type Decision = wire.Decision

Decision answers a Permission.

type Detection

type Detection struct {
	Harness Harness
	// Path is the resolved binary, empty when the harness is not installed.
	Path string
	// Version is the first line of `<bin> --version`, empty when unknown.
	Version string
	// Supported reports that the binary exists and meets the version floor.
	Supported bool
	// Ready reports Supported && Auth == AuthOK. Runtime.Detect sets it for
	// every harness, so a picker reads one field.
	Ready bool
	// Auth is the best-effort login state.
	Auth AuthState
	// AuthDetail is a short note from the auth probe, such as an account
	// email. It never contains a token.
	AuthDetail string
	// Features is the static capability table for the harness.
	Features Features
	// Detail explains an unsupported verdict.
	Detail string
}

Detection is the result of probing one harness on this machine.

type Detector added in v0.3.0

type Detector interface {
	Detect(ctx context.Context) (Detection, error)
}

Detector lets a driver answer Detect without a binary lookup. The native driver implements it: it is in-process and has no binary to find.

type Driver

type Driver interface {
	Name() string
	Start(ctx context.Context, req StartRequest) (Session, error)
	Resume(ctx context.Context, req StartRequest) (Session, error)
}

Driver starts sessions for one vendor protocol. The built-in drivers are unexported and reached through Runtime.Driver.

type Event

type Event = wire.Event

Event is one normalized wire event from a vendor session.

type EventType

type EventType = wire.EventType

EventType is the normalized wire vocabulary.

type FailureClass

type FailureClass string

FailureClass is the entitlement/quota classification family. No pricing logic lives here, and no operator-facing text: a consumer maps the class and the code to its own wording.

const (
	FailNone        FailureClass = ""
	FailAuth        FailureClass = "auth"
	FailLimit       FailureClass = "limit"
	FailEntitlement FailureClass = "entitlement"
	FailOverloaded  FailureClass = "overloaded"
	FailProtocol    FailureClass = "protocol"
	FailUnknown     FailureClass = "unknown"
)

type FakeDriver

type FakeDriver struct {
	// Script is a shell program. Empty uses FakeScriptEnv, then
	// DefaultFakeScript.
	Script string
	// Dir overrides the working directory.
	Dir string
}

FakeDriver is the scripted driver for tests: it spawns a shell that emits the same NDJSON vocabulary the vendors do, so a consumer's event pump is exercised without a vendor binary.

It implements Driver on its own, so a consumer injects it with Runtime.SetDriver.

func (FakeDriver) Name

func (FakeDriver) Name() string

Name implements Driver.

func (FakeDriver) Resume

func (f FakeDriver) Resume(ctx context.Context, req StartRequest) (Session, error)

Resume implements Driver.

func (FakeDriver) Start

func (f FakeDriver) Start(ctx context.Context, req StartRequest) (Session, error)

Start implements Driver.

type Features

type Features struct {
	// MCPInject says how MCPServers reach the harness.
	MCPInject MCPCapability
	// SkillsInject says how Skills reach the harness.
	SkillsInject SkillSupport
	// Instructions reports per-session instruction support.
	Instructions bool
	// Attachments reports prompt image support.
	Attachments bool
	// Resume reports session resume support.
	Resume bool
	// Permissions reports that the harness asks before a tool runs and the
	// library can answer.
	Permissions bool
	// FS reports that the harness can route file reads and writes to
	// StartRequest.FS.
	FS bool
}

Features reports what a harness can take per session. It is static per harness: a feature that needs StartRequest.Home degrades at run time rather than appearing and disappearing here.

type FileSystem

type FileSystem interface {
	ReadTextFile(ctx context.Context, path string) (string, error)
	WriteTextFile(ctx context.Context, path, content string) error
}

FileSystem is the optional file access an ACP agent may request. When it is set, the library advertises fs.readTextFile and fs.writeTextFile and routes those calls here, so a consumer can observe or approve every file write.

type Harness

type Harness string

Harness identifies one vendor coding agent.

const (
	Claude    Harness = "claude"
	Codex     Harness = "codex"
	OpenCode  Harness = "opencode"
	OpenCode2 Harness = "opencode2"
	Pi        Harness = "pi"
	Copilot   Harness = "copilot"
	Cursor    Harness = "cursor"
	Gemini    Harness = "gemini"
	Fake      Harness = "fake"
	// Native is the in-process harness the native module serves. It is not
	// in New's built-in driver table: a consumer registers it with
	// SetDriver(Native, nativedriver) or leaves it out entirely.
	Native Harness = "native"
)

func Harnesses

func Harnesses() []Harness

Harnesses lists every harness the library can drive a wire for.

type LedgerEntry

type LedgerEntry struct {
	Key        string    `json:"key"`
	Harness    string    `json:"harness"`
	PID        int       `json:"pid"`
	PGID       int       `json:"pgid,omitempty"`
	StartToken string    `json:"start_token,omitempty"`
	StartedAt  time.Time `json:"started_at"`
	Argv       []string  `json:"argv,omitempty"`
}

LedgerEntry is one spawned child, recorded so a crashed process can reconcile leftovers on boot. StartToken is the positive-match guard against pid reuse.

type MCPCapability

type MCPCapability string

MCPCapability reports how a harness accepts per-session MCP servers.

const (
	// MCPNone means the harness cannot take an injected server.
	MCPNone MCPCapability = "none"
	// MCPFlags means the library renders the servers into the launch.
	MCPFlags MCPCapability = "flags"
	// MCPSession means the servers travel in the session-open request.
	MCPSession MCPCapability = "session"
)

type MCPServer

type MCPServer struct {
	Name    string
	Command string
	Args    []string
	Env     map[string]string
	URL     string
	Headers map[string]string
}

MCPServer is one Model Context Protocol server injected into a session. Command selects a stdio server; URL selects an HTTP server.

type Options

type Options struct {
	// ClientName is the product name the harness sees in clientInfo and in
	// vendor client markers, for example "trebi" or "sling".
	ClientName string
	// ClientVersion is the product version reported next to ClientName.
	ClientVersion string
	// LedgerPath is the crash ledger file. Empty disables the ledger.
	LedgerPath string
	// Logger receives structured lifecycle lines. Nil discards them.
	Logger *slog.Logger
	// FingerprintIgnoreEnv lists environment keys left out of the OpenCode
	// server fingerprint, so per-run values do not split a shared server.
	FingerprintIgnoreEnv []string
	// MaxFrameBytes bounds one wire frame. Zero uses 16 MiB.
	MaxFrameBytes int
	// OnStderr receives live stderr lines from every child.
	OnStderr func(Harness, string)
	// AuthCacheTTL is how long a harness login probe answers from cache.
	// Zero uses 5 min. A negative value disables the cache.
	AuthCacheTTL time.Duration
}

Options configures a Runtime.

type Permission

type Permission = wire.Permission

Permission is a vendor permission request awaiting a decision.

type PermissionMode

type PermissionMode string

PermissionMode is the one policy that every harness maps onto its own mechanism.

const (
	// PermissionAsk routes every request to the consumer as an
	// EventPermission. It is the default.
	PermissionAsk PermissionMode = "ask"
	// PermissionAutoEdit answers edit requests in the library and routes the
	// rest to the consumer.
	PermissionAutoEdit PermissionMode = "auto_edit"
	// PermissionAuto answers every request in the library.
	PermissionAuto PermissionMode = "auto"
	// PermissionInherit adds no permission-mode flag, so the operator's own
	// harness configuration decides. It is the mode for a consumer that must
	// not override a setting the operator made in the harness itself. The
	// host prompt callback is still installed, and requests the harness sends
	// are routed to the consumer, like ask.
	PermissionInherit PermissionMode = "inherit"
)

type PermissionPolicy

type PermissionPolicy struct {
	Mode PermissionMode
	// AllowTools lists extra tools that are allowed without asking, in "ask"
	// and "auto_edit" mode. Patterns may end in "*", for example
	// "mcp__sling__*".
	AllowTools []string
}

PermissionPolicy is the normalized permission policy.

func (PermissionPolicy) Answer

func (p PermissionPolicy) Answer(kind ToolKind, tool string) (Decision, bool)

Answer returns the decision the policy makes without the consumer. ok=false means the request becomes an EventPermission.

func (PermissionPolicy) Normalized

func (p PermissionPolicy) Normalized() PermissionPolicy

Normalized returns the policy with a concrete mode.

type Prompt

type Prompt = wire.Prompt

Prompt is one user turn written to a live session.

type Registry

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

Registry owns the live sessions and the pid ledger. A Runtime builds one when Options.LedgerPath is set.

func NewRegistry

func NewRegistry(path string) *Registry

NewRegistry builds a registry whose ledger lives at path. An empty path disables persistence, which is what tests want.

func (*Registry) Add

func (r *Registry) Add(key string, s Session)

Add registers a live session under key and records its pid.

func (*Registry) CloseAll

func (r *Registry) CloseAll(ctx context.Context)

CloseAll gracefully closes every live session and empties the registry.

func (*Registry) Get

func (r *Registry) Get(key string) (Session, bool)

Get returns the live session for a key.

func (*Registry) Keys

func (r *Registry) Keys() []string

Keys lists live session keys in no particular order.

func (*Registry) List

func (r *Registry) List() []LedgerEntry

List reads the persisted ledger.

func (*Registry) Path

func (r *Registry) Path() string

Path is the ledger file, empty when the registry is memory-only.

func (*Registry) Reconcile

func (r *Registry) Reconcile() (killed []string, stale []string)

Reconcile is the boot path: for each ledger row, kill the process only when it is positively matched (alive and start token equal); every row is then dropped. It returns the run keys whose processes were killed and every key that needs a session_gone row.

func (*Registry) Remove

func (r *Registry) Remove(key string)

Remove unregisters a session and drops its ledger row.

func (*Registry) Track

func (r *Registry) Track(key, harness string, pid, pgid int, argv []string)

Track records a process without a live Session. Use it for a daemon-owned helper such as an OpenCode server.

type Result

type Result = wire.Result

Result is the terminal turn payload.

type Runtime

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

Runtime owns every piece of per-process state: the driver table, the OpenCode server pool, version caches, the crash ledger and the id counter.

func New

func New(opts Options) *Runtime

New builds a runtime.

func (*Runtime) Close

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

Close stops every live session and shared server.

func (*Runtime) Detect

func (rt *Runtime) Detect(ctx context.Context, h Harness) (Detection, error)

Detect probes one harness: binary, version, floor and login state. A missing binary is a supported=false verdict, not an error. A driver that implements Detector answers for itself without a binary lookup.

func (*Runtime) Driver

func (rt *Runtime) Driver(h Harness) (Driver, bool)

Driver resolves the driver for a harness. ok=false means the harness has no proven wire.

func (*Runtime) HelpText

func (rt *Runtime) HelpText(ctx context.Context, bin string) string

HelpText returns the cached `<bin> --help` output, for a decision that needs to compare two flags instead of testing one.

func (*Runtime) InvalidateDetect added in v0.2.2

func (rt *Runtime) InvalidateDetect(h Harness)

InvalidateDetect drops the cached login probes of one harness. A consumer calls it after a login or a logout, so Detect sees the new state at once.

func (*Runtime) Logger

func (rt *Runtime) Logger() *slog.Logger

Logger is the runtime logger.

func (*Runtime) MaxFrameBytes

func (rt *Runtime) MaxFrameBytes() int

MaxFrameBytes is the effective frame cap.

func (*Runtime) OpenCodeMessages

func (rt *Runtime) OpenCodeMessages(ctx context.Context, sessionID string) ([]byte, bool)

OpenCodeMessages returns the persisted message list for one session from a live server. ok=false means no server currently holds the session.

func (*Runtime) Reconcile

func (rt *Runtime) Reconcile() error

Reconcile kills leftover children from a previous process and drops the ledger. Only positively matched pids die: a reused pid is left alone.

func (*Runtime) Resume

func (rt *Runtime) Resume(ctx context.Context, req StartRequest) (Session, error)

Resume reopens a known session. It returns ErrResumeUnsupported when the vendor no longer knows the session, or the protocol has no resume.

func (*Runtime) SetDriver

func (rt *Runtime) SetDriver(h Harness, d Driver)

SetDriver replaces the driver for a harness. Tests use it to inject a scripted driver. nil drops the override and restores the built-in driver, so a caller can always get back to the default table.

func (*Runtime) Start

func (rt *Runtime) Start(ctx context.Context, req StartRequest) (Session, error)

Start opens a new session and waits for the protocol handshake. A handshake failure is returned as an error; the child is killed first.

func (*Runtime) Version

func (rt *Runtime) Version(ctx context.Context, bin string) string

Version returns the cached `--version` line of a binary.

type Session

type Session interface {
	// Provider is the harness name.
	Provider() string
	// ID is the vendor session id; it may be empty until init.
	ID() string
	// PID is the child process id (0 for in-process sessions).
	PID() int
	// Prompt writes one user turn.
	Prompt(ctx context.Context, p Prompt) error
	// Interrupt asks the harness to stop the current turn.
	Interrupt(ctx context.Context) error
	// AnswerPermission routes a decision to the vendor.
	AnswerPermission(ctx context.Context, permissionID string, d Decision) error
	// InterruptTurn is the protocol-level interrupt. It is the same call as
	// Interrupt for every built-in driver, and is kept for callers that want
	// to be explicit about not falling back to a signal.
	InterruptTurn(ctx context.Context) error
	// Done is closed when the session has ended and Events is drained.
	Done() <-chan struct{}
	// Err is the first failure the session recorded, if any.
	Err() error
	// Close runs the graceful close sequence and waits for the drain.
	Close(ctx context.Context) error
	// Events is closed after the terminal events.
	Events() <-chan Event
}

Session is one vendor conversation. The caller holds it for the life of the run.

A caller MUST read Events until it closes, or call Close. A caller that stops reading without closing leaks the framing goroutine.

type SessionStatus

type SessionStatus = wire.SessionStatus

SessionStatus is the activity view of a session.

type Skill

type Skill struct {
	// Name overrides the name in the SKILL.md frontmatter.
	Name string
	// Dir is the absolute path of the directory that holds SKILL.md.
	Dir string
}

Skill is one extra skill made visible to a session.

type SkillSupport

type SkillSupport string

SkillSupport reports how a harness takes extra skills for one session.

const (
	// SkillNone means the harness gets no extra skills.
	SkillNone SkillSupport = "none"
	// SkillNative means the harness loads the skills itself, from a directory
	// the library generates or points at.
	SkillNative SkillSupport = "native"
	// SkillOverlay means the library links the skills into a per-session
	// config directory the harness already reads.
	SkillOverlay SkillSupport = "overlay"
	// SkillInstructions means the skills are described in the session
	// instructions and the agent reads them from disk.
	SkillInstructions SkillSupport = "instructions"
)

type Snapshot

type Snapshot struct {
	State         SessionStatus
	TurnActive    bool
	Progressing   bool
	Stalled       bool
	StallSince    time.Time
	InflightTools int
	OpenApprovals int
	LastEventAt   time.Time
	LastEventKind EventType
	LastToolName  string
	IdleSince     time.Time
	WaitingSince  time.Time
	LastResult    *Result
	// Exited reports that the session's process ended.
	Exited bool
	// ExitedAt is the moment the exit event landed.
	ExitedAt time.Time
}

Snapshot is the derived state a consumer and its reaper read.

type StartRequest

type StartRequest struct {
	Harness Harness
	// Binary is the executable. Empty searches PATH.
	Binary     string
	WorkingDir string
	// Env is added to the parent environment and wins on conflict.
	Env map[string]string
	// Home is an optional isolated config home. Empty uses the user's own
	// configuration, including their login, MCP servers and skills.
	Home string

	// SessionID is a resume target, or a pre-assigned id where the harness
	// allows one.
	SessionID string
	// Model is the vendor model name. Empty uses the vendor default.
	Model string
	// Effort is one of low, medium, high, max. Empty uses the vendor default.
	Effort string

	Permissions  PermissionPolicy
	MCPServers   []MCPServer
	Skills       []Skill
	Instructions string

	// Credentials carries the provider credentials for the native harness
	// only. The library never reads env or secrets to fill it, and the CLI
	// drivers ignore it.
	Credentials *Credentials

	// OneShot writes OneShotPrompt and closes stdin, so the child exits after
	// the turn. Use it for a job with no follow-up surface.
	OneShot       bool
	OneShotPrompt string
	// HandshakeTimeout overrides the protocol handshake wait.
	HandshakeTimeout time.Duration

	// ExtraArgs is appended verbatim after the library's own arguments. Use
	// it for consumer flags such as --settings or -c notify=...
	ExtraArgs []string
	// BaseCommand is an expert override: when it is set the launch builder is
	// skipped and the command is used as-is.
	BaseCommand []string
	// Raw carries per-harness overrides the typed fields do not cover:
	// "strict_mcp" (bool, Claude), "acp_auth_method" (string), "approvalPolicy"
	// and "sandbox" (string, Codex), "auth_method" (string, ACP).
	Raw map[string]any

	// LogPath writes every frame in and out as NDJSON. Secrets are not
	// redacted, so the file is created mode 0600.
	LogPath string
	// FS is the optional ACP file-system adapter.
	FS FileSystem
	// OnStderr receives live stderr lines from this child.
	OnStderr func(Harness, string)
}

StartRequest describes one session. The caller describes; the library builds the command.

func (StartRequest) RawBool

func (r StartRequest) RawBool(key string) (bool, bool)

RawBool reads a boolean override.

func (StartRequest) RawString

func (r StartRequest) RawString(key string) string

RawString reads a string override.

type ToolEvent

type ToolEvent = wire.ToolEvent

ToolEvent is one tool invocation (start or completion).

type ToolKind

type ToolKind = wire.ToolKind

ToolKind is the coarse class of a tool invocation.

func ClassifyTool

func ClassifyTool(name string) ToolKind

ClassifyTool maps a vendor tool name to a coarse kind, so a consumer can render or auto-approve without a per-vendor name table.

type Tracker

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

Tracker is the per-run activity tracker. Wire events are the only input; a consumer reads snapshots instead of scraping a terminal.

func NewTracker

func NewTracker() *Tracker

NewTracker builds an empty tracker.

func (*Tracker) InflightCount

func (t *Tracker) InflightCount() int

InflightCount reports tool invocations without a matching result.

func (*Tracker) Observe

func (t *Tracker) Observe(e Event) Snapshot

Observe folds one event into the tracker and returns the derived snapshot.

func (*Tracker) OldestApprovalID

func (t *Tracker) OldestApprovalID() string

OldestApprovalID returns the oldest open wire permission id, or "" when the pending approval has none.

func (*Tracker) PermissionAdded

func (t *Tracker) PermissionAdded(id string) Snapshot

PermissionAdded registers an approval the wire did not open.

func (*Tracker) PermissionDecided

func (t *Tracker) PermissionDecided(id string) Snapshot

PermissionDecided clears an open approval. An empty id clears the oldest one (approvals that arrived without a wire id have none).

func (*Tracker) PromptWritten

func (t *Tracker) PromptWritten()

PromptWritten marks the optimistic running state when a prompt is written: model latency is not idleness.

func (*Tracker) Snapshot

func (t *Tracker) Snapshot() Snapshot

Snapshot returns the derived state.

type Usage

type Usage = wire.Usage

Usage is token/cost accounting in vendor units.

Directories

Path Synopsis
internal
flight
Package flight collapses concurrent same-key calls into one execution.
Package flight collapses concurrent same-key calls into one execution.
jsonrpc
Package jsonrpc is the JSON-RPC 2.0 client shared by the harness drivers that speak it (Codex app-server, ACP agents).
Package jsonrpc is the JSON-RPC 2.0 client shared by the harness drivers that speak it (Codex app-server, ACP agents).
proc
Package proc spawns and reaps one child process with explicit pipes, group control, and a single Wait.
Package proc spawns and reaps one child process with explicit pipes, group control, and a single Wait.
wire
Package wire is the session plumbing shared by every harness driver: it owns the child process, the NDJSON framing, the event fan-out, and the graceful close sequence.
Package wire is the session plumbing shared by every harness driver: it owns the child process, the NDJSON framing, the event fan-out, and the graceful close sequence.
native module

Jump to

Keyboard shortcuts

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