/** Which agent produced or receives a session. Adding an agent adds one value (FR-57). */ export type AgentId = "pi" | "codex" | "claude-code"; /** Absolute path of an agent profile directory, for example "/Users/me/.claude-team" (FR-2). */ export type HomePath = string; /** The agent's own identifier for a session. Unique inside one home. */ export type SessionId = string; /** A session is identified by three values (FR-1). */ export interface SessionRef { agent: AgentId; home: HomePath; id: SessionId; } /** One row of the selection list (FR-11). */ export interface SessionDescriptor { ref: SessionRef; /** Short human title. Derived from the first user message when the format has none. */ title: string; /** ISO-8601 UTC. */ startedAt: string; /** ISO-8601 UTC. Sort key of the listing, newest first (FR-14). */ updatedAt: string; /** Turns the source holds, before any rule of section D or E runs. */ turnCount: number; /** Absolute path of the repository the session ran in, or null when unknown (FR-13). */ repoPath: string | null; /** Absolute path of the source file. Lets the user select by path (FR-12). */ filePath: string; } /** Who produced a turn. */ export type TurnRole = "user" | "agent"; /** Why the turn exists. Decides pinning and drop order (FR-22, FR-31, FR-32). */ export type TurnKind = "message" | "summary" | "tool-call"; /** Did the call change the repository? (FR-26) */ export type ToolEffect = "read-only" | "mutating" | "unknown"; /** * A tool call that crossed over (FR-23). * There is deliberately no field for the result body: FR-24 and FR-60 are * enforced by this shape, not by adapter discipline. */ export interface ToolCallRecord { /** The original tool name. Never translated (FR-27). */ toolName: string; /** The source arguments after deterministic credential redaction. */ argumentsText: string; /** Exactly one line about the outcome (FR-23). */ outcomeLine: string; effect: ToolEffect; /** True when the source had a result body and it was dropped (FR-25). */ bodyDropped: boolean; /** * True when the source recorded any answer to this call (even an empty or error result). * False when no result entry exists at all — the broken-tail signal (FR-54). * This is a presence flag, not a content field; it cannot hold a result body. */ resultRecorded?: boolean; } /** One turn of the canonical session. */ export interface CanonicalTurn { /** Zero-based position in the source session. Stable across re-reads. */ index: number; role: TurnRole; kind: TurnKind; /** Visible text. Empty when kind is "tool-call". */ text: string; /** Set when kind is "tool-call", null otherwise. */ toolCall: ToolCallRecord | null; /** ISO-8601 UTC when the source recorded one, null otherwise. */ timestamp: string | null; } /** Repository state of the source session (FR-36). */ export interface RepoSnapshot { /** Commit the source ran at, or null when the source format does not record it. */ commit: string | null; branch: string | null; /** Files the source changed, derived from its mutating tool calls. */ changedPaths: string[]; } /** Where a session came from (FR-22 metadata, FR-47 provenance). */ export interface SourceProvenance { ref: SessionRef; title: string; startedAt: string; updatedAt: string; repo: RepoSnapshot; } /** A source session in the neutral vocabulary. Every rule of sections D to I runs on this. */ export interface CanonicalSession { provenance: SourceProvenance; turns: CanonicalTurn[]; } /** Where the import is going, and how much room it has (FR-18, FR-29). */ export interface TargetProfile { agent: AgentId; home: HomePath; /** Context window of the target, in tokens. */ windowTokens: number; } /** The import marker the user sees. It never enters the model context (FR-47, FR-48). */ export interface ProvenanceMarker { sourceAgent: AgentId; sourceHome: HomePath; sourceSessionId: SessionId; /** ISO-8601 UTC. */ importedAt: string; /** One line naming what the rules dropped. */ droppedSummary: string; /** The rendered marker, in the order it must be shown. */ lines: string[]; }