Documentation
¶
Overview ¶
Package agent provides interfaces and types for integrating with coding agents. It abstracts agent-specific behavior (hooks, log parsing, session storage) so that the same Strategy implementations can work with any coding agent.
Index ¶
- Constants
- func AllProtectedDirs() []string
- func ChunkFileName(baseName string, index int) string
- func ChunkJSONL(content []byte, maxSize int) ([][]byte, error)
- func ChunkTranscript(ctx context.Context, content []byte, agentType types.AgentType) ([][]byte, error)
- func DetectAgentTypeFromContent(content []byte) types.AgentType
- func List() []types.AgentName
- func ParseChunkIndex(filename, baseName string) int
- func ReadAndParseHookInput[T any](stdin io.Reader) (*T, error)
- func ReassembleJSONL(chunks [][]byte) []byte
- func ReassembleTranscript(chunks [][]byte, agentType types.AgentType) ([]byte, error)
- func Register(name types.AgentName, factory Factory)
- func SortChunkFiles(files []string, baseName string) []string
- func StringList() []string
- type Agent
- type AgentSession
- type EntryType
- type Event
- type EventType
- type Factory
- type FileWatcher
- type HookInput
- type HookResponseWriter
- type HookSupport
- type HookType
- type SessionChange
- type SessionEntry
- type SubagentAwareExtractor
- type TokenCalculator
- type TokenUsage
- type TranscriptAnalyzer
- type TranscriptPreparer
Constants ¶
const ( // MaxChunkSize is the maximum size for a single transcript chunk. // GitHub has a 100MB limit per blob, so we use 50MB to be safe. MaxChunkSize = 50 * 1024 * 1024 // 50MB // ChunkSuffix is the format for chunk file suffixes (e.g., ".001", ".002") ChunkSuffix = ".%03d" )
const ( AgentNameClaudeCode types.AgentName = "claude-code" AgentNameCursor types.AgentName = "cursor" AgentNameGemini types.AgentName = "gemini" AgentNameOpenCode types.AgentName = "opencode" AgentNameFactoryAIDroid types.AgentName = "factoryai-droid" )
Agent name constants (registry keys)
const ( AgentTypeClaudeCode types.AgentType = "Claude Code" AgentTypeCursor types.AgentType = "Cursor" AgentTypeGemini types.AgentType = "Gemini CLI" AgentTypeOpenCode types.AgentType = "OpenCode" AgentTypeFactoryAIDroid types.AgentType = "Factory AI Droid" AgentTypeUnknown types.AgentType = "Agent" // Fallback for backwards compatibility )
Agent type constants (type identifiers stored in metadata/trailers)
const DefaultAgentName types.AgentName = AgentNameClaudeCode
DefaultAgentName is the registry key for the default agent.
Variables ¶
This section is empty.
Functions ¶
func AllProtectedDirs ¶ added in v0.4.3
func AllProtectedDirs() []string
AllProtectedDirs returns the union of ProtectedDirs from all registered agents.
func ChunkFileName ¶
ChunkFileName returns the filename for a chunk at the given index. Index 0 returns the base filename, index 1+ returns with chunk suffix.
func ChunkJSONL ¶
ChunkJSONL splits JSONL content at line boundaries. This is the default chunking for agents using JSONL format (like Claude Code).
func ChunkTranscript ¶
func ChunkTranscript(ctx context.Context, content []byte, agentType types.AgentType) ([][]byte, error)
ChunkTranscript splits a transcript into chunks using the appropriate agent. If agentType is empty or the agent is not found, falls back to JSONL (line-based) chunking.
func DetectAgentTypeFromContent ¶
DetectAgentTypeFromContent detects the agent type from transcript content. Returns AgentTypeGemini if it appears to be Gemini JSON format, empty AgentType otherwise. This is used when the agent type is unknown but we need to chunk/reassemble correctly.
func ParseChunkIndex ¶
ParseChunkIndex extracts the chunk index from a filename. Returns 0 for the base file (no suffix), or the chunk number for suffixed files. Returns -1 if the filename doesn't match the expected pattern.
func ReadAndParseHookInput ¶ added in v0.4.6
ReadAndParseHookInput reads all bytes from stdin and unmarshals JSON into the given type. This is a shared helper for agent ParseHookEvent implementations.
func ReassembleJSONL ¶
ReassembleJSONL concatenates JSONL chunks with newlines.
func ReassembleTranscript ¶
ReassembleTranscript combines chunks back into a single transcript. If agentType is empty or the agent is not found, falls back to JSONL (line-based) reassembly.
func Register ¶
Register adds an agent factory to the registry. Called from init() in each agent implementation.
func SortChunkFiles ¶
SortChunkFiles sorts chunk filenames in order (base file first, then numbered chunks).
func StringList ¶ added in v0.4.8
func StringList() []string
Types ¶
type Agent ¶
type Agent interface {
// Name returns the agent registry key (e.g., "claude-code", "gemini")
Name() types.AgentName
// Type returns the agent type identifier (e.g., "Claude Code", "Gemini CLI")
// This is stored in metadata and trailers.
Type() types.AgentType
// Description returns a human-readable description for UI
Description() string
// IsPreview returns whether the agent integration is in preview or stable
IsPreview() bool
// DetectPresence checks if this agent is configured in the repository
DetectPresence(ctx context.Context) (bool, error)
// ProtectedDirs returns repo-root-relative directories that should never be
// modified or deleted during rewind or other destructive operations.
// Examples: [".claude"] for Claude, [".gemini"] for Gemini.
ProtectedDirs() []string
// ReadTranscript reads the raw transcript bytes for a session.
ReadTranscript(sessionRef string) ([]byte, error)
// ChunkTranscript splits a transcript into chunks if it exceeds maxSize.
// Returns a slice of chunks. If the transcript fits in one chunk, returns single-element slice.
// The chunking is format-aware: JSONL splits at line boundaries, JSON splits message arrays.
ChunkTranscript(ctx context.Context, content []byte, maxSize int) ([][]byte, error)
// ReassembleTranscript combines chunks back into a single transcript.
// Handles format-specific reassembly (JSONL concatenation, JSON message merging).
ReassembleTranscript(chunks [][]byte) ([]byte, error)
// GetSessionID extracts session ID from hook input.
GetSessionID(input *HookInput) string
// GetSessionDir returns where agent stores session data for this repo.
GetSessionDir(repoPath string) (string, error)
// ResolveSessionFile returns the path to the session transcript file.
ResolveSessionFile(sessionDir, agentSessionID string) string
// ReadSession reads session data from agent's storage.
ReadSession(input *HookInput) (*AgentSession, error)
// WriteSession writes session data for resumption.
WriteSession(ctx context.Context, session *AgentSession) error
// FormatResumeCommand returns command to resume a session.
FormatResumeCommand(sessionID string) string
}
Agent defines the interface for interacting with a coding agent. Each agent implementation (Claude Code, Cursor, Aider, etc.) converts its native format to the normalized types defined in this package.
The interface is organized into three groups:
- Identity (5 methods): Name, Type, Description, DetectPresence, ProtectedDirs
- Transcript Storage (3 methods): ReadTranscript, ChunkTranscript, ReassembleTranscript
- Legacy (6 methods): Will be moved to optional interfaces or removed in a future phase
func Default ¶
func Default() Agent
Default returns the default agent. Returns nil if the default agent is not registered.
func Detect ¶
Detect attempts to auto-detect which agent is being used. Iterates registered agents in sorted name order for deterministic results. Returns the first agent whose DetectPresence reports true.
func DetectAll ¶ added in v0.4.6
DetectAll returns all agents whose DetectPresence reports true. Agents are checked in sorted name order (via List()) for deterministic results. Returns an empty slice when no agent is detected.
func GetByAgentType ¶
GetByAgentType retrieves an agent by its type identifier.
Note: This uses a linear search that instantiates agents until a match is found. This is acceptable because:
- Agent count is small (~2-20 agents)
- Agent factories are lightweight (empty struct allocation)
- Called infrequently (commit hooks, rewind, debug commands - not hot paths)
- Cost is ~400ns worst case vs milliseconds for I/O operations
Only optimize if agent count exceeds 100 or profiling shows this as a bottleneck.
type AgentSession ¶
type AgentSession struct {
SessionID string
AgentName types.AgentName
RepoPath string
SessionRef string // Path/reference to session in agent's storage
StartTime time.Time
// NativeData holds the session content in the agent's native format.
// Only the originating agent can interpret this data.
// Examples:
// - Claude Code: raw JSONL bytes
// - Cursor: serialized SQLite rows
// - Aider: Markdown content
NativeData []byte
// Computed fields - populated by the agent when reading
ModifiedFiles []string
NewFiles []string
DeletedFiles []string
// Optional normalized entries - agents may populate this if needed
// for operations that benefit from structured access
Entries []SessionEntry
}
AgentSession represents a coding session's data. Each agent stores data in its native format (JSONL, SQLite, Markdown, etc.) and only the originating agent can read/write it.
Design: Sessions are NOT interoperable between agents. A session created by Claude Code can only be read/written by Claude Code. This simplifies the implementation as we don't need format conversion.
func (*AgentSession) FindToolResultUUID ¶
func (s *AgentSession) FindToolResultUUID(toolUseID string) (string, bool)
FindToolResultUUID finds the UUID of the entry containing the tool result for the given tool use ID. Returns the UUID and true if found.
func (*AgentSession) GetLastAssistantResponse ¶
func (s *AgentSession) GetLastAssistantResponse() string
GetLastAssistantResponse returns the last assistant message content
func (*AgentSession) GetLastUserPrompt ¶
func (s *AgentSession) GetLastUserPrompt() string
GetLastUserPrompt returns the last user message content
func (*AgentSession) TruncateAtUUID ¶
func (s *AgentSession) TruncateAtUUID(uuid string) *AgentSession
TruncateAtUUID returns a new session truncated at the given UUID (inclusive)
type Event ¶ added in v0.4.6
type Event struct {
// Type is the kind of lifecycle event.
Type EventType
// SessionID identifies the agent session.
SessionID string
// PreviousSessionID is non-empty when this event represents a session continuation
// or handoff (e.g., Claude starting a new session ID after exiting plan mode).
PreviousSessionID string
// SessionRef is an agent-specific reference to the transcript (typically a file path).
SessionRef string
// Prompt is the user's prompt text (populated on TurnStart events).
Prompt string
// Timestamp is when the event occurred.
Timestamp time.Time
// ToolUseID identifies the tool invocation (for SubagentStart/SubagentEnd events).
ToolUseID string
// SubagentID identifies the subagent instance (for SubagentEnd events).
SubagentID string
// ToolInput is the raw tool input JSON (for subagent type/description extraction).
// Used when both SubagentType and TaskDescription are empty (agents that don't provide
// these fields directly parse them from ToolInput).
ToolInput json.RawMessage
// SubagentType is the kind of subagent (for SubagentStart/SubagentEnd events).
// Used with TaskDescription instead of ToolInput
SubagentType string
TaskDescription string
// ResponseMessage is an optional message to display to the user via the agent.
ResponseMessage string
// Metadata holds agent-specific state that the framework stores and makes available
// on subsequent events. Examples: Pi's activeLeafId, Cursor's is_background_agent.
Metadata map[string]string
}
Event is a normalized lifecycle event produced by an agent's ParseHookEvent method. The framework dispatcher uses these events to drive checkpoint/session lifecycle actions.
type EventType ¶ added in v0.4.6
type EventType int
EventType represents a normalized lifecycle event from any agent. Agents translate their native hooks into these event types via ParseHookEvent.
const ( // SessionStart indicates the agent session has begun. SessionStart EventType = iota + 1 // TurnStart indicates the user submitted a prompt and the agent is about to work. TurnStart // TurnEnd indicates the agent finished responding to a prompt. TurnEnd // Compaction indicates the agent is about to compress its context window. // This triggers the same save logic as TurnEnd but also resets the transcript offset. Compaction // SessionEnd indicates the session has been terminated. SessionEnd // SubagentStart indicates a subagent (task) has been spawned. SubagentStart // SubagentEnd indicates a subagent (task) has completed. SubagentEnd )
type FileWatcher ¶
type FileWatcher interface {
Agent
// GetWatchPaths returns paths to watch for session changes
GetWatchPaths() ([]string, error)
// OnFileChange handles a detected file change and returns session info
OnFileChange(path string) (*SessionChange, error)
}
FileWatcher is implemented by agents that use file-based detection. Agents like Aider that don't support hooks can use file watching to detect session activity.
type HookInput ¶
type HookInput struct {
HookType HookType
SessionID string
// SessionRef is an agent-specific session reference (file path, db key, etc.)
SessionRef string
Timestamp time.Time
// UserPrompt is the user's prompt text (from UserPromptSubmit hooks)
UserPrompt string
// Tool-specific fields (PreToolUse/PostToolUse)
ToolName string
ToolUseID string
ToolInput []byte // Raw JSON
ToolResponse []byte // Raw JSON (PostToolUse only)
// RawData preserves agent-specific data for extension
RawData map[string]interface{}
}
HookInput contains normalized data from hook callbacks
type HookResponseWriter ¶ added in v0.4.9
type HookResponseWriter interface {
Agent
// WriteHookResponse outputs a message to the user via the agent's hook response protocol.
WriteHookResponse(message string) error
}
HookResponseWriter is implemented by agents that support structured hook responses. Agents that implement this can output messages (e.g., banners) to the user via the agent's response protocol. For example, Claude Code outputs JSON with a systemMessage field to stdout. Agents that don't implement this will silently skip hook response output.
type HookSupport ¶
type HookSupport interface {
Agent
// HookNames returns the hook verbs this agent supports.
// These become subcommands under `entire hooks <agent>`.
// e.g., ["stop", "user-prompt-submit", "session-start", "session-end"]
HookNames() []string
// ParseHookEvent translates an agent-native hook into a normalized lifecycle Event.
// Returns nil if the hook has no lifecycle significance (e.g., pass-through hooks).
// This is the core contribution surface for new agent implementations.
ParseHookEvent(ctx context.Context, hookName string, stdin io.Reader) (*Event, error)
// InstallHooks installs agent-specific hooks.
// If localDev is true, hooks point to local development build.
// If force is true, removes existing Entire hooks before installing.
// Returns the number of hooks installed.
InstallHooks(ctx context.Context, localDev bool, force bool) (int, error)
// UninstallHooks removes installed hooks
UninstallHooks(ctx context.Context) error
// AreHooksInstalled checks if hooks are currently installed
AreHooksInstalled(ctx context.Context) bool
}
HookSupport is implemented by agents with lifecycle hooks. This optional interface allows agents like Claude Code and Cursor to install and manage hooks that notify Entire of agent events.
The interface is organized into two groups:
- Hook Mapping (2 methods): HookNames, ParseHookEvent
- Hook Management (3 methods): InstallHooks, UninstallHooks, AreHooksInstalled
type SessionChange ¶
type SessionChange struct {
SessionID string
SessionRef string
EventType HookType
Timestamp time.Time
}
SessionChange represents detected session activity (for FileWatcher)
type SessionEntry ¶
type SessionEntry struct {
UUID string
Type EntryType
Timestamp time.Time
Content string
// Tool-specific fields
ToolName string
ToolInput interface{}
ToolOutput interface{}
FilesAffected []string
}
SessionEntry represents a single entry in the session
type SubagentAwareExtractor ¶ added in v0.4.6
type SubagentAwareExtractor interface {
Agent
// ExtractAllModifiedFiles extracts files modified by both the main agent and any spawned subagents.
// The subagentsDir parameter specifies where subagent transcripts are stored.
// Returns a deduplicated list of all modified file paths.
ExtractAllModifiedFiles(transcriptData []byte, fromOffset int, subagentsDir string) ([]string, error)
// CalculateTotalTokenUsage computes token usage including all spawned subagents.
// The subagentsDir parameter specifies where subagent transcripts are stored.
CalculateTotalTokenUsage(transcriptData []byte, fromOffset int, subagentsDir string) (*TokenUsage, error)
}
SubagentAwareExtractor provides methods for extracting files and tokens including subagents. Agents that support spawning subagents (like Claude Code's Task tool) should implement this to ensure subagent contributions are included in checkpoints.
type TokenCalculator ¶ added in v0.4.6
type TokenCalculator interface {
Agent
// CalculateTokenUsage computes token usage from the transcript starting at the given offset.
CalculateTokenUsage(transcriptData []byte, fromOffset int) (*TokenUsage, error)
}
TokenCalculator provides token usage calculation for a session. The framework calls this during step save and checkpoint if implemented.
type TokenUsage ¶
type TokenUsage struct {
// InputTokens is the number of input tokens (fresh, not from cache)
InputTokens int `json:"input_tokens"`
// CacheCreationTokens is tokens written to cache (billable at cache write rate)
CacheCreationTokens int `json:"cache_creation_tokens"`
// CacheReadTokens is tokens read from cache (discounted rate)
CacheReadTokens int `json:"cache_read_tokens"`
// OutputTokens is the number of output tokens generated
OutputTokens int `json:"output_tokens"`
// APICallCount is the number of API calls made
APICallCount int `json:"api_call_count"`
// SubagentTokens contains token usage from spawned subagents (if any)
SubagentTokens *TokenUsage `json:"subagent_tokens,omitempty"`
}
TokenUsage represents aggregated token usage for a checkpoint. This is agent-agnostic and can be populated by any agent that tracks token usage.
func CalculateTokenUsage ¶ added in v0.4.8
func CalculateTokenUsage(ctx context.Context, ag Agent, transcriptData []byte, transcriptLinesAtStart int, subagentsDir string) *TokenUsage
CalculateTokenUsage calculates token usage from transcript data. Returns nil if the agent doesn't support token calculation or on error. Errors are debug-logged because callers treat nil token usage as "no data available".
type TranscriptAnalyzer ¶
type TranscriptAnalyzer interface {
Agent
// GetTranscriptPosition returns the current position (length) of a transcript.
// For JSONL formats (Claude Code), this is the line count.
// For JSON formats (Gemini CLI), this is the message count.
// Returns 0 if the file doesn't exist or is empty.
GetTranscriptPosition(path string) (int, error)
// ExtractModifiedFilesFromOffset extracts files modified since a given offset.
// For JSONL formats (Claude Code), offset is the starting line number.
// For JSON formats (Gemini CLI), offset is the starting message index.
// Returns:
// - files: list of file paths modified by the agent (from Write/Edit tools)
// - currentPosition: the current position (line count or message count)
// - error: any error encountered during reading
ExtractModifiedFilesFromOffset(path string, startOffset int) (files []string, currentPosition int, err error)
// ExtractPrompts extracts user prompts from the transcript starting at the given offset.
ExtractPrompts(sessionRef string, fromOffset int) ([]string, error)
// ExtractSummary extracts a summary of the session from the transcript.
ExtractSummary(sessionRef string) (string, error)
}
TranscriptAnalyzer provides format-specific transcript parsing. Agents that implement this get richer checkpoints (transcript-derived file lists, prompts, summaries). Agents that don't still participate in the checkpoint lifecycle via git-status-based file detection and raw transcript storage.
type TranscriptPreparer ¶ added in v0.4.6
type TranscriptPreparer interface {
Agent
// PrepareTranscript ensures the transcript is ready to read.
// For Claude Code, this waits for the async transcript flush to complete.
PrepareTranscript(ctx context.Context, sessionRef string) error
}
TranscriptPreparer is called before ReadTranscript to handle agent-specific flush/sync requirements (e.g., Claude Code's async transcript writing). The framework calls PrepareTranscript before ReadTranscript if implemented.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package claudecode implements the Agent interface for Claude Code.
|
Package claudecode implements the Agent interface for Claude Code. |
|
Package cursor implements the Agent interface for Cursor.
|
Package cursor implements the Agent interface for Cursor. |
|
Package factoryaidroid implements the Agent interface for Factory AI Droid.
|
Package factoryaidroid implements the Agent interface for Factory AI Droid. |
|
Package geminicli implements the Agent interface for Gemini CLI.
|
Package geminicli implements the Agent interface for Gemini CLI. |
|
Package opencode implements the Agent interface for OpenCode.
|
Package opencode implements the Agent interface for OpenCode. |
|
Package testutil provides shared test utilities for agent packages.
|
Package testutil provides shared test utilities for agent packages. |