Documentation
¶
Overview ¶
Package trajectory implements the append-only session ledger (M9–M12).
Owns: event catalog, in-memory store, async JSONL persist, live Hub fan-out, export/list/search, import append, policy replay, log fork. Must not: hook wire decode (hookedge), config compile (config), or own route matching. ReplayPolicy accepts an injected dispatch.Invoker for offline policy dry-run only (does not compile routes or match on the live hot path).
Invariants:
- No disk I/O on the sync Invoke path; enqueue only.
- Contiguous seq per session; events immutable after append.
- Opt-in via config.Trajectory.Enabled (default off).
- schema_version frozen at SchemaVersion for v0.0.2 contract.
Entry: Recorder.Record, Hub.Publish, ListSessions, Export, ExportToFile, Search, AppendImported, ReplayPolicy, ReplayPolicyFromConfig, ForkSession, ResolveSessionKey, ResolveSessionKeyID, EventFromSessionEvent, EventToSessionEvent. Import orchestration and L2 importer status: importer.ImportSession, importer.ProviderImporterStatus. See DESIGN.md §1.5 (async_side), §14.
Index ¶
- Constants
- Variables
- func AppendEvents(root string, key SessionKey, events []Event) error
- func AppendImported(root string, key SessionKey, events []Event) error
- func CanonicalProvider(name string) string
- func DefaultImportConfig() config.TrajectoryConfig
- func DefaultSessionsDir() string
- func EventToSessionEvent(ev Event) *agentdv1.SessionEvent
- func Export(w io.Writer, root, providerFilter, sessionID string) error
- func ExportToFile(outPath, root, providerFilter, sessionID string) error
- func FindSessionPath(root, provider, sessionID string) (string, error)
- func ImportSidecarPath(sessionsRoot, provider, sessionID string) string
- func InvocationModeString(m agentdv1.InvocationMode) string
- func PrepareRaw(raw []byte, cfg config.TrajectoryConfig) json.RawMessage
- func PrepareTranscriptText(text string, cfg config.TrajectoryConfig) string
- func SaveImportCheckpoint(sidecarPath string, cp ImportCheckpoint) error
- func SessionFileName(sessionID string) string
- func SessionFilePath(root string, key SessionKey) string
- type AsyncDispatchedData
- type AsyncDroppedData
- type Event
- type ForkResult
- type HookDecidedData
- type HookInvokedData
- type Hub
- type ImportCheckpoint
- type Persister
- type Queue
- type RecordInput
- type Recorder
- type ReplayHit
- type ReplayOptions
- type ReplayPolicyConfigOptions
- type ReplayResult
- type SearchHit
- type SearchOptions
- type SessionEndSeedData
- type SessionForkData
- type SessionKey
- type SessionOpenData
- type SessionSummary
- type Store
- type SubscribeFilter
- type TranscriptMessageData
- type TranscriptThinkingData
Constants ¶
const ( TypeSessionOpen = "session/open" TypeHookInvoked = "hook/invoked" TypeHookDecided = "hook/decided" TypeAsyncDispatched = "async/dispatched" TypeAsyncDropped = "async/dropped" TypeTranscriptMessage = "transcript/message" TypeTranscriptThinking = "transcript/thinking" TypeSessionFork = "session/fork" TypeSessionEndSeed = "session/end-seed" )
const ( SourceSystem = "system" SourceHook = "hook" SourceDecision = "decision" SourceTranscript = "transcript" )
const SchemaVersion uint32 = 1
SchemaVersion is the frozen trajectory event contract version (v0.0.2).
Variables ¶
var ( ErrSessionsDirUnavailable = errors.New("sessions dir unavailable") // ErrSessionNotFound means no session JSONL exists for the provider and session id. ErrSessionNotFound = errors.New("session not found") // ErrNewSessionIDRequired means fork requires a non-empty destination session id. ErrNewSessionIDRequired = errors.New("new session id required") // ErrSourceSessionEmpty means the fork source ledger has no events. ErrSourceSessionEmpty = errors.New("source session is empty") // ErrSessionAlreadyExists means the fork destination session already exists. ErrSessionAlreadyExists = errors.New("session already exists") // ErrReplayNoRaw means no hook/invoked events have stored Raw payloads. ErrReplayNoRaw = errors.New("policy replay requires stored raw payloads at record time") // ErrReplayNoEvents means the session has no hook/invoked events to replay. ErrReplayNoEvents = errors.New("no hook/invoked events to replay") // ErrReplaySeqNotFound means no hook/invoked event with Raw exists for the requested seq. ErrReplaySeqNotFound = errors.New("no hook/invoked event with raw payload for seq") // ErrNilConfigSnap means ReplayPolicy was called without a config snapshot. ErrNilConfigSnap = errors.New("nil config snapshot") // ErrNilEngine means ReplayPolicy was called without a dispatch engine. ErrNilEngine = errors.New("nil dispatch engine") )
Functions ¶
func AppendEvents ¶
func AppendEvents(root string, key SessionKey, events []Event) error
AppendEvents writes events to the session JSONL file immediately (offline import/fork).
func AppendImported ¶
func AppendImported(root string, key SessionKey, events []Event) error
AppendImported assigns contiguous seq after existing ledger events and persists.
func CanonicalProvider ¶
CanonicalProvider normalizes CLI/provider ids for ledger keys and path filters.
func DefaultImportConfig ¶
func DefaultImportConfig() config.TrajectoryConfig
DefaultImportConfig returns conservative defaults for offline import.
func DefaultSessionsDir ¶
func DefaultSessionsDir() string
DefaultSessionsDir returns the ledger root ($XDG_STATE_HOME/agentd/sessions, else ~/.local/state/agentd/sessions when XDG_STATE_HOME is unset).
func EventToSessionEvent ¶
func EventToSessionEvent(ev Event) *agentdv1.SessionEvent
EventToSessionEvent maps a ledger Event to agentd.v1.SessionEvent.
func Export ¶
Export writes session JSONL to w. When sessionID is empty, concatenates all sessions for providerFilter (or all providers when filter is empty).
func ExportToFile ¶
ExportToFile writes export output to outPath.
func FindSessionPath ¶
FindSessionPath resolves provider + session id to a JSONL path under root.
func ImportSidecarPath ¶
ImportSidecarPath returns the import checkpoint path beside a session ledger.
func InvocationModeString ¶
func InvocationModeString(m agentdv1.InvocationMode) string
InvocationModeString maps proto enum to ledger strings.
func PrepareRaw ¶
func PrepareRaw(raw []byte, cfg config.TrajectoryConfig) json.RawMessage
PrepareRaw returns raw payload bytes for storage per trajectory config.
func PrepareTranscriptText ¶
func PrepareTranscriptText(text string, cfg config.TrajectoryConfig) string
PrepareTranscriptText applies redaction and max size to imported transcript text.
func SaveImportCheckpoint ¶
func SaveImportCheckpoint(sidecarPath string, cp ImportCheckpoint) error
SaveImportCheckpoint writes the sidecar atomically.
func SessionFileName ¶
SessionFileName returns the on-disk basename for a session id.
func SessionFilePath ¶
func SessionFilePath(root string, key SessionKey) string
SessionFilePath returns the JSONL path for one session key.
Types ¶
type AsyncDispatchedData ¶
type AsyncDispatchedData struct {
Count uint32 `json:"count"`
}
AsyncDispatchedData is the payload for async/dispatched.
type AsyncDroppedData ¶
type AsyncDroppedData struct {
Reason string `json:"reason"`
}
AsyncDroppedData is the payload for async/dropped (trajectory queue overflow).
type Event ¶
type Event struct {
SchemaVersion uint32 `json:"schema_version,omitempty"`
Seq uint64 `json:"seq"`
Type string `json:"type"`
Source string `json:"source"`
TS time.Time `json:"ts"`
Provider string `json:"provider"`
InvocationMode string `json:"invocation_mode,omitempty"`
SessionID string `json:"session_id"`
ProjectRoot string `json:"project_root,omitempty"`
CWD string `json:"cwd,omitempty"`
Data json.RawMessage `json:"data,omitempty"`
Raw json.RawMessage `json:"raw,omitempty"`
Ignorable bool `json:"ignorable,omitempty"`
}
Event is one append-only ledger record (DESIGN §14.2).
func EventFromSessionEvent ¶
func EventFromSessionEvent(ev *agentdv1.SessionEvent) Event
EventFromSessionEvent maps agentd.v1.SessionEvent to a ledger Event.
func ReadEvents ¶
ReadEvents loads all events from a session JSONL file.
type ForkResult ¶
type ForkResult struct {
Provider string `json:"provider"`
ParentSession string `json:"parent_session"`
NewSessionID string `json:"new_session_id"`
BoundarySeq uint64 `json:"boundary_seq"`
Copied int `json:"copied"`
Path string `json:"path"`
}
ForkResult summarizes a successful log fork.
func ForkSession ¶
func ForkSession(root string, src SessionKey, newSessionID string, atSeq uint64) (ForkResult, error)
ForkSession copies events with seq <= atSeq (or all if atSeq == 0) into a new session ledger and appends session/fork + session/end-seed metadata. The source JSONL is never modified.
type HookDecidedData ¶
type HookDecidedData struct {
Kind string `json:"kind"`
Decision string `json:"decision"`
Reason string `json:"reason,omitempty"`
ConfigGeneration uint64 `json:"config_generation"`
ConfigFingerprint string `json:"config_fingerprint"`
AsyncDispatchedCount uint32 `json:"async_dispatched_count,omitempty"`
}
HookDecidedData is the payload for hook/decided.
type HookInvokedData ¶
type HookInvokedData struct {
Kind string `json:"kind"`
ToolName string `json:"tool_name,omitempty"`
ToolUseID string `json:"tool_use_id,omitempty"`
HasRoute bool `json:"has_route"`
}
HookInvokedData is the payload for hook/invoked.
type Hub ¶
type Hub struct {
// contains filtered or unexported fields
}
Hub fans out post-commit ledger events to live subscribers.
func (*Hub) Register ¶
func (h *Hub) Register(filter SubscribeFilter) (<-chan Event, func())
Register adds a filtered subscriber. The returned unregister func removes it.
type ImportCheckpoint ¶
type ImportCheckpoint struct {
LastLineIndex int `json:"last_line_index"`
SourcePath string `json:"source_path"`
SourceModTime time.Time `json:"source_mod_time"`
}
ImportCheckpoint tracks incremental import progress for one transcript source.
func LoadImportCheckpoint ¶
func LoadImportCheckpoint(sidecarPath string) (ImportCheckpoint, error)
LoadImportCheckpoint reads the sidecar for a session, or zero value if missing.
type Persister ¶
type Persister struct {
// contains filtered or unexported fields
}
Persister writes JSONL lines asynchronously with debounced flush.
func NewPersister ¶
NewPersister returns a persister rooted at dir (typically DefaultSessionsDir()).
func (*Persister) Schedule ¶
func (p *Persister) Schedule(key SessionKey, events []Event)
Schedule queues events for debounced disk append.
type Queue ¶
type Queue struct {
// contains filtered or unexported fields
}
Queue is a bounded worker pool for trajectory append + persist.
type RecordInput ¶
type RecordInput struct {
Provider agentdv1.Provider
InvocationMode agentdv1.InvocationMode
CWD string
ProjectRoot string
RawPayload []byte
Result dispatch.InvokeResult
Snap *config.Snapshot
}
RecordInput is one Invoke worth of trajectory data.
type Recorder ¶
type Recorder struct {
// contains filtered or unexported fields
}
Recorder enqueues trajectory events from HookService.Invoke (async side).
func NewRecorder ¶
NewRecorder wires store + persist + hub into a bounded queue.
func (*Recorder) Record ¶
func (r *Recorder) Record(in RecordInput)
Record enqueues ledger events when trajectory is enabled in snap.
type ReplayHit ¶
type ReplayHit struct {
Seq uint64 `json:"seq"`
Kind string `json:"kind,omitempty"`
StoredDecision string `json:"stored_decision,omitempty"`
ReplayDecision string `json:"replay_decision,omitempty"`
Match bool `json:"match"`
Error string `json:"error,omitempty"`
}
ReplayHit is one replayed hook/invoked event.
type ReplayOptions ¶
type ReplayOptions struct {
SessionsRoot string
Provider string
SessionID string
Seq uint64 // 0 = all hook/invoked with Raw
Snap *config.Snapshot
Engine dispatch.Invoker
}
ReplayOptions configures an offline policy dry-run against stored Raw payloads.
type ReplayPolicyConfigOptions ¶
type ReplayPolicyConfigOptions struct {
ConfigPath string
SessionsRoot string
Provider string
SessionID string
Seq uint64
}
ReplayPolicyConfigOptions configures offline policy replay with config load and engine setup.
type ReplayResult ¶
type ReplayResult struct {
Provider string `json:"provider"`
SessionID string `json:"session_id"`
Hits []ReplayHit `json:"hits"`
}
ReplayResult is the full policy replay output.
func ReplayPolicy ¶
func ReplayPolicy(ctx context.Context, opts ReplayOptions) (ReplayResult, error)
ReplayPolicy re-Invokes stored Raw through Engine (offline; no live agent).
func ReplayPolicyFromConfig ¶
func ReplayPolicyFromConfig(ctx context.Context, opts ReplayPolicyConfigOptions) (ReplayResult, error)
ReplayPolicyFromConfig loads config, builds a dispatch engine, and runs ReplayPolicy.
type SearchHit ¶
type SearchHit struct {
Provider string `json:"provider"`
SessionID string `json:"session_id"`
Seq uint64 `json:"seq"`
Type string `json:"type"`
Source string `json:"source"`
Snippet string `json:"snippet,omitempty"`
Path string `json:"path"`
}
SearchHit is one matching ledger event.
func Search ¶
func Search(opts SearchOptions) ([]SearchHit, error)
Search scans session JSONL files under root and returns matching events.
type SearchOptions ¶
type SearchOptions struct {
Root string
Provider string
SessionID string
Types []string
Source string
Query string
Limit int
}
SearchOptions filters ledger JSONL scans. Search walks every matching session file line-by-line — O(total bytes) with no index (by design; see docs/en/trajectory.md).
type SessionEndSeedData ¶
type SessionEndSeedData struct {
ParentProvider string `json:"parent_provider"`
ParentSession string `json:"parent_session"`
BoundarySeq uint64 `json:"boundary_seq"`
}
SessionEndSeedData marks the lineage boundary after a fork seed copy.
type SessionForkData ¶
type SessionForkData struct {
ParentProvider string `json:"parent_provider"`
ParentSession string `json:"parent_session"`
BoundarySeq uint64 `json:"boundary_seq"`
}
SessionForkData is the payload for session/fork (audit lineage).
type SessionKey ¶
SessionKey identifies one ledger stream.
func ResolveSessionKey ¶
func ResolveSessionKey(providerName, sessionID, projectRoot, cwd string) SessionKey
ResolveSessionKey builds a stable ledger key; empty session_id gets a weak synthetic id.
func ResolveSessionKeyID ¶
func ResolveSessionKeyID(id provider.ID, sessionID, projectRoot, cwd string) SessionKey
ResolveSessionKeyID is ResolveSessionKey with a validated provider.ID.
func (SessionKey) String ¶
func (k SessionKey) String() string
type SessionOpenData ¶
type SessionOpenData struct {
Provider string `json:"provider"`
CWD string `json:"cwd,omitempty"`
ProjectRoot string `json:"project_root,omitempty"`
}
SessionOpenData is the payload for session/open.
type SessionSummary ¶
type SessionSummary struct {
Provider string `json:"provider"`
SessionID string `json:"session_id"`
Path string `json:"path"`
Lines int `json:"lines,omitempty"`
ImporterStatus string `json:"importer_status,omitempty"`
}
SessionSummary is one on-disk session ledger.
func ListSessions ¶
func ListSessions(root, providerFilter string) ([]SessionSummary, error)
ListSessions scans root for session JSONL files, optionally filtered by provider.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store holds in-memory append-only session logs.
func (*Store) Append ¶
func (s *Store) Append(key SessionKey, events []Event) []Event
Append adds events to the session log and returns copies of appended records.
func (*Store) Events ¶
func (s *Store) Events(key SessionKey) []Event
Events returns a copy of all events for a session key.
type SubscribeFilter ¶
SubscribeFilter selects events for one subscriber.
type TranscriptMessageData ¶
type TranscriptMessageData struct {
Role string `json:"role,omitempty"`
Text string `json:"text,omitempty"`
ToolUseID string `json:"tool_use_id,omitempty"`
TranscriptLineIndex int `json:"transcript_line_index"`
}
TranscriptMessageData is the payload for transcript/message.
type TranscriptThinkingData ¶
type TranscriptThinkingData struct {
Text string `json:"text,omitempty"`
TranscriptLineIndex int `json:"transcript_line_index"`
}
TranscriptThinkingData is the payload for transcript/thinking.