adapters

package
v0.0.8 Latest Latest
Warning

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

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

Documentation

Overview

Package adapters defines the HarnessAdapter interface and shared types for harness-specific agent protocol implementations.

Adapters live in internal/adapters/<harness>/. The boid core references only this package (the interface and Usage type); harness details stay inside each sub-package.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type BindMount

type BindMount struct {
	// Source is the absolute host path to mount in.
	Source string
	// Target is the absolute sandbox path. Empty means "same as Source".
	Target string
	// Mode is "rw" for read-write, "" (default) for read-only.
	Mode string
	// IsFile flips the mount to file-bind (touch target first) instead of
	// directory-bind. Used by the claude binding for ~/.claude.json.
	IsFile bool
	// Optional makes a missing Source on the host skip the mount instead of
	// failing dispatch. Dispatcher converts this to a shell-level if-guard.
	Optional bool
}

BindMount is the adapter-facing DTO for a host bind-mount request.

It is shape-compatible with orchestrator.BindMount and sandbox.BindMount on purpose — the dispatcher converts adapter-declared BindMounts straight into the same sandbox.Mount entries the kit-declared additional_bindings produce — but is defined here so the adapters package stays free of orchestrator and sandbox imports. orchestrator depends on adapters (HarnessAdapter is held on JobSpec.Adapter); the reverse import would close that cycle.

type HarnessAdapter

type HarnessAdapter interface {
	// Run forks the agent process, manages its signal lifecycle, and returns
	// the captured payload patch and usage. See RunContext / Result for the
	// I/O contract.
	Run(ctx context.Context, rc RunContext) (Result, error)

	// Usage returns token consumption metrics for the job identified by jobID.
	// Returns a zero Usage and a nil error when metrics are not yet available.
	Usage(ctx context.Context, jobID string) (Usage, error)

	// Bindings declares the host bind-mounts this harness needs inside the
	// sandbox. The dispatcher prepends this set to the kit-declared
	// additional_bindings (if any) and lets the standard buildPATH / mount
	// pipeline turn them into sandbox.Mount entries — so a binding whose
	// Source ends in "/bin" automatically lands on PATH inside the sandbox.
	//
	// homeDir is the host home of the user the daemon runs as ($HOME, not the
	// sandbox-internal HOME); adapters compose Source paths against it. The
	// dispatcher fills it from os/user so the call is pure (testable without
	// touching the environment).
	//
	// Returned BindMounts should typically set Optional=true so a missing
	// source dir is silently skipped (the dispatcher converts Optional →
	// shell-level if-guard). The recipe an adapter normally follows:
	//
	//  1. its private state dir (e.g. ~/.claude) — Mode "rw", Optional
	//  2. the resolved CLI binary's parent dir found via exec.LookPath +
	//     filepath.EvalSymlinks — Mode "" (ro), Optional
	//  3. any runtime shim tree the CLI needs at exec time (e.g. ~/.volta for
	//     volta-shimmed installs) — Mode "" (ro), Optional
	//
	// Adapters return nil when they have nothing to declare. claude.Adapter
	// ships a non-trivial set (Phase 3-e absorbed it from the retired
	// boid-kits claude-code kit); other adapters typically return nil for
	// now.
	Bindings(homeDir string) []BindMount
}

HarnessAdapter abstracts harness-specific agent protocol from boid core. Each supported harness (claude, codex, opencode, …) provides one implementation. The boid core calls these methods without knowing which harness is in use.

Phase 3-b shrank the interface to two members: Run() owns the entire agent process lifecycle (session resolve, fork, signal handling, payload capture, exit normalisation), and Usage() exposes post-run token metrics. Signal stop is delivered out-of-band via api.JobLifecycle.SignalJobRuntime so the daemon does not need to round-trip through the adapter to nudge a running agent.

type Result

type Result struct {
	// ExitCode is the child process's exit code, normalised for stop signals
	// (a daemon-initiated SIGTERM is mapped to 0 via StoppedByDaemon).
	ExitCode int

	// PayloadPatch holds the contents of payload_patch.json after the agent
	// exits. May be nil if the file was never written.
	PayloadPatch json.RawMessage

	// Usage carries token / cost metrics for the run when available. May be
	// the zero value when the harness does not expose post-run metrics or
	// the read path is not wired yet.
	Usage Usage

	// StoppedByDaemon is true when Run intercepted the daemon's stop signal
	// (SIGUSR1) and translated the child's SIGTERM-induced exit into 0.
	// Daemon callers use this to distinguish Q&A pauses from real failures.
	StoppedByDaemon bool
}

Result is the output of HarnessAdapter.Run.

type RunContext

