Documentation
¶
Overview ¶
Package claudecode is the Claude Code provider: a Claude Code session is an agent loop in its own process on the operator's machine. The package holds that session's agent address and ClaudeCodeBrain, the model.IBrain that drives Claude Code as a turn executor: CSF owns the session, and each turn runs one Claude Code process through the ipc/proc capability, forwarding every input and every stream-json event unchanged and reporting each one to a structured log under the turn's trace.
A Claude Code session reaches CSF as a host-tier agent: it calls CSF's per-agent authenticated MCP endpoint over a local socket (a unix socket, or loopback), registers its HostAddress, and fetches and acknowledges its inbox through MCP tools.
Index ¶
- Constants
- Variables
- type ClaudeCodeBrain
- type ClaudeCodeBrainOption
- func WithArguments(arguments ...string) ClaudeCodeBrainOption
- func WithDirectory(directory string) ClaudeCodeBrainOption
- func WithEnvironment(variables ...string) ClaudeCodeBrainOption
- func WithExecutable(executable string) ClaudeCodeBrainOption
- func WithLogger(logger *slog.Logger) ClaudeCodeBrainOption
- func WithResumedSession() ClaudeCodeBrainOption
- type ClaudeCodeSession
- func (open *ClaudeCodeSession) Close(ctx context.Context) error
- func (open *ClaudeCodeSession) Exited() <-chan struct{}
- func (open *ClaudeCodeSession) Interrupt(ctx context.Context) error
- func (open *ClaudeCodeSession) Propose(ctx context.Context, turn *Turn) (*model.Proposal[Event], error)
- func (open *ClaudeCodeSession) Session() uuid.UUID
- type Event
- type HostAddress
- type LocalSocket
- type MalformedEventError
- type Turn
- type TurnError
Constants ¶
const ( // SocketUnix is a unix domain socket, named by its absolute path. SocketUnix = "unix" // SocketLoopback is a TCP socket on a loopback address. SocketLoopback = "tcp" )
const ( // EventTypeSystem is a Claude Code system event; its init subtype opens a // run and names the session. EventTypeSystem = "system" // EventTypeResult is the event that ends a turn. EventTypeResult = "result" // EventTypeMalformed names, in the report only, an output line that was // not a stream-json event. No [Event] ever carries it. EventTypeMalformed = "malformed" )
Stream-json event types and subtypes the brain reads. It reads them only to report and to know when a turn is done; every event is forwarded unchanged.
const DefaultExecutable = "claude"
DefaultExecutable is the Claude Code command line, resolved on PATH.
const ( // EventTypeControlResponse answers a control request; it is forwarded like // every other event. EventTypeControlResponse = "control_response" )
Stream-json control messages the open session writes.
const ProviderName = "claudecode"
ProviderName names the Claude Code provider in addresses.
Variables ¶
var ( // ErrNoSession reports an address built without a session identifier. ErrNoSession = errors.New("claudecode address: a nonzero session identifier is required") // ErrNotLocal reports a socket another machine could reach, which a // host-tier address may not name. ErrNotLocal = errors.New("claudecode address: the socket must be a unix socket or a loopback address") )
var ( // ErrNoLauncher reports a brain constructed without the process // capability. ErrNoLauncher = errors.New("claudecode brain: a process launcher is required") // ErrInvalidOption reports a nil option or an option value the brain // cannot use. ErrInvalidOption = errors.New("claudecode brain: invalid option") // ErrNoTurn reports a nil turn or a turn with no input message. ErrNoTurn = errors.New("claudecode brain: a turn needs at least one input message") // ErrMalformedInput reports an input message that is not a JSON object. ErrMalformedInput = errors.New("claudecode brain: input message is not a JSON object") // ErrMalformedEvent reports an output line that is not a stream-json // event. ErrMalformedEvent = errors.New("claudecode brain: Claude Code wrote a line that is not a stream-json event") // ErrTurnIncomplete reports a Claude Code process that exited without the // result event that ends a turn. ErrTurnIncomplete = errors.New("claudecode brain: Claude Code exited before reporting the turn's result") // ErrSessionMismatch reports a Claude Code run that opened a session other // than the one CSF gave it. ErrSessionMismatch = errors.New("claudecode brain: Claude Code opened a different session") )
var ( // ErrSessionClosed reports a turn or an interrupt on an open session whose // Claude Code process has exited or been closed. ErrSessionClosed = errors.New("claudecode session: the Claude Code process is closed") // ErrTurnAbandoned reports a turn whose caller stopped waiting before // Claude Code reported the result; the process may still be working on it. ErrTurnAbandoned = errors.New("claudecode session: the turn was abandoned before its result") )
Functions ¶
This section is empty.
Types ¶
type ClaudeCodeBrain ¶
type ClaudeCodeBrain struct {
// contains filtered or unexported fields
}
ClaudeCodeBrain drives Claude Code as a turn executor for one session CSF owns. Each Propose is one turn: one Claude Code process, launched through the granted process capability, fed the turn's messages and read to its result event. Inputs and events pass through unchanged; every one is reported to the brain's logger with the turn's trace.
Turns of one session never overlap: Propose waits for the previous turn of the same brain to finish, and a context canceled while waiting abandons the wait.
func NewClaudeCodeBrain ¶
func NewClaudeCodeBrain(launcher proc.ILauncher, session uuid.UUID, options ...ClaudeCodeBrainOption) (*ClaudeCodeBrain, error)
NewClaudeCodeBrain builds the Claude Code turn executor for session. The launcher is the process capability every turn's Claude Code process is started through; the caller owns it.
func (*ClaudeCodeBrain) Open ¶
func (brain *ClaudeCodeBrain) Open(ctx context.Context) (*ClaudeCodeSession, error)
Open starts Claude Code on the brain's session and keeps it open. The process is bounded by the session's own lifetime, not by ctx: ctx only carries the trace and bounds the start itself.
func (*ClaudeCodeBrain) Propose ¶
func (brain *ClaudeCodeBrain) Propose(ctx context.Context, turn *Turn) (*model.Proposal[Event], error)
Propose runs one turn: it starts Claude Code on the session, writes the turn's messages and returns every event Claude Code reported, unchanged and in order, ending with the result event. A turn that does not reach its result returns a *TurnError carrying the events seen before the failure.
func (*ClaudeCodeBrain) Session ¶
func (brain *ClaudeCodeBrain) Session() uuid.UUID
Session is the session identifier CSF gave this brain.
type ClaudeCodeBrainOption ¶
type ClaudeCodeBrainOption func(brain *ClaudeCodeBrain) error
ClaudeCodeBrainOption configures a ClaudeCodeBrain.
func WithArguments ¶
func WithArguments(arguments ...string) ClaudeCodeBrainOption
WithArguments appends arguments to every Claude Code invocation, after the flags the brain owns. They pass through unchanged; the brain's own flags (print mode, stream formats, verbose, session selection) may not be repeated.
func WithDirectory ¶
func WithDirectory(directory string) ClaudeCodeBrainOption
WithDirectory runs Claude Code in directory instead of the caller's working directory.
func WithEnvironment ¶
func WithEnvironment(variables ...string) ClaudeCodeBrainOption
WithEnvironment appends variables, as NAME=value, to the environment every Claude Code process inherits.
func WithExecutable ¶
func WithExecutable(executable string) ClaudeCodeBrainOption
WithExecutable runs Claude Code from executable, a path or a name on PATH, instead of DefaultExecutable.
func WithLogger ¶
func WithLogger(logger *slog.Logger) ClaudeCodeBrainOption
WithLogger reports every turn and event to logger instead of slog.Default().
func WithResumedSession ¶
func WithResumedSession() ClaudeCodeBrainOption
WithResumedSession declares that Claude Code already holds the session, so the first turn resumes it rather than creating it.
type ClaudeCodeSession ¶
type ClaudeCodeSession struct {
// contains filtered or unexported fields
}
ClaudeCodeSession is one Claude Code process kept open across turns: the stream-json input stays open, every turn's messages are written to it, and the events of each turn are read to its result. It is the turn executor an open harness session holds, so a later Send is a turn on the same process rather than a new one resuming the session.
Open starts the process; Propose runs a turn on it; Interrupt asks Claude Code to end the running turn at its next safepoint; Close ends the input, waits for the process to exit and joins every goroutine the session started. Turns never overlap: Propose waits for the previous turn.
func (*ClaudeCodeSession) Close ¶
func (open *ClaudeCodeSession) Close(ctx context.Context) error
Close ends the input, which makes Claude Code exit once its current turn is done, and waits for the process. When ctx ends first the process group is killed and the wait completes on that. Close joins every goroutine the session started; it is idempotent.
func (*ClaudeCodeSession) Exited ¶
func (open *ClaudeCodeSession) Exited() <-chan struct{}
Exited is closed once the process has exited, however it ended.
func (*ClaudeCodeSession) Interrupt ¶
func (open *ClaudeCodeSession) Interrupt(ctx context.Context) error
Interrupt asks Claude Code to end the running turn at its next safepoint, a tool boundary; the turn then reports its result like any other. It is the cancel a harness session delivers while a turn is in flight.
func (*ClaudeCodeSession) Propose ¶
func (open *ClaudeCodeSession) Propose(ctx context.Context, turn *Turn) (*model.Proposal[Event], error)
Propose runs one turn on the open process: it writes the turn's messages and returns every event Claude Code reported until the result event, unchanged and in order. A turn the process ends before its result, or that ctx abandons, returns a *TurnError carrying the events seen so far.
func (*ClaudeCodeSession) Session ¶
func (open *ClaudeCodeSession) Session() uuid.UUID
Session is the session identifier Claude Code was opened on.
type Event ¶
type Event struct {
Type string
Raw json.RawMessage
}
Event is one stream-json event Claude Code reported during a turn. Raw is the line exactly as Claude Code wrote it; Type is its "type" field, read for the report.
type HostAddress ¶
type HostAddress struct {
model.HostTier
Session uuid.UUID
Socket LocalSocket
}
HostAddress is a Claude Code session on this machine: its session identifier and the local socket it is reached on.
func NewHostAddress ¶
func NewHostAddress(session uuid.UUID, endpoint string) (HostAddress, error)
NewHostAddress addresses session at the local socket endpoint.
func (HostAddress) Key ¶
func (address HostAddress) Key() string
Key is claudecode/host/<socket>/<session>.
func (HostAddress) Provider ¶
func (address HostAddress) Provider() string
Provider is ProviderName.
type LocalSocket ¶
type LocalSocket struct {
// Network is [SocketUnix] or [SocketLoopback].
Network string
// Address is an absolute path for a unix socket and a loopback
// host:port for TCP.
Address string
}
LocalSocket is a socket only processes on this machine can reach.
func ParseLocalSocket ¶
func ParseLocalSocket(endpoint string) (LocalSocket, error)
ParseLocalSocket reads "unix:/absolute/path", "/absolute/path" or a loopback "host:port", where the host is a loopback IP or "localhost". Anything another machine could reach is rejected: the tier of a Claude Code address is decided by the socket it registers, never inferred from where a request happened to arrive.
func (LocalSocket) String ¶
func (socket LocalSocket) String() string
String is the socket in the form ParseLocalSocket reads.
type MalformedEventError ¶
MalformedEventError locates the first output line that was not a stream-json event. It wraps ErrMalformedEvent.
func (*MalformedEventError) Error ¶
func (failure *MalformedEventError) Error() string
func (*MalformedEventError) Unwrap ¶
func (failure *MalformedEventError) Unwrap() []error
type Turn ¶
type Turn struct {
Messages []json.RawMessage
}
Turn is the context the Claude Code brain decides on: the stream-json input messages of one turn, each a JSON object such as {"type":"user","message":{"role":"user","content":"..."}}. They are written to Claude Code unchanged, one per line.