Documentation
¶
Overview ¶
Package harness drives AI coding command-line tools through a common API.
Index ¶
- Constants
- func AccountErrorResumable(s string) bool
- func CostFromUsage(model string, usage Usage) float64
- func DirectivePaths() (dirs, files []string)
- func FormatEvent(e Event) string
- func Name(h Harness) string
- func Names() string
- func PreferAccountErrorText(current, candidate string) string
- func ResumableReset(errText string, limit *RateLimitInfo) *time.Time
- func Run(ctx context.Context, h Harness, j Job, emit func(Event)) error
- func StreamCmd(cmd *exec.Cmd, h Harness, emit func(Event)) (stderr string, err error)
- func StripDirectives(root string) (int, error)
- func WriteSystemPrompt(h Harness, j Job) error
- type AccountError
- type ClaudeHarness
- func (ClaudeHarness) AccountErrorText(s string) string
- func (ClaudeHarness) Args(j Job) []string
- func (ClaudeHarness) Binary() string
- func (ClaudeHarness) DefaultModels() []ModelDefault
- func (ClaudeHarness) EgressHosts() []string
- func (ClaudeHarness) Env(baseURL string) []string
- func (ClaudeHarness) GuideFilename() string
- func (ClaudeHarness) ParseStream(r io.Reader, emit func(Event))
- func (ClaudeHarness) Prompt(j Job) string
- func (ClaudeHarness) SkillDir(workspace, name string) string
- func (ClaudeHarness) StateEnv(dir string) []string
- func (ClaudeHarness) SystemPromptViaArgs() bool
- type CodexHarness
- func (CodexHarness) AccountErrorText(s string) string
- func (CodexHarness) Args(j Job) []string
- func (CodexHarness) Binary() string
- func (CodexHarness) DefaultModels() []ModelDefault
- func (CodexHarness) EgressHosts() []string
- func (CodexHarness) Env(_ string) []string
- func (CodexHarness) GuideFilename() string
- func (CodexHarness) ParseStream(r io.Reader, emit func(Event))
- func (CodexHarness) Prompt(j Job) string
- func (CodexHarness) SkillDir(workspace, name string) string
- func (CodexHarness) StateEnv(dir string) []string
- func (CodexHarness) SystemPromptViaArgs() bool
- type CopilotHarness
- func (CopilotHarness) AccountErrorText(s string) string
- func (CopilotHarness) Args(j Job) []string
- func (CopilotHarness) Binary() string
- func (CopilotHarness) DefaultModels() []ModelDefault
- func (CopilotHarness) EgressHosts() []string
- func (CopilotHarness) Env(baseURL string) []string
- func (CopilotHarness) GuideFilename() string
- func (CopilotHarness) ParseStream(r io.Reader, emit func(Event))
- func (CopilotHarness) Prompt(j Job) string
- func (CopilotHarness) SkillDir(workspace, name string) string
- func (CopilotHarness) StateEnv(dir string) []string
- func (CopilotHarness) SystemPromptViaArgs() bool
- type Event
- type Harness
- type Job
- type ModelDefault
- type OpencodeHarness
- func (OpencodeHarness) AccountErrorText(s string) string
- func (OpencodeHarness) Args(j Job) []string
- func (OpencodeHarness) Binary() string
- func (OpencodeHarness) DefaultModels() []ModelDefault
- func (OpencodeHarness) EgressHosts() []string
- func (OpencodeHarness) Env(_ string) []string
- func (OpencodeHarness) GuideFilename() string
- func (OpencodeHarness) ParseStream(r io.Reader, emit func(Event))
- func (OpencodeHarness) Prompt(j Job) string
- func (OpencodeHarness) SkillDir(workspace, name string) string
- func (OpencodeHarness) StateEnv(dir string) []string
- func (OpencodeHarness) SystemPromptViaArgs() bool
- type RateLimitInfo
- type Usage
Constants ¶
const ( KindThinking = "thinking" KindText = "text" KindTool = "tool" KindResult = "result" KindError = "error" KindSession = "session" KindRateLimit = "rate_limit" KindEgress = "egress" )
const (
DefaultMaxTurns = 30
)
Variables ¶
This section is empty.
Functions ¶
func AccountErrorResumable ¶
AccountErrorResumable reports whether an account error describes a transient limit. Revoked access wins when both permanent and transient phrases appear.
func CostFromUsage ¶
CostFromUsage calculates a result event's list-price cost. It returns zero for an unknown model rather than presenting an incorrect estimate.
InputTokens includes all prompt tokens. CacheReadTokens is a discounted subset. CacheWriteTokens is separate only for models with a dedicated write rate; it remains ordinary input when CacheWrite is zero.
func DirectivePaths ¶ added in v0.1.5
func DirectivePaths() (dirs, files []string)
DirectivePaths returns directory and file basename patterns that agent CLIs automatically load as project instructions or configuration. Matching is case-insensitive and follows path.Match semantics. The returned slices are copies and may be modified by the caller.
func FormatEvent ¶
FormatEvent renders an event as one plain-text log line.
func Name ¶
Name returns the registered name of h. Comparing concrete types avoids an interface equality panic if a future implementation contains a slice or map. An unregistered implementation falls back to its binary name.
func Names ¶
func Names() string
Names returns the registered backend names in lexical order, excluding the empty default alias.
func PreferAccountErrorText ¶
PreferAccountErrorText keeps the first account error unless a later message is non-resumable while the earlier one is a transient limit. Keying on AccountErrorResumable rather than the shared revoked-phrase list means a backend-local permanent phrase such as "invalid_api_key" still displaces an earlier "rate limit", so a permanent failure is never scheduled for retry.
func ResumableReset ¶
func ResumableReset(errText string, limit *RateLimitInfo) *time.Time
ResumableReset returns a rejected limit's reset time only when the associated account error is transient. Revoked access always requires manual action.
func StreamCmd ¶ added in v0.1.11
StreamCmd starts cmd, streams its combined output through h.ParseStream to emit, and returns after the process exits and parsing completes. It sets cmd.SysProcAttr and cmd.Cancel so context cancellation SIGTERMs the process group instead of orphaning children. On non-zero exit it classifies stderr and any parsed KindError event via h.AccountErrorText and returns an *AccountError on a match; otherwise it returns the raw exec error unwrapped so the caller can add its own context. stderr is returned so a caller can include a runtime failure message in that error.
func StripDirectives ¶ added in v0.1.5
StripDirectives removes files and directories below root whose basenames match DirectivePaths. A removed directory counts as one item regardless of its contents. The .git subtree is skipped. A missing root is a no-op.
func WriteSystemPrompt ¶
WriteSystemPrompt writes j.SystemPrompt to the guide file used by h. A backend that passes the system prompt in Args has no file to write.
Types ¶
type AccountError ¶
AccountError reports a provider-level account problem for which immediately retrying the command is unlikely to help.
func (*AccountError) Error ¶
func (e *AccountError) Error() string
type ClaudeHarness ¶
type ClaudeHarness struct{}
ClaudeHarness drives Claude Code in print mode. It is the default backend because the original caller used Claude before the shared interface existed.
func (ClaudeHarness) AccountErrorText ¶
func (ClaudeHarness) AccountErrorText(s string) string
func (ClaudeHarness) Args ¶
func (ClaudeHarness) Args(j Job) []string
Args builds the claude -p invocation. An allowed-tools list uses acceptEdits only when the job has a legitimate output file; otherwise the default permission mode keeps the requested read-only boundary intact.
func (ClaudeHarness) Binary ¶
func (ClaudeHarness) Binary() string
func (ClaudeHarness) DefaultModels ¶
func (ClaudeHarness) DefaultModels() []ModelDefault
func (ClaudeHarness) EgressHosts ¶
func (ClaudeHarness) EgressHosts() []string
func (ClaudeHarness) Env ¶
func (ClaudeHarness) Env(baseURL string) []string
func (ClaudeHarness) GuideFilename ¶
func (ClaudeHarness) GuideFilename() string
func (ClaudeHarness) ParseStream ¶
func (ClaudeHarness) ParseStream(r io.Reader, emit func(Event))
ParseStream reads Claude's stream-json output. scanJSONL uses a buffered reader rather than Scanner so an oversized thinking or tool-result line cannot discard the later result event that carries usage and turn counts.
func (ClaudeHarness) Prompt ¶
func (ClaudeHarness) Prompt(j Job) string
func (ClaudeHarness) SkillDir ¶
func (ClaudeHarness) SkillDir(workspace, name string) string
func (ClaudeHarness) StateEnv ¶
func (ClaudeHarness) StateEnv(dir string) []string
func (ClaudeHarness) SystemPromptViaArgs ¶
func (ClaudeHarness) SystemPromptViaArgs() bool
type CodexHarness ¶
type CodexHarness struct{}
CodexHarness drives Codex in headless exec mode. Codex reads AGENTS.md as project guidance and stores resumable threads under CODEX_HOME.
func (CodexHarness) AccountErrorText ¶
func (CodexHarness) AccountErrorText(s string) string
func (CodexHarness) Args ¶
func (CodexHarness) Args(j Job) []string
Args builds codex exec argv. Headless Codex has no slash-style skill invocation, so Prompt names the staged SKILL.md. Resume inserts the thread id after "exec resume". Codex has no per-turn cap, so Job.MaxTurns is intentionally ignored.
func (CodexHarness) Binary ¶
func (CodexHarness) Binary() string
func (CodexHarness) DefaultModels ¶
func (CodexHarness) DefaultModels() []ModelDefault
func (CodexHarness) EgressHosts ¶
func (CodexHarness) EgressHosts() []string
func (CodexHarness) Env ¶
func (CodexHarness) Env(_ string) []string
func (CodexHarness) GuideFilename ¶
func (CodexHarness) GuideFilename() string
func (CodexHarness) ParseStream ¶
func (CodexHarness) ParseStream(r io.Reader, emit func(Event))
ParseStream maps codex exec --json output onto backend-neutral events. Session announcements enable resume, item completions carry text and tools, and unknown or non-JSON lines pass through as text. Codex has no max-turns event because its exec command has no turn cap.
func (CodexHarness) Prompt ¶
func (CodexHarness) Prompt(j Job) string
func (CodexHarness) SkillDir ¶
func (CodexHarness) SkillDir(workspace, name string) string
func (CodexHarness) StateEnv ¶
func (CodexHarness) StateEnv(dir string) []string
func (CodexHarness) SystemPromptViaArgs ¶
func (CodexHarness) SystemPromptViaArgs() bool
type CopilotHarness ¶
type CopilotHarness struct{}
CopilotHarness drives GitHub Copilot CLI in non-interactive prompt mode. Its arguments and JSONL mapping target Copilot CLI 1.0.80 while retaining compatibility with the prompt-mode stream introduced in 1.0.75.
func (CopilotHarness) AccountErrorText ¶
func (CopilotHarness) AccountErrorText(s string) string
func (CopilotHarness) Args ¶
func (CopilotHarness) Args(j Job) []string
Args enables autopilot and tool use without interactive confirmation. The caller must isolate the process and workspace because --allow-all grants the CLI every tool it exposes.
func (CopilotHarness) Binary ¶
func (CopilotHarness) Binary() string
func (CopilotHarness) DefaultModels ¶
func (CopilotHarness) DefaultModels() []ModelDefault
func (CopilotHarness) EgressHosts ¶
func (CopilotHarness) EgressHosts() []string
func (CopilotHarness) Env ¶
func (CopilotHarness) Env(baseURL string) []string
func (CopilotHarness) GuideFilename ¶
func (CopilotHarness) GuideFilename() string
func (CopilotHarness) ParseStream ¶
func (CopilotHarness) ParseStream(r io.Reader, emit func(Event))
ParseStream combines per-call token usage and cumulative billing checkpoints into one result emitted after Copilot's final envelope. Sub-agent calls remain part of the usage total, but their nested conversation events stay out of the parent stream.
func (CopilotHarness) Prompt ¶
func (CopilotHarness) Prompt(j Job) string
func (CopilotHarness) SkillDir ¶
func (CopilotHarness) SkillDir(workspace, name string) string
func (CopilotHarness) StateEnv ¶
func (CopilotHarness) StateEnv(dir string) []string
func (CopilotHarness) SystemPromptViaArgs ¶
func (CopilotHarness) SystemPromptViaArgs() bool
type Event ¶
type Event struct {
Kind string
Tool string
Text string
CostUSD float64
Turns int
Usage Usage
SessionID string
RateLimit *RateLimitInfo
}
Event is one backend-neutral item from an agent's output stream. Tool, CostUSD, Turns, Usage, SessionID, and RateLimit are populated only for their corresponding Kind.
type Harness ¶
type Harness interface {
// Binary is the executable name expected on PATH.
Binary() string
// Args returns argv without the binary for one job.
Args(Job) []string
// Prompt returns the final user prompt passed by Args.
Prompt(Job) string
// ParseStream maps the backend's combined output onto Event values.
ParseStream(io.Reader, func(Event))
// SkillDir returns the directory where the backend discovers a staged
// SKILL.md and its sibling files.
SkillDir(workspace, name string) string
// GuideFilename is the workspace-relative project instruction file loaded
// by the backend.
GuideFilename() string
// SystemPromptViaArgs reports whether Args passes Job.SystemPrompt itself.
// When false, WriteSystemPrompt writes it to GuideFilename instead.
SystemPromptViaArgs() bool
// EgressHosts lists the model and authentication hosts needed by the
// backend, using "*.example.com" for wildcard suffixes.
EgressHosts() []string
// Env returns backend environment entries. A bare key asks a process
// runner to pass through the caller's value.
Env(baseURL string) []string
// StateEnv points the backend at a caller-owned persistent state directory
// so a later process can resume the same session.
StateEnv(dir string) []string
// AccountErrorText returns the matching provider account error, or an empty
// string. Callers should consult it only after a non-zero process exit so
// ordinary model text cannot pause retries.
AccountErrorText(string) string
// DefaultModels returns the built-in model picker entries. The first entry
// is the backend default.
DefaultModels() []ModelDefault
}
Harness describes the CLI-specific parts of an agent invocation. It owns the binary, arguments, stream format, project guide, skill discovery, provider environment, and default models. Process placement and isolation remain the caller's responsibility.
type Job ¶
type Job struct {
// Workspace is the command's working directory. Paths passed to a CLI are
// relative to it.
Workspace string
// SrcDir is the workspace-relative repository directory used in generated
// prompts. It defaults to "src"; use "." when Workspace is the repository
// root.
SrcDir string
// SkillName selects a staged SKILL.md directory. An empty value means no
// staged skill.
SkillName string
// Prompt is the user turn for a fresh run. When it is empty and SkillName
// is set, the harness builds an activation prompt.
Prompt string
// SystemPrompt supplies additional instructions. Claude receives it via
// --system-prompt. Run writes it to the project guide file used by the
// other backends.
SystemPrompt string
Model string
// Effort is the backend-native reasoning effort accepted by Claude and
// Copilot. An empty value leaves the backend default unchanged.
Effort string
MaxTurns int
// OutputFile is a workspace-relative path the skill should write. It is
// empty for free-form runs.
OutputFile string
// ValidationHint is appended to generated prompts after the OutputFile
// clause when OutputFile ends in .json. It lets a caller tell the agent
// how to validate its output (an API endpoint, a staged validator) in
// terms of the caller's own context. When empty, a generic instruction
// to check against ./schema.json is used.
ValidationHint string
// AllowedTools is Claude's comma-separated tool allowlist. Other backends
// leave tool restrictions to their caller's sandbox.
AllowedTools string
// BaseURL overrides the model API endpoint where the backend supports it.
BaseURL string
// ResumeSessionID continues a prior conversation. ResumePrompt is the
// corrective turn used for the resumed invocation.
ResumeSessionID string
ResumePrompt string
}
Job contains the resolved inputs for one CLI invocation.
type ModelDefault ¶
ModelDefault is one model offered by a backend. Tier is "mid", "high", "max", or empty when the model is selectable but not a tier default.
type OpencodeHarness ¶
type OpencodeHarness struct{}
OpencodeHarness drives OpenCode in headless run mode. OpenCode is provider-neutral, so its environment and egress list cover Anthropic, OpenAI, and OpenCode's model registry by default.
func (OpencodeHarness) AccountErrorText ¶
func (OpencodeHarness) AccountErrorText(s string) string
func (OpencodeHarness) Args ¶
func (OpencodeHarness) Args(j Job) []string
Args builds opencode run argv. OpenCode discovers SKILL.md but does not invoke a named skill itself, so Prompt points at the staged file. --auto suppresses interactive permission prompts and --format json selects JSONL. The caller remains responsible for process isolation.
func (OpencodeHarness) Binary ¶
func (OpencodeHarness) Binary() string
func (OpencodeHarness) DefaultModels ¶
func (OpencodeHarness) DefaultModels() []ModelDefault
func (OpencodeHarness) EgressHosts ¶
func (OpencodeHarness) EgressHosts() []string
func (OpencodeHarness) Env ¶
func (OpencodeHarness) Env(_ string) []string
Env ignores baseURL because OpenCode has no single provider endpoint. A caller can configure each provider through OPENCODE_CONFIG_CONTENT.
func (OpencodeHarness) GuideFilename ¶
func (OpencodeHarness) GuideFilename() string
func (OpencodeHarness) ParseStream ¶
func (OpencodeHarness) ParseStream(r io.Reader, emit func(Event))
func (OpencodeHarness) Prompt ¶
func (OpencodeHarness) Prompt(j Job) string
func (OpencodeHarness) SkillDir ¶
func (OpencodeHarness) SkillDir(workspace, name string) string
func (OpencodeHarness) StateEnv ¶
func (OpencodeHarness) StateEnv(dir string) []string
func (OpencodeHarness) SystemPromptViaArgs ¶
func (OpencodeHarness) SystemPromptViaArgs() bool
type RateLimitInfo ¶
type RateLimitInfo struct {
Status string `json:"status"`
OverageStatus string `json:"overageStatus"`
IsUsingOverage bool `json:"isUsingOverage"`
ResetsAt int64 `json:"resetsAt"`
Type string `json:"rateLimitType"`
}
RateLimitInfo contains subscription limit status reported by a backend.
func PreferRateLimitReset ¶
func PreferRateLimitReset(current, candidate *RateLimitInfo) *RateLimitInfo
PreferRateLimitReset returns the rejected rate limit with the later reset so a retry is not scheduled while another reported window still blocks use.
func (*RateLimitInfo) Rejected ¶
func (r *RateLimitInfo) Rejected() bool
Rejected reports whether the limit currently blocks requests.
func (*RateLimitInfo) ResetTime ¶
func (r *RateLimitInfo) ResetTime() *time.Time
ResetTime converts ResetsAt to UTC. It returns nil for an absent or invalid reset time.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
harness-proxy
command
|
|
|
Package container runs a harness backend inside an ephemeral OCI container.
|
Package container runs a harness backend inside an ephemeral OCI container. |
|
Package egress provides an authenticated outbound proxy with a hostname allowlist and a helper for Claude workspace sandbox settings.
|
Package egress provides an authenticated outbound proxy with a hostname allowlist and a helper for Claude workspace sandbox settings. |
|
Package llm provides small, structured one-shot model calls for work that does not need a full agent workspace or tool loop.
|
Package llm provides small, structured one-shot model calls for work that does not need a full agent workspace or tool loop. |
|
Package skills parses, filters, and stages agent skill directories.
|
Package skills parses, filters, and stages agent skill directories. |