piacp

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Aug 6, 2026 License: GPL-3.0 Imports: 36 Imported by: 0

README

acp-go-pi

Go ACP agent that exposes the local pi coding agent CLI as an Agent Client Protocol agent.

Go Reference CI

It wraps the local pi CLI in RPC mode, speaks ACP over JSON-RPC streams, and builds on github.com/coder/acp-go-sdk.

Use it as either:

  • a standalone ACP subprocess: acp-go-pi
  • an embedded Go adapter through piacp.Serve

Install

Library:

go get github.com/savid/acp-go-pi

CLI:

go install github.com/savid/acp-go-pi/cmd/acp-go-pi@latest

The acp-go-pi binary speaks ACP over stdin/stdout; an editor or ACP host launches it as a subprocess rather than a human-facing chat UI.

Quickstart

The example programs run from a checkout of this repo, so clone it first:

git clone https://github.com/savid/acp-go-pi && cd acp-go-pi

Run a tiny local client against the agent:

go run ./examples/minimal-client \
  -auth-file "$HOME/.pi/agent/auth.json" \
  "Reply with a short hello from ACP."

Start an interactive session against the agent:

go run ./examples/interactive-chat -auth-file "$HOME/.pi/agent/auth.json"

Load and resume a stored session transcript:

go run ./examples/resume-from-file \
  -file ./transcript.jsonl \
  -auth-file "$HOME/.pi/agent/auth.json"

Each example copies the explicitly named credential file into its isolated pi agent directory. It never inherits ambient provider keys or reads the normal pi home implicitly.

Embedded Go

package main

import (
	"context"
	"log"
	"os"

	piacp "github.com/savid/acp-go-pi"
)

func main() {
	err := piacp.Serve(context.Background(), os.Stdin, os.Stdout,
		piacp.WithDefaultModel("openai/gpt-4o"),
	)
	if err != nil {
		log.Fatal(err)
	}
}

See the Go API reference for options such as the pi executable path, scratch directory, default model, session storage, permissions, raw events, and OpenTelemetry providers.

What It Provides

  • ACP session lifecycle: create, prompt, cancel, close, list, load, resume, and extension-based fork.
  • pi RPC-mode subprocess management with isolated per-session agent directories and a scrubbed child environment.
  • A target-owned version-probe agent directory below WithScratchDir, passed as an exact non-empty PI_CODING_AGENT_DIR so policy HOME is never used as pi's fallback settings root.
  • Prompt streaming for messages, thoughts, tool calls, tool results, usage, and session metadata.
  • Permission prompts through a wrapper-owned pi bridge extension with ask/allow modes.
  • Elicitation bridging through the wrapper-owned question tool and any explicitly seeded extension dialogs.
  • MCP stdio and HTTP server declarations through a wrapper-owned, dependency-free pi MCP client extension.
  • Deliberate provider credential injection through the isolated child environment or seeded auth.json.
  • Optional durable mirroring through a host-provided SessionStore.
  • Optional raw pi event extension notifications.
  • OpenTelemetry spans, metrics, trace propagation, and structured logs without recording prompt or tool secrets by default.

Process containment

Linux provides the authoritative native process boundary. Windows native launch fails closed because its process API cannot apply the mandatory Unix UID/GID identity boundary with empty supplementary groups; cross-compilation proves only that this refusal path builds, not runtime support. Darwin fails closed by default because a process-group check cannot account for descendants that call setsid. The standalone command is Linux-only. Embedded hosts who accept that limitation can opt in with WithDarwinBestEffortContainment. The effective mode is available from Agent.ContainmentMode and is reported as authoritative, best_effort, or unavailable. FreeBSD, OpenBSD, and other unsupported platforms continue to fail closed.

Darwin best-effort mode reaps the direct child and applies a bounded TERM-to-KILL ladder to the captured original process group. It does not claim that escaped descendants are absent. The adapter prints a warning on startup and retains runtime records that operators can inspect with acp-go-pi containment diagnose; see the CLI reference and security limits. Only group_absent records expire after 30 days; running and cleanup_incomplete records remain actionable. Forced PID-by-PID cleanup has a PID-reuse time-of-check/time-of-use race and can signal an unrelated reused PID despite immediate identity revalidation.

Slash Commands

The adapter projects only commands returned by pi's RPC command inventory. Ambient extensions, prompt templates, and skills are disabled for isolated sessions. Explicit WithSeedFiles entries under extensions/, prompts/, and skills/**/SKILL.md are loaded by exact path and therefore are reachable; the shipped wrapper extensions register no slash commands, so a default session still advertises an empty command set. Nothing is synthesized from the terminal UI's built-in commands.

Docs

Full Go API reference: pkg.go.dev/github.com/savid/acp-go-pi.

