launch

package
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Aug 25, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

claude_connector.go implements ClaudeConnector's session-level behavior: locating claude-agent-acp's "model" select config option (tolerating a legacy identifier quirk some adapter versions have), applying model selection, and setting only permission modes the adapter actually advertised. claudecode.go implements the other half of the same type: HarnessAdapter.Configure (executable, argument, and environment wiring).

claudecode.go implements ClaudeConnector's HarnessAdapter half: the executable, argument, and environment contract claude-agent-acp (the ACP adapter for Anthropic's claude-agent SDK) requires. See claude_connector.go for ClaudeConnector's session-level (config option / permission mode) behavior.

codex.go implements CodexConnector's HarnessAdapter half: the codex-acp adapter's executable, argument, and environment contract. See codex_connector.go for CodexConnector's own type, constructor, and immutable selector APIs (the bounded post-session model/effort selection mechanism and the fresh-launch state-copy helpers).

Configure never runs, spawns, or otherwise reaches for codex-acp's own process: version verification is a distinct, explicit preflight step (see version.go's ProbeCodexVersion) a caller runs itself before ever constructing a Config.Command whose Path this method configures -- the same reason the design doc's "one-shot --version invocation" is called out as living outside the ACP session lifecycle (see acp/CLAUDE.md). This file's own contract is purely the argv/env shape.

codex_connector.go defines CodexConnector: the caller-facing type constructed via Codex and passed as Config.Harness to Dial. Its immutable selector state is applied to a connected ACP session through the two bounded, advertised config-option selectors below. The connector never exposes arbitrary config/mode setters or the unstable session/set_model extension; changing the stored selector state still means constructing a new CodexConnector (see WithModel/WithModelEffort), while applying that state to an existing session uses session/set_config_option only for the advertised model and thought_level categories.

Package launch supervises a foreign ACP agent's process together with an inference model proxy that stands in for a real upstream model provider: it starts (or borrows) a proxy, points a stdio.Command at its binding via a HarnessAdapter, spawns and initializes the ACP child through acp/client, and tears both down in the right order.

This package sits alongside acp/client in this module's wire layer (see acp/CLAUDE.md): it imports acp/client, acp/transport/stdio, and acp/protocol (for the wire types acp/client's own public API already exposes, e.g. protocol.SessionConfigOption), but never Harness, Core, or inference, directly or transitively. It exists independently of any particular model-proxy implementation: ModelProxy's Binding method returns a bare (string, string, bool) tuple rather than a ProxyBinding value specifically so a real proxy server built elsewhere (for example inference/gateway.Server) can satisfy it structurally, with zero compile-time coupling to this package.

env.go builds the environment a HarnessAdapter hands its ACP child, reusable by any adapter this package (or a later one, e.g. Codex/Gemini) implements: deep-copy the caller's Command rather than mutate it, add or replace only the adapter's own documented variables, and reject rather than silently strip a caller-supplied value for a name the child contract forbids.

gemini.go implements GeminiAdapter: the Gemini CLI harness adapter's gateway environment contract. Unlike ClaudeConnector and CodexConnector, this is deliberately NOT an ACP connector -- it is a bare HarnessAdapter, env-var construction only. Per the design doc's "Gemini CLI connector" section: "None of the surveyed hosts drive Gemini CLI over ACP, so there is no proven adapter contract to pin ... its ACP connector ships only after an adapter path is verified against a real Gemini CLI release." This file must never grow a GeminiConnector type that dials/manages a Gemini CLI ACP subprocess, claim a supported adapter executable/spawn contract, or add session/config-option handling -- that is scope creep against an explicit, deliberate design decision. See gemini_test.go's regression guard.

managed.go implements Dial and ManagedClient: the owned/shared model-proxy lifecycle around one ACP connection (see contracts.go for the types this wires together).

version.go implements ProbeCodexVersion: a bounded, injectable one-shot probe of a codex-acp binary's own "--version" output, and the fail-closed classification of its result. See codex.go/codex_connector.go for the connector this probe gates: Configure itself never runs this probe (it takes no context and must never spawn a process -- see codex.go's own doc), so a caller is expected to run ProbeCodexVersion once, before ever constructing a Config.Command whose Path points at that binary, exactly the "one-shot --version invocation ... outside the ACP session lifecycle" carve-out acp/CLAUDE.md documents for exec.CommandContext.

