Help us shape the future of Go! Take the 2026 Go Developer Survey

events

package
v0.4.5 Latest Latest
Warning

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

Go to latest
Published: Oct 16, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Event append API: validates a payload, coerces invalid UTF-8 to U+FFFD, mints a UUIDv7 record id, stamps the canonical timestamp, and appends the event record to the active session store.

EmitInteractionMode appends and immediately flushes the interaction_mode event recording the active mode at session start, on /clear, and on a /mode switch

EmitLaunchBegan appends and immediately flushes the launch_began boundary event for a launch or in-process session switch (7b1).

Package events defines the event payload schemas, their per-kind cross-field validators, and the append API that mints a record id, stamps the canonical timestamp, coerces invalid UTF-8 to U+FFFD, and appends an event to the active session store. It stays independent of internal/sessions's load path so load orchestration can compose spine validation with the payload validation here without an import cycle.

Permission-event emitter helpers: thin wrappers that build the permission event payloads and append them to the active session store. The payload validators in payloads.go own the cross-field rules; these helpers only construct the typed payload and call Append.

EmitProviderRequestFailed appends and immediately flushes the provider_request_failed event for a transport-level failure, in the same append-then-flush style as EmitLaunchBegan.

EmitProviderRequestRetry appends and immediately flushes the provider_request_retry event for one scheduled auto-retry of a transient provider-round failure, in the same append-then-flush style as EmitProviderRequestFailed.

EmitProviderRequestRetrySkipped appends and immediately flushes the provider_request_retry_skipped event for a classified retryable provider-round failure that is not scheduled for another attempt, in the same append-then-flush style as EmitProviderRequestRetry.

Child-agent lifecycle payloads and their emitters: one start/end pair per delegate call, linked by a stable invocation id, recorded in the parent's own session so a reader can see what was delegated, what it changed, and how it ended.

Tool-exec event emitter helpers plus the shared stdout/stderr truncation used for both event persistence and the model-input feed.

Index

Constants

View Source
const (
	KindLaunchBegan             = "launch_began"
	KindSessionForked           = "session_forked"
	KindPermissionPromptShown   = "permission_prompt_shown"
	KindPermissionDecision      = "permission_decision"
	KindPermissionMatched       = "permission_matched"
	KindToolExecStart           = "tool_exec_start"
	KindToolExecEnd             = "tool_exec_end"
	KindAgentInstructionsLoaded = "agent_instructions_loaded"
	KindSessionPruneSummary     = "session_prune_summary"
	KindSessionWriteFailed      = "session_write_failed"
	KindPermissionPersistFailed = "permission_persist_failed"
	KindModelTurn               = "model_turn"
	KindModelResponse           = "model_response"
	KindModelChanged            = "model_changed"
	KindModelEffortUnsupported  = "model_effort_unsupported"
	KindModelRecoveryFailed     = "model_recovery_failed"
	KindModelPersistFailed      = "model_persist_failed"
	KindModelGlobalMirrorFailed = "model_global_mirror_failed"
	KindModelCatalogRefreshed   = "model_catalog_refreshed"
	KindSlashCommand            = "slash_command"
	KindStreamInterrupted       = "stream_interrupted"
	KindProviderRequestFailed   = "provider_request_failed"
	KindInteractionMode         = "interaction_mode"
	// turn_latency and provider_round_trip are additive timing events: they
	// record how long a user-visible turn and a single provider
	// round took so a session reviewer can see where the time went. Like
	// interaction_mode they are non-breaking additions — older sessions that lack
	// them load unchanged with no schema_version bump.
	KindTurnLatency       = "turn_latency"
	KindProviderRoundTrip = "provider_round_trip"
	// provider_request_retry records one scheduled auto-retry of a transient
	// provider-round failure, appended and flushed before each
	// backoff wait. Like the timing events it is a non-breaking addition that older
	// sessions load unchanged with no schema_version bump; the terminal
	// provider_request_failed schema is deliberately left untouched, so the attempt
	// count is reconstructed from these events rather than added to that one.
	KindProviderRequestRetry = "provider_request_retry"
	// provider_request_retry_skipped records that a classified retryable
	// provider-round failure was NOT scheduled for another attempt, carrying the
	// closed skip reason (retry_disabled / max_attempts_reached / budget_exhausted).
	// It mirrors provider_request_retry's (phase, reason) taxonomy and, like it, is a
	// non-breaking addition that older sessions load unchanged with no schema_version
	// bump. This slice only defines and validates the event; the retry loop
	// that emits it is wired separately.
	KindProviderRequestRetrySkipped = "provider_request_retry_skipped"
	// suggest_followup_debug is the development-only trace of the
	// suggest_followup path. It is written only when the
	// dev-debug gate is on (CODEMELD_DEV_DEBUG / `run --dev-debug`); a normal
	// session never carries it. Like the timing events it is a non-breaking
	// addition that older sessions load unchanged with no schema_version bump, and
	// it deliberately persists only lengths/booleans/reasons — never suggestion
	// label or prompt text — so it cannot reintroduce the ephemeral UI text the
	// follow-up suggestion channel keeps out of the record.
	KindSuggestFollowupDebug      = "suggest_followup_debug"
	KindQueuedPromptDiscarded     = "queued_prompt_discarded"
	KindProviderStreamIdle        = "provider_stream_idle"
	KindStructuredReadStart       = "structured_read_start"
	KindStructuredReadEnd         = "structured_read_end"
	KindStructuredReadRefused     = "structured_read_refused"
	KindTurnTerminal              = "turn_terminal"
	KindPermissionRevokeRequested = "permission_revoke_requested"
	KindSecurityStateHalted       = "security_state_halted"
	// child_agent_start and child_agent_end are the delegate lifecycle pair, linked by one
	// invocation id the child's own tool and permission events also carry. Like the timing
	// events they are a non-breaking addition: a parent's own events omit the attribution
	// field, so older sessions load unchanged with no schema_version bump.
	KindChildAgentStart = "child_agent_start"
	KindChildAgentEnd   = "child_agent_end"

	KindExecutionCapability = "execution_capability"
)

Event-kind constants. Each EventPayload reports its kind via Kind(); these are also the kind strings written to the record.

