--- title: internal/session description: Go API reference for the session package. --- # session ```go import "github.com/jbcom/radioactive-ralph/internal/session" ``` Package session wraps a \`claude \-p\` subprocess so the supervisor can drive it via stream\-json over stdin/stdout. The stream\-json protocol pairs message objects, one per JSON line: - Messages FROM the supervisor to Claude carry \`\{"type":"user", …\}\` with a user\-scoped content block. - Messages FROM Claude carry \`\{"type":"assistant", …\}\` or \`\{"type":"result", …\}\` when the agent finishes its turn. Only the shape Ralph depends on is modeled here. Unknown fields are preserved as raw JSON so a future Claude Code version that adds a field doesn't break replay. ## Index - [Constants](<#constants>) - [func RenderSystemPrompt\(opts PromptOptions\) string](<#RenderSystemPrompt>) - [type BiasChoice](<#BiasChoice>) - [type Event](<#Event>) - [type Inbound](<#Inbound>) - [type Options](<#Options>) - [type Outbound](<#Outbound>) - [func NewUserMessage\(text string\) Outbound](<#NewUserMessage>) - [type OutboundContentPart](<#OutboundContentPart>) - [type OutboundInner](<#OutboundInner>) - [type PromptOptions](<#PromptOptions>) - [type Session](<#Session>) - [func Spawn\(ctx context.Context, opts Options\) \(\*Session, error\)](<#Spawn>) - [func \(s \*Session\) Close\(\) error](<#Session.Close>) - [func \(s \*Session\) Events\(\) \<\-chan Event](<#Session.Events>) - [func \(s \*Session\) Interrupt\(\) error](<#Session.Interrupt>) - [func \(s \*Session\) SendUserMessage\(\_ context.Context, text string\) error](<#Session.SendUserMessage>) - [func \(s \*Session\) SessionID\(\) string](<#Session.SessionID>) - [func \(s \*Session\) WaitForIdle\(ctx context.Context\) error](<#Session.WaitForIdle>) ## Constants DefaultClaudeBin is the binary name the supervisor invokes when no override is provided. Tests override this to use a fake binary. ```go const DefaultClaudeBin = "claude" ``` ## func [RenderSystemPrompt]() ```go func RenderSystemPrompt(opts PromptOptions) string ``` RenderSystemPrompt combines the variant's bias snippets with operator preferences and installed inventory to produce the \-\-append\-system\-prompt text. Rules: 1. For each bias category the variant declares a snippet for: a. If OperatorChoices has Disabled=true, skip. b. Else if OperatorChoices has Skill\!="" AND inventory has it, render the snippet with \{skill\} expanded. c. Else if variant snippet contains \{skill\} and inventory has ANY helper matching the category name, pick the first alphabetically for determinism. d. Else skip. 2. Output is newline\-joined with a fixed preamble announcing the variant and a safety note about the tool allowlist. 3. Output is deterministic given the same inputs — categories are iterated in alphabetical order. ## type [BiasChoice]() BiasChoice is the operator's resolved preference for a single bias category. Typically sourced from config.toml's \[capabilities\] section plus per\-variant overrides. Skill is the full helper name \(e.g. "coderabbit:review"\). Empty means "no preference, use variant default if inventory has it". Disabled means "skip this bias entirely regardless of inventory". ```go type BiasChoice struct { Skill string Disabled bool } ``` ## type [Event]() Event is a parsed inbound frame plus any decoding error. Consumers handle Err before Inbound. ```go type Event struct { Inbound Inbound Err error } ``` ## type [Inbound]() Inbound is a JSON\-line frame received from \`claude \-p\`. Each frame is one of several shapes depending on type; the raw bytes are retained so downstream consumers \(event log, supervisor\) can re\-emit or re\-parse as needed. ```go type Inbound struct { // Type is the top-level message kind. Common values: "assistant", // "user", "result", "system". Unknown values are preserved as // strings and handled gracefully. Type string `json:"type"` // SessionID is Claude's view of its session UUID. The supervisor // uses this to correlate resume operations. SessionID string `json:"session_id,omitempty"` // Subtype narrows the meaning of Type. For type=result, the // subtypes Ralph cares about are "success" (agent done, clean exit) // and "error_max_turns" (turn cap hit). Subtype string `json:"subtype,omitempty"` // Message is the assistant/user payload for type in {assistant, user}. // Kept as a raw JSON so we don't flatten Claude's content-block shape. Message json.RawMessage `json:"message,omitempty"` // Result is the final result payload for type=result. Result json.RawMessage `json:"result,omitempty"` // Raw is the full JSON line as received, retained for event-log // archival. Raw []byte `json:"-"` } ``` ## type [Options]() Options configures a Session spawn. ```go type Options struct { // ClaudeBin overrides DefaultClaudeBin. Tests use the fake-claude // binary path here. ClaudeBin string // WorkingDir is the cwd for the subprocess. Typically a worktree. WorkingDir string // SystemPrompt is the content for --append-system-prompt. Combined // variant + inventory biases go here. SystemPrompt string // Model pins the model tier — "haiku", "sonnet", or "opus". Empty // means "let claude choose" which in practice is sonnet. Model string // Effort pins the reasoning-effort level — "low", "medium", // "high", or "max" (as accepted by `claude --effort`). Empty // means "claude decides" which for sonnet is medium. Fixit's // advisor subprocess defaults to "high" so opus reasons deeply // during planning. Effort string // AllowedTools are passed to --allowed-tools. Supervisor builds // this from the variant profile's ToolAllowlist. AllowedTools []string // SessionID is the Claude session UUID to pin. Empty means // generate a new one (Spawn will populate this field). SessionID string // ResumeMode triggers `claude -p --resume ` instead of a // fresh spawn. Requires a non-empty SessionID. ResumeMode bool // SentinelTaskID is the task identifier the supervisor prompts // about after a resume, to verify continuity. Empty in fresh spawns. SentinelTaskID string // ExtraArgs are additional `claude -p` flags the caller wants. ExtraArgs []string } ``` ## type [Outbound]() Outbound is a JSON\-line frame sent TO \`claude \-p\`. Only user messages and interrupts are supported — the CLI does not accept assistant or system messages over stdin. ```go type Outbound struct { Type string `json:"type"` Message OutboundInner `json:"message"` } ``` ### func [NewUserMessage]() ```go func NewUserMessage(text string) Outbound ``` NewUserMessage builds an Outbound frame wrapping the given text as a single user\-role text block. ## type [OutboundContentPart]() OutboundContentPart is a single content block. Ralph only emits text. ```go type OutboundContentPart struct { Type string `json:"type"` // "text" Text string `json:"text"` } ``` ## type [OutboundInner]() OutboundInner mirrors the subset of the Anthropic message shape that \`claude \-p \-\-input\-format stream\-json\` accepts. ```go type OutboundInner struct { Role string `json:"role"` Content []OutboundContentPart `json:"content"` } ``` ## type [PromptOptions]() PromptOptions feeds RenderSystemPrompt. ```go type PromptOptions struct { // Variant is the active profile. Required. Variant variant.Profile // Inventory is the capability inventory. Required; if empty, bias // injection silently skips slots. Inventory inventory.Inventory // OperatorChoices is the per-category operator preference map. // Keys are BiasCategory values; missing keys fall back to the // variant's declared snippet target. OperatorChoices map[variant.BiasCategory]BiasChoice } ``` ## type [Session]() Session wraps a running \`claude \-p\` subprocess speaking stream\-json. Usage: ``` s, err := session.Spawn(ctx, session.Options{...}) for ev := range s.Events() { // handle ev.Inbound } s.SendUserMessage(ctx, "do X") s.WaitForIdle(ctx) s.Close() ``` The session is single\-goroutine\-safe on Send\* and Close; Events\(\) may be consumed from any goroutine. Resume reuses the SessionID so Claude's conversation history is preserved across subprocess restarts. ```go type Session struct { // contains filtered or unexported fields } ``` ### func [Spawn]() ```go func Spawn(ctx context.Context, opts Options) (*Session, error) ``` Spawn launches a new Claude subprocess and starts the reader goroutine. For ResumeMode=false, a fresh SessionID is generated \(UUID v4\) and passed via \-\-session\-id so Ralph can later resume by that ID. For ResumeMode=true, the SessionID must be set and is passed via \-\-resume; on first successful read, Spawn emits a sentinel user message naming SentinelTaskID so the caller can verify continuity. ### func \(\*Session\) [Close]() ```go func (s *Session) Close() error ``` Close terminates the subprocess and waits for the reader to exit. ### func \(\*Session\) [Events]() ```go func (s *Session) Events() <-chan Event ``` Events returns a channel of inbound frames. Closed when the reader goroutine exits \(EOF or error\). ### func \(\*Session\) [Interrupt]() ```go func (s *Session) Interrupt() error ``` Interrupt sends SIGINT to the subprocess, which Claude Code treats as an in\-flight cancellation. Safe to call on a closed session. ### func \(\*Session\) [SendUserMessage]() ```go func (s *Session) SendUserMessage(_ context.Context, text string) error ``` SendUserMessage writes a user\-role message as a JSON line on stdin. Resets the idle signal so a subsequent WaitForIdle waits for the next result frame. ### func \(\*Session\) [SessionID]() ```go func (s *Session) SessionID() string ``` SessionID returns Claude's session UUID for this process. ### func \(\*Session\) [WaitForIdle]() ```go func (s *Session) WaitForIdle(ctx context.Context) error ``` WaitForIdle blocks until the subprocess emits a \`type=result\` frame or the context is cancelled. Generated by [gomarkdoc]()