Development

make audit
make test-integration-smoke
make test-integration-live
make test-integration-cover

make audit runs the full local gate: format, lint, build, unit tests, coverage, cross-compile, vuln, and docs checks. Live integration tests require a local pi CLI (v0.80.6 or newer) and are double-gated: the integration build tag plus ACP_GO_PI_RUN_INTEGRATION=1. make test-integration-smoke runs the integration tier without spending model tokens; tests that spend tokens additionally require ACP_GO_PI_RUN_LIVE_TOKENS=1, which only make test-integration-live sets. make test-integration-cover runs the integration tier against a coverage-instrumented binary. Integration tests always launch pi with an isolated temp PI_CODING_AGENT_DIR and a scrubbed environment; the live tier injects provider credentials into that isolated directory and never reads a shared mutable pi home.

License

Distributed under the GNU General Public License v3.0. See LICENSE.

Documentation

Overview

Package piacp exposes the local pi coding agent CLI as an Agent Client Protocol agent.

Most hosts run the agent over a pair of JSON-RPC streams using Serve. Serve starts one pi RPC-mode process per ACP session, maps ACP requests into pi JSONL RPC commands, and streams ACP session updates back to the client. Hosts must complete ACP initialization before issuing session or other agent methods.

Hosts should use Serve for the JSON-RPC transport. Provider authentication remains owned by the operator: credentials are injected into each isolated per-session pi agent directory from options and environment, never brokered through ACP auth methods.

Hosts that need durable remote resume can provide WithSessionStore. A session store receives pi session JSONL mirror rows, can back session/list, and can hydrate a stored session file into an isolated pi session directory for session/load or session/resume when local native state is absent.

Hosts that need adapter telemetry can provide OpenTelemetry providers with WithTracerProvider and WithMeterProvider. The package never configures global OpenTelemetry providers; the acp-go-pi binary handles env-based exporter setup for command-line use. Caller-supplied providers remain owned by the caller, including ForceFlush and Shutdown.

Linux provides authoritative native process containment. Windows refuses native launch because it cannot apply the mandatory Unix UID/GID isolation. Darwin fails native startup closed unless WithDarwinBestEffortContainment is supplied; that mode reaps the direct child and observes only the captured original process group, so it does not establish escaped-descendant absence.

Index

Examples

Constants

View Source
const (
	// ForkSessionMethod is the pi extension method used to fork a session.
	// Fork duplicates the parent session's active branch into a new session
	// with a new session id (pi's native clone).
	ForkSessionMethod = "_pi/session/fork"

	// RawEventMethod is the pi extension notification method carrying raw
	// native pi RPC event payloads when a session opts in to raw events.
	RawEventMethod = "_pi/rawEvent"
)
View Source
const (
	// SessionStoreFormat identifies the durable store format written by this
	// package: raw pi session JSONL rows appended after settled turns.
	SessionStoreFormat = "pi-session-jsonl-v1"
	// SessionStoreMainSubpath addresses a session's main entry log.
	SessionStoreMainSubpath = ""
)

Variables

View Source
var ErrProcessContainmentIncomplete = internalpi.ErrProcessContainmentIncomplete

ErrProcessContainmentIncomplete reports that the selected native process boundary did not complete. Callers must retain resources that may still be reachable by the native runtime.

Functions

func CallForkSession

CallForkSession calls the pi fork extension method and decodes the SDK payload shape.

func CancelRequest

func CancelRequest(sessionID acp.SessionId, turnNonce string) acp.CancelNotification

CancelRequest builds an active-turn cancellation carrying the mandatory route nonce. Invalid nonces omit route metadata and fail closed at the agent.

func DeleteSessionRequest

func DeleteSessionRequest(sessionID acp.SessionId) acp.UnstableDeleteSessionRequest

DeleteSessionRequest constructs a session/delete request.

func ForkSessionRequest

func ForkSessionRequest(sessionID acp.SessionId, cwd string, opts ...SessionRequestOption) acp.UnstableForkSessionRequest

ForkSessionRequest constructs params for the pi fork extension method.

func HTTPMCPServer

func HTTPMCPServer(name string, url string, headers map[string]string) acp.McpServer

HTTPMCPServer constructs an ACP HTTP MCP server declaration.

func ListSessionsRequest

func ListSessionsRequest(opts ...ListSessionsRequestOption) acp.ListSessionsRequest

ListSessionsRequest constructs a session/list request.

func LoadSessionRequest

func LoadSessionRequest(sessionID acp.SessionId, cwd string, opts ...SessionRequestOption) acp.LoadSessionRequest

LoadSessionRequest constructs a session/load request with ACP-required empty slices initialized for embedded Go callers.

func NewSessionRequest

