source dump of claude code
at main 330 lines 11 kB view raw
1import type { UUID } from 'crypto' 2import type { FileHistorySnapshot } from 'src/utils/fileHistory.js' 3import type { ContentReplacementRecord } from 'src/utils/toolResultStorage.js' 4import type { AgentId } from './ids.js' 5import type { Message } from './message.js' 6import type { QueueOperationMessage } from './messageQueueTypes.js' 7 8export type SerializedMessage = Message & { 9 cwd: string 10 userType: string 11 entrypoint?: string // CLAUDE_CODE_ENTRYPOINT — distinguishes cli/sdk-ts/sdk-py/etc. 12 sessionId: string 13 timestamp: string 14 version: string 15 gitBranch?: string 16 slug?: string // Session slug for files like plans (used for resume) 17} 18 19export type LogOption = { 20 date: string 21 messages: SerializedMessage[] 22 fullPath?: string 23 value: number 24 created: Date 25 modified: Date 26 firstPrompt: string 27 messageCount: number 28 fileSize?: number // File size in bytes (for display) 29 isSidechain: boolean 30 isLite?: boolean // True for lite logs (messages not loaded) 31 sessionId?: string // Session ID for lite logs 32 teamName?: string // Team name if this is a spawned agent session 33 agentName?: string // Agent's custom name (from /rename or swarm) 34 agentColor?: string // Agent's color (from /rename or swarm) 35 agentSetting?: string // Agent definition used (from --agent flag or settings.agent) 36 isTeammate?: boolean // Whether this session was created by a swarm teammate 37 leafUuid?: UUID // If given, this uuid must appear in the DB 38 summary?: string // Optional conversation summary 39 customTitle?: string // Optional user-set custom title 40 tag?: string // Optional tag for the session (searchable in /resume) 41 fileHistorySnapshots?: FileHistorySnapshot[] // Optional file history snapshots 42 attributionSnapshots?: AttributionSnapshotMessage[] // Optional attribution snapshots 43 contextCollapseCommits?: ContextCollapseCommitEntry[] // Ordered — commit B may reference commit A's summary 44 contextCollapseSnapshot?: ContextCollapseSnapshotEntry // Last-wins — staged queue + spawn state 45 gitBranch?: string // Git branch at the end of the session 46 projectPath?: string // Original project directory path 47 prNumber?: number // GitHub PR number linked to this session 48 prUrl?: string // Full URL to the linked PR 49 prRepository?: string // Repository in "owner/repo" format 50 mode?: 'coordinator' | 'normal' // Session mode for coordinator/normal detection 51 worktreeSession?: PersistedWorktreeSession | null // Worktree state at session end (null = exited, undefined = never entered) 52 contentReplacements?: ContentReplacementRecord[] // Replacement decisions for resume reconstruction 53} 54 55export type SummaryMessage = { 56 type: 'summary' 57 leafUuid: UUID 58 summary: string 59} 60 61export type CustomTitleMessage = { 62 type: 'custom-title' 63 sessionId: UUID 64 customTitle: string 65} 66 67/** 68 * AI-generated session title. Distinct from CustomTitleMessage so that: 69 * - User renames (custom-title) always win over AI titles in read preference 70 * - reAppendSessionMetadata never re-appends AI titles (they're ephemeral/ 71 * regeneratable; re-appending would clobber user renames on resume) 72 * - VS Code's onlyIfNoCustomTitle CAS check only matches user titles, 73 * allowing AI to overwrite its own previous AI title but not user titles 74 */ 75export type AiTitleMessage = { 76 type: 'ai-title' 77 sessionId: UUID 78 aiTitle: string 79} 80 81export type LastPromptMessage = { 82 type: 'last-prompt' 83 sessionId: UUID 84 lastPrompt: string 85} 86 87/** 88 * Periodic fork-generated summary of what the agent is currently doing. 89 * Written every min(5 steps, 2min) by forking the main thread mid-turn so 90 * `claude ps` can show something more useful than the last user prompt 91 * (which is often "ok go" or "fix it"). 92 */ 93export type TaskSummaryMessage = { 94 type: 'task-summary' 95 sessionId: UUID 96 summary: string 97 timestamp: string 98} 99 100export type TagMessage = { 101 type: 'tag' 102 sessionId: UUID 103 tag: string 104} 105 106export type AgentNameMessage = { 107 type: 'agent-name' 108 sessionId: UUID 109 agentName: string 110} 111 112export type AgentColorMessage = { 113 type: 'agent-color' 114 sessionId: UUID 115 agentColor: string 116} 117 118export type AgentSettingMessage = { 119 type: 'agent-setting' 120 sessionId: UUID 121 agentSetting: string 122} 123 124/** 125 * PR link message stored in session transcript. 126 * Links a session to a GitHub pull request for tracking and navigation. 127 */ 128export type PRLinkMessage = { 129 type: 'pr-link' 130 sessionId: UUID 131 prNumber: number 132 prUrl: string 133 prRepository: string // e.g., "owner/repo" 134 timestamp: string // ISO timestamp when linked 135} 136 137export type ModeEntry = { 138 type: 'mode' 139 sessionId: UUID 140 mode: 'coordinator' | 'normal' 141} 142 143/** 144 * Worktree session state persisted to the transcript for resume. 145 * Subset of WorktreeSession from utils/worktree.ts — excludes ephemeral 146 * fields (creationDurationMs, usedSparsePaths) that are only used for 147 * first-run analytics. 148 */ 149export type PersistedWorktreeSession = { 150 originalCwd: string 151 worktreePath: string 152 worktreeName: string 153 worktreeBranch?: string 154 originalBranch?: string 155 originalHeadCommit?: string 156 sessionId: string 157 tmuxSessionName?: string 158 hookBased?: boolean 159} 160 161/** 162 * Records whether the session is currently inside a worktree created by 163 * EnterWorktree or --worktree. Last-wins: an enter writes the session, 164 * an exit writes null. On --resume, restored only if the worktreePath 165 * still exists on disk (the /exit dialog may have removed it). 166 */ 167export type WorktreeStateEntry = { 168 type: 'worktree-state' 169 sessionId: UUID 170 worktreeSession: PersistedWorktreeSession | null 171} 172 173/** 174 * Records content blocks whose in-context representation was replaced with a 175 * smaller stub (the full content was persisted elsewhere). Replayed on resume 176 * for prompt cache stability. Written once per enforcement pass that replaces 177 * at least one block. When agentId is set, the record belongs to a subagent 178 * sidechain (AgentTool resume reads these); when absent, it's main-thread 179 * (/resume reads these). 180 */ 181export type ContentReplacementEntry = { 182 type: 'content-replacement' 183 sessionId: UUID 184 agentId?: AgentId 185 replacements: ContentReplacementRecord[] 186} 187 188export type FileHistorySnapshotMessage = { 189 type: 'file-history-snapshot' 190 messageId: UUID 191 snapshot: FileHistorySnapshot 192 isSnapshotUpdate: boolean 193} 194 195/** 196 * Per-file attribution state tracking Claude's character contributions. 197 */ 198export type FileAttributionState = { 199 contentHash: string // SHA-256 hash of file content 200 claudeContribution: number // Characters written by Claude 201 mtime: number // File modification time 202} 203 204/** 205 * Attribution snapshot message stored in session transcript. 206 * Tracks character-level contributions by Claude for commit attribution. 207 */ 208export type AttributionSnapshotMessage = { 209 type: 'attribution-snapshot' 210 messageId: UUID 211 surface: string // Client surface (cli, ide, web, api) 212 fileStates: Record<string, FileAttributionState> 213 promptCount?: number // Total prompts in session 214 promptCountAtLastCommit?: number // Prompts at last commit 215 permissionPromptCount?: number // Total permission prompts shown 216 permissionPromptCountAtLastCommit?: number // Permission prompts at last commit 217 escapeCount?: number // Total ESC presses (cancelled permission prompts) 218 escapeCountAtLastCommit?: number // ESC presses at last commit 219} 220 221export type TranscriptMessage = SerializedMessage & { 222 parentUuid: UUID | null 223 logicalParentUuid?: UUID | null // Preserves logical parent when parentUuid is nullified for session breaks 224 isSidechain: boolean 225 gitBranch?: string 226 agentId?: string // Agent ID for sidechain transcripts to enable resuming agents 227 teamName?: string // Team name if this is a spawned agent session 228 agentName?: string // Agent's custom name (from /rename or swarm) 229 agentColor?: string // Agent's color (from /rename or swarm) 230 promptId?: string // Correlates with OTel prompt.id for user prompt messages 231} 232 233export type SpeculationAcceptMessage = { 234 type: 'speculation-accept' 235 timestamp: string 236 timeSavedMs: number 237} 238 239/** 240 * Persisted context-collapse commit. The archived messages themselves are 241 * NOT persisted — they're already in the transcript as ordinary user/ 242 * assistant messages. We only persist enough to reconstruct the splice 243 * instruction (boundary uuids) and the summary placeholder (which is NOT 244 * in the transcript because it's never yielded to the REPL). 245 * 246 * On restore, the store reconstructs CommittedCollapse with archived=[]; 247 * projectView lazily fills the archive the first time it finds the span. 248 * 249 * Discriminator is obfuscated to match the gate name. sessionStorage.ts 250 * isn't feature-gated (it's the generic transcript plumbing used by every 251 * entry type), so a descriptive string here would leak into external builds 252 * via the appendEntry dispatch / loadTranscriptFile parser even though 253 * nothing in an external build ever writes or reads this entry. 254 */ 255export type ContextCollapseCommitEntry = { 256 type: 'marble-origami-commit' 257 sessionId: UUID 258 /** 16-digit collapse ID. Max across entries reseeds the ID counter. */ 259 collapseId: string 260 /** The summary placeholder's uuid — registerSummary() needs it. */ 261 summaryUuid: string 262 /** Full <collapsed id="...">text</collapsed> string for the placeholder. */ 263 summaryContent: string 264 /** Plain summary text for ctx_inspect. */ 265 summary: string 266 /** Span boundaries — projectView finds these in the resumed Message[]. */ 267 firstArchivedUuid: string 268 lastArchivedUuid: string 269} 270 271/** 272 * Snapshot of the staged queue and spawn trigger state. Unlike commits 273 * (append-only, replay-all), snapshots are last-wins — only the most 274 * recent snapshot entry is applied on restore. Written after every 275 * ctx-agent spawn resolves (when staged contents may have changed). 276 * 277 * Staged boundaries are UUIDs (session-stable), not collapse IDs (which 278 * reset with the uuidToId bimap). Restoring a staged span issues fresh 279 * collapse IDs for those messages on the next decorate/display, but the 280 * span itself resolves correctly. 281 */ 282export type ContextCollapseSnapshotEntry = { 283 type: 'marble-origami-snapshot' 284 sessionId: UUID 285 staged: Array<{ 286 startUuid: string 287 endUuid: string 288 summary: string 289 risk: number 290 stagedAt: number 291 }> 292 /** Spawn trigger state — so the +interval clock picks up where it left off. */ 293 armed: boolean 294 lastSpawnTokens: number 295} 296 297export type Entry = 298 | TranscriptMessage 299 | SummaryMessage 300 | CustomTitleMessage 301 | AiTitleMessage 302 | LastPromptMessage 303 | TaskSummaryMessage 304 | TagMessage 305 | AgentNameMessage 306 | AgentColorMessage 307 | AgentSettingMessage 308 | PRLinkMessage 309 | FileHistorySnapshotMessage 310 | AttributionSnapshotMessage 311 | QueueOperationMessage 312 | SpeculationAcceptMessage 313 | ModeEntry 314 | WorktreeStateEntry 315 | ContentReplacementEntry 316 | ContextCollapseCommitEntry 317 | ContextCollapseSnapshotEntry 318 319export function sortLogs(logs: LogOption[]): LogOption[] { 320 return logs.sort((a, b) => { 321 // Sort by modified date (newest first) 322 const modifiedDiff = b.modified.getTime() - a.modified.getTime() 323 if (modifiedDiff !== 0) { 324 return modifiedDiff 325 } 326 327 // If modified dates are equal, sort by created date (newest first) 328 return b.created.getTime() - a.created.getTime() 329 }) 330}