View Source
const (
	SuggestFollowupStageRequestBuilt  = "request_built"
	SuggestFollowupStageNoCall        = "provider_round_no_call"
	SuggestFollowupStageToolCallSeen  = "tool_call_seen"
	SuggestFollowupStageIntercepted   = "intercepted"
	SuggestFollowupStageSurfaceCalled = "surface_called"
	SuggestFollowupStageGhostSet      = "ghost_set"
	SuggestFollowupStageGhostAccepted = "ghost_accepted"
	SuggestFollowupStageGhostCleared  = "ghost_cleared"
)

suggest_followup_debug stage labels. Each marks one observable point on the suggest_followup path so a developer reproducing a missing suggestion can see exactly how far it got: whether the tool was advertised, whether the model called it, where it was intercepted, and what the TUI surface did with it. turn-side stages (request_built…intercepted) come from the turn loop; the ghost stages come from the TUI event loop.

View Source
const (
	RetryPhaseToolHandoff     = "tool_handoff"
	RetryReasonAssemblyFailed = "assembly_failed"
	RetryReasonInvalidPlan    = "invalid_plan"
)
View Source
const (
	SecurityDetectionBeforeMutatingCall = "before_mutating_call"
	SecurityDetectionBeforeFinalSuccess = "before_final_success"
	SecurityDetectionStartup            = "startup"
)
View Source
const (
	ChildAgentReasonCompleted = "completed"
	ChildAgentReasonCancelled = "cancelled"
	ChildAgentReasonStepCap   = "step_cap"
	ChildAgentReasonFailed    = "failed"
)

Terminal reasons for a child agent, mirroring internal/subagent's Reason values.

View Source
const (
	ChildRollbackNotNeeded   = "not_needed"
	ChildRollbackRolledBack  = "rolled_back"
	ChildRollbackUnrecovered = "unrecovered"
)
View Source
const (
	TurnOutcomeCompleted          = "completed"
	TurnOutcomeCancelled          = "cancelled"
	TurnOutcomeProviderFailed     = "provider_failed"
	TurnOutcomeInvalidResponse    = "invalid_response"
	TurnOutcomeToolAssemblyFailed = "tool_assembly_failed"
	TurnOutcomeToolPlanInvalid    = "tool_plan_invalid"
	TurnOutcomeDispatchFailed     = "dispatch_failed"
	TurnOutcomeContinuationFailed = "continuation_failed"
	// TurnOutcomeSecurityHalted always names a real started round: a startup checkpoint
	// has none, so it records only security_state_halted and no terminal at all.
	TurnOutcomeSecurityHalted = "security_halted"
)
View Source
const (
	DispatchReasonToolDispatchFailed = "tool_dispatch_failed"
	DispatchReasonPostPlanFailed     = "post_plan_failed"
)
View Source
const RevokeSourcePermissionsUI = "permissions_ui"

RevokeSourcePermissionsUI is the only source permission_revoke_requested is ever written with: the dedicated /permissions revoke control. A revoke recorded from anywhere else would make the event's provenance unreadable, so the vocabulary stays closed to this one value.

Variables

This section is empty.

Functions

func Append

func Append(store *sessions.Store, payload EventPayload) (sessions.Record, error)

Append validates payload, mints a fresh UUIDv7 record id, stamps the canonical timestamp, and appends the event record to store. The payload's string fields are coerced to valid UTF-8 (invalid byte sequences replaced with U+FFFD) before marshalling, and always-present list fields render as “ rather than null. It returns the appended record. Append does not flush; callers trigger durability at the points via the store. An invalid payload is rejected before anything is appended.

func Build

func Build(payload EventPayload) (sessions.Record, error)

func BuildModelInputBytes

func BuildModelInputBytes(stdout, stderr []byte, cap int) (modelStdout, modelStderr []byte)