func NewSessionRequest(cwd string, opts ...SessionRequestOption) acp.NewSessionRequest

NewSessionRequest constructs a session/new request with ACP-required empty slices initialized for embedded Go callers.

Example
package main

import (
	"fmt"

	piacp "github.com/savid/acp-go-pi"
)

func main() {
	request := piacp.NewSessionRequest(
		"/workspace",
		piacp.WithSessionAdditionalDirectories("/shared"),
		piacp.WithSessionRawEvents(true),
	)

	fmt.Println(request.Cwd)
	fmt.Println(request.AdditionalDirectories[0])
}
Output:
/workspace
/shared

func PromptRequest

func PromptRequest(sessionID acp.SessionId, turnNonce string, blocks ...acp.ContentBlock) acp.PromptRequest

PromptRequest constructs a session/prompt request with a non-nil prompt slice for embedded Go callers. turnNonce must be non-empty and at most 4096 bytes; an invalid value omits route metadata so the agent rejects the request.

func ResumeSessionRequest

func ResumeSessionRequest(sessionID acp.SessionId, cwd string, opts ...SessionRequestOption) acp.ResumeSessionRequest

ResumeSessionRequest constructs a session/resume request.

func Serve

func Serve(ctx context.Context, input io.Reader, output io.Writer, opts ...Option) (returnErr error)

Serve runs an ACP agent over the provided streams.

func SetConfigOptionRequest

func SetConfigOptionRequest(
	sessionID acp.SessionId,
	configID acp.SessionConfigId,
	value acp.SessionConfigValueId,
) acp.SetSessionConfigOptionRequest

SetConfigOptionRequest constructs a value-id session/set_config_option request.

func SetModelRequest

func SetModelRequest(sessionID acp.SessionId, model string) acp.SetSessionConfigOptionRequest

SetModelRequest constructs a model selector update request. Models are addressed as "provider/id".

func StdioMCPServer

func StdioMCPServer(name string, command string, args []string, env map[string]string) acp.McpServer

StdioMCPServer constructs an ACP stdio MCP server declaration.

func TextPromptRequest

func TextPromptRequest(sessionID acp.SessionId, turnNonce, text string) acp.PromptRequest

TextPromptRequest constructs a session/prompt request containing one text content block. It applies the same turn-nonce validation as PromptRequest.

Types

type Agent

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

Agent exposes the pi coding agent through ACP.

func NewAgent

func NewAgent(opts ...Option) *Agent

NewAgent creates an ACP agent for the pi coding agent CLI.

func (*Agent) Authenticate

func (a *Agent) Authenticate(ctx context.Context, params acp.AuthenticateRequest) (resp acp.AuthenticateResponse, err error)

Authenticate rejects agent-handled auth methods.

func (*Agent) Cancel

func (a *Agent) Cancel(ctx context.Context, params acp.CancelNotification) (err error)

Cancel interrupts an active pi turn for the session.

func (*Agent) Close

func (a *Agent) Close() error

Close cancels and closes all resources owned by the agent.

func (*Agent) CloseSession

func (a *Agent) CloseSession(ctx context.Context, params acp.CloseSessionRequest) (resp acp.CloseSessionResponse, err error)

CloseSession closes a pi session process and removes it from the active map.

func (*Agent) ContainmentMode

func (a *Agent) ContainmentMode() RuntimeContainmentMode

ContainmentMode reports the effective native process boundary.

func (*Agent) HandleExtensionMethod

func (a *Agent) HandleExtensionMethod(ctx context.Context, method string, params json.RawMessage) (any, error)

HandleExtensionMethod handles pi-specific ACP extension methods. A closed agent rejects every extension call up front, before method dispatch and before any parameter validation.

func (*Agent) Initialize

func (a *Agent) Initialize(ctx context.Context, params acp.InitializeRequest) (resp acp.InitializeResponse, err error)

Initialize implements ACP initialize.

func (*Agent) ListSessions

func (a *Agent) ListSessions(ctx context.Context, params acp.ListSessionsRequest) (resp acp.ListSessionsResponse, err error)

ListSessions lists active sessions and stored pi sessions.

func (*Agent) LoadSession

func (a *Agent) LoadSession(ctx context.Context, params acp.LoadSessionRequest) (resp acp.LoadSessionResponse, err error)

LoadSession restores a pi session and replays saved history as session updates.

func (*Agent) Logout

Logout clears auth state owned by this adapter.

func (*Agent) NewSession

func (a *Agent) NewSession(ctx context.Context, params acp.NewSessionRequest) (resp acp.NewSessionResponse, err error)

NewSession creates and starts a pi RPC session.

func (*Agent) Prompt

func (a *Agent) Prompt(ctx context.Context, params acp.PromptRequest) (resp acp.PromptResponse, err error)

