client

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

client.go implements Client: the connection runtime that drives a foreign ACP agent as a client, over a subprocess spawned via acp/transport/stdio and a protocol.Conn built on top of it. It owns the lazy, start-once connection lifecycle (Task 5.1's "start-once connection state machine": a not-started -> starting -> started progression where concurrent Dial callers share one in-flight attempt and a failed attempt resets so a later call can retry), the registered client-served ACP method handlers (session/update, permission requests, filesystem, and terminal operations — see dispatch.go), and the live-session registry Session objects are tracked under.

acp/client is pure wire layer (see acp/CLAUDE.md): it imports only acp/protocol and acp/transport/stdio, never harness or core.

dispatch.go registers this Client's client-served ACP method handlers on a connected protocol.Conn: session/update (always), and session/request_permission / fs/* / terminal/* only when the corresponding Options handler is configured (see options.go's capability doc). Every handler validates its inbound sessionId (and, where applicable, path or terminalId) before ever invoking the injected handler, per acp/CLAUDE.md's boundary-validation rule: a foreign agent's requests are untrusted input, not a trusted internal call.

prompt.go implements Session.Prompt and Session.Cancel: the one prompt-in-flight-per-session gate, and cancellation-as-success (a cancelled prompt resolves as StopReasonCancelled, never as an error — see the design doc's "Cancellation-as-success").

session.go implements Session (one ACP session on a Client's connection) and Client's session-lifecycle methods: NewSession, LoadSession, ResumeSession. Prompt/Cancel live in prompt.go; inbound session/update routing and dedup live here (deliver), since they are intrinsic to a Session's identity and lifetime.

updates.go implements Update (the typed value delivered on Session.Updates()) and the _meta decoding used both to expose that metadata to callers and to drive live-update dedup (see session.go's deliver).

Index

Constants

View Source
const EventDedupWindowDepth = 512

EventDedupWindowDepth bounds how many distinct live _meta.eventIds a Session remembers for dedup (see deliver). Harness event ids (event.Header.EventID, minted by event.Factory.Stamp via github.com/looprig/core/uuid.New, which reads crypto/rand) are random UUIDv4 values, so the id VALUE itself carries no ordering information a highwater mark could compare against.

Delivery ORDER does, however, carry a real ordering guarantee: every session/update notification for one Client is drained by protocol.Conn's single notifyWorker goroutine strictly in wire order, completing each job before starting the next (see Conn.notifyWorker's doc), so successive calls to deliver for one session happen in true chronological order even though the ids themselves are unordered. A genuine duplicate (redelivery/retry) is therefore expected to reappear shortly after the original, not arbitrarily far in the future — so remembering only the most recently delivered EventDedupWindowDepth ids (an insertion-order window; the oldest is evicted first once the window is full) is enough to catch every realistic duplicate while keeping the dedup map's memory bounded across a session's full, potentially unbounded, lifetime. An id that reappears after it has aged out of the window is (by this deliberate tradeoff) no longer recognized as a duplicate and is delivered again — bounded, observable-in-principle loss traded for bounded memory, exactly as UpdateQueueDepth trades update loss for the same property. 512 matches UpdateQueueDepth/NotifyBufferDepth's existing precedent rather than inventing a new value.

View Source
const LoadTimeout = 90 * time.Second

LoadTimeout is the default bound session/load's response is awaited under once replay updates have started streaming (see session.go's LoadSession and the design doc's "Replay-idle tolerance in the client"): a foreign agent may stream a full replay but be slow to resolve the call itself. Rather than synthesizing a response from wall-clock heuristics, a hung load fails typed at this deadline. Options.LoadTimeout overrides it per Client.

View Source
const UpdateQueueDepth = 512

UpdateQueueDepth bounds how many not-yet-delivered updates a Session's internal queue holds before it starts dropping the OLDEST queued update to make room for the newest, mirroring protocol.Conn's NotifyBufferDepth (see Conn.DroppedNotifications) for the identical bounded-buffer, drop-oldest, observable-counter shape. A caller that calls Updates() and drains it promptly — the expected steady state, and the shape of every existing Task 5.1 test — never sees a drop: this bound only bites a consumer that falls behind delivery by more than UpdateQueueDepth updates, trading unbounded memory growth in a permanently-non-draining session for bounded, observable loss (see Session.DroppedUpdates). The oldest entry is dropped rather than the newest because a client actively draining Updates() cares about catching up to CURRENT state, not preserving ancient history it may never read anyway. 512 matches NotifyBufferDepth's existing precedent in this module rather than inventing a new value.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

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

Client is one connection to a foreign ACP agent, spawned as a subprocess and driven over acp/protocol's JSON-RPC layer. The zero value is not usable; construct with New.

func Dial

func Dial(ctx context.Context, cmd stdio.Command, opts Options) (*Client, error)

Dial constructs a Client via New and connects it, for callers that want a single spawn-and-initialize call rather than New's lazy, dial-later lifecycle (which foreignloops/driver/acp uses to dial on first Spawn).

func New

func New(cmd stdio.Command, opts Options) *Client

New constructs a Client that will spawn cmd and negotiate opts's capabilities the first time Dial (the package function, or the (*Client) method of the same name) is called. No process is started and no I/O happens until then.

func (*Client) Close

func (c *Client) Close(ctx context.Context) error

Close tears down the connection: it stops accepting new Dial attempts, fails every tracked Session's update stream, closes the protocol connection, and kills the subprocess (SIGINT, grace period, then SIGKILL — see stdio.Proc.Kill), waiting for it to be reaped. It is idempotent. If ctx is done before teardown completes, Close returns ctx.Err() but teardown continues in the background rather than being abandoned, so no goroutine or process is ever leaked.

func (*Client) Dial

func (c *Client) Dial(ctx context.Context) error

Dial runs the start-once connection state machine: the first caller (per idle period) becomes the attempt's owner and actually connects; any caller that arrives while an attempt is already in flight shares that one attempt's outcome instead of starting its own. A failed attempt resets the state to idle so that a later call — by the same or a different caller — starts a genuinely fresh attempt. Once connected, Dial is a fast no-op until the connection is closed or dies.

A context canceled while waiting on someone else's attempt unblocks with ctx.Err() without affecting that attempt, which every other caller may still be waiting on.

func (*Client) Done

func (c *Client) Done() <-chan struct{}

Done returns a channel that is closed once this Client reaches a terminal closed state: an explicit Close call, or watchDeath observing the connection end on its own (peer disconnect or transport failure). It is safe to read from immediately after New, before any Dial — it simply never closes until the Client is actually terminated, including the case where Close is called before Dial ever succeeded (see Close's own doc: it transitions to closed and tears down whatever partial state exists, unconditionally, once called). A merely FAILED Dial attempt that leaves the Client retryable (see Dial's start-once state machine) does not close this channel: only a genuine terminal transition does. This lets a caller (in particular the ACP launch layer's owned-proxy lifecycle) react to unexpected child death without polling Client state.

func (*Client) DroppedUpdates

func (c *Client) DroppedUpdates() uint64

DroppedUpdates reports how many inbound session/update notifications named a sessionId this Client has no registered Session for (an unknown or already-closed session), and so could not be routed anywhere. This mirrors protocol.Conn.DroppedNotifications: a diagnostic counter, never a silent failure with no way to observe it.

func (*Client) InitializeMetadata

func (c *Client) InitializeMetadata() (InitializeMetadata, error)

InitializeMetadata returns the connected agent's initialize metadata. Before a successful Dial it returns a *NotDialedError; after Client reaches its terminal closed state it returns a *ClosedError, matching the lifecycle behavior of the other Client accessors.

func (*Client) LoadSession

func (c *Client) LoadSession(ctx context.Context, p LoadSessionParams) (*Session, error)

LoadSession calls the agent's session/load method. The Session is registered under p.SessionID BEFORE the call is issued (unlike NewSession, the id is caller-supplied here, so this is possible and necessary): a foreign agent's session/load handler streams the session's full replay as session/update notifications before it ever returns its own response (see acp/agent/replay.go's handleSessionLoad), so the Session must already be listening the instant the call goes out, not only once it returns.

The call itself is bounded by the Client's load timeout (LoadTimeout, overridable via Options.LoadTimeout): replay updates are consumed as they arrive regardless of how long the response itself takes, but a load that never resolves within the deadline fails with a typed *LoadTimeoutError rather than hanging forever or synthesizing a result.

func (*Client) NewSession

func (c *Client) NewSession(ctx context.Context, p NewSessionParams) (*Session, error)

NewSession calls the agent's session/new method and returns the resulting Session, registered so its update stream begins delivering immediately.

func (*Client) ProveSetModelCapability

func (c *Client) ProveSetModelCapability(key string) (proof SetModelCapability, ok bool)

ProveSetModelCapability reports whether this Client's stored "initialize" response `_meta` contains key as a present, non-null top-level field, and returns a SetModelCapability recording the answer (ok is the same boolean, returned separately so a caller can branch without inspecting the capability value's private state).

Different ACP adapters that implement the unstable session/set_model extension are expected to advertise it under different, adapter-specific _meta keys — there is no pinned schema for this extension in protocol/types_gen.go to standardize one — so the caller (a connector in acp/launch, which knows its specific adapter's documented key) supplies the exact key to check. This method's only job is making that check unforgeable and centralized: it is always evaluated against the real initialize response, never a value a caller could fabricate directly, and it deliberately never calls session/set_model itself to "check" for support — some ACP adapters answer an unrecognized method with a bare `{}` success rather than a JSON-RPC error, which would make a speculative probe silently misread as support.

ok is false both when the key is genuinely absent (or _meta is empty or not a JSON object) and when this Client has never dialed successfully; SetModel refuses either way.

func (*Client) ResumeSession

func (c *Client) ResumeSession(ctx context.Context, p ResumeSessionParams) (*Session, error)

ResumeSession calls the agent's session/resume method. Like LoadSession, the Session is registered under the caller-supplied id before the call is issued, so any updates the agent sends while resuming are never dropped.

type ClosedError

type ClosedError struct {
	Cause error
}

ClosedError reports that a Client or Session operation was attempted after the underlying connection closed — whether by an explicit Close, the subprocess dying, or a transport failure. Cause, when set, is the concrete reason (typically a *protocol.ConnClosedError); Unwrap exposes it for errors.Is/errors.As.

func (*ClosedError) Error

func (e *ClosedError) Error() string

func (*ClosedError) Unwrap

func (e *ClosedError) Unwrap() error

type DuplicateSessionError

type DuplicateSessionError struct {
	SessionID protocol.SessionID
}

DuplicateSessionError reports that an operation attempted to register a Session ID that is already tracked by this Client. The ID is retained for programmatic inspection, but the bounded error text does not echo it.

func (*DuplicateSessionError) Error

func (e *DuplicateSessionError) Error() string

type FSHandler

FSHandler answers the client-served filesystem methods (fs/read_text_file, fs/write_text_file) a foreign agent may call back into this Client for. A nil FSHandler in Options means the filesystem capability is not advertised at all: InitializeRequest.ClientCapabilities.Fs is omitted, and the corresponding methods are never registered on the connection (so an agent that calls them anyway gets Conn's own MethodNotFound, exactly as if this Client had never heard of them).

type InitializeMetadata

type InitializeMetadata struct {
	AgentInfo *protocol.Implementation
	Meta      json.RawMessage
}

InitializeMetadata is a defensive snapshot of the metadata returned by an agent's initialize handshake. AgentInfo and Meta are copied for each read; mutating either field (or AgentInfo's nested fields) never changes the Client's stored handshake response.

type LoadSessionParams

type LoadSessionParams struct {
	SessionID             protocol.SessionID
	Cwd                   string
	AdditionalDirectories []string
	McpServers            []protocol.McpServer
}

LoadSessionParams are the caller-supplied parameters for Client.LoadSession.

type LoadTimeoutError

type LoadTimeoutError struct {
	SessionID protocol.SessionID
	Timeout   time.Duration
}

LoadTimeoutError reports that session/load's response did not arrive within the client's load timeout, even though replay updates may have already been consumed (see the design doc's "Replay-idle tolerance in the client"). No response is ever synthesized: a hung load is reported as a failure, typed so callers can distinguish it from an ordinary transport or protocol error.

func (*LoadTimeoutError) Error

func (e *LoadTimeoutError) Error() string

type NewSessionParams

type NewSessionParams struct {
	// Cwd is the session's working directory. Must be an absolute path (ACP
	// requirement; enforced by the agent, not re-validated here).
	Cwd string
	// AdditionalDirectories are extra workspace roots. Nil/empty means none.
	AdditionalDirectories []string
	// McpServers are the MCP servers the agent should connect to for this
	// session. Nil is normalized to an empty (but present) list: ACP's
	// session/new request requires the field, never `null`.
	McpServers []protocol.McpServer
}

NewSessionParams are the caller-supplied parameters for Client.NewSession.

type NotDialedError

type NotDialedError struct{}

NotDialedError reports that a Client method was called before Dial ever completed successfully.

func (*NotDialedError) Error

func (e *NotDialedError) Error() string

type Options

type Options struct {
	// FS answers client-served filesystem methods. Nil disables the
	// capability entirely.
	FS FSHandler
	// Terminal answers client-served terminal/* methods. Nil disables the
	// capability entirely.
	Terminal TerminalHandler
	// Permissions answers session/request_permission. Nil disables it.
	Permissions PermissionHandler

	// ClientInfo identifies this client to the agent during "initialize".
	// Nil is valid: InitializeRequest.ClientInfo is optional.
	ClientInfo *protocol.Implementation

	// LoadTimeout overrides the package LoadTimeout constant as the bound
	// session/load's response is awaited under. Zero means "use LoadTimeout".
	LoadTimeout time.Duration
}

Options configures the client capabilities a Client advertises to, and dispatches on behalf of, a foreign agent.

Elicitation is deliberately not one of these fields. The design doc's implementation-techniques section and this task's own contract describe an injectable Elicitation capability, but the pinned v1.20.0 ACP schema this module generates from has no elicitation method or capability at all — the same absence acp/internal/mockpeer/main.go already documents and, per the plan's own precedence rule ("when a generated-schema detail conflicts with this plan's exact constant names, the pinned artifact wins"), resolves the same way here: there is no wire method this package could dispatch an ElicitationHandler through, so none is invented.

type PermissionHandler

type PermissionHandler interface {
	RequestPermission(ctx context.Context, req protocol.RequestPermissionRequest) (protocol.RequestPermissionResponse, error)
}

PermissionHandler answers session/request_permission. A nil PermissionHandler means the method is never registered.

Unlike FS and Terminal, the pinned ACP schema (protocol/methods_gen.go, protocol/types_gen.go's ClientCapabilities) has no boolean capability flag for permission support at all — session/request_permission is not something a client advertises support for or against on the wire; a client either implements it or does not. So "not advertised" here is purely a dispatch-level fact (the method is never registered, and an agent that calls it anyway gets MethodNotFound), not a bit flipped in InitializeRequest.

type PromptResult

type PromptResult struct {
	// StopReason is why the agent stopped processing the turn. A cancelled
	// turn (protocol.StopReasonCancelled) is reported here, in a successful
	// result — never as an error.
	StopReason protocol.StopReason
	// ReceiveSequence is the Conn-owned monotonic sequence assigned to the
	// prompt response. Notifications with lower sequences were observed by
	// the same read loop before this completion.
	ReceiveSequence uint64
	// ResponseSequence is an additive spelling for callers that use the
	// protocol terminology for the same receive-order fact.
	ResponseSequence uint64
	// WriteAdmitted records whether the prompt request crossed Writer's
	// admission boundary before any later cancellation or transport error.
	WriteAdmitted bool
}

PromptResult is the outcome of one session/prompt turn.

type ResumeSessionParams

type ResumeSessionParams struct {
	SessionID             protocol.SessionID
	Cwd                   string
	AdditionalDirectories []string
	McpServers            []protocol.McpServer
}

ResumeSessionParams are the caller-supplied parameters for Client.ResumeSession.

type Session

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

Session is one ACP session on a Client's connection: its id, its inbound session/update stream, and the one-prompt-in-flight gate (see prompt.go).

func (*Session) Cancel

func (s *Session) Cancel(ctx context.Context) error

Cancel sends the session/cancel notification for this session. It does not itself wait for the in-flight Prompt call to resolve: Prompt's own pending call resolves independently once the agent's response arrives (StopReasonCancelled, delivered as a successful *PromptResult per the design doc's cancellation-as-success rule), and this call simply requests that.

func (*Session) ConfigOptions

func (s *Session) ConfigOptions() []protocol.SessionConfigOption

ConfigOptions returns a defensive copy of this Session's most recently known set of session configuration options: session/new, session/load, or session/resume's response initially, replaced wholesale by SetConfigOption's own response on every successful call (see SetConfigOption's doc — never a partial merge). Nil if the agent never advertised any. The returned slice is this Session's own copy: mutating it never affects the Session's internal state, and a later SetConfigOption response never mutates a slice a caller is still holding from an earlier call.

func (*Session) DroppedUpdates

func (s *Session) DroppedUpdates() uint64

DroppedUpdates reports how many queued session/update notifications have been dropped (oldest-first) for this Session because its internal queue exceeded UpdateQueueDepth — see deliver. This mirrors protocol.Conn.DroppedNotifications' shape (a diagnostic counter, never a silent failure with no way to observe it) and is distinct from Client.DroppedUpdates: the Client-level counter tracks updates that could not be routed to any session at all (unknown/unregistered sessionId), while this one tracks updates that WERE routed to this exact session but then evicted by its own queue bound because the consumer fell behind. Zero in the expected steady state of an actively-drained session.

func (*Session) ID

func (s *Session) ID() protocol.SessionID

ID returns the ACP session id this Session was created or loaded with.

func (*Session) Modes

func (s *Session) Modes() *protocol.SessionModeState

Modes returns a defensive copy of this Session's most recently known mode state: session/new, session/load, or session/resume's response initially, updated by SetMode on every successful call (see SetMode's doc). Nil if the agent never advertised mode state.

func (*Session) Prompt

func (s *Session) Prompt(ctx context.Context, blocks []protocol.ContentBlock) (*PromptResult, error)

Prompt sends blocks as one session/prompt turn and blocks until the agent responds. Only one Prompt call may be in flight per Session at a time (enforced by an internal semaphore): a concurrent caller blocks until the prior call completes, rather than racing two prompts onto the same session. ctx cancellation while waiting for the semaphore unblocks with ctx.Err() without disturbing a prompt already in flight.

func (*Session) SetConfigOption

func (s *Session) SetConfigOption(ctx context.Context, configID protocol.SessionConfigID, valueID protocol.SessionConfigValueID) error

SetConfigOption calls the agent's session/set_config_option method, selecting valueID for configID (the single-value-selector variant of protocol.SetSessionConfigOptionRequest — the boolean variant is not reachable through this method, matching this method's own signature: a caller with a boolean option to flip has no valueID to pass in the first place). On success, this Session's cached ConfigOptions is replaced wholesale with the response's own full set (never a partial merge: the agent's response is authoritative, and the local cache before the call might already be stale by the time it resolves).

func (*Session) SetMode

func (s *Session) SetMode(ctx context.Context, modeID protocol.SessionModeID) error

SetMode calls the agent's session/set_mode method. Unlike SetConfigOption, session/set_mode's own response carries no state at all (see protocol.SetSessionModeResponse: only _meta), so on success this Session's cached mode state is updated locally instead: CurrentModeID is replaced with the id the caller just requested — the call succeeding is the only confirmation the wire gives — leaving AvailableModes as most recently known. If this Session has no cached mode state yet (because the agent omitted Modes from session/new, session/load, or session/resume), a minimal SessionModeState carrying only the new CurrentModeID is recorded rather than silently discarding the confirmed change.

func (*Session) SetModel

func (s *Session) SetModel(ctx context.Context, proof SetModelCapability, modelID string) error

SetModel calls the non-standard "session/set_model" extension some ACP adapters implement, gated behind proof (a SetModelCapability) that this Session's Client actually observed the extension advertised in its "initialize" response (see Client.ProveSetModelCapability). Without a granted proof, SetModel fails closed with *SetModelUnsupportedError before ever reaching the wire: this package never speculatively probes an ACP peer for an undeclared method, because some adapters answer an unrecognized method with a bare `{}` success rather than a JSON-RPC error, which would make such a probe silently misread as support.

func (*Session) StartSteer

func (s *Session) StartSteer(ctx context.Context, p SteerParams) *SteerHandle

StartSteer starts the fixed _session/steering request for this Session. It does not perform capability/profile allowlisting or an unknown-method probe; those policy decisions belong to the foreign driver. The returned handle always resolves both channels exactly once, including not-dialed and closed-client failures.

func (*Session) Steer

func (s *Session) Steer(ctx context.Context, p SteerParams) (SteerResult, error)

Steer sends the fixed _session/steering request for this Session and waits for StartSteer's exactly-once completion. It is retained as the synchronous compatibility wrapper for callers that do not need early admission.

func (*Session) Updates

func (s *Session) Updates() <-chan Update

Updates returns the channel Session delivers session/update notifications on, typed and decoded. It is ready to receive from immediately: delivery begins the moment the Session is registered (at NewSession/LoadSession/ ResumeSession time, before the caller could possibly have called Updates() yet), buffered internally by a queue (see deliver) so nothing arriving before the caller starts reading is dropped, up to UpdateQueueDepth. The channel is closed once the session is closed (Client.Close, connection death, or a future explicit session close) and every already-queued update has been delivered. Forced client/connection teardown may abandon an update still blocked for an absent reader so the pump can exit.

func (*Session) WaitForUpdates

func (s *Session) WaitForUpdates(ctx context.Context) error

WaitForUpdates waits for the protocol notification barrier and then until the Session's pump has handed off every update delivered before that barrier. It does not consume Updates; callers can wait here while another goroutine continues reading the channel. Cancellation and forced session or client teardown release the wait.

func (*Session) WaitForUpdatesThrough

func (s *Session) WaitForUpdatesThrough(ctx context.Context, sequence uint64) error

WaitForUpdatesThrough waits for Conn's receive-order barrier at sequence and therefore proves every session/update notification through that point has completed the registered client handler and entered this Session's delivery queue. It does not consume Updates or create another reader.

type SetModelCapability

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

SetModelCapability is unforgeable proof that this Client's negotiated "initialize" response advertised — under a caller-checked _meta key — the non-standard "session/set_model" extension some ACP adapters implement (see Session.SetModel). The zero value is not proof of anything and SetModel refuses it; the only way to obtain a granted SetModelCapability is ProveSetModelCapability, which checks the real bytes this Client received during the handshake rather than trusting any caller assertion.

type SetModelUnsupportedError

type SetModelUnsupportedError struct{}

SetModelUnsupportedError reports that Session.SetModel was called without a granted SetModelCapability: the connected agent's initialize response _meta never proved (see Client.ProveSetModelCapability) that it advertises the non-standard session/set_model extension, so the call was refused before ever reaching the wire.

func (*SetModelUnsupportedError) Error

func (e *SetModelUnsupportedError) Error() string

type SteerCompletion

type SteerCompletion struct {
	Result SteerResult
	Err    error
}

SteerCompletion is the exactly-once terminal value delivered by a SteerHandle. Result retains typed wire and transport facts even when Err is non-nil; Err is bounded by newSteeringError whenever the request reached protocol transport.

type SteerHandle

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

SteerHandle owns one asynchronous fixed-method steering request. Both channels have capacity one and deliver exactly one value before closing. Cancel is idempotent. If admission already reported true, cancellation stops response observation but does not retract the admitted frame.

func (*SteerHandle) Admission

func (h *SteerHandle) Admission() <-chan bool

Admission reports whether this request crossed the protocol Writer queue admission boundary. A false value proves the steering frame was not eligible for the underlying transport.

func (*SteerHandle) Cancel

func (h *SteerHandle) Cancel()

Cancel cancels this handle's response observation. It never retracts a frame that already crossed Writer admission.

func (*SteerHandle) Result

func (h *SteerHandle) Result() <-chan SteerCompletion

Result reports the one final steering completion.

type SteerOutcome

type SteerOutcome string

SteerOutcome is the bounded normalized outcome vocabulary understood by the foreign driver. An empty or unknown value is preserved as an empty/unknown outcome so the driver can fail closed; the client never guesses policy from a method error or probes a second method.

const (
	SteerOutcomeInjected       SteerOutcome = "injected"
	SteerOutcomePromptRequired SteerOutcome = "promptRequired"
	SteerOutcomeStartedNewTurn SteerOutcome = "startedNewTurn"
	SteerOutcomeFailed         SteerOutcome = "failed"
)

type SteerParams

type SteerParams struct {
	SessionID protocol.SessionID      `json:"sessionId"`
	Prompt    []protocol.ContentBlock `json:"prompt"`
	Meta      json.RawMessage         `json:"_meta,omitempty"`
}

SteerParams is the typed request shape for _session/steering. SessionID is overwritten with the receiver's ID by Session.Steer, so a caller cannot steer another Session through an existing Session value. Meta is optional ACP extension metadata and is passed through as caller-owned JSON bytes; this package does not interpret adapter capability or profile policy.

type SteerResult

type SteerResult struct {
	Outcome SteerOutcome
	Reason  string

	WriteAdmitted    bool
	ReceiveSequence  uint64
	ResponseSequence uint64
}

SteerResult is the typed, bounded result of Session.Steer. Outcome and Reason are the extension's normalized response facts; raw wire payloads are deliberately not exposed. Transport facts remain available even when err is non-nil, which lets a caller distinguish a proven pre-admission failure from an admitted but ambiguous/erroring call.

type SteeringError

type SteeringError struct {
	Code             protocol.ErrorCode
	Message          string
	WriteAdmitted    bool
	ReceiveSequence  uint64
	ResponseSequence uint64
	// contains filtered or unexported fields
}

SteeringError is the bounded typed error returned by Session.Steer after a request reached the protocol layer. It never exposes the peer's raw error Data or an unbounded transport diagnostic. Code is the peer JSON-RPC code when one was received, or ErrorCodeInternalError for a local/transport failure.

func (*SteeringError) Error

func (e *SteeringError) Error() string

func (*SteeringError) Unwrap

func (e *SteeringError) Unwrap() error

Unwrap preserves cancellation and local transport classification without ever retaining a peer *protocol.Fault (whose Data may contain raw wire payload). Peer faults are represented only by SteeringError's bounded code and message.

type TerminalHandler

TerminalHandler answers the client-served terminal/* methods. A nil TerminalHandler means the terminal capability is not advertised (InitializeRequest.ClientCapabilities.Terminal is false) and none of the terminal/* methods are registered.

type Update

type Update struct {
	// SessionUpdate is the update payload (a message chunk, tool call, plan,
	// usage update, and so on — see protocol.SessionUpdate).
	SessionUpdate protocol.SessionUpdate
	// Meta is this update's decoded _meta object.
	Meta UpdateMeta
	// ReceiveSequence is the Conn-owned monotonic sequence assigned to the
	// inbound session/update notification before it entered the ordered
	// notification worker.
	ReceiveSequence uint64
}

Update is one session/update notification delivered to a Session's Updates() channel, decoded into the ACP update payload plus its _meta.

type UpdateMeta

type UpdateMeta struct {
	EventID  string
	PromptID string
	IsReplay bool
}

UpdateMeta is this package's decoding of the `_meta` object a producing agent facade stamps onto every session/update notification. Field names and JSON tags intentionally mirror acp/agent/translate.go's updateMeta wire shape exactly (eventId, promptId, isReplay) — acp/client cannot import acp/agent (see acp/CLAUDE.md's layering rule), so this is a independently-owned but wire-compatible twin, not a shared type.

A notification with no _meta object, or one that fails to decode, yields the zero UpdateMeta rather than an error: _meta is optional ACP extensibility data (see protocol/types_gen.go's SessionNotification.Meta doc), and an agent that omits or mis-shapes it should degrade to "no metadata available," never break update delivery entirely.

func DecodeUpdateMeta

func DecodeUpdateMeta(raw json.RawMessage) UpdateMeta

DecodeUpdateMeta parses raw (a SessionNotification's Meta field) into an UpdateMeta, defaulting to the zero value on absence or malformed input. Exported (not just used internally by dispatch.go) so wire-compatibility tests outside this package — see acp/agent/meta_roundtrip_test.go, which cannot import this package's unexported symbols across the acp/agent / acp/client boundary any other way — can feed it real producer-side bytes and assert the fields land correctly, without this package ever needing to import acp/agent back.

Jump to

Keyboard shortcuts

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