Documentation
¶
Overview ¶
Package agent watches the JSONL transcripts that Codex and Claude Code write while their TUI runs, and projects those files as normalized events. The PTY byte stream stays the source of truth; this is a side channel for clients (notably Web) that want a structured view of the same process.
Index ¶
- Constants
- func BindDir() string
- func BindEnvironment(warrenSessionID, kind string) []string
- func BindPath(warrenSessionID string) string
- func ClaudeConfigDir() string
- func ClaudeProjectsRoot() string
- func ClaudeTranscriptPath(claudeRoot, workspacePath, sessionID string) string
- func CodexHome() string
- func EnsureClaudeBindHook(claudeConfigDir string) (changed bool, err error)
- func EnsureCodexBindHook(codexHome string) (changed bool, err error)
- func EnsureOpenCodeBindPlugin() (bool, error)
- func InjectClaudeSessionID(command, warrenSessionID string) string
- func OpenCodeCachePath(warrenSessionID, opencodeSessionID string) string
- func OpenCodeDataRoot(override string) string
- func OpenCodeDatabasePath(dataRoot string) string
- func OpenCodePluginPath() string
- func ProjectEvents(events []api.AgentEvent, options ReadOptions) ([]api.AgentEvent, error)
- func ReadAgentStatus(path string) (api.AgentStatus, error)
- func ReadTranscript(ctx context.Context, provider, path string, options ReadOptions) ([]api.AgentEvent, error)
- func ReadTranscriptChunk(path string, offset int64, limit int) ([]byte, int64, bool, error)
- func RemoveBinding(warrenSessionID string)
- func RemoveOpenCodeCache(path string) error
- func StatePath(warrenSessionID string) string
- func ValidateOpenCodeCommand(command string) error
- func WriteAgentStatus(path string, status api.AgentStatus) error
- func WriteBinding(path string, binding Binding) error
- type ActivityTracker
- func (t *ActivityTracker) Activity() api.AgentActivity
- func (t *ActivityTracker) ClearAttention()
- func (t *ActivityTracker) DrainTurns() []api.AgentTurn
- func (t *ActivityTracker) Exited()
- func (t *ActivityTracker) MarkAttention(kind api.AgentAttentionKind, reason, requestID string, since time.Time)
- func (t *ActivityTracker) Observe(event api.AgentEvent)
- func (t *ActivityTracker) Status() api.AgentStatus
- func (t *ActivityTracker) Tick(now time.Time)
- func (t *ActivityTracker) Turn() uint64
- func (t *ActivityTracker) TurnAborted()
- func (t *ActivityTracker) TurnComplete()
- func (t *ActivityTracker) TurnFailed()
- func (t *ActivityTracker) TurnStarted()
- type Binding
- type BindingCandidatesFinder
- type BindingFinder
- type DefaultFinder
- func (f DefaultFinder) Find(ctx context.Context, kind, workspacePath string, after time.Time) (string, error)
- func (f DefaultFinder) FindBinding(ctx context.Context, warrenSessionID, kind, workspacePath string, ...) (*OpenCodeBinding, error)
- func (f DefaultFinder) FindBindingBySessionID(ctx context.Context, warrenSessionID, workspacePath, opencodeSessionID string) (*OpenCodeBinding, error)
- func (f DefaultFinder) FindBindings(ctx context.Context, warrenSessionID, kind, workspacePath string, ...) ([]*OpenCodeBinding, error)
- type Finder
- type OpenCodeBinding
- type OpenCodeTailer
- type ReadOptions
- type Watcher
Constants ¶
const ( // BindEnvSession marks the Warren session owning the CLI process. Hooks // and tool subprocesses see it, but only the managed hook uses it. BindEnvSession = "WARREN_SESSION_ID" // BindEnvFile points the managed hook at the file it must write with the // CLI session ID and transcript path. BindEnvFile = "WARREN_BIND_FILE" // BindEnvKind names the agent kind (codex or claude) for the hook. BindEnvKind = "WARREN_AGENT_KIND" // BindEnvState points the managed hook at the file it must update when the // CLI session changes lifecycle, so the daemon can update the status overlay. BindEnvState = "WARREN_STATE_FILE" // CodexSessionID and CodexThreadID are launcher-provided identity hints. // An interactive shell can inherit them from its parent Codex process; an // independently-created Warren session must not reuse that conversation. BindEnvCodexSessionID = "CODEX_SESSION_ID" BindEnvCodexThreadID = "CODEX_THREAD_ID" )
const ( // DefaultReadRecent bounds a command result so an external agent does not // accidentally ingest an entire long-running conversation. DefaultReadRecent = 20 // DefaultReadContentLimit is measured in Unicode code points, not bytes. DefaultReadContentLimit = 2000 // MaxReadActivities bounds an unbounded (--all) read. Callers that need a // smaller response should set Recent explicitly. MaxReadActivities = 100000 )
Variables ¶
This section is empty.
Functions ¶
func BindEnvironment ¶
BindEnvironment returns the runtime environment entries a session needs so the managed hook can report an agent CLI's own session ID to Warren.
func ClaudeConfigDir ¶
func ClaudeConfigDir() string
ClaudeConfigDir returns the directory Claude reads settings from, honoring CLAUDE_CONFIG_DIR just like the CLI itself.
func ClaudeProjectsRoot ¶
func ClaudeProjectsRoot() string
ClaudeProjectsRoot returns the directory Claude writes transcripts into, honoring CLAUDE_CONFIG_DIR just like the CLI itself.
func ClaudeTranscriptPath ¶
ClaudeTranscriptPath computes the transcript file Claude writes for a given session ID in a given working directory.
func CodexHome ¶
func CodexHome() string
CodexHome returns the Codex configuration directory the daemon's children inherit, honoring CODEX_HOME just like the CLI itself.
func EnsureClaudeBindHook ¶
EnsureClaudeBindHook installs the same two hooks into Claude's user settings file, preserving every existing entry.
func EnsureCodexBindHook ¶
EnsureCodexBindHook installs the Warren SessionStart and SessionEnd hooks into Codex's user hooks file, preserving every existing entry.
func EnsureOpenCodeBindPlugin ¶ added in v0.10.0
EnsureOpenCodeBindPlugin installs the Warren binding plugin for OpenCode. It is the hook-equivalent for OpenCode: on session.created/status=busy the plugin reads WARREN_SESSION_ID and atomically writes the binding file so the daemon can resolve the SQLite session without cwd+time guessing.
func InjectClaudeSessionID ¶
InjectClaudeSessionID makes Claude's transcript path deterministic for a Warren session. Existing resume/session flags are left untouched so a user who explicitly resumes an older conversation keeps their intent.
func OpenCodeCachePath ¶ added in v0.10.0
OpenCodeCachePath is deterministic across daemon restarts and includes both Warren and OpenCode IDs, preventing a /clear or a reused Warren tab from consuming another conversation's projection.
func OpenCodeDataRoot ¶ added in v0.10.0
OpenCodeDataRoot returns OpenCode's application data directory. The explicit override is intended for tests and embedders. Warren's own override is deliberately namespaced so it cannot be mistaken for an OpenCode setting (the provider currently follows XDG_DATA_HOME and platform defaults).
func OpenCodeDatabasePath ¶ added in v0.10.0
OpenCodeDatabasePath returns the current SQLite database path.
func OpenCodePluginPath ¶ added in v0.10.0
func OpenCodePluginPath() string
OpenCodePluginPath returns the Warren-managed OpenCode plugin path. The plugin is auto-discovered from ~/.config/opencode/plugin/ and project .opencode/plugin/; global ensures remote/empty projects still bind.
func ProjectEvents ¶
func ProjectEvents(events []api.AgentEvent, options ReadOptions) ([]api.AgentEvent, error)
ProjectEvents applies the same filtering and content limits as ReadTranscript to events already retained by a live Host watcher. This is used by remote session readers, where the transcript file lives on the Host rather than on the CLI machine.
func ReadAgentStatus ¶
func ReadAgentStatus(path string) (api.AgentStatus, error)
ReadAgentStatus returns the status recorded by the managed hook, or a zero status when the file is missing or malformed.
func ReadTranscript ¶
func ReadTranscript(ctx context.Context, provider, path string, options ReadOptions) ([]api.AgentEvent, error)
ReadTranscript parses one Codex, Claude, or OpenCode JSONL transcript into the same normalized events used by Warren's live agent view. The file is consumed line by line, so the reader never loads the whole transcript into memory.
func ReadTranscriptChunk ¶ added in v0.9.0
ReadTranscriptChunk returns one exact byte range from a bound transcript. It deliberately does not parse or normalize the JSONL: callers use it for the explicit full-transcript escape hatch and stream the result onward in small chunks. The returned EOF applies to the file size observed for this read; a concurrently-running Agent may append another record afterwards.
func RemoveBinding ¶
func RemoveBinding(warrenSessionID string)
RemoveBinding cleans up the per-session file when the session is deleted.
func RemoveOpenCodeCache ¶ added in v0.10.0
RemoveOpenCodeCache removes a persisted cache when no live tailer exists, while leaving missing files as a successful cleanup.
func StatePath ¶
StatePath is the per-session file the managed hook uses to report lifecycle changes to the daemon.
func ValidateOpenCodeCommand ¶ added in v0.10.0
ValidateOpenCodeCommand keeps every session.create caller from selecting a provider conversation that Warren did not create. The CLI performs the same validation for agent create, but Desktop and Web launch presets call session.create directly and must receive the invariant at the Host boundary.
func WriteAgentStatus ¶
func WriteAgentStatus(path string, status api.AgentStatus) error
WriteAgentStatus persists a status reported by a hook. The write is atomic so the daemon never reads a half-written status file.
func WriteBinding ¶
WriteBinding persists the binding reported by a hook. It is safe for the hook and the daemon to race: both write the same structured JSON and the daemon only starts a watcher for a path that exists on disk.
Types ¶
type ActivityTracker ¶
type ActivityTracker struct {
// contains filtered or unexported fields
}
ActivityTracker folds normalized transcript events into an AgentStatus. It owns lifecycle and liveness state; provider-specific attention observations can be applied through MarkAttention without teaching the tracker provider event names.
func NewActivityTracker ¶
func NewActivityTracker() *ActivityTracker
NewActivityTracker starts an agent in the ready state: open and idle.
func (*ActivityTracker) Activity ¶
func (t *ActivityTracker) Activity() api.AgentActivity
Activity returns the lifecycle portion of Status.
func (*ActivityTracker) ClearAttention ¶
func (t *ActivityTracker) ClearAttention()
ClearAttention removes a pending human-facing condition without changing the current lifecycle state.
func (*ActivityTracker) DrainTurns ¶
func (t *ActivityTracker) DrainTurns() []api.AgentTurn
DrainTurns returns lifecycle transitions observed since the previous call.
func (*ActivityTracker) Exited ¶
func (t *ActivityTracker) Exited()
Exited marks the agent process as gone.
func (*ActivityTracker) MarkAttention ¶
func (t *ActivityTracker) MarkAttention(kind api.AgentAttentionKind, reason, requestID string, since time.Time)
MarkAttention applies a provider-neutral human-facing observation. The reducer owns the resulting activity so callers cannot accidentally create a yellow state without the corresponding blocked/warning lifecycle state.
func (*ActivityTracker) Observe ¶
func (t *ActivityTracker) Observe(event api.AgentEvent)
Observe advances the tracker from one normalized transcript event.
func (*ActivityTracker) Status ¶
func (t *ActivityTracker) Status() api.AgentStatus
Status returns the current complete presentation state.
func (*ActivityTracker) Tick ¶
func (t *ActivityTracker) Tick(now time.Time)
Tick notices a tool call that has made no progress for the liveness grace period. It intentionally produces a stalled warning, never an input or approval request.
func (*ActivityTracker) Turn ¶
func (t *ActivityTracker) Turn() uint64
Turn returns the current turn number. It remains stable after completion so events emitted at the boundary can still be associated with that turn.
func (*ActivityTracker) TurnAborted ¶
func (t *ActivityTracker) TurnAborted()
TurnAborted marks a turn the user interrupted. An intentional interruption is a normal return to idle, not an outstanding request for attention.
func (*ActivityTracker) TurnComplete ¶
func (t *ActivityTracker) TurnComplete()
TurnComplete marks a finished turn. The agent is idle and ready for the next instruction.
func (*ActivityTracker) TurnFailed ¶
func (t *ActivityTracker) TurnFailed()
TurnFailed marks a turn that ended with an error.
func (*ActivityTracker) TurnStarted ¶
func (t *ActivityTracker) TurnStarted()
TurnStarted marks the beginning of a new turn and clears attention from a request that the new user turn supersedes.
type Binding ¶
type Binding struct {
Provider string `json:"provider"`
SessionID string `json:"sessionId"`
TranscriptPath string `json:"transcriptPath"`
Cwd string `json:"cwd,omitempty"`
UpdatedAt time.Time `json:"updatedAt"`
}
Binding records which Codex/Claude conversation belongs to one Warren session. It is written by the Warren-managed SessionStart hook (Codex) or derived from the deterministic `--session-id` flag (Claude), and read by the daemon so transcript discovery is keyed by the CLI's own session ID instead of guessing by cwd and mtime.
func ReadBinding ¶
ReadBinding returns nil when the file does not exist or is malformed; a malformed file is transient while a hook is mid-write, so callers retry.
type BindingCandidatesFinder ¶ added in v0.10.0
type BindingCandidatesFinder interface {
FindBindings(ctx context.Context, warrenSessionID, kind, workspacePath string, after time.Time) ([]*OpenCodeBinding, error)
}
BindingCandidatesFinder is an optional extension used when more than one OpenCode session is created in the same workspace at nearly the same time. A caller can skip candidates already bound to another Warren session instead of repeatedly selecting the same earliest row.
type BindingFinder ¶ added in v0.10.0
type BindingFinder interface {
FindBinding(ctx context.Context, warrenSessionID, kind, workspacePath string, after time.Time) (*OpenCodeBinding, error)
FindBindingBySessionID(ctx context.Context, warrenSessionID, workspacePath, opencodeSessionID string) (*OpenCodeBinding, error)
}
BindingFinder is an additive interface. Finder remains source-compatible with the Codex/Claude file finder, while OpenCode callers can obtain the complete backend/session binding needed for a stable tailer.
type DefaultFinder ¶
type DefaultFinder struct {
// CodexRoot is the Codex sessions directory (default ~/.codex/sessions).
CodexRoot string
// ClaudeRoot is the Claude Code projects directory (default ~/.claude/projects).
ClaudeRoot string
// OpenCodeRoot overrides OpenCode's current data directory containing
// opencode.db.
OpenCodeRoot string
}
DefaultFinder implements Finder for the stock Codex and Claude Code layouts.
func (DefaultFinder) FindBinding ¶ added in v0.10.0
func (f DefaultFinder) FindBinding(ctx context.Context, warrenSessionID, kind, workspacePath string, after time.Time) (*OpenCodeBinding, error)
DefaultFinder implements the stock Codex/Claude finder and OpenCode's current SQLite binding finder. The old Find method remains available for the other providers and returns the cache path for OpenCode.
func (DefaultFinder) FindBindingBySessionID ¶ added in v0.10.0
func (f DefaultFinder) FindBindingBySessionID(ctx context.Context, warrenSessionID, workspacePath, opencodeSessionID string) (*OpenCodeBinding, error)
func (DefaultFinder) FindBindings ¶ added in v0.10.0
func (f DefaultFinder) FindBindings(ctx context.Context, warrenSessionID, kind, workspacePath string, after time.Time) ([]*OpenCodeBinding, error)
type Finder ¶
type Finder interface {
Find(ctx context.Context, kind, workspacePath string, after time.Time) (string, error)
}
Finder locates the transcript file for a running Codex or Claude session. OpenCode uses BindingFinder to resolve its database-backed conversation and returns a Warren-owned cache path through this compatibility method. A missing file is not an error: the CLI may not be installed, may not have written a transcript yet, or may be running a version with a different layout. Callers retry until the file appears.
type OpenCodeBinding ¶ added in v0.10.0
type OpenCodeBinding struct {
Provider string `json:"provider"`
SessionID string `json:"sessionId"`
Backend string `json:"backend"`
DataRoot string `json:"dataRoot,omitempty"`
DatabasePath string `json:"databasePath,omitempty"`
CachePath string `json:"cachePath"`
WorkspacePath string `json:"workspacePath,omitempty"`
}
OpenCodeBinding is the immutable identity used by one Warren session. A binding points at one OpenCode conversation, not at whichever conversation happens to be newest in the same checkout on the next poll.
func (OpenCodeBinding) Valid ¶ added in v0.10.0
func (b OpenCodeBinding) Valid() bool
type OpenCodeTailer ¶ added in v0.10.0
type OpenCodeTailer struct {
// contains filtered or unexported fields
}
OpenCodeTailer polls one bound session and appends complete snapshots to its private cache. Failed writes and incomplete message/part races never mark a message as seen, so the next poll can recover the missing content.
func NewOpenCodeTailer ¶ added in v0.10.0
func NewOpenCodeTailer(binding OpenCodeBinding) (*OpenCodeTailer, error)
func StartOpenCodeSessionTailer ¶ added in v0.10.0
func StartOpenCodeSessionTailer(binding OpenCodeBinding) (*OpenCodeTailer, error)
StartOpenCodeSessionTailer starts the stable, bound tailer used by Service.
func StartOpenCodeTailer ¶ added in v0.10.0
func StartOpenCodeTailer(cachePath, workspace string, after time.Time) *OpenCodeTailer
StartOpenCodeTailer is retained as a small compatibility helper for callers that only have a workspace. New code should resolve and pass a binding.
func (*OpenCodeTailer) Close ¶ added in v0.10.0
func (t *OpenCodeTailer) Close()
func (*OpenCodeTailer) Path ¶ added in v0.10.0
func (t *OpenCodeTailer) Path() string
Path returns the Warren-owned JSONL cache consumed by the transcript watcher.
func (*OpenCodeTailer) Poll ¶ added in v0.10.0
func (t *OpenCodeTailer) Poll(parent context.Context) (err error)
Poll performs one bounded read and is exported for deterministic fixtures.
func (*OpenCodeTailer) RemoveCache ¶ added in v0.10.0
func (t *OpenCodeTailer) RemoveCache() error
RemoveCache is reserved for explicit Warren session deletion. Close keeps the cache so a daemon restart can restore the structured conversation.
type ReadOptions ¶
type ReadOptions struct {
Recent int
ContentLimit int
Full bool
IncludeTypes []string
ExcludeTypes []string
// Tools adds compact tool-call records to the conversation projection.
// ToolOutput additionally includes the bounded raw tool results.
ToolOutput bool
Tools bool
}
ReadOptions controls how a transcript is projected for an external caller. A zero Recent means that every matching event is returned. ContentLimit is ignored when Full is true; a zero ContentLimit uses the default limit.
type Watcher ¶
type Watcher struct {
// contains filtered or unexported fields
}
Watcher tails one transcript file and emits normalized events. The first poll replays existing history; later polls only deliver newly appended lines. A truncated file restarts from byte zero without re-emitting the already-served prefix, which is acceptable for a best-effort side channel.
func Start ¶
func Start( sessionID, provider, path string, onEvents func([]api.AgentEvent, api.AgentStatus), onStatus func(api.AgentStatus), onTurns func([]api.AgentTurn, bool), ) *Watcher
Start begins tailing path immediately in a background goroutine.
func (*Watcher) Close ¶
func (w *Watcher) Close()
Close stops the poll loop and waits for it to finish.
func (*Watcher) Snapshot ¶
func (w *Watcher) Snapshot() []api.AgentEvent
Snapshot returns the retained event history.