Prompt sends a user prompt to pi and streams ACP session updates until the run settles.

func (*Agent) ResumeSession

func (a *Agent) ResumeSession(ctx context.Context, params acp.ResumeSessionRequest) (resp acp.ResumeSessionResponse, err error)

ResumeSession restores a pi session without replaying previous updates.

func (*Agent) SetSessionConfigOption

SetSessionConfigOption handles supported configuration changes.

func (*Agent) SetSessionMode

SetSessionMode exists only because github.com/coder/acp-go-sdk's generated Agent interface still requires it. Remove this when the upstream SDK drops session/set_mode; the local ACP dispatcher intentionally does not route it.

func (*Agent) UnstableDeleteSession

func (a *Agent) UnstableDeleteSession(
	ctx context.Context,
	params acp.UnstableDeleteSessionRequest,
) (acp.UnstableDeleteSessionResponse, error)

UnstableDeleteSession implements ACP session/delete: durable tombstone first, then close and clean up the active session and its native state.

type ConcurrencyLimits

type ConcurrencyLimits struct {
	MaxActiveSessions        int
	MaxConcurrentClientCalls int
}

ConcurrencyLimits controls per-agent/session backpressure. Zero fields use defaults.

type ImageLimits

type ImageLimits struct {
	// MaxInputBytesPerImage bounds one prompt image's decoded bytes.
	MaxInputBytesPerImage int64
	// MaxInputBytesPerPrompt bounds the decoded bytes of all images in one
	// prompt combined.
	MaxInputBytesPerPrompt int64
	// MaxOutputBytesPerImage bounds one emitted image's decoded bytes.
	MaxOutputBytesPerImage int64
	// MaxOutputBytesPerToolCall bounds the combined decoded image bytes of
	// one tool call's content array, which travels as a whole in each
	// content-bearing update.
	MaxOutputBytesPerToolCall int64
}

ImageLimits bounds decoded image bytes on both prompt input and emitted output. Every field defaults to 6 MiB (6,291,456 bytes) when the option is omitted. A field explicitly set to zero disables that adapter policy limit but never bypasses hard framing, provider, memory, or host limits; negative values are rejected at agent construction.

type InMemorySessionStore

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

InMemorySessionStore is a process-local SessionStore for tests and embedding.

func NewInMemorySessionStore

func NewInMemorySessionStore() *InMemorySessionStore

NewInMemorySessionStore constructs an empty in-memory session store.

func (*InMemorySessionStore) Append

func (s *InMemorySessionStore) Append(ctx context.Context, key SessionKey, entries []SessionStoreEntry) error

Append durably appends entries to the addressed key in input order.

func (*InMemorySessionStore) Delete

func (s *InMemorySessionStore) Delete(ctx context.Context, key SessionKey) error

Delete durably tombstones the key; deleting main cascades to all subpaths.

func (*InMemorySessionStore) ListSessions

func (s *InMemorySessionStore) ListSessions(ctx context.Context) ([]SessionSummary, error)

ListSessions lists committed, non-tombstoned main keys, newest first.

func (*InMemorySessionStore) ListSubkeys

func (s *InMemorySessionStore) ListSubkeys(ctx context.Context, key SessionKey) ([]string, error)

ListSubkeys lists committed, non-tombstoned subpaths sorted bytewise ascending.

func (*InMemorySessionStore) Load

Load returns the latest committed entries for the key in append order.

func (*InMemorySessionStore) Replace

func (s *InMemorySessionStore) Replace(ctx context.Context, main SessionKey, replacements []SessionStoreReplacement) error

Replace atomically installs a full committed generation for a session.

type ListSessionsRequestOption

type ListSessionsRequestOption func(*acp.ListSessionsRequest)

ListSessionsRequestOption configures embedded-Go session/list requests.

func WithListSessionsCursor

func WithListSessionsCursor(cursor string) ListSessionsRequestOption

WithListSessionsCursor sets the cursor for session/list pagination.

func WithListSessionsCwd

func WithListSessionsCwd(cwd string) ListSessionsRequestOption

WithListSessionsCwd filters session/list by cwd.

func WithListSessionsMeta

func WithListSessionsMeta(meta map[string]any) ListSessionsRequestOption

WithListSessionsMeta sets metadata on a session/list request.

type Option

type Option func(*Options)

Option configures the pi ACP agent.

func WithAgentName

func WithAgentName(name string) Option

WithAgentName sets the protocol identifier advertised during ACP initialize.

func WithAgentTitle

func WithAgentTitle(title string) Option

WithAgentTitle sets the human-readable agent name advertised during ACP initialize.

func WithAgentVersion

func WithAgentVersion(version string) Option