Index

Constants

View Source
const DefaultCodexVersionProbeTimeout = 5 * time.Second

DefaultCodexVersionProbeTimeout is the bound ProbeCodexVersion applies to its probe when a caller passes timeout <= 0.

Variables

View Source
var MinCodexVersion = CodexVersion{Major: 1, Minor: 1, Patch: 7}

MinCodexVersion is the oldest codex-acp version this connector supports (see the design doc's Codex connector section): the legacy @zed-industries/codex-acp 0.16.x binary predates this and does not even answer --version. Not meant to be mutated by callers; exposed as a documented, inspectable constant rather than an unexported literal.

Functions

This section is empty.

Types

type ClaudeConnector

type ClaudeConnector struct {
	// Models are the harness-facing model aliases SelectDefaultModel and
	// SelectSmallModel apply.
	Models ClaudeModels
	// Effort is the harness-facing reasoning effort SelectEffort applies via
	// the advertised thought_level config option. Empty leaves effort
	// selection to Claude Code.
	Effort string
	// CLIPath, if non-empty, must be an absolute path pinning the
	// underlying `claude` CLI claude-agent-acp drives (CLAUDE_CODE_EXECUTABLE
	// -- see claudecode.go's Configure). Empty omits the variable entirely.
	CLIPath string
}

ClaudeConnector adapts a launched ACP session to claude-agent-acp's specific conventions. Construct with ClaudeCode.

func ClaudeCode

func ClaudeCode(models ClaudeModels) *ClaudeConnector

ClaudeCode constructs a ClaudeConnector for the given model aliases. The caller may set the returned value's CLIPath field before passing it to Dial as Config.Harness if it needs to pin the underlying `claude` CLI to a specific absolute path.

func (*ClaudeConnector) ApplyPermissionMode

func (c *ClaudeConnector) ApplyPermissionMode(ctx context.Context, sess *client.Session, modeID protocol.SessionModeID) error

ApplyPermissionMode sets modeID on sess via session/set_mode, but only if modeID actually appears in sess's currently advertised mode list (Session.Modes().AvailableModes) -- an unadvertised mode is silently left alone rather than sent to the wire, matching the design doc's "never set a mode that wasn't advertised" (unlike an unmatched model alias, this is a deliberate no-op, not an error: permission modes are optional by nature).

func (*ClaudeConnector) Configure

func (c *ClaudeConnector) Configure(cmd stdio.Command, binding ProxyBinding) (stdio.Command, error)

Configure implements HarnessAdapter for claude-agent-acp: cmd.Path is the caller-supplied absolute path to the claude-agent-acp executable itself (this connector never performs PATH lookup, invokes npx, or installs anything -- discovery and installation are entirely the caller's responsibility); gateway configuration requires both ClaudeModels aliases; the returned Command always has an empty argument list (claude-agent-acp takes none) and carries ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN set to binding's values, plus CLAUDE_CODE_EXECUTABLE if c.CLIPath is set. cmd is never mutated; the returned Command is always a fresh copy (see buildChildCommand).

func (*ClaudeConnector) ConfigureNative

func (c *ClaudeConnector) ConfigureNative(cmd stdio.Command) (stdio.Command, error)

ConfigureNative implements NativeHarnessAdapter. It leaves Claude Code's login and model picker in control when either or both model aliases are omitted.

func (*ClaudeConnector) SelectDefaultModel

func (c *ClaudeConnector) SelectDefaultModel(ctx context.Context, sess *client.Session) error

SelectDefaultModel applies c.Models.Default via session/set_config_option against sess's advertised "model" option.

func (*ClaudeConnector) SelectEffort

func (c *ClaudeConnector) SelectEffort(ctx context.Context, sess *client.Session) error

SelectEffort applies c.Effort via the connected adapter's advertised thought_level select config option. An omitted effort is a deliberate no-op; an unavailable option or value returns *EffortAliasError without a wire call.

func (*ClaudeConnector) SelectSmallModel

func (c *ClaudeConnector) SelectSmallModel(ctx context.Context, sess *client.Session) error

SelectSmallModel applies c.Models.Small via session/set_config_option against sess's advertised "model" option.

type ClaudeModels

type ClaudeModels struct {
	Default string
	Small   string
}

ClaudeModels selects the harness-facing aliases for Claude Code's default and lightweight ("small") model roles: the two values SelectDefaultModel and SelectSmallModel look up among the connected adapter's advertised "model" select config option. Gateway configuration requires both aliases; native no-proxy configuration permits either or both to be empty.

type CodexConnector

type CodexConnector struct {
	// Model is the harness-facing model alias. Gateway Configure embeds it in
	// its provider argv; SelectModel applies it to an existing session's
	// advertised model select option. Native ConfigureNative retains it as
	// managed state and emits no model override. An empty value is a deliberate
	// no-op for session selection and native configuration.
	Model string
	// Effort is the neutral reasoning-effort selector SelectEffort applies to
	// the advertised thought_level option (codex-acp's config ID is currently
	// reasoning_effort). Native ConfigureNative retains it as managed state and
	// emits no model_reasoning_effort override. An empty value is a deliberate
	// no-op.
	Effort string

	// Posture is the sandbox/approval posture Configure applies. The
	// zero value resolves to sane, least-privilege defaults (see
	// CodexPosture.resolve).
	Posture CodexPosture
	// contains filtered or unexported fields
}

CodexConnector adapts a launched ACP session to codex-acp's specific conventions. Construct with Codex.

func Codex

func Codex(model string) *CodexConnector

Codex constructs a CodexConnector for model, the harness-facing model alias codex-acp is launched with via the `-c model=` override. Posture may be set on the returned value before passing it to Dial as Config.Harness.

func (*CodexConnector) Configure

func (c *CodexConnector) Configure(cmd stdio.Command, binding ProxyBinding) (stdio.Command, error)

Configure implements HarnessAdapter for codex-acp: cmd.Path is the caller-supplied absolute path to the codex-acp executable itself (this connector never performs PATH lookup, invokes npx, or installs anything -- discovery and installation are entirely the caller's responsibility, the same rule claude-agent-acp's Configure follows). Gateway configuration requires Model to be non-empty. The returned Command's Args is entirely replaced with the fixed, ordered `-c key=value` override sequence codexConfigArgs builds (never merged with cmd's own caller-supplied Args: codex-acp's config surface is this connector's exclusive concern, the same "replace rather than merge" precedent claude-agent-acp's Configure sets); its Env carries LOOPRIG_PROXY_TOKEN and never CODEX_HOME. cmd is never mutated; the returned Command is always a fresh copy (see buildChildCommand).

func (*CodexConnector) ConfigureNative

func (c *CodexConnector) ConfigureNative(cmd stdio.Command) (stdio.Command, error)

ConfigureNative implements NativeHarnessAdapter. It validates the existing native model/effort tuple semantics but deliberately emits neither selector as argv: Codex ACP session options are selected after session/new through SelectModel and SelectEffort. An empty Model is passed through so codex-acp can choose its own model.

func (*CodexConnector) SelectEffort

func (c *CodexConnector) SelectEffort(ctx context.Context, sess *client.Session) error

SelectEffort applies c.Effort through the connected adapter's advertised thought_level select config option. Empty effort is a deliberate no-op. A missing option or an alias absent from its advertised values returns a typed *EffortAliasError without making a wire call.

func (*CodexConnector) SelectModel

func (c *CodexConnector) SelectModel(ctx context.Context, sess *client.Session) error

SelectModel applies c.Model through the connected adapter's advertised model select config option. Empty model is a deliberate no-op. A missing model option or an alias absent from its advertised values returns a typed *ModelAliasError without making a wire call.

func (*CodexConnector) WithModel

func (c *CodexConnector) WithModel(model string) *CodexConnector

WithModel returns a new *CodexConnector identical to c except for its Model field (Posture is copied unchanged). c itself is never mutated, and the two resulting connectors never alias any state (CodexPosture has no pointer or slice fields, so the struct copy below is a fully independent value). Call SelectModel to apply the stored alias to an existing session; WithModel remains useful when a caller wants a separately configured connector or a fresh launch.

func (*CodexConnector) WithModelEffort

func (c *CodexConnector) WithModelEffort(model, effort string) *CodexConnector

WithModelEffort returns a new *CodexConnector identical to c except for its model and reasoning-effort selectors. c itself is never mutated. A model and effort must either both be non-empty or both be empty when the resulting connector is configured for a native launch; ConfigureNative reports a typed ConfigError for a partial pair. SelectModel and SelectEffort apply the stored selectors to an existing session.

type CodexPosture

type CodexPosture struct {
	// ApprovalPolicy is codex-acp's approval_policy `-c` override (e.g.
	// "untrusted", "on-failure", "on-request", "never"). Empty resolves
	// to defaultCodexApprovalPolicy.
	ApprovalPolicy string
	// SandboxMode is codex-acp's sandbox_mode `-c` override (e.g.
	// "read-only", "workspace-write", "danger-full-access"). Empty
	// resolves to defaultCodexSandboxMode.
	SandboxMode string
	// SandboxNetworkAccess is codex-acp's
	// sandbox_workspace_write.network_access `-c` override: required
	// when the child must reach loopback HTTP MCP servers or the
	// gateway from inside Codex's sandbox (see the design doc). Its zero
	// value (false, deny) is already the least-privilege default and is
	// never substituted for.
	SandboxNetworkAccess bool
}

CodexPosture is the caller-chosen sandbox/approval posture codex-acp is launched under: an application-level choice this package never hardcodes (see codex.go's Configure/codexConfigArgs).

type CodexVersion

type CodexVersion struct {
	Major, Minor, Patch int
}

CodexVersion is a parsed codex-acp "<major>.<minor>.<patch>" version.

func ParseCodexVersion

func ParseCodexVersion(raw string) (CodexVersion, bool)

ParseCodexVersion parses codex-acp's own "--version" output: package name and dotted version separated by whitespace, e.g. "@agentclientprotocol/codex-acp 1.1.7" (the reference shape this connector's contract was verified against -- see the design doc). The parse is deliberately strict: the last whitespace-separated field must be exactly three non-negative, all-digit, dot-separated components. Partial versions ("1.2") and prerelease/build tags ("1.2.0-rc1") both fail closed as unparseable rather than being optimistically accepted.

A bare dotted version ("1.1.7") -- the shape codex-acp reports in its ACP initialize response's agentInfo.version, rather than on stdout -- parses identically, because it is its own last whitespace-separated field. This is exported so a caller gating behavior on an adapter-advertised version (see foreignloops' ACP steering capability gate) reuses exactly this strict, fail-closed parse instead of introducing a second, laxer version comparison scheme.

func (CodexVersion) Less

func (v CodexVersion) Less(other CodexVersion) bool

Less reports whether v sorts strictly before other.

func (CodexVersion) String

func (v CodexVersion) String() string

String renders v in dotted form.

type CodexVersionClass

type CodexVersionClass int

CodexVersionClass classifies a codex-acp version probe's outcome. Every value other than CodexVersionModern is a fail-closed rejection: an adapter is accepted only at CodexVersionModern, never on ambiguity.

const (
	// CodexVersionUnknown is CodexVersionClass's zero value: it is never
	// returned by ProbeCodexVersion, which always assigns one of the
	// named classes below.
	CodexVersionUnknown CodexVersionClass = iota
	// CodexVersionModern: parseable, >= MinCodexVersion. The only class
	// ProbeCodexVersion accepts (returns a nil error for).
	CodexVersionModern
	// CodexVersionBelowMinimum: parseable, but < MinCodexVersion.
	CodexVersionBelowMinimum
	// CodexVersionLegacyNoVersion: the probe exited successfully but
	// produced no recognizable version output at all (empty stdout) --
	// one plausible shape for a legacy binary that silently ignores
	// --version. (The historical @zed-industries/codex-acp 0.16.x binary
	// in practice exits non-zero for an unrecognized flag, which
	// classifies as CodexVersionNonzeroExit instead; both fail closed
	// identically, so the practical rejection outcome is the same either
	// way.)
	CodexVersionLegacyNoVersion
	// CodexVersionUnparseable: the probe produced non-empty output that
	// does not match the expected "<major>.<minor>.<patch>" shape (a
	// partial version, a prerelease/build suffix, or unrelated text).
	CodexVersionUnparseable
	// CodexVersionNonzeroExit: the probe process itself failed to run to
	// a clean, successful exit (a real invocation error, or a nonzero
	// exit status).
	CodexVersionNonzeroExit
	// CodexVersionTimeout: the probe did not complete within the bound
	// ProbeCodexVersion applied.
	CodexVersionTimeout
)

func (CodexVersionClass) String

func (c CodexVersionClass) String() string

String renders c as a short, stable diagnostic label.

type CodexVersionError

type CodexVersionError struct {
	Path   string
	Result CodexVersionResult
}

CodexVersionError reports that a codex-acp version probe (see ProbeCodexVersion in version.go) did not clear MinCodexVersion, for any reason: below the floor, a legacy binary with no recognizable version output, unparseable output, a nonzero exit, or a timeout. Every one of those classifications fails closed identically from a caller's perspective -- reject the adapter -- with Result carrying the specific CodexVersionClass a caller can inspect or log.

func (*CodexVersionError) Error

func (e *CodexVersionError) Error() string

type CodexVersionResult

type CodexVersionResult struct {
	Class   CodexVersionClass
	Version CodexVersion
	Raw     string
}

CodexVersionResult is ProbeCodexVersion's outcome: a classification plus (when parseable) the actual version observed and the raw probe output, kept only for diagnosis -- callers must never parse Raw themselves; the only supported interpretation of a probe is Class (and Version, when Class is CodexVersionModern or CodexVersionBelowMinimum).

func ProbeCodexVersion

func ProbeCodexVersion(ctx context.Context, path string, timeout time.Duration, runner CodexVersionRunner) (CodexVersionResult, error)

ProbeCodexVersion runs runner(ctx, path) -- defaultCodexVersionRunner when runner is nil, the production path -- bounded by timeout (or DefaultCodexVersionProbeTimeout when timeout <= 0), and classifies the result. path must already be a clean, absolute path (see cleanAbsolutePath): an invalid path fails with *PathError before runner is ever invoked.

ProbeCodexVersion returns a nil error only for CodexVersionModern; every other classification returns a non-nil *CodexVersionError alongside the same CodexVersionResult, so a caller can treat "err == nil" as the entire adapter-acceptance decision while still inspecting Result.Class for diagnostics.

type CodexVersionRunner

type CodexVersionRunner func(ctx context.Context, path string) (stdout []byte, err error)

CodexVersionRunner runs one bounded codex-acp version probe and returns its raw stdout and the invocation's error (nil on a clean, successful exit). ctx is already bound to ProbeCodexVersion's own timeout; a real implementation (defaultCodexVersionRunner) must honor ctx's deadline/cancellation and must never block past it. Tests substitute a fake runner so no real codex-acp binary is ever spawned (see version_test.go / codex_test.go).

type Config

type Config struct {
	// OwnedProxy, if set, is started before the ACP child is spawned and
	// closed when the resulting ManagedClient closes, or when its ACP child
	// dies unexpectedly. Mutually exclusive with SharedProxy: exactly one of
	// the two is required (see validateConfig).
	OwnedProxy ModelProxy
	// SharedProxy, if set, is borrowed data only: this package never starts
	// or closes it. The application that created the shared proxy owns its
	// lifecycle and is responsible for closing it once every borrower is
	// finished; this package has no opinion about that and, since
	// *ProxyBinding carries no lifecycle methods at all, no way to act on it
	// even if it wanted to. Mutually exclusive with OwnedProxy.
	SharedProxy *ProxyBinding
	// NoProxy explicitly selects a child-owned authentication path. It is
	// mutually exclusive with OwnedProxy and SharedProxy and requires Harness
	// to implement either NativeHarnessAdapter or the legacy internal no-proxy
	// connector seam. New callers should prefer DialNative.
	NoProxy bool
	// Harness configures Command for whichever ProxyBinding results from
	// OwnedProxy or SharedProxy.
	Harness HarnessAdapter
	// Command is the base stdio.Command Harness.Configure adapts. Its
	// executable Path is the caller's responsibility to supply (this
	// package never performs PATH lookup, invokes a shell, or installs
	// anything).
	Command stdio.Command
	// Client is passed through unchanged to the underlying acp/client
	// connection.
	Client client.Options
}

Config is Dial's complete configuration.

type ConfigError

type ConfigError struct{ Reason string }

ConfigError reports an invalid Config discovered before Dial starts anything: neither or both of OwnedProxy/SharedProxy set, or no HarnessAdapter supplied. Caught here rather than surfacing as a nil-pointer panic or a wastefully-started proxy.

func (*ConfigError) Error

func (e *ConfigError) Error() string

type ConflictingEnvError

type ConflictingEnvError struct{ Key string }

ConflictingEnvError reports that a caller-supplied stdio.Command.Env already contained a security-sensitive variable name a HarnessAdapter must exclusively own (either because the adapter sets it itself, or because the child's own contract requires it be absent) -- rejected rather than silently overwritten or stripped.

func (*ConflictingEnvError) Error

func (e *ConflictingEnvError) Error() string

type EffortAliasError

type EffortAliasError struct {
	Effort string
	Alias  string
}

EffortAliasError reports that a requested reasoning-effort selector matched no value the connected adapter's advertised thought_level option exposed. Alias is retained as a compatibility spelling for callers that classify selector errors uniformly; Effort is the canonical field.

func (*EffortAliasError) Error

func (e *EffortAliasError) Error() string

type EffortSelectionError

type EffortSelectionError = EffortAliasError

EffortSelectionError is a descriptive alias for EffortAliasError.

type GeminiAdapter

type GeminiAdapter struct {
	// Model is the harness-facing model alias GEMINI_MODEL is set to.
	// Required.
	Model string
}

GeminiAdapter is a HarnessAdapter for the Gemini CLI harness. Construct with Gemini.

func Gemini

func Gemini(model string) *GeminiAdapter

Gemini constructs a GeminiAdapter for model.

func (*GeminiAdapter) Configure

func (g *GeminiAdapter) Configure(cmd stdio.Command, binding ProxyBinding) (stdio.Command, error)

Configure sets exactly GOOGLE_GEMINI_BASE_URL, GEMINI_API_KEY, and GEMINI_MODEL -- Gemini API-key mode -- and nothing else. cmd.Path is validated the same way every other Configure in this package validates its child's executable path (see cleanAbsolutePath); this package does not otherwise claim to know what that executable's own argument contract looks like, so Args is left exactly as the caller supplied it (copied defensively by buildChildCommand, never cleared or replaced the way ClaudeConnector/CodexConnector's Configure do for their own pinned adapters). cmd is never mutated; the returned Command is always a fresh copy.

type HarnessAdapter

type HarnessAdapter interface {
	Configure(stdio.Command, ProxyBinding) (stdio.Command, error)
}

HarnessAdapter configures a stdio.Command for one specific foreign ACP agent's launch contract -- its executable, argument shape, and environment variables -- given the ProxyBinding it should be pointed at.

Configure must return a fresh Command derived from cmd, never mutate cmd (or its Env/Args backing arrays) in place, and must never inherit the ambient process environment or forward upstream provider credentials (see the design doc's "Environment safety" and acp/CLAUDE.md's own rule to the same effect).

type ManagedClient

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

ManagedClient is one launched ACP connection together with whatever model proxy it owns, if any. Construct with Dial.

func Dial

func Dial(ctx context.Context, cfg Config) (*ManagedClient, error)

Dial validates cfg, starts an owned proxy (if configured), configures Command for the resulting ProxyBinding via cfg.Harness or through its explicit internal no-proxy connector path, spawns and initializes the ACP child through acp/client, and returns a ManagedClient.

Lifecycle order: validate -> start proxy -> configure a copied stdio.Command -> spawn ACP child -> initialize ACP -> return ManagedClient. A failure at any step after the owned proxy has successfully started closes that proxy before Dial returns, so a failed Dial never leaks a running owned proxy; a SharedProxy is never started in the first place, so there is nothing to unwind for it.

func DialNative

func DialNative(ctx context.Context, cfg NativeConfig) (*ManagedClient, error)

DialNative launches an ACP harness using its own authentication and model selection. It deliberately constructs the existing Config.NoProxy form and sends it through Dial, so all established no-proxy validation and lifecycle behavior remains in one place. NativeConfig has no proxy fields, making it impossible for this helper to start or borrow a model proxy.

func (*ManagedClient) Client

func (m *ManagedClient) Client() *client.Client

Client returns the underlying acp/client connection, ready for session creation. It is nil only for a ManagedClient built around a connCloser other than a real *client.Client -- something only managed_test.go's fakes ever do; a ManagedClient returned by Dial always yields a non-nil *client.Client here.

func (*ManagedClient) Close

func (m *ManagedClient) Close(ctx context.Context) error

Close closes the ACP connection first and the owned proxy second (never the reverse -- see the design doc's "Owned proxy lifecycle"), joining both errors with errors.Join so neither is ever silently discarded if both fail. Both the connection close and the proxy close are individually idempotent at this layer (each guarded by its own sync.Once), so Close is safe to call more than once and safe to race against watchOwnedDeath's own proxy teardown; whichever reaches a given step first performs it, and every other caller observes that same cached result. A SharedProxy borrower's Close never touches the shared binding at all: owned is nil in that case, so closeOwnedProxy is a no-op.

type ModelAliasError

type ModelAliasError struct{ Alias string }

ModelAliasError reports that a requested harness-facing model alias matched no value the connected adapter's "model" select config option advertised -- including the case where no such option was advertised at all. Model selection never silently no-ops: an unmatched alias is always a typed error.

func (*ModelAliasError) Error

func (e *ModelAliasError) Error() string

type ModelProxy

type ModelProxy interface {
	// Start brings the proxy up. It must return before Binding is called.
	Start(context.Context) error
	// Binding reports the proxy's current connection info. ready is false
	// until the proxy has something callers can actually use; Dial treats a
	// false ready immediately after a successful Start as a startup-contract
	// violation (see ProxyNotReadyError) rather than proceeding with an
	// undefined binding.
	Binding() (baseURL, token string, ready bool)
	// Close tears the proxy down. Implementations must make Close safe to
	// call at most once in practice; ManagedClient itself also never calls
	// it more than once for a given owned proxy (see ManagedClient.Close's
	// doc), but a well-behaved ModelProxy should not depend on that.
	Close(context.Context) error
}

ModelProxy is a model proxy this package owns: started before the ACP child is spawned and closed when the resulting ManagedClient closes or its ACP child dies unexpectedly (see ManagedClient's doc).

Binding's three-plain-value return shape (rather than returning a ProxyBinding) is deliberate, not an oversight: Go requires identical method signatures for structural interface satisfaction, and a bare (string, string, bool) tuple is far more likely to already be exactly what an independently-built proxy server type naturally exposes than a package-specific struct type would be. That is what lets such a type satisfy ModelProxy with no import of, or coupling to, this package at all.

type NativeConfig

type NativeConfig struct {
	Harness NativeHarnessAdapter
	Command stdio.Command
	Client  client.Options
}

NativeConfig is the proxy-free configuration accepted by DialNative. Its shape deliberately has no OwnedProxy, SharedProxy, or binding field, so a native launch cannot accidentally acquire gateway lifecycle or credentials.

type NativeHarnessAdapter

type NativeHarnessAdapter interface {
	HarnessAdapter
	ConfigureNative(stdio.Command) (stdio.Command, error)
}

NativeHarnessAdapter configures a child to use the harness's own authentication and model selection. ConfigureNative must preserve the same command-copying, absolute-path, environment, and credential-safety rules as Configure. Explicit native model selection may produce a caller-selected model override (for example, Codex `-c model`), while ConfigureNative must never add gateway URL, gateway token, or gateway provider/base-url overrides. When the model is omitted, model selection remains harness-managed.

It embeds HarnessAdapter so one connector has an explicit, reviewable gateway path and native path rather than an ambiguous Configure call whose behavior depends on the binding value.

type PathError

type PathError struct{ Field, Reason string }

PathError reports that a caller-supplied executable path -- the ACP adapter binary itself, or an underlying CLI a connector pins -- was not a clean, absolute path. Validated before any process is started; this package never resolves a path via PATH lookup or a shell.

func (*PathError) Error

func (e *PathError) Error() string

type ProxyBinding

type ProxyBinding struct {
	BaseURL string
	Token   string
}

ProxyBinding is the connection info a model proxy exposes to whatever ACP adapter it fronts: a local base URL and bearer token the adapter's upstream-model client should be pointed at instead of a real provider.

It is plain data, never a lifecycle handle: a SharedProxy is borrowed (see Config.SharedProxy's doc), and *ProxyBinding deliberately has no Start or Close method at all, so there is no call path by which this package could ever start or stop a proxy it does not own.

type ProxyNotReadyError

type ProxyNotReadyError struct{}

ProxyNotReadyError reports that an owned ModelProxy's Start returned successfully but its own Binding immediately reported ready=false -- a startup-contract violation Dial treats as a failure (closing the proxy it just started) rather than proceeding with an undefined binding.

func (*ProxyNotReadyError) Error

func (e *ProxyNotReadyError) Error() string

Jump to

Keyboard shortcuts

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