BuildModelInputBytes truncates stdout and stderr exactly as EmitToolExecEnd persists them, so the next model turn is fed the same bytes that were recorded in tool_exec_end. Because both paths go through truncateStream, the returned bytes are byte-for-byte identical to the persisted tool_exec_end stdout/stderr (after Append's UTF-8 canonicalization, which truncateStream already applies).

func BuildToolExecEndBounded

func BuildToolExecEndBounded(exec BoundedToolExec, cap int) (sessions.Record, error)

BuildToolExecEndBounded exists beside EmitToolExecEnd because that emitter derives truncated and original_bytes from the full streams it is handed, which would record a stage already cut short at capture time as complete. The retained bytes still go through truncateStream at the same cap: the capture bound is raw, so a retained prefix can end mid-rune or grow past the cap once invalid bytes become U+FFFD.

func ChildInstructionDigest

func ChildInstructionDigest(instruction string) string

ChildInstructionDigest lives beside the validator that pins the digest format so a caller hands the instruction to nothing but this function.

func EmitChildAgentEnd

func EmitChildAgentEnd(store *sessions.Store, projectRoot string, payload ChildAgentEnd) error

A terminal record that cannot be appended leaves its child_agent_start unclosed, so the one bound a well-behaved child can still exceed — a declared directory holding more distinct files than the record carries — is applied here rather than refused, exactly as EmitToolExecEnd bounds a stream it is handed. A path outside the project root is refused instead: the child ceiling should have made it impossible, so it is a confinement breach, not a size.

func EmitChildAgentStart

func EmitChildAgentStart(store *sessions.Store, projectRoot string, payload ChildAgentStart) error

EmitChildAgentStart appends child_agent_start. It refuses a declared path outside projectRoot, so a start-append failure — which keeps the child from beginning its first provider round — is also what stops a path from outside the project reaching the record.

func EmitDecision

func EmitDecision(store *sessions.Store, subjectKind, subject, pathScope, outcome, grantKind, grantPathScope, directCommandSubject, directRequestSubject string) error

EmitDecision appends a permission_decision event recording the user's response to a prompt. On a denied outcome the caller passes empty grantKind/ grantPathScope so omitempty drops them. This event is emitted before any degradation rewrite, so grantKind/grantPathScope record the user's chosen values rather than the rewritten ones. It appends only.

func EmitDecisionAttributed

func EmitDecisionAttributed(store *sessions.Store, subjectKind, subject, pathScope, outcome, grantKind, grantPathScope, directCommandSubject, directRequestSubject, childInvocationID string) error

EmitDecisionAttributed is EmitDecision with the child-agent attribution.

func EmitDecisionGrant

func EmitDecisionGrant(store *sessions.Store, subjectKind, subject, pathScope, grantKind, grantScope, grantSubject, directCommandSubject, directRequestSubject string) error

func EmitDecisionGrantAttributed

func EmitDecisionGrantAttributed(store *sessions.Store, subjectKind, subject, pathScope, grantKind, grantScope, grantSubject, directCommandSubject, directRequestSubject, childInvocationID string) error

EmitDecisionGrantAttributed is EmitDecisionGrant with the child-agent attribution.

func EmitInteractionMode

func EmitInteractionMode(ctx context.Context, store *sessions.Store, mode, source, trigger string, onWriteFailure WriteFailureHandler) error

EmitInteractionMode appends an interaction_mode event with the given mode, source, and trigger and then immediately flushes the active session, giving the session-start (launch/resume), /clear, and /mode emissions the same durability as the adjacent launch_began boundary (point (e)). mode, source, and trigger are validated by the payload's per-kind rules during Append; an invalid value is rejected before anything is appended or flushed. On an atomic-write failure (Flush returns a real WriteReason) it invokes onWriteFailure with Flush's category, reason, and error so the caller can run the write-failure protocol; cancellation and other non-write flush errors (sessions.ReasonNone) bypass the handler. In all failure cases it returns the flush (or append) error; onWriteFailure may be nil. It mirrors EmitLaunchBegan so launch/resume/clear share one persistence policy rather than each rolling its own.

func EmitLaunchBegan

func EmitLaunchBegan(ctx context.Context, store *sessions.Store, source string, onWriteFailure WriteFailureHandler) error

EmitLaunchBegan appends a launch_began event with the given source and then immediately flushes the active session, satisfying point (e)'s flush-after-this-event rule. source must be one of fresh, resume, or fork an invalid source is rejected before anything is appended or flushed. On an atomic-write failure (Flush returns a real WriteReason) it invokes onWriteFailure with Flush's category, reason, and error so the caller can run the write-failure protocol; cancellation and other non-write flush errors (sessions.ReasonNone) bypass the handler. In all failure cases it returns the flush error; onWriteFailure may be nil. It is called from five sites:

  • bootstrap step 7b1 on fresh launch -> source "fresh"
  • resume sub-sequence on cold-start --resume -> source "resume"
  • /clear in-process switch -> source "fresh"
  • in-TUI /resume switch -> source "resume"
  • fork action -> source "fork"

func EmitMatchedIdentity

func EmitMatchedIdentity(store *sessions.Store, subjectKind, subject, pathScope, grantKind, grantPathScope, outcome, outcomeSource, argumentHash, directRequestSubject string) error

func EmitMatchedIdentityAttributed

func EmitMatchedIdentityAttributed(store *sessions.Store, subjectKind, subject, pathScope, grantKind, grantPathScope, outcome, outcomeSource, argumentHash, directRequestSubject, childInvocationID string) error

EmitMatchedIdentityAttributed is EmitMatchedIdentity with the child-agent attribution.

func EmitPermissionRevokeRequested

func EmitPermissionRevokeRequested(store *sessions.Store, entryKind, subject, pathScope, hostScope, grantKind, argumentHash string) error

func EmitPersistFailed

func EmitPersistFailed(store *sessions.Store, targetPath, grantKind, subjectKind, subject, reason, errorCategory, errorDetail, degradedToPathScope string) error

EmitPersistFailed appends a permission_persist_failed event for a failed permission-grant persist. degradedToPathScope is the rewritten in-memory path scope, populated only when grantKind is "global" and empty for a folder grant. For a lock_timeout reason the caller must pass the canonical error_detail the validator pins ("lock acquisition timed out"); error_category is omitted (empty) for the reasons that do not originate in a file syscall. It appends only.

func EmitPromptShown

func EmitPromptShown(store *sessions.Store, subjectKind, subject, pathScope string, directTokens []string, directCommandSubject, directRequestSubject string) error

EmitPromptShown appends a permission_prompt_shown event. directTokens is the request's canonical direct-command token sequence (permissions.MatchRequest.SubjectTokens), recorded verbatim so a later audit replays direct matching from the tokens rather than re-tokenizing the flattened subject. It is nil for every non-direct subject kind and for a direct request that carries no canonical tokens; Append deep-copies it, so no caller alias survives in the record.

func EmitPromptShownAttributed

func EmitPromptShownAttributed(store *sessions.Store, subjectKind, subject, pathScope string, directTokens []string, directCommandSubject, directRequestSubject, childInvocationID string) error

EmitPromptShownAttributed is EmitPromptShown for a caller that knows which child agent's call this was; childInvocationID is empty for the run's own calls.

func EmitProviderRequestFailed

func EmitProviderRequestFailed(ctx context.Context, store *sessions.Store, providerName, modelID, phase, reason, errorDetail string, httpStatus *int64, partialResponseChars int64, onWriteFailure WriteFailureHandler) error

EmitProviderRequestFailed builds a ProviderRequestFailed event from the already-classified (phase, reason) tuple — produced by provider.Classify* — appends it via Append (which validates and rejects an invalid payload before the store is touched), then flushes the active session so the failure is durable at the point. httpStatus is nil except for the http phase, and partialResponseChars is non-zero only for stream_drop; the payload validator enforces both. On a real atomic-write failure (Flush returns a non-ReasonNone WriteReason) it invokes onWriteFailure with Flush's category, reason, and error so the caller can run the write-failure protocol; a cancelled context or other ReasonNone flush failure surfaces to the caller without invoking the handler, since the session_write_failed contract cannot encode ReasonNone. onWriteFailure may be nil. It returns the append or flush error, or nil on success.

func EmitProviderRequestRetry

func EmitProviderRequestRetry(ctx context.Context, store *sessions.Store, providerName, modelID, phase, reason string, attempt, delayMs, partialCharsDiscarded int64, onWriteFailure WriteFailureHandler) error

EmitProviderRequestRetry builds a ProviderRequestRetry event from the already classified (phase, reason) tuple, the 1-based failed-attempt number, the scheduled backoff delay, and the partial-content runes being discarded, appends it via Append (which validates and rejects an invalid payload before the store is touched), then flushes the active session so the retry is durable before the turn waits out the backoff — a failed attempt survives a process exit during the wait. partialCharsDiscarded is non-zero only for a stream_drop; the payload validator enforces that. On a real atomic-write failure (Flush returns a non-ReasonNone WriteReason) it invokes onWriteFailure with Flush's category, reason, and error so the caller can run the write-failure protocol; a cancelled context or other ReasonNone flush failure surfaces to the caller without invoking the handler. onWriteFailure may be nil. It returns the append or flush error, or nil on success.

func EmitProviderRequestRetrySkipped

func EmitProviderRequestRetrySkipped(ctx context.Context, store *sessions.Store, providerName, modelID, phase, reason, skipReason string, attempt, partialCharsDiscarded int64, onWriteFailure WriteFailureHandler) error

EmitProviderRequestRetrySkipped builds a ProviderRequestRetrySkipped event from the already classified (phase, reason) tuple, the 1-based failed-attempt number, the closed skip reason, and the partial-content runes discarded, appends it via Append (which validates and rejects an invalid payload before the store is touched), then flushes the active session so the skip is durable before the turn proceeds to terminal failure. partialCharsDiscarded is non-zero only for a stream_drop; the payload validator enforces that. On a real atomic-write failure (Flush returns a non-ReasonNone WriteReason) it invokes onWriteFailure with Flush's category, reason, and error so the caller can run the write-failure protocol; a cancelled context or other ReasonNone flush failure surfaces to the caller without invoking the handler. onWriteFailure may be nil. It returns the append or flush error, or nil on success.

func EmitProviderStreamIdle

func EmitProviderStreamIdle(ctx context.Context, store *sessions.Store, providerName, modelID string, roundElapsedMs, idleMs, partialChars int64, onWriteFailure WriteFailureHandler) error

func EmitSecurityStateHalt

func EmitSecurityStateHalt(ctx context.Context, store *sessions.Store, records []SecurityStateHalted, terminal *TurnTerminal, onWriteFailure WriteFailureHandler) error

EmitSecurityStateHalt makes one checkpoint's halt durable in a single store commit; a startup checkpoint has no started round to close and passes a nil terminal. A returned error means the halt was not recorded, and the caller must still stop the turn rather than treat the records as durable or present a recorded halt.

func EmitStructuredReadEnd

func EmitStructuredReadEnd(store *sessions.Store, invocationID, toolName, outcome string, durationMs int64, childInvocationID string) error

func EmitStructuredReadRefused

func EmitStructuredReadRefused(store *sessions.Store, toolName, reason string, batchSize int64, childInvocationID string) error

func EmitStructuredReadStart

func EmitStructuredReadStart(store *sessions.Store, invocationID, toolName, pathScope string, configuredConcurrency int64, childInvocationID string) error

childInvocationID attributes the record to the child agent whose call produced it; the parent's own reads pass "" and the field is omitted from the persisted payload.

func EmitToolExecEnd

func EmitToolExecEnd(store *sessions.Store, invocationID string, subjectKind, subject string, exitCode int, durationMs int64, stdout, stderr []byte, cap int, childInvocationID string) error

EmitToolExecEnd appends a tool_exec_end event for a completed invocation, paired to its start event by invocationID. Each of stdout/stderr is truncated independently to cap bytes at a UTF-8 rune boundary (see truncateStream); truncated and original_bytes are set only when at least one stream was actually shortened, and original_bytes then carries the pre-truncation raw byte size of both streams. A kept-empty stream is stored as the empty string so omitempty drops it from the persisted payload. It appends only; the caller owns the flush.

func EmitToolExecStart

func EmitToolExecStart(store *sessions.Store, invocationID, subjectKind, subject, pathScope, childInvocationID string) error

EmitToolExecStart appends a tool_exec_start event recording the start of a tool invocation. invocationID is the runner-minted UUIDv7 for this invocation and is reused on the matching tool_exec_end so the pair shares one id; subjectKind/subject describe the dispatched command and pathScope is its resolved working directory (a cleaned absolute path). It validates the payload through Append, so a non-UUIDv7 invocationID, an unrecognized subjectKind, an empty subject, or a non-absolute pathScope is rejected and nothing is appended. It appends only; the caller owns the flush.

func EmitTurnTerminal

func EmitTurnTerminal(ctx context.Context, store *sessions.Store, payload TurnTerminal, onWriteFailure WriteFailureHandler) error

func SafeFailedTool

func SafeFailedTool(name string) bool

func ValidatePayload

func ValidatePayload(kind string, raw json.RawMessage) error

ValidatePayload is the exported load-time validator: it unmarshals raw into the struct for kind and runs that kind's cross-field validation. Load orchestration combines this with the session spine validation without internal/sessions importing this package. An unknown kind is an error so a codemeld-written record stream is held to the closed set.

Types

type AgentInstructionsLoaded

type AgentInstructionsLoaded struct {
	Path          string `json:"path"`
	Layer         string `json:"layer"`
	LoadOrder     int64  `json:"load_order"`
	SkipReason    string `json:"skip_reason,omitempty"`
	ErrorCategory string `json:"error_category,omitempty"`
	ErrorDetail   string `json:"error_detail,omitempty"`
}

AgentInstructionsLoaded records one AGENTS.md file loaded or skipped.

func (AgentInstructionsLoaded) Kind

type BoundedToolExec

type BoundedToolExec struct {
	InvocationID      string
	ChildInvocationID string
	SubjectKind       string
	Subject           string
	ExitCode          int
	DurationMs        int64
	Stdout            []byte
	Stderr            []byte
	StdoutTotal       int64
	StderrTotal       int64
}

BoundedToolExec is one completed invocation whose stdout/stderr were already bounded before they reached the event layer. Stdout/Stderr are the bytes that were retained; StdoutTotal/StderrTotal are how many bytes were read from each stream in full, which is what original_bytes must report.

type ChildAgentEnd

type ChildAgentEnd struct {
	InvocationID    string   `json:"invocation_id"`
	Reason          string   `json:"reason"`
	Steps           int64    `json:"steps"`
	WrittenPaths    []string `json:"written_paths"`
	RollbackOutcome string   `json:"rollback_outcome"`
	DurationMs      int64    `json:"duration_ms"`
	ReplyTruncated  bool     `json:"reply_truncated"`
	// WrittenPathsTotal is the child's true distinct-write count, present only when the list
	// above was bounded to fit the record.
	WrittenPathsTotal int64 `json:"written_paths_total,omitempty"`

	DeletedPaths      []string `json:"deleted_paths,omitempty"`
	DeletedPathsTotal int64    `json:"deleted_paths_total,omitempty"`

	// MovedPairs needs no total: a child moves only pairs it declared, so the distinct list never
	// outgrows the declaration's own limit.
	MovedPairs []ChildMovePair `json:"moved_pairs,omitempty"`
}

ChildAgentEnd records how a child agent ended. RollbackOutcome is a third outcome alongside a clean finish and a clean undo: "unrecovered" says the child's writes are still on disk after a failed restore, so it must never be reported as "rolled_back".

func (ChildAgentEnd) Kind

func (ChildAgentEnd) Kind() string

type ChildAgentStart

type ChildAgentStart struct {
	InvocationID      string   `json:"invocation_id"`
	ProviderID        string   `json:"provider_id"`
	ModelID           string   `json:"model_id"`
	DeclaredPaths     []string `json:"declared_paths"`
	StepCap           int64    `json:"step_cap"`
	InstructionBytes  int64    `json:"instruction_bytes"`
	InstructionDigest string   `json:"instruction_digest"`

	DeclaredDeletePaths []string        `json:"declared_delete_paths,omitempty"`
	DeclaredMovePairs   []ChildMovePair `json:"declared_move_pairs,omitempty"`
}

ChildAgentStart records a child agent about to run. The instruction is reduced to its length and digest so the record stays bounded and carries no delegated text.

func (ChildAgentStart) Kind

func (ChildAgentStart) Kind() string

type ChildMovePair

type ChildMovePair struct {
	Source      string `json:"source"`
	Destination string `json:"destination"`
}

type EventPayload

type EventPayload interface {
	// Kind returns the kind string for this payload.
	Kind() string
	// contains filtered or unexported methods
}

EventPayload is the closed set of event payloads. The unexported validate keeps the set closed to this package (mirroring sessions.Record) and is exercised externally only through Append and ValidatePayload.

type ExecutionCapability

type ExecutionCapability struct {
	Capability string                   `json:"capability"`
	Reason     string                   `json:"reason,omitempty"`
	Legs       []ExecutionCapabilityLeg `json:"legs,omitempty"`
}

func (ExecutionCapability) Kind

func (ExecutionCapability) Kind() string

type ExecutionCapabilityLeg

type ExecutionCapabilityLeg struct {
	Name     string `json:"name"`
	Observed string `json:"observed"`
}

type InteractionMode

type InteractionMode struct {
	Mode    string `json:"mode"`
	Source  string `json:"source"`
	Trigger string `json:"trigger"`
}

InteractionMode records the active interaction mode at session start and on every mode switch, mirroring model_turn. It is an additive event kind — non-breaking schema-version semantics.

func (InteractionMode) Kind

func (InteractionMode) Kind() string

type LaunchBegan

type LaunchBegan struct {
	Source string `json:"source"` // fresh | resume | fork
}

LaunchBegan marks the boundary of a launch or in-process session switch.

func (LaunchBegan) Kind

func (LaunchBegan) Kind() string

type ModelCatalogRefreshed

type ModelCatalogRefreshed struct {
	Provider string `json:"provider"`
	Outcome  string `json:"outcome"`
}

func (ModelCatalogRefreshed) Kind

type ModelChanged

type ModelChanged struct {
	NewProvider   string `json:"new_provider"`
	NewModelID    string `json:"new_model_id"`
	NewEffort     string `json:"new_effort"`
	PriorProvider string `json:"prior_provider,omitempty"`
	PriorModelID  string `json:"prior_model_id,omitempty"`
	PriorEffort   string `json:"prior_effort,omitempty"`
}

ModelChanged records a TUI model-picker selection.

func (ModelChanged) Kind

func (ModelChanged) Kind() string

type ModelEffortUnsupported

type ModelEffortUnsupported struct {
	Provider         string `json:"provider"`
	ModelID          string `json:"model_id"`
	ConfiguredEffort string `json:"configured_effort"`
}

ModelEffortUnsupported records that the configured effort was dropped.

func (ModelEffortUnsupported) Kind

type ModelGlobalMirrorFailed

type ModelGlobalMirrorFailed struct {
	TargetPath    string `json:"target_path"`
	Provider      string `json:"provider"`
	ModelID       string `json:"model_id"`
	Effort        string `json:"effort"`
	Reason        string `json:"reason"`
	ErrorCategory string `json:"error_category,omitempty"`
	ErrorDetail   string `json:"error_detail"`
}

ModelGlobalMirrorFailed records a failed global mirror write (step 2).

func (ModelGlobalMirrorFailed) Kind

type ModelPersistFailed

type ModelPersistFailed struct {
	TargetPath    string `json:"target_path"`
	Provider      string `json:"provider"`
	ModelID       string `json:"model_id"`
	Effort        string `json:"effort"`
	Reason        string `json:"reason"`
	ErrorCategory string `json:"error_category,omitempty"`
	ErrorDetail   string `json:"error_detail"`
}

ModelPersistFailed records a failed per-project model write (step 1).

func (ModelPersistFailed) Kind

func (ModelPersistFailed) Kind() string

type ModelRecoveryFailed

type ModelRecoveryFailed struct {
	Reason      string `json:"reason"`
	ErrorDetail string `json:"error_detail"`
}

ModelRecoveryFailed records a failed model recovery on resume (step 1).

func (ModelRecoveryFailed) Kind

func (ModelRecoveryFailed) Kind() string

type ModelResponse

type ModelResponse struct {
	Provider     string `json:"provider"`
	ModelID      string `json:"model_id"`
	InputTokens  int64  `json:"input_tokens"`
	OutputTokens int64  `json:"output_tokens"`
	// nil is not reported; a non-nil 0 is a reported zero. Without omitempty,
	// requireShape would reject every record written before this field existed.
	CachedInputTokens *int64 `json:"cached_input_tokens,omitempty"`
	// Written only as true, marking the present CachedInputTokens a known lower bound.
	CachedInputTokensPartial bool     `json:"cached_input_tokens_partial,omitempty"`
	Anomaly                  []string `json:"anomaly"`
	UpstreamProvider         string   `json:"upstream_provider,omitempty"`
}

ModelResponse records an assistant turn's token usage.

func (ModelResponse) Kind

func (ModelResponse) Kind() string

type ModelTurn

type ModelTurn struct {
	Provider string `json:"provider"`
	ModelID  string `json:"model_id"`
	Effort   string `json:"effort"`
	Source   string `json:"source"`
}

ModelTurn records the active model/effort at the start of an assistant turn. Effort validation is intentionally independent of provider catalog support.

func (ModelTurn) Kind

func (ModelTurn) Kind() string

type OriginalBytes

type OriginalBytes struct {
	Stdout int64 `json:"stdout"`
	Stderr int64 `json:"stderr"`
}

OriginalBytes carries the pre-truncation byte sizes of a captured stream pair.

func TruncateToolOutput

func TruncateToolOutput(stdout, stderr []byte, cap int) (keptOut, keptErr []byte, truncated bool, original *OriginalBytes)

TruncateToolOutput caps a stdout/stderr pair for a tool_exec_end payload: each stream is independently truncated to max(cap, 0) bytes at a UTF-8 rune boundary (see truncateStream). truncated is true iff at least one stream was actually shortened, and original is non-nil exactly when truncated is true, carrying the pre-truncation raw byte size of both streams (so a non-truncated pair leaves ToolExecEnd.OriginalBytes nil, matching the absence rule). Production emitters — EmitToolExecEnd and the VCS invocation path — share this helper so their persisted bytes stay byte-for-byte identical to what BuildModelInputBytes feeds the model.

type PermissionDecision

type PermissionDecision struct {
	SubjectKind    string `json:"subject_kind"`
	Subject        string `json:"subject"`
	PathScope      string `json:"path_scope,omitempty"`
	Outcome        string `json:"outcome"`
	GrantKind      string `json:"grant_kind,omitempty"`
	GrantPathScope string `json:"grant_path_scope,omitempty"`
	HostScope      string `json:"host_scope,omitempty"`
	GrantHostScope string `json:"grant_host_scope,omitempty"`
	// An absent GrantSubject is a legacy record, not an equal subject.
	GrantSubject string `json:"grant_subject,omitempty"`
	// Stored verbatim; see PermissionPromptShown for the ownership rule.
	DirectCommandSubject string `json:"direct_command_subject,omitempty"`
	DirectRequestSubject string `json:"direct_request_subject,omitempty"`
	// See ToolExecStart.ChildInvocationID.
	ChildInvocationID string `json:"child_invocation_id,omitempty"`
}

PermissionDecision records the user's response to a permission prompt.

func (PermissionDecision) Kind

func (PermissionDecision) Kind() string

type PermissionMatched

type PermissionMatched struct {
	SubjectKind    string `json:"subject_kind"`
	Subject        string `json:"subject"`
	PathScope      string `json:"path_scope,omitempty"`
	GrantKind      string `json:"grant_kind,omitempty"`
	GrantPathScope string `json:"grant_path_scope,omitempty"`
	Outcome        string `json:"outcome"`
	OutcomeSource  string `json:"outcome_source"`
	ArgumentHash   string `json:"argument_hash,omitempty"`
	HostScope      string `json:"host_scope,omitempty"`
	GrantHostScope string `json:"grant_host_scope,omitempty"`
	// Stored verbatim; see PermissionPromptShown for the ownership rule.
	DirectRequestSubject string `json:"direct_request_subject,omitempty"`
	// See ToolExecStart.ChildInvocationID.
	ChildInvocationID  string `json:"child_invocation_id,omitempty"`
	BlockedEntry       string `json:"blocked_entry,omitempty"`
	BlockedEntrySource string `json:"blocked_entry_source,omitempty"`
}

PermissionMatched records an existing grant or in-turn cache hit that authorized a command without prompting.

func (PermissionMatched) Kind

func (PermissionMatched) Kind() string

type PermissionPersistFailed

type PermissionPersistFailed struct {
	TargetPath          string `json:"target_path"`
	GrantKind           string `json:"grant_kind"`
	SubjectKind         string `json:"subject_kind"`
	Subject             string `json:"subject"`
	Reason              string `json:"reason"`
	ErrorCategory       string `json:"error_category,omitempty"`
	ErrorDetail         string `json:"error_detail"`
	DegradedToPathScope string `json:"degraded_to_path_scope,omitempty"`
	HostScope           string `json:"host_scope,omitempty"`
}

PermissionPersistFailed records a failed permission-grant persist.

func (PermissionPersistFailed) Kind

type PermissionPromptShown

type PermissionPromptShown struct {
	SubjectKind string `json:"subject_kind"`
	Subject     string `json:"subject"`
	PathScope   string `json:"path_scope,omitempty"`
	HostScope   string `json:"host_scope,omitempty"`
	// DirectTokens is the canonical direct-command token sequence the matcher
	// was given (permissions.MatchRequest.SubjectTokens). It is a pointer so a
	// legacy record's absence (unknown tokens) stays distinct from a present
	// empty array, which canonicalize would otherwise manufacture from a nil
	// slice. Absence must never be reconstructed by re-tokenizing Subject.
	DirectTokens *[]string `json:"direct_tokens,omitempty"`
	// DirectCommandSubject and DirectRequestSubject are the permission checker's
	// boundary-preserving direct identities, stored verbatim. This package never
	// decodes or cross-validates them against Subject or DirectTokens; only
	// internal/permissions parses them.
	DirectCommandSubject string `json:"direct_command_subject,omitempty"`
	DirectRequestSubject string `json:"direct_request_subject,omitempty"`
	// See ToolExecStart.ChildInvocationID.
	ChildInvocationID string `json:"child_invocation_id,omitempty"`
}

PermissionPromptShown records that a permission prompt was displayed.

func (PermissionPromptShown) Kind

type PermissionRevokeRequested

type PermissionRevokeRequested struct {
	EntryKind    string `json:"kind"`
	Subject      string `json:"subject"`
	PathScope    string `json:"path_scope,omitempty"`
	HostScope    string `json:"host_scope,omitempty"`
	GrantKind    string `json:"grant_kind"`
	ArgumentHash string `json:"argument_hash,omitempty"`
	Source       string `json:"source"`
}

PermissionRevokeRequested records that the /permissions revoke control asked for one stored grant to be removed. Its fields are the removed grant's own identity — permissions.DedupKey (EntryKind/Subject/PathScope/HostScope/GrantKind) plus the literal ArgumentHash — never the subject of the request that once triggered the grant, so a reader can match the record to the exact stored entry.

The record is not proof that this process removed a settings entry: the writable target is inspected before the delete takes its lock, so a persisted revoke means /permissions observed the grant and requested an idempotent delete that reported success.

func (PermissionRevokeRequested) Kind

type ProviderRequestFailed

type ProviderRequestFailed struct {
	Provider             string `json:"provider"`
	ModelID              string `json:"model_id"`
	Phase                string `json:"phase"`
	HTTPStatus           *int64 `json:"http_status,omitempty"`
	PartialResponseChars int64  `json:"partial_response_chars"`
	Reason               string `json:"reason"`
	ErrorDetail          string `json:"error_detail"`
}

ProviderRequestFailed records a provider request failure before a usable response.

func (ProviderRequestFailed) Kind

type ProviderRequestRetry

type ProviderRequestRetry struct {
	Provider              string `json:"provider"`
	ModelID               string `json:"model_id"`
	Attempt               int64  `json:"attempt"`
	Phase                 string `json:"phase"`
	Reason                string `json:"reason"`
	DelayMs               int64  `json:"delay_ms"`
	PartialCharsDiscarded int64  `json:"partial_chars_discarded"`
}

ProviderRequestRetry records one scheduled auto-retry of a transient provider-round failure. It is appended and flushed before the backoff wait, so a failed attempt is durable even if the process exits during the wait. attempt is the 1-based number of the attempt that just failed and is being retried; phase/reason carry the classified failure; delay_ms is the backoff (or honored Retry-After) wait in milliseconds; partial_chars_discarded is the assistant-content runes rolled back before re-issue and is meaningful only for a stream_drop (0 otherwise). The terminal provider_request_failed is unchanged, so the attempt count is reconstructed from these events.

func (ProviderRequestRetry) Kind

type ProviderRequestRetrySkipped

type ProviderRequestRetrySkipped struct {
	Provider              string `json:"provider"`
	ModelID               string `json:"model_id"`
	Attempt               int64  `json:"attempt"`
	Phase                 string `json:"phase"`
	Reason                string `json:"reason"`
	SkipReason            string `json:"skip_reason"`
	PartialCharsDiscarded int64  `json:"partial_chars_discarded"`
}

ProviderRequestRetrySkipped records that a classified retryable provider-round failure was not scheduled for another attempt. attempt is the 1-based number of the attempt that just failed; phase/reason reuse the provider_request_failed taxonomy so a skip only ever records a (phase, reason) the classifier can produce; skip_reason is the closed set explaining why no retry followed; partial_chars_discarded is the assistant-content runes rolled back and is meaningful only for a stream_drop (0 otherwise). HTTP retryability stays emitter-enforced (via provider.Retryable in the retry loop), so there is no http_status field — mirroring provider_request_retry.

func (ProviderRequestRetrySkipped) Kind

type ProviderRoundTrip

type ProviderRoundTrip struct {
	Provider         string `json:"provider"`
	ModelID          string `json:"model_id"`
	DurationMs       int64  `json:"duration_ms"`
	TurnID           string `json:"turn_id,omitempty"`
	RoundID          string `json:"round_id,omitempty"`
	UpstreamProvider string `json:"upstream_provider,omitempty"`
}

ProviderRoundTrip records the wall-clock latency of one provider round: the time around a single model call, from just before the request to the round's terminal outcome — a final answer, a tool-call handoff, an empty-response classification, a stream drop, or a pre-stream failure. An agentic turn records one per provider round. It is recorded even when the round fails, so a slow failure is visible alongside the paired provider_request_failed event. duration_ms is int64 milliseconds, matching the existing duration convention.

func (ProviderRoundTrip) Kind

func (ProviderRoundTrip) Kind() string

type ProviderStreamIdle

type ProviderStreamIdle struct {
	Provider       string `json:"provider"`
	ModelID        string `json:"model_id"`
	RoundElapsedMs int64  `json:"round_elapsed_ms"`
	IdleMs         int64  `json:"idle_ms"`
	PartialChars   int64  `json:"partial_chars"`
}

func (ProviderStreamIdle) Kind

func (ProviderStreamIdle) Kind() string

type PruneFailure

type PruneFailure struct {
	Path   string `json:"path"`
	Reason string `json:"reason"`
	Kind   string `json:"kind"` // file_delete | dir_enumerate
}

PruneFailure is one entry of session_prune_summary.failures.

type QueuedPromptDiscarded

type QueuedPromptDiscarded struct {
	Text       string `json:"text"`
	ImageCount int64  `json:"image_count"`
	Reason     string `json:"reason"`
}

func (QueuedPromptDiscarded) Kind

type SecurityStateHalted

type SecurityStateHalted struct {
	Path              string   `json:"path"`
	DetectionPoint    string   `json:"detection_point"`
	Sections          []string `json:"sections,omitempty"`
	Labels            []string `json:"labels,omitempty"`
	UnavailableReason string   `json:"unavailable_reason,omitempty"`
}

SecurityStateHalted records one protected settings layer whose checkpoint stopped the launch or the turn.

func (SecurityStateHalted) Kind

func (SecurityStateHalted) Kind() string

type SessionForked

type SessionForked struct {
	ToSessionID string `json:"to_session_id"`
}

SessionForked is appended to the original session as its final event on fork.

func (SessionForked) Kind

func (SessionForked) Kind() string

type SessionPruneSummary

type SessionPruneSummary struct {
	FilesScanned int64          `json:"files_scanned"`
	FilesDeleted int64          `json:"files_deleted"`
	FilesFailed  int64          `json:"files_failed"`
	Failures     []PruneFailure `json:"failures"`
	ElapsedMs    int64          `json:"elapsed_ms"`
}

SessionPruneSummary records the outcome of a retention prune pass.

func (SessionPruneSummary) Kind

func (SessionPruneSummary) Kind() string

type SessionWriteFailed

type SessionWriteFailed struct {
	TargetPath    string `json:"target_path"`
	Reason        string `json:"reason"`
	ErrorCategory string `json:"error_category"`
	ErrorDetail   string `json:"error_detail"`
}

SessionWriteFailed records a failed atomic session-file write.

func (SessionWriteFailed) Kind

func (SessionWriteFailed) Kind() string

type SlashCommand

type SlashCommand struct {
	Name        string   `json:"name"`
	Args        []string `json:"args"`
	Outcome     string   `json:"outcome"`
	ErrorDetail string   `json:"error_detail,omitempty"`
}

SlashCommand records execution of a recognized slash command.

func (SlashCommand) Kind

func (SlashCommand) Kind() string

type StreamInterrupted

type StreamInterrupted struct {
	Source       string `json:"source"`
	ContentChars int64  `json:"content_chars"`
}

StreamInterrupted records a user-initiated interruption of a model response.

func (StreamInterrupted) Kind

func (StreamInterrupted) Kind() string

type StructuredReadEnd

type StructuredReadEnd struct {
	ToolName     string `json:"tool_name"`
	InvocationID string `json:"invocation_id"`
	Outcome      string `json:"outcome"`
	DurationMs   int64  `json:"duration_ms"`
	// See StructuredReadStart.ChildInvocationID.
	ChildInvocationID string `json:"child_invocation_id,omitempty"`
}

func (StructuredReadEnd) Kind

func (StructuredReadEnd) Kind() string

type StructuredReadRefused

type StructuredReadRefused struct {
	ToolName  string `json:"tool_name"`
	Reason    string `json:"reason"`
	BatchSize int64  `json:"batch_size"`
	// See StructuredReadStart.ChildInvocationID. A refusal is child activity too: without it a
	// child's rejected read is indistinguishable from the parent's.
	ChildInvocationID string `json:"child_invocation_id,omitempty"`
}

func (StructuredReadRefused) Kind

type StructuredReadStart

type StructuredReadStart struct {
	ToolName              string `json:"tool_name"`
	PathScope             string `json:"path_scope"`
	InvocationID          string `json:"invocation_id"`
	ConfiguredConcurrency int64  `json:"configured_concurrency"`
	// See ToolExecStart.ChildInvocationID. A child agent's read tools run through the
	// structured-read path rather than tool_exec_*, so this is where their attribution lives.
	ChildInvocationID string `json:"child_invocation_id,omitempty"`
}

func (StructuredReadStart) Kind

func (StructuredReadStart) Kind() string

type SuggestFollowupDebug

type SuggestFollowupDebug struct {
	Stage      string `json:"stage"`
	Provider   string `json:"provider"`
	ModelID    string `json:"model_id"`
	Mode       string `json:"mode"`
	Advertised *bool  `json:"advertised,omitempty"`
	LabelLen   int64  `json:"label_len,omitempty"`
	PromptLen  int64  `json:"prompt_len,omitempty"`
	Reason     string `json:"reason,omitempty"`
}

SuggestFollowupDebug records one ordered stage of the development-only suggest_followup trace. provider/model_id/mode identify the turn that produced it. The remaining fields are stage-specific and deliberately privacy-safe: advertised is set only on request_built (was the tool in the advertised set), label_len/prompt_len carry rune lengths — never the suggestion text — on the stages that handle a concrete suggestion, and reason explains a discard on ghost_cleared. Persisting lengths and booleans rather than the label/prompt keeps the follow-up suggestion's ephemeral UI text out of the record.

func (SuggestFollowupDebug) Kind

type ToolExecEnd

type ToolExecEnd struct {
	SubjectKind   string         `json:"subject_kind"`
	Subject       string         `json:"subject"`
	ExitCode      int64          `json:"exit_code"`
	DurationMs    int64          `json:"duration_ms"`
	Stdout        string         `json:"stdout,omitempty"`
	Stderr        string         `json:"stderr,omitempty"`
	Truncated     bool           `json:"truncated"`
	OriginalBytes *OriginalBytes `json:"original_bytes,omitempty"`
	InvocationID  string         `json:"invocation_id"`
	// See ToolExecStart.ChildInvocationID.
	ChildInvocationID string `json:"child_invocation_id,omitempty"`
}

ToolExecEnd records the completion of a tool invocation.

func ToolExecEndFromBounded

func ToolExecEndFromBounded(exec BoundedToolExec, cap int) ToolExecEnd

func (ToolExecEnd) Kind

func (ToolExecEnd) Kind() string

type ToolExecStart

type ToolExecStart struct {
	SubjectKind  string `json:"subject_kind"`
	Subject      string `json:"subject"`
	PathScope    string `json:"path_scope"`
	InvocationID string `json:"invocation_id"`
	// ChildInvocationID names the child_agent_start this call belongs to; a parent's own
	// call omits it. Attribution must be read from here rather than inferred from a child's
	// start/end interval, because disjoint children may run concurrently.
	ChildInvocationID string `json:"child_invocation_id,omitempty"`
}

ToolExecStart records the start of a tool invocation.

func (ToolExecStart) Kind

func (ToolExecStart) Kind() string

type TurnLatency

type TurnLatency struct {
	Provider   string `json:"provider"`
	ModelID    string `json:"model_id"`
	DurationMs int64  `json:"duration_ms"`
}

TurnLatency records the end-to-end wall-clock latency of one completed, user-visible turn: the elapsed time from the point the user's input was accepted to the point the final answer was committed. One is recorded per completed agentic turn, on the final no-tool answer — failed or interrupted turns record none. duration_ms is int64 milliseconds, matching the tool_exec_end.duration_ms / session_prune_summary.elapsed_ms convention.

func (TurnLatency) Kind

func (TurnLatency) Kind() string

type TurnTerminal

type TurnTerminal struct {
	TurnID          string `json:"turn_id"`
	RoundID         string `json:"round_id"`
	Outcome         string `json:"outcome"`
	FailurePhase    string `json:"failure_phase,omitempty"`
	FailedTool      string `json:"failed_tool,omitempty"`
	FailureReason   string `json:"failure_reason,omitempty"`
	DispatchStarted bool   `json:"dispatch_started"`
}

func (TurnTerminal) Kind

func (TurnTerminal) Kind() string

type WriteFailureHandler

type WriteFailureHandler func(cat fserrors.Category, reason sessions.WriteReason, err error)

WriteFailureHandler receives the outcome of the post-append flush when it fails (point e). It carries exactly the values sessions.Store.Flush returns, so a caller can drive the session write-failure protocol (sessionfailures.HandleWriteFailure) without internal/events importing that failure code and coupling the event package to session failure policy. Keeping the callback minimal — only Flush's return tuple — lets the final write-failure API change without touching this helper.

Jump to

Keyboard shortcuts

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