WithAgentVersion sets the agent version advertised during ACP initialize and used by adapter OpenTelemetry instrumentation.

func WithConcurrencyLimits

func WithConcurrencyLimits(limits ConcurrencyLimits) Option

WithConcurrencyLimits sets process-local backpressure limits. Zero fields use defaults.

func WithDarwinBestEffortContainment

func WithDarwinBestEffortContainment() Option

WithDarwinBestEffortContainment opts into Darwin process-group containment. The boundary reaps the direct child and waits for the captured original process group to disappear, but cannot contain descendants that leave it.

func WithDefaultModel

func WithDefaultModel(model string) Option

WithDefaultModel selects a pi model for newly created sessions as "provider/id".

func WithEnv

func WithEnv(env map[string]string) Option

WithEnv adds environment variables to every launched pi process. pi children run with a scrubbed environment, so provider API keys must travel here. NODE_OPTIONS, BASH_ENV, ENV, LD_*, DYLD_*, and invalid names are rejected at session start. PATH is an explicit overlay on the isolation policy PATH.

func WithExecutablePath

func WithExecutablePath(path string) Option

WithExecutablePath sets the pi CLI executable path. If unset, PATH is searched.

func WithExtraPathDirs

func WithExtraPathDirs(dirs ...string) Option

WithExtraPathDirs prepends absolute directories, in the order given, to the PATH of every launched pi process, so their executables resolve ahead of every inherited entry. It is the sanctioned counterpart to the rejected raw PATH key: a caller places its own executable in front of the child without being able to replace the search order wholesale. Every directory must be absolute and free of the platform list separator; a bad entry fails session start. Per-session directories are prepended ahead of these.

func WithImageLimits

func WithImageLimits(limits ImageLimits) Option

WithImageLimits configures decoded-byte limits for prompt image input and emitted image output. Supplying the struct owns all four fields: an explicit zero disables that policy limit, and omitting the option leaves every field at its 6 MiB default.

func WithInputHandoffRoot

func WithInputHandoffRoot(dir string) Option

WithInputHandoffRoot sets the absolute directory under which handoff-form prompt images are read. Omitting the option rejects the handoff form, so a host that expects it can tell from the absence of the handoff capability advertisement that its option never reached this adapter. The directory is read-only to the adapter: handoff files stay owned by the host, which may remove them as soon as session/prompt returns.

func WithLogger

func WithLogger(logger *slog.Logger) Option

WithLogger configures structured diagnostic logging.

func WithMeterProvider

func WithMeterProvider(provider metric.MeterProvider) Option

WithMeterProvider configures the OpenTelemetry meter provider used for adapter metrics. If unset, metrics are no-ops.

func WithProcessIsolation

func WithProcessIsolation(isolation ProcessIsolation) Option

WithProcessIsolation requires every native process and probe to run as the supplied non-root identity with no supplementary groups. The base environment is a complete replacement for the adapter environment; WithEnv and session environment values overlay it.

func WithRuntimeResourceHooks

func WithRuntimeResourceHooks(hooks RuntimeResourceHooks) Option

WithRuntimeResourceHooks installs host-facing native-root and scratch-root admission hooks.

func WithScratchDir

func WithScratchDir(dir string) Option