type RunContext struct {
	// JobID is the broker job_done key and transcript correlation id.
	JobID string
	// TaskID is the task id of the hook job, or "" for session/exec jobs.
	TaskID string

	// UserAnswer carries the bootstrap text the adapter delivers to the agent
	// as the first user turn. Used by user-initiated session jobs to plumb the
	// `boid agent <harness> --instruction` flag through to the agent's first
	// turn; hook jobs leave it empty (the agent receives the harness' default
	// bootstrap skill instead).
	UserAnswer string

	// InvokedBehavior is the canonical behaviour name ("executor" /
	// "supervisor" / a free-naming behaviour) used to pick the bootstrap
	// skill prompt for a fresh session.
	InvokedBehavior string
	// InvokedName is the behaviour-instance name (e.g. verifier suffix)
	// used when resolving / persisting session entries in the payload.
	InvokedName string

	// Model overrides the agent's default model (e.g. "claude-opus-4-8").
	// Empty leaves model selection to the harness binary.
	Model string

	// Workspace is the child process's cwd.
	Workspace string
	// PayloadPath points at the payload.json the adapter reads to recover
	// prior session entries. Defaults to ~/.boid/context/payload.json when
	// empty (resolved by the adapter, not the caller).
	PayloadPath string
	// OutputDir is where payload_patch.json is written. Defaults to
	// ~/.boid/output when empty.
	OutputDir string

	// Stdin / Stdout / Stderr are passed verbatim to the child. The caller
	// is responsible for PTY allocation; the adapter only forwards fds.
	Stdin  io.Reader
	Stdout io.Writer
	Stderr io.Writer

	// Env contains environment variables to inject into the child on top
	// of the parent's environment. The adapter may add harness-specific
	// entries (e.g. IS_SANDBOX=1 for Claude CLI 2.1.181+ uid 0 bypass).
	Env map[string]string

	// SkillsDir is the absolute path of the directory where harness-visible
	// skills are exposed (~/.claude/skills for Claude). Reserved for future
	// use; PR1 does not act on it (skill bind-mounts are wired in the
	// dispatcher under Phase 3-b PR2).
	SkillsDir string

	// Argv is the literal program + arguments to exec. Only the shell adapter
	// consumes this field — claude / codex / opencode build their own argv
	// from their CLI conventions and ignore Argv entirely. Phase 3-d added
	// Argv so non-agent hooks and `boid exec` can flow through the same
	// adapter pipeline as agent jobs instead of branching to a separate
	// runExecArgv path in the runner-inner-child.
	Argv []string

	// StdinBytes, when non-empty, is piped into the child's stdin instead of
	// Stdin. Shell adapter only — agent adapters allocate a PTY and route
	// through Stdin. Used by hook scripts that read a JSON payload on stdin.
	StdinBytes []byte
	// StdoutCaptureFile, when non-empty, makes the shell adapter redirect the
	// child's stdout to this host-side path (instead of writing to Stdout).
	// Doubles as the broker job-done output fallback when no payload patch
	// exists.
	StdoutCaptureFile string
}

RunContext is the input to HarnessAdapter.Run. It bundles everything the adapter needs to fork the agent process, manage its lifecycle, and return the captured payload. Phase 3-b draft; see docs/plans/agent-aware-boid.md "Phase 3-b" section.

Run() owns the following responsibilities internally:

  1. signal.Notify(SIGUSR1) → child SIGTERM (Go signal.Notify auto-bypasses inherited sigprocmask block / SIG_IGN — verified by Phase 3-b PoC).
  2. signal.Notify(SIGWINCH) → forward to child.
  3. cmd.SysProcAttr.Setsid = true so child sits in its own session/pgrp (Python start_new_session=True equivalent).
  4. Exit code normalisation (stop signal terminations are reported as 0 via Result.StoppedByDaemon).
  5. Persisting a freshly-generated session id to payload_patch.json so the jsonl transcript path is recorded in the task's artifact even when the child terminates abnormally (SIGKILL, OOM).

type Usage

type Usage struct {
	// Model is the model identifier used for this job (e.g. "claude-opus-4-8").
	Model string `json:"model,omitempty"`

	// InputTokens is the number of uncached input tokens consumed.
	InputTokens int64 `json:"input_tokens"`

	// OutputTokens is the number of generated output tokens.
	OutputTokens int64 `json:"output_tokens"`

	// CacheCreationTokens is the number of tokens written to the prompt cache.
	// Zero for harnesses that do not support prompt caching.
	CacheCreationTokens int64 `json:"cache_creation_tokens,omitempty"`

	// CacheReadTokens is the number of tokens served from the prompt cache.
	// Zero for harnesses that do not support prompt caching.
	CacheReadTokens int64 `json:"cache_read_tokens,omitempty"`

	// Extra holds harness-specific data not captured by the fixed fields above.
	// Nil when no additional data is available.
	Extra json.RawMessage `json:"extra,omitempty"`
}

Usage holds token consumption metrics for a completed job. Fixed fields cover the common denominator across harnesses; Extra stores harness-specific data without requiring schema migrations.

Directories

Path Synopsis
Package claude implements adapters.HarnessAdapter for Claude Code.
Package claude implements adapters.HarnessAdapter for Claude Code.
Package codex implements adapters.HarnessAdapter for the Codex CLI.
Package codex implements adapters.HarnessAdapter for the Codex CLI.
Package opencode implements adapters.HarnessAdapter for the opencode CLI.
Package opencode implements adapters.HarnessAdapter for the opencode CLI.
Package registry maps sandbox.HarnessType to a HarnessAdapter implementation.
Package registry maps sandbox.HarnessType to a HarnessAdapter implementation.
Package shell implements adapters.HarnessAdapter for plain shell / exec jobs that do not embed an agent harness.
Package shell implements adapters.HarnessAdapter for plain shell / exec jobs that do not embed an agent harness.
Package sigutil hosts the small signal-forwarding loop every harness adapter runs while its child process is alive.
Package sigutil hosts the small signal-forwarding loop every harness adapter runs while its child process is alive.

Jump to

Keyboard shortcuts

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