agent

package
v0.3.12 Latest Latest
Warning

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

Go to latest
Published: Feb 8, 2026 License: MIT Imports: 8 Imported by: 0

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

View Source
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"
)

Variables

This section is empty.

Functions

func ChunkFileName

func ChunkFileName(baseName string, index int) string

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

func ChunkJSONL(content []byte, maxSize int) ([][]byte, error)

ChunkJSONL splits JSONL content at line boundaries. This is the default chunking for agents using JSONL format (like Claude Code).

func ChunkTranscript

func ChunkTranscript(content []byte, agentType AgentType) ([][]byte, error)

ChunkTranscript splits a transcript into chunks using the appropriate agent. If agentType is empty or the agent doesn't implement TranscriptChunker, falls back to JSONL (line-based) chunking.

func ParseChunkIndex

func ParseChunkIndex(filename, baseName string) int

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 ReassembleJSONL

func ReassembleJSONL(chunks [][]byte) []byte

ReassembleJSONL concatenates JSONL chunks with newlines.

func ReassembleTranscript

func ReassembleTranscript(chunks [][]byte, agentType AgentType) ([]byte, error)

ReassembleTranscript combines chunks back into a single transcript. If agentType is empty or the agent doesn't implement TranscriptChunker, falls back to JSONL (line-based) reassembly.

func Register

func Register(name AgentName, factory Factory)

Register adds an agent factory to the registry. Called from init() in each agent implementation.

func SortChunkFiles

func SortChunkFiles(files []string, baseName string) []string

SortChunkFiles sorts chunk filenames in order (base file first, then numbered chunks).

Types

type Agent

type Agent interface {
	// Name returns the agent registry key (e.g., "claude-code", "gemini")
	Name() AgentName

	// Type returns the agent type identifier (e.g., "Claude Code", "Gemini CLI")
	// This is stored in metadata and trailers.
	Type() AgentType

	// Description returns a human-readable description for UI
	Description() string

	// DetectPresence checks if this agent is configured in the repository
	DetectPresence() (bool, error)

	// GetHookConfigPath returns path to hook config file (empty if none)
	GetHookConfigPath() string

	// SupportsHooks returns true if agent supports lifecycle hooks
	SupportsHooks() bool

	// ParseHookInput parses hook callback input from stdin
	ParseHookInput(hookType HookType, reader io.Reader) (*HookInput, error)

	// GetSessionID extracts session ID from hook input
	GetSessionID(input *HookInput) string

	// TransformSessionID converts agent session ID to Entire session ID
	TransformSessionID(agentSessionID string) string

	// ExtractAgentSessionID extracts agent session ID from Entire ID
	ExtractAgentSessionID(entireSessionID string) string

	// GetSessionDir returns where agent stores session data for this repo.
	// Examples:
	//   Claude: ~/.claude/projects/<sanitized-repo-path>/
	//   Aider: current working directory (returns repoPath)
	//   Cursor: ~/Library/Application Support/Cursor/User/globalStorage/
	GetSessionDir(repoPath string) (string, error)

	// ReadSession reads session data from agent's storage.
	// Handles different formats: JSONL (Claude), SQLite (Cursor), Markdown (Aider)
	ReadSession(input *HookInput) (*AgentSession, error)

	// WriteSession writes session data for resumption.
	// Agent handles format conversion (JSONL, SQLite, etc.)
	WriteSession(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.

func Default

func Default() Agent

Default returns the default agent. Returns nil if the default agent is not registered.

func Detect

func Detect() (Agent, error)

func Get

func Get(name AgentName) (Agent, error)

func GetByAgentType

func GetByAgentType(agentType AgentType) (Agent, error)

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 AgentName

type AgentName string

AgentName is the registry key type for agents (e.g., "claude-code", "gemini").

const (
	AgentNameClaudeCode AgentName = "claude-code"
	AgentNameGemini     AgentName = "gemini"
)

Agent name constants (registry keys)

const DefaultAgentName AgentName = AgentNameClaudeCode

DefaultAgentName is the registry key for the default agent.

func List

func List() []AgentName

List returns all registered agent names in sorted order.

type AgentSession

type AgentSession struct {
	SessionID  string
	AgentName  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 AgentType

type AgentType string

AgentType is the display name type stored in metadata/trailers (e.g., "Claude Code", "Gemini CLI").

const (
	AgentTypeClaudeCode AgentType = "Claude Code"
	AgentTypeGemini     AgentType = "Gemini CLI"
	AgentTypeUnknown    AgentType = "Agent" // Fallback for backwards compatibility
)

Agent type constants (type identifiers stored in metadata/trailers)

func DetectAgentTypeFromContent

func DetectAgentTypeFromContent(content []byte) AgentType

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.

type EntryType

type EntryType string

EntryType categorizes session entries

const (
	EntryUser      EntryType = "user"
	EntryAssistant EntryType = "assistant"
	EntryTool      EntryType = "tool"
	EntrySystem    EntryType = "system"
)

type Factory

type Factory func() Agent

Factory creates a new agent instance

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 HookHandler

type HookHandler interface {
	Agent

	// GetHookNames returns the hook verbs this agent supports.
	// These are the subcommand names that will appear under `entire hooks <agent>`.
	// e.g., ["stop", "user-prompt-submit", "pre-task", "post-task", "post-todo"]
	GetHookNames() []string
}

HookHandler is implemented by agents that define their own hook vocabulary. Each agent defines its own hook names (verbs) which become subcommands under `entire hooks <agent>`. The actual handling is done by handlers registered in the CLI package to avoid circular dependencies.

This allows different agents to have completely different hook vocabularies (e.g., Claude Code has "stop", Cursor might have "completion").

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 HookSupport

type HookSupport interface {
	Agent

	// 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(localDev bool, force bool) (int, error)

	// UninstallHooks removes installed hooks
	UninstallHooks() error

	// AreHooksInstalled checks if hooks are currently installed
	AreHooksInstalled() bool

	// GetSupportedHooks returns the hook types this agent supports
	GetSupportedHooks() []HookType
}

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.

type HookType

type HookType string

HookType represents agent lifecycle events

const (
	HookSessionStart     HookType = "session_start"
	HookSessionEnd       HookType = "session_end"
	HookUserPromptSubmit HookType = "user_prompt_submit"
	HookStop             HookType = "stop"
	HookPreToolUse       HookType = "pre_tool_use"
	HookPostToolUse      HookType = "post_tool_use"
)

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 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.

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.
	// Use this to efficiently check if the transcript has grown since last checkpoint.
	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)
}

TranscriptAnalyzer is implemented by agents that support transcript analysis. This allows agent-agnostic detection of work done between checkpoints.

type TranscriptChunker

type TranscriptChunker interface {
	Agent

	// 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(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)
}

TranscriptChunker is implemented by agents that support transcript chunking. This allows agents to split large transcripts into chunks for storage (GitHub has a 100MB blob limit) and reassemble them when reading.

Directories

Path Synopsis
Package claudecode implements the Agent interface for Claude Code.
Package claudecode implements the Agent interface for Claude Code.
Package geminicli implements the Agent interface for Gemini CLI.
Package geminicli implements the Agent interface for Gemini CLI.

Jump to

Keyboard shortcuts

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