WithScratchDir sets the parent directory for all ephemeral on-disk materialization (per-session roots, hydration temp files, and the version probe's isolated PI_CODING_AGENT_DIR/settings residence). Empty means the system temp directory. The directory is created 0700 when missing.

func WithSeedFiles

func WithSeedFiles(files map[string]string) Option

WithSeedFiles registers files written into each session's isolated pi agent directory before the pi process launches. Keys are paths relative to that directory and values are the file contents. settings.json is deep-merged under the adapter's managed keys; other files are written verbatim. Paths are confined to the agent directory: absolute paths, ".." escapes, and empty keys fail closed at session start.

func WithSessionStore

func WithSessionStore(store SessionStore) Option

WithSessionStore configures external pi session storage.

func WithSessionStoreLoadTimeout

func WithSessionStoreLoadTimeout(timeout time.Duration) Option

WithSessionStoreLoadTimeout bounds session store reads used during resume.

func WithTextMapPropagator

func WithTextMapPropagator(propagator propagation.TextMapPropagator) Option

WithTextMapPropagator configures trace-context propagation for ACP _meta and pi process launch environment. If unset, W3C trace context plus baggage propagation is used.

func WithTracerProvider

func WithTracerProvider(provider trace.TracerProvider) Option

WithTracerProvider configures the OpenTelemetry tracer provider used for adapter spans. If unset, tracing is a no-op.

func WithTurnTimeout

func WithTurnTimeout(timeout time.Duration) Option

WithTurnTimeout bounds one pi prompt turn. Zero (the default) disables the deadline. On expiry the native turn is aborted and session/prompt fails with a pi_turn_failed error whose cause is "timeout" (never cancelled).

type Options

type Options struct {
	// AgentName is the protocol identifier advertised during ACP initialize.
	AgentName string
	// AgentTitle is the human-readable agent name advertised during ACP initialize.
	AgentTitle string
	// AgentVersion is the agent version advertised during ACP initialize.
	AgentVersion string

	// ExecutablePath is the pi CLI executable path. If empty, PATH is searched.
	ExecutablePath string
	// ProcessIsolation is the mandatory child identity and complete base
	// environment. It is installed with WithProcessIsolation.
	ProcessIsolation *ProcessIsolation
	// ScratchDir is the parent directory for all ephemeral on-disk
	// materialization (per-session roots, hydration temp files, and the version
	// probe's isolated PI_CODING_AGENT_DIR/settings residence). Empty means the
	// system temp directory. The directory is created 0700 when missing.
	ScratchDir string
	// DarwinBestEffortContainment explicitly selects Darwin process-group
	// containment. It is invalid on every other platform.
	DarwinBestEffortContainment bool
	// DefaultModel selects the model for newly created pi sessions when
	// non-empty, as "provider/id" (for example "openai/gpt-4o").
	DefaultModel string
	// Env is added to every launched pi process environment. pi children run
	// with a scrubbed environment, so provider API keys must travel here (or
	// per session) rather than relying on ambient variables. Process-loader,
	// shell-loader, PATH, and Node loader keys are rejected at session start.
	Env map[string]string
	// ExtraPathDirs are absolute directories prepended, in order, to the PATH
	// of every launched pi process. Per-session directories from
	// _meta.pi.options.extraPathDirs are prepended ahead of these.
	ExtraPathDirs []string

	// Logger receives structured diagnostic logs. If nil, the default logger is used.
	Logger *slog.Logger
	// TracerProvider records adapter spans. If nil, tracing is a no-op.
	TracerProvider trace.TracerProvider
	// MeterProvider records adapter metrics. If nil, metrics are no-ops.
	MeterProvider metric.MeterProvider
	// TextMapPropagator extracts ACP _meta trace context and injects pi launch
	// env. If nil, W3C trace context plus baggage propagation is used.
	TextMapPropagator propagation.TextMapPropagator

	// SessionStore mirrors pi session JSONL rows and backs store restores.
	SessionStore SessionStore
	// SessionStoreLoadTimeout bounds store load/list operations used for resume.
	SessionStoreLoadTimeout time.Duration
	// TurnTimeout bounds one pi prompt turn. Zero (the default) means no
	// deadline. On expiry the turn is aborted and fails with cause "timeout".
	TurnTimeout          time.Duration
	RuntimeResourceHooks RuntimeResourceHooks
	// ConcurrencyLimits controls process-local backpressure.
	ConcurrencyLimits ConcurrencyLimits
	// SeedFiles maps paths relative to each session's isolated pi agent
	// directory to file contents written there before the pi process launches,
	// so the launched CLI reads them as its own config (e.g. settings.json,
	// which is deep-merged under the adapter's managed keys).
	SeedFiles map[string]string
	// ImageLimits bounds decoded image bytes on prompt input and emitted
	// output. Set via WithImageLimits; every field defaults to 6 MiB when the
	// option is omitted, and an explicit zero in a supplied struct disables
	// that policy limit.
	ImageLimits ImageLimits
	// InputHandoffRoot is the absolute directory under which handoff-form
	// prompt images are read. Empty (the default) rejects the handoff form.
	// The adapter only reads under it and never writes, moves, or removes
	// anything there.
	InputHandoffRoot string
	// contains filtered or unexported fields
}

Options configures the ACP agent process and the pi RPC-mode sessions it starts.

type PiOption

type PiOption func(*PiOptions)

PiOption configures PiOptions values.

func WithPiAutoRetry

func WithPiAutoRetry(enabled bool) PiOption

WithPiAutoRetry opts the session in to pi's native automatic retry of transient provider errors. Off by default: without it a native failure surfaces once, immediately, with the real cause.

func WithPiEnv

func WithPiEnv(env map[string]string) PiOption

WithPiEnv configures pi session environment overrides.

func WithPiExtraPathDirs

func WithPiExtraPathDirs(dirs ...string) PiOption

WithPiExtraPathDirs configures absolute directories prepended, in the order given, to the PATH of the session's pi process.

func WithPiModel

func WithPiModel(model string) PiOption

WithPiModel configures the initial pi model as "provider/id".

func WithPiOutputSchema

func WithPiOutputSchema(schema map[string]any) PiOption

WithPiOutputSchema configures JSON Schema structured output. pi has no native structured-output surface, so sessions carrying it fail closed at session start.

func WithPiPermission

func WithPiPermission(mode string) PiOption

WithPiPermission configures the adapter permission mode for the session: "ask" or "allow".

func WithPiThinkingLevel

func WithPiThinkingLevel(level string) PiOption

WithPiThinkingLevel configures the pi reasoning level for the session.

type PiOptions

type PiOptions struct {
	// Model selects the pi model for this session as "provider/id".
	Model string `json:"model,omitempty"`
	// Env adds environment variables for this pi session's process.
	Env map[string]string `json:"env,omitempty"`
	// ExtraPathDirs are absolute directories prepended, in order, to the PATH
	// of this session's pi process, so the first entry resolves ahead of every
	// other. A raw PATH in Env stays rejected: this is the whole sanctioned
	// surface for placing a host-owned executable in front of the child.
	ExtraPathDirs []string `json:"extraPathDirs,omitempty"`
	// OutputSchema requests JSON Schema structured output. pi has no native
	// structured-output surface, so setting it fails closed at session start.
	OutputSchema map[string]any `json:"outputSchema,omitempty"`
	// ThinkingLevel selects the pi reasoning level for this session:
	// off, minimal, low, medium, high, xhigh, or max.
	ThinkingLevel string `json:"thinkingLevel,omitempty"`
	// Permission selects the adapter permission mode for this session:
	// "ask" (deny-by-default dialog, the default) or "allow" (auto-allow).
	Permission string `json:"permission,omitempty"`
	// AutoRetry opts this session in to pi's native automatic retry of
	// transient provider errors (5xx, timeouts). Off by default so a native
	// failure surfaces once, immediately, with the real cause; when enabled,
	// the final error after exhausted retries still carries the last cause.
	AutoRetry bool `json:"autoRetry,omitempty"`
}

PiOptions is the stable, supported pi-specific subset accepted at _meta.pi.options. The JSON field names below are part of this package's wire contract; unsupported option keys are rejected.

func NewPiOptions

func NewPiOptions(opts ...PiOption) PiOptions

NewPiOptions constructs PiOptions from functional options.

Example
package main

import (
	"fmt"

	piacp "github.com/savid/acp-go-pi"
)

func main() {
	options := piacp.NewPiOptions(
		piacp.WithPiModel("provider/model"),
		piacp.WithPiThinkingLevel("high"),
		piacp.WithPiPermission("ask"),
	)

	fmt.Println(options.Model)
	fmt.Println(options.ThinkingLevel)
	fmt.Println(options.Permission)
}
Output:
provider/model
high
ask

func (PiOptions) Meta

func (options PiOptions) Meta() map[string]any

Meta returns an ACP _meta object for the supported pi-specific options.

type ProcessIdentityLockCapability

type ProcessIdentityLockCapability interface {
	Duplicate() (*os.File, error)
}

ProcessIsolation is the mandatory operating-system identity and complete base environment for every native pi process.

type ProcessIsolation

type ProcessIsolation struct {
	UID             uint32
	GID             uint32
	BaseEnvironment map[string]string
	// IdentityLock is an optional trusted-supervisor descriptor for the
	// host-global UID lock. Linux supervisors validate it and never expose it to
	// the native pi process. Standalone embeddings should leave it nil.
	IdentityLock        ProcessIdentityLockCapability
	AuthorityDomain     ProcessIdentityLockCapability
	StandaloneOwnerID   string
	StandaloneStateRoot string
}

type RuntimeContainmentMode

type RuntimeContainmentMode string

RuntimeContainmentMode identifies the effective native process boundary.

const (
	RuntimeContainmentAuthoritative RuntimeContainmentMode = "authoritative"
	RuntimeContainmentBestEffort    RuntimeContainmentMode = "best_effort"
	RuntimeContainmentUnavailable   RuntimeContainmentMode = "unavailable"
)

type RuntimeProcessKind

type RuntimeProcessKind string
const (
	RuntimeProcessProviderDescendant RuntimeProcessKind = "provider_descendant"
)

type RuntimeResourceHooks

type RuntimeResourceHooks struct {
	AcquireNativeRoot      func(context.Context, RuntimeResourceKind) (func(), error)
	ReserveScratchRoot     func(context.Context, RuntimeResourceKind) (func(), error)
	ObserveProcess         func(context.Context, RuntimeProcessKind, int64)
	ObserveProcessSnapshot func(context.Context, RuntimeProcessKind, int)
	ObserveStartupStage    func(context.Context, RuntimeResourceKind, RuntimeStartupStage, time.Duration, error)
	ObserveContainment     func(context.Context, RuntimeContainmentMode)
}

RuntimeResourceHooks lets an embedding host enforce native-root and scratch-root limits.

type RuntimeResourceKind

type RuntimeResourceKind string

RuntimeResourceKind identifies the lifecycle scope consuming a host-managed resource.

const (
	RuntimeResourceRuntime   RuntimeResourceKind = "runtime"
	RuntimeResourceSession   RuntimeResourceKind = "session"
	RuntimeResourcePrompt    RuntimeResourceKind = "prompt"
	RuntimeResourceDiscovery RuntimeResourceKind = "discovery"
)

type RuntimeStartupStage

type RuntimeStartupStage string
const (
	RuntimeStartupSpawn         RuntimeStartupStage = "spawn"
	RuntimeStartupReadiness     RuntimeStartupStage = "readiness"
	RuntimeStartupConfiguration RuntimeStartupStage = "configuration"
	RuntimeStartupSession       RuntimeStartupStage = "session"
)

type SessionKey

type SessionKey struct {
	SessionID string
	Subpath   string
}

SessionKey addresses one entry log inside the session store.

type SessionRequestOption

type SessionRequestOption func(*sessionRequestConfig)

SessionRequestOption configures embedded-Go ACP session lifecycle requests.

func WithSessionAdditionalDirectories

func WithSessionAdditionalDirectories(paths ...string) SessionRequestOption

WithSessionAdditionalDirectories sets additional workspace directories for a session lifecycle request.

func WithSessionMCPServers

func WithSessionMCPServers(servers ...acp.McpServer) SessionRequestOption

WithSessionMCPServers sets MCP servers for a session lifecycle request.

func WithSessionMeta

func WithSessionMeta(meta map[string]any) SessionRequestOption

WithSessionMeta merges metadata into a session lifecycle request.

func WithSessionOutputSchema

func WithSessionOutputSchema(schema map[string]any) SessionRequestOption

WithSessionOutputSchema sets JSON Schema structured output for a session lifecycle request. pi has no native structured-output surface, so sessions carrying it fail closed at session start.

func WithSessionPiOptions

func WithSessionPiOptions(options PiOptions) SessionRequestOption

WithSessionPiOptions merges pi-specific options into a session lifecycle request's _meta.pi.options object.

func WithSessionRawEvents

func WithSessionRawEvents(enabled bool) SessionRequestOption

WithSessionRawEvents toggles raw pi event emission for a session lifecycle request.

type SessionStore

type SessionStore interface {
	Append(ctx context.Context, key SessionKey, entries []SessionStoreEntry) error
	Load(ctx context.Context, key SessionKey) ([]SessionStoreEntry, error)
	Replace(ctx context.Context, main SessionKey, replacements []SessionStoreReplacement) error
	Delete(ctx context.Context, key SessionKey) error
	ListSessions(ctx context.Context) ([]SessionSummary, error)
	ListSubkeys(ctx context.Context, key SessionKey) ([]string, error)
}

SessionStore is the host-provided durability boundary for pi sessions.

type SessionStoreEntry

type SessionStoreEntry = json.RawMessage

SessionStoreEntry is one raw native JSON row.

type SessionStoreReplacement

type SessionStoreReplacement struct {
	Key     SessionKey
	Entries []SessionStoreEntry
}

SessionStoreReplacement is one key's full replacement contents for Replace.

type SessionSummary

type SessionSummary struct {
	SessionID          string
	UpdatedAtUnixMilli int64
	Cwd                string
	Title              string
	Meta               map[string]any
}

SessionSummary describes one stored session for session/list.

Directories

Path Synopsis
cmd
acp-go-pi command
examples
minimal-client command
internal
observer
Package observer centralizes the adapter's OpenTelemetry instrumentation: ACP request spans/metrics, prompt-turn GenAI metrics, permission and elicitation dialogs, session store operations, and pi process lifecycle.
Package observer centralizes the adapter's OpenTelemetry instrumentation: ACP request spans/metrics, prompt-turn GenAI metrics, permission and elicitation dialogs, session store operations, and pi process lifecycle.
pi
Package pi implements the native boundary to the pi coding agent CLI: launching `pi --mode rpc` processes with isolated per-session agent directories, speaking pi's LF-delimited JSONL RPC protocol, and authoring the wrapper-owned extension and configuration files each session needs.
Package pi implements the native boundary to the pi coding agent CLI: launching `pi --mode rpc` processes with isolated per-session agent directories, speaking pi's LF-delimited JSONL RPC protocol, and authoring the wrapper-owned extension and configuration files each session needs.
raster
Package raster inspects raster image containers structurally: format sniffing from magic bytes, dimensions from headers, and animation from block/chunk lists.
Package raster inspects raster image containers structurally: format sniffing from magic bytes, dimensions from headers, and animation from block/chunk lists.

Jump to

Keyboard shortcuts

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