agent

package
v0.4.6 Latest Latest
Warning

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

Go to latest
Published: May 12, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package agent contains the logic that runs inside the cliwrap-agent subprocess. It owns the child CLI process and the IPC connection back to the host.

Index

Constants

View Source
const FeatureFrameAck = "frame_ack"

FeatureFrameAck is the capability token advertised when the agent emits MsgAckData in response to any inbound frame whose header has FlagAckRequired set. Hosts that detect this bit may use the ack- await Conn helpers (SendAndAwaitAck) to turn fire-and-forget control messages into synchronous request/response. Without the bit, hosts must fall back to the historical fire-and-forget behaviour.

View Source
const FeaturePTY = "pty"

FeaturePTY is the capability token advertised when the agent supports PTY-mode child processes.

View Source
const FeaturePersistence = "persistence"

FeaturePersistence is the capability token advertised when the agent supports CW-G4 persistent sessions (--persistent flag, UNIX listener, reattach handshake).

Variables

View Source
var ErrNoActivePTY = errors.New("agent: no active PTY child")

ErrNoActivePTY is returned when a PTY write or control operation is attempted but no PTY child is currently running.

Functions

func BuildCapabilityReply added in v0.3.0

func BuildCapabilityReply() ipc.CapabilityReply

BuildCapabilityReply constructs the CapabilityReply the agent sends in response to a MsgTypeCapabilityQuery frame.

func IsPidAlive added in v0.3.0

func IsPidAlive(pid int) bool

IsPidAlive returns true if the process exists and is reachable via signal 0.

Caller must additionally compare meta.StartedAt to the agent's reported startup time to defend against PID rollover (handled in Manager.Reattach, CW-G4 Task 14).

func ReadPidFile added in v0.3.0

func ReadPidFile(dir string) (int, error)

ReadPidFile parses the integer in <dir>/pid.

func Run

func Run(ctx context.Context, cfg Config) error

Run is the main entry point invoked from cmd/cliwrap-agent/main.go. It blocks until the IPC connection closes or ctx is canceled.

func WritePersistentMeta added in v0.3.0

func WritePersistentMeta(dir string, meta PersistentMeta) error

WritePersistentMeta serializes meta to <dir>/meta.json with mode 0600. Caller is responsible for creating <dir> with appropriate permissions before calling.

func WritePidFile added in v0.3.0

func WritePidFile(dir string, pid int) error

WritePidFile writes <dir>/pid as a single decimal line, mode 0600.

Types

type Config

type Config struct {
	IPCFD          int
	AgentID        string
	RuntimeDir     string
	OutboxCapacity int
	WALBytes       int64
	MaxRecvPayload uint32

	// Persistent enables CW-G4 daemon mode: SIGHUP ignored, UNIX listener
	// at <CLIWRAP_SESSION_DIR>/sock for reattach, in-memory PTY ring buffer.
	// Set by cmd/cliwrap-agent when --persistent argv flag is present.
	Persistent bool
}

Config is the runtime configuration passed from the host via env vars and argv. For v1, the fd index is fixed at 3.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns sensible defaults, reading host-supplied env vars when present:

  • CLIWRAP_AGENT_ID — overrides AgentID (set by spawner)
  • CLIWRAP_AGENT_RUNTIME — overrides RuntimeDir (set by spawner)
  • CLIWRAP_AGENT_OUTBOX_CAPACITY — overrides OutboxCapacity when set and parseable as a positive integer. Lets hosts use WithAgentOutboxCapacity to tune the agent's outbox for high- throughput PTY scenarios without recompiling the agent binary.

type Dispatcher

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

Dispatcher routes inbound IPC messages and coordinates child lifecycles.

func NewDispatcher

func NewDispatcher(conn *ipc.Conn) *Dispatcher

NewDispatcher constructs a Dispatcher bound to conn.

func (*Dispatcher) AdoptConn added in v0.3.0

func (d *Dispatcher) AdoptConn(newConn *ipc.Conn)

AdoptConn replaces the dispatcher's current conn with newConn. Used by the persistent agent's accept-loop on reattach: after sending Hello + PTYRingDump on the freshly accepted socket, the agent rewires d.conn so all subsequent inbound messages route through it and outbound messages (PTY data, child status, etc.) go to the new host.

The previously-adopted conn is closed before the new one is installed. Callers must call newConn.OnMessage(d.Handle) and newConn.Start() before or after AdoptConn — both orderings are safe; OnMessage is atomic.

CW-G4 Task 12e.

func (*Dispatcher) Drain added in v0.3.0

func (d *Dispatcher) Drain(ctx context.Context) error

Drain blocks until all goroutines registered via markRunnerActive have released their tokens, or until ctx is canceled / its deadline elapses. Returns nil on clean drain, ctx.Err() otherwise.

Intended caller: agent.Run, after the IPC connection has begun shutting down. See CW-G3 spec.

func (*Dispatcher) Handle

func (d *Dispatcher) Handle(msg ipc.OutboxMessage)

Handle processes an inbound message from the host.

If the inbound frame has FlagAckRequired set, an MsgAckData carrying the inbound SeqNo is emitted AFTER the switch-handler completes — the ack promises "synchronous processing done", so it must follow rather than precede the work. Hosts that opted into this flag via SendAndAwaitAck can then turn fire-and-forget control messages (e.g., MsgStopChild) into synchronous request/response without bespoke per-message replies.

func (*Dispatcher) SendControl

func (d *Dispatcher) SendControl(t ipc.MsgType, payload any, ackRequired bool) error

SendControl serializes payload and enqueues it as an outbound frame. Uses Conn.SendWithNewSeq so the (seq assignment → outbox enqueue) pair is atomic across concurrent senders; otherwise the receiver's watermark dedup would silently drop the lower-seq frame when a higher-seq one raced ahead. CW-G5.

func (*Dispatcher) SetPersistentSessionDir added in v0.3.0

func (d *Dispatcher) SetPersistentSessionDir(dir string)

SetPersistentSessionDir enables CW-G4 meta.json updates on StartChild — the full Spec (Command, Args, PTY config, etc.) is recorded so reattach hosts can reconstruct it. Required because initPersistent runs BEFORE StartChild arrives, so the initial meta.json only has the agent ID.

func (*Dispatcher) SetPersistentShutdown added in v0.3.0

func (d *Dispatcher) SetPersistentShutdown(ch chan struct{})

SetPersistentShutdown wires the persistent-mode shutdown channel. When set (persistent=true), the dispatcher closes shutdownReq after the child terminates (Stop or natural exit), allowing agent.Run to fall through to cleanup before the bootstrap socketpair would EOF.

CW-G4 Task 15+16. Called once at agent.Run startup when in persistent mode; safe to call before or after the dispatcher has begun handling IPC messages.

type PersistentMeta added in v0.3.0

type PersistentMeta struct {
	Version   string       `json:"version"`
	ID        string       `json:"id"`
	Spec      cwtypes.Spec `json:"spec"`
	AgentPID  int          `json:"agentPID"`
	StartedAt time.Time    `json:"startedAt"`
}

PersistentMeta is the on-disk schema for persistent-session metadata. Stored at <sessionDir>/meta.json with mode 0600.

The Version field is for future schema migrations. v1 is "1.0".

func ReadPersistentMeta added in v0.3.0

func ReadPersistentMeta(dir string) (PersistentMeta, error)

ReadPersistentMeta loads <dir>/meta.json. Returns wrapped err if missing or unparseable; callers should treat any error as "session not loadable".

type RunResult

type RunResult struct {
	PID      int
	ExitCode int
	Signal   int
	ExitedAt time.Time
	Reason   string
}

RunResult summarizes the child's exit.

type RunSpec

type RunSpec struct {
	Command     string
	Args        []string
	Env         map[string]string
	WorkDir     string
	StopTimeout time.Duration
	OnStarted   func(pid int) // called after cmd.Start() succeeds, before Wait()
	PTY         *cwtypes.PTYConfig
}

RunSpec describes the child process to fork/exec.

type Runner

type Runner struct {
	// StdoutSink, if non-nil, is used to tee the child's stdout bytes.
	StdoutSink io.Writer
	// StderrSink, if non-nil, is used to tee the child's stderr bytes.
	StderrSink io.Writer
	// contains filtered or unexported fields
}

Runner starts a single child process and blocks until it exits.

func NewRunner

func NewRunner() *Runner

NewRunner returns a Runner with default sinks set to io.Discard.

func (*Runner) ResizeActivePTY added in v0.3.0

func (r *Runner) ResizeActivePTY(cols, rows uint16) error

ResizeActivePTY sends TIOCSWINSZ to the running PTY child with the given cols and rows. Returns ErrNoActivePTY if no PTY child is currently active.

func (*Runner) Run

func (r *Runner) Run(ctx context.Context, spec RunSpec) (RunResult, error)

Run forks/execs the child and blocks until it exits or ctx is canceled. On ctx cancel it sends SIGTERM, waits StopTimeout, then SIGKILL. If spec.PTY is non-nil, the child is started inside a pseudo-terminal and output is emitted as MsgTypePTYData frames via the configured sender.

func (*Runner) SetPersistentRingBuffer added in v0.3.0

func (r *Runner) SetPersistentRingBuffer(rb *ringBuffer)

SetPersistentRingBuffer wires the per-session ring buffer into the PTY OnData broadcast path. Called once at agent startup when the agent is invoked with --persistent. Nil disables the integration (no-op tee). CW-G4.

func (*Runner) SetSender added in v0.3.0

func (r *Runner) SetSender(s chunkSender)

SetSender configures the outbound frame sender used in PTY mode. Call this before Run when RunSpec.PTY is non-nil.

func (*Runner) SignalActivePTY added in v0.3.0

func (r *Runner) SignalActivePTY(sig syscall.Signal) error

SignalActivePTY delivers sig to the foreground process group of the running PTY child. Returns ErrNoActivePTY if no PTY child is currently active.

func (*Runner) WriteToActivePTY added in v0.3.0

func (r *Runner) WriteToActivePTY(b []byte) error

WriteToActivePTY forwards b to the running PTY child's stdin. Returns ErrNoActivePTY if no PTY child is currently active.

Jump to

Keyboard shortcuts

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