Documentation
¶
Overview ¶
Package client wraps acp-go-sdk's low-level Connection for ACP relays. It manages a single stdio child agent process (e.g. fir --mode acp) and dispatches inbound server-initiated ACP calls — session updates, permission requests, fs reads/writes — back to the relay.
One AgentProc runs one ACP child process. It can serve many sessions concurrently — each NewSession/ResumeSession registers a per-session sink that receives the stream of session/update notifications.
We talk to acp.Connection directly (rather than acp.ClientSideConnection) so we can issue the unstable session/list and session/resume methods that the SDK doesn't model. The standard methods are sent via acp.SendRequest with the SDK's typed request/response structs.
Security: the fs methods (ReadTextFile / WriteTextFile) currently require absolute paths but do not sandbox to the session cwd. That is adequate for trusted-agent relay deployments; do not expose this client to untrusted agents.
Package client: defensive helpers for agent-spawn paths the production caller cannot trigger. Excluded from coverage via the `_must.go` suffix rule in .covignore.
Index ¶
- func AllowAllPermissions(_ context.Context, req acp.RequestPermissionRequest) acp.RequestPermissionResponse
- func DenyAllPermissions(_ context.Context, req acp.RequestPermissionRequest) acp.RequestPermissionResponse
- func ReadOnlyPermissions(_ context.Context, req acp.RequestPermissionRequest) acp.RequestPermissionResponse
- type AgentProc
- func (a *AgentProc) AuthMethods() []AuthMethod
- func (a *AgentProc) Authenticate(ctx context.Context, methodID, id, redirect string, cancel bool) (AuthResult, error)
- func (a *AgentProc) Cancel(ctx context.Context, sid acp.SessionId) error
- func (a *AgentProc) Caps() Caps
- func (a *AgentProc) Close() error
- func (a *AgentProc) DropSession(sid acp.SessionId)
- func (a *AgentProc) ListSessions(ctx context.Context, cwd string) ([]SessionInfo, error)
- func (a *AgentProc) Models() (models []ModelInfo, currentID string)
- func (a *AgentProc) NewSession(ctx context.Context, cwd string, sink SessionUpdateSink, ...) (acp.SessionId, error)
- func (a *AgentProc) ProbeModels(ctx context.Context) error
- func (a *AgentProc) Prompt(ctx context.Context, sid acp.SessionId, prompt []acp.ContentBlock) (acp.StopReason, error)
- func (a *AgentProc) RebindSink(sid acp.SessionId, sink SessionUpdateSink)
- func (a *AgentProc) ResumeSession(ctx context.Context, cwd string, sid acp.SessionId, sink SessionUpdateSink) error
- func (a *AgentProc) SetConfigOption(ctx context.Context, sid acp.SessionId, configID, value string) error
- func (a *AgentProc) SetModel(ctx context.Context, sid acp.SessionId, modelID string) error
- type AuthMethod
- type AuthResult
- type Caps
- type Config
- type ModelInfo
- type PermissionFunc
- type PermissionPolicy
- type SessionInfo
- type SessionUpdateSink
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AllowAllPermissions ¶
func AllowAllPermissions(_ context.Context, req acp.RequestPermissionRequest) acp.RequestPermissionResponse
AllowAllPermissions approves a request by selecting an allow-shaped option, falling back to the first option when no option advertises allow/approve.
func DenyAllPermissions ¶
func DenyAllPermissions(_ context.Context, req acp.RequestPermissionRequest) acp.RequestPermissionResponse
DenyAllPermissions rejects a request by selecting a reject-shaped option, falling back to the first option when no option advertises reject/deny.
func ReadOnlyPermissions ¶
func ReadOnlyPermissions(_ context.Context, req acp.RequestPermissionRequest) acp.RequestPermissionResponse
ReadOnlyPermissions allows read-like tool calls and rejects everything else. Heuristic: if the tool call title contains a write/exec-shaped verb (write, edit, bash, exec, run, delete, rm), the request is rejected; otherwise it is allowed.
Types ¶
type AgentProc ¶
type AgentProc struct {
// contains filtered or unexported fields
}
AgentProc wraps a single stdio-connected ACP agent process and the ACP connection driving it.
func Start ¶
Start launches the agent process, performs Initialize (capturing caps), and returns a ready-to-use AgentProc.
func (*AgentProc) AuthMethods ¶
func (a *AgentProc) AuthMethods() []AuthMethod
AuthMethods returns the auth methods the agent advertised at Initialize. Empty if the agent didn't advertise any (or initialize hasn't run yet).
func (*AgentProc) Authenticate ¶
func (a *AgentProc) Authenticate(ctx context.Context, methodID, id, redirect string, cancel bool) (AuthResult, error)
Authenticate invokes the ACP authenticate RPC. Modes:
- id == "" && redirect == "" && !cancel : start an interactive login. The agent returns a fresh id (and URL) in AuthResult.
- id != "" && redirect != "" : submit the pasted redirect.
- id != "" && cancel == true : cancel that pending login.
methodID must match the id advertised in the initialize response (e.g. "oauth-anthropic"). Requires an agent that supports the _meta.auth.interactive extension; older agents will run the legacy blocking flow and may return an empty AuthResult.
func (*AgentProc) Close ¶
Close terminates the agent process. Returns after the process has exited (or been force-killed).
func (*AgentProc) DropSession ¶
DropSession removes the sink for a session.
func (*AgentProc) ListSessions ¶
ListSessions calls the unstable session/list. Caller must check Caps().ListSessions first.
func (*AgentProc) Models ¶
Models returns a snapshot of the agent's last-seen available models. Empty until a session has been created (or ProbeModels has run).
func (*AgentProc) NewSession ¶
func (a *AgentProc) NewSession(ctx context.Context, cwd string, sink SessionUpdateSink, systemPromptBlocks []acp.ContentBlock) (acp.SessionId, error)
NewSession creates a new ACP session and wires the given sink to receive its updates. Returns the ACP session id.
systemPromptBlocks, when non-nil, is sent in the request's _meta under "session.systemPrompt".blocks. Callers should only pass it when Caps().SystemPrompt is true; agents that haven't advertised the cap will simply ignore the unknown _meta key, but skipping it keeps the wire clean.
func (*AgentProc) ProbeModels ¶
ProbeModels creates a throwaway session in the agent's cwd to read its available-models list, then cancels it. The cached snapshot is returned from Models() afterwards. Idempotent: a no-op if Models() already has a list.
func (*AgentProc) Prompt ¶
func (a *AgentProc) Prompt(ctx context.Context, sid acp.SessionId, prompt []acp.ContentBlock) (acp.StopReason, error)
Prompt sends a user message to the session. Returns the stop reason. The prompt is a sequence of ACP content blocks; callers build these from the latest user text plus any attachments.
func (*AgentProc) RebindSink ¶
func (a *AgentProc) RebindSink(sid acp.SessionId, sink SessionUpdateSink)
RebindSink replaces the sink for an existing session id.
func (*AgentProc) ResumeSession ¶
func (a *AgentProc) ResumeSession(ctx context.Context, cwd string, sid acp.SessionId, sink SessionUpdateSink) error
ResumeSession calls the unstable session/resume and registers the sink for the resumed session. Caller must check Caps().ResumeSession first. The given sid is the agent-returned identifier (as listed by ListSessions).
func (*AgentProc) SetConfigOption ¶
type AuthMethod ¶
AuthMethod is an alias for auth.Method kept on client.AgentProc's surface.
type AuthResult ¶
AuthResult is an alias for auth.Result kept on client.AgentProc's surface.
type Caps ¶
type Caps struct {
// LoadSession is the standard agentCapabilities.loadSession bool.
LoadSession bool
// ListSessions reflects agentCapabilities.sessionCapabilities.list
// (unstable RFD).
ListSessions bool
// ResumeSession reflects agentCapabilities.sessionCapabilities.resume
// (unstable RFD).
ResumeSession bool
// EmbeddedContext reflects
// agentCapabilities.promptCapabilities.embeddedContext: when true,
// the relay may emit ContentBlock::Resource (with TextResourceContents)
// in prompt requests instead of a bare ResourceLink, avoiding an
// agent-side fetch.
EmbeddedContext bool
// SystemPrompt reflects agentCapabilities._meta["session.systemPrompt"].
// When true the consumer may pass a system-prompt block list via
// session/new._meta and the agent will treat it as durable across
// compaction. When false the consumer must fall back to inlining the
// same content on first prompt (and re-arm on resume).
SystemPrompt bool
}
Caps captures the agent capabilities the relay cares about, parsed from the initialize response.
type Config ¶
type Config struct {
// Command is the argv used to spawn the agent (e.g. []string{"fir", "--mode", "acp"}).
Command []string
// Cwd is the working directory for the child process.
Cwd string
// Env is the environment for the child. If nil, os.Environ() is used.
Env []string
// Policy decides permission responses. If nil, AllowAllPermissions is used.
Policy PermissionPolicy
// CloseGrace is how long Close waits after SIGINT before SIGKILL. Default 2s.
CloseGrace time.Duration
// Stderr is where the child's stderr is forwarded. If nil, os.Stderr.
Stderr io.Writer
}
Config configures an AgentProc.
type PermissionFunc ¶
type PermissionFunc func(context.Context, acp.RequestPermissionRequest) acp.RequestPermissionResponse
PermissionFunc adapts a function into a PermissionPolicy.
func (PermissionFunc) Decide ¶
func (f PermissionFunc) Decide(ctx context.Context, req acp.RequestPermissionRequest) acp.RequestPermissionResponse
Decide implements PermissionPolicy.
type PermissionPolicy ¶
type PermissionPolicy interface {
Decide(ctx context.Context, req acp.RequestPermissionRequest) acp.RequestPermissionResponse
}
PermissionPolicy decides how to respond to session/request_permission.
type SessionInfo ¶
type SessionInfo struct {
SessionId string `json:"sessionId"`
Cwd string `json:"cwd,omitempty"`
Title *string `json:"title,omitempty"`
UpdatedAt string `json:"updatedAt,omitempty"`
}
SessionInfo is one entry from a session/list response.
type SessionUpdateSink ¶
type SessionUpdateSink interface {
OnUpdate(ctx context.Context, n acp.SessionNotification) error
}
SessionUpdateSink receives streaming updates for a single ACP session. Each consumer (e.g. a relay) implements this to forward the stream to its own transport (SSE, WebSocket, IM channel, ...).