cogpit-memory 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -57,7 +57,36 @@ var Database = import_better_sqlite3.default;
57
57
  var import_node_fs2 = require("node:fs");
58
58
 
59
59
  // src/lib/types.ts
60
- var AGENT_KINDS = ["codex", "copilot", "claude"];
60
+ var AGENT_KINDS = ["acp", "codex", "copilot", "claude"];
61
+
62
+ // src/lib/instances.ts
63
+ function instanceSessionId(instanceId, nativeId) {
64
+ if (instanceId === "default") return nativeId;
65
+ const bytes = new TextEncoder().encode(nativeId);
66
+ const encoded = btoa(Array.from(bytes, (byte) => String.fromCharCode(byte)).join("")).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
67
+ return `i-${instanceId}__${encoded}`;
68
+ }
69
+ function splitInstanceSessionId(id) {
70
+ const match = /^i-([A-Za-z0-9-]+)__([A-Za-z0-9_-]+)$/.exec(id);
71
+ if (!match) return { instanceId: "default", nativeId: id };
72
+ try {
73
+ const bytes = Uint8Array.from(atob(match[2].replace(/-/g, "+").replace(/_/g, "/")), (character) => character.charCodeAt(0));
74
+ return { instanceId: match[1], nativeId: new TextDecoder("utf-8", { fatal: true }).decode(bytes) };
75
+ } catch {
76
+ throw new Error("Invalid instance session ID");
77
+ }
78
+ }
79
+ function instanceDirName(instanceId, nativeDirName) {
80
+ return instanceId === "default" ? nativeDirName : `instance__${instanceId}__${nativeDirName}`;
81
+ }
82
+ function splitInstanceDirName(dirName) {
83
+ const match = /^instance__([A-Za-z0-9-]+)__(.+)$/.exec(dirName ?? "");
84
+ return match ? { instanceId: match[1], nativeDirName: match[2] } : { instanceId: "default", nativeDirName: dirName ?? "" };
85
+ }
86
+ function scopeParsedSession(session, instanceId) {
87
+ if (instanceId === "default") return session;
88
+ return { ...session, instanceId, sessionId: instanceSessionId(instanceId, splitInstanceSessionId(session.sessionId).nativeId), ...session.branchedFrom ? { branchedFrom: { ...session.branchedFrom, sessionId: instanceSessionId(instanceId, splitInstanceSessionId(session.branchedFrom.sessionId).nativeId) } } : {} };
89
+ }
61
90
 
62
91
  // src/lib/agent-descriptors.ts
63
92
  var SESSION_UUID = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
@@ -84,6 +113,10 @@ function base64DirNameCodec(prefix) {
84
113
  return {
85
114
  lossy: false,
86
115
  owns: (dirName) => typeof dirName === "string" && dirName.startsWith(prefix),
116
+ recognizes: (dirName) => {
117
+ const cwd = decodeBase64DirName(prefix, dirName);
118
+ return cwd !== null && cwd !== "" && encodeBase64DirName(prefix, cwd) === dirName;
119
+ },
87
120
  encode: (cwd) => encodeBase64DirName(prefix, cwd),
88
121
  decode: (dirName) => decodeBase64DirName(prefix, dirName)
89
122
  };
@@ -92,6 +125,14 @@ function sessionIdFromJsonlName(fileName) {
92
125
  const match = new RegExp(`(${SESSION_UUID})\\.jsonl$`, "i").exec(fileName);
93
126
  return match ? match[1] : null;
94
127
  }
128
+ function ownTranscriptRoot(sessionId) {
129
+ return sessionId ? { rootSessionId: sessionId.toLowerCase(), isRootTranscript: true } : null;
130
+ }
131
+ function plainPathSegments(relativePath) {
132
+ const segments = relativePath.split("/");
133
+ const plain = segments.every((segment) => segment !== "" && segment !== "." && segment !== ".." && !segment.includes("\\"));
134
+ return plain ? segments : null;
135
+ }
95
136
  var jsonlPathUrlCodec = {
96
137
  urlId: (fileName) => fileName.replace(/\.jsonl$/, ""),
97
138
  fileNameFromUrlId: (urlId) => urlId.endsWith(".jsonl") ? urlId : `${urlId}.jsonl`
@@ -109,6 +150,17 @@ var ARGV_SESSION_ID = "[0-9a-f-]{36}";
109
150
  var CODEX_DIR_PREFIX = "codex__";
110
151
  var COPILOT_DIR_PREFIX = "copilot__";
111
152
  var CLAUDE_PERMISSION_MODES = /* @__PURE__ */ new Set(["default", "plan", "acceptEdits", "dontAsk", "auto"]);
153
+ var CLAUDE_SESSION_CHILD_DIRS = /* @__PURE__ */ new Set(["subagents", "workflows"]);
154
+ function claudeTranscriptRoot(relativePath) {
155
+ const segments = plainPathSegments(relativePath);
156
+ if (!segments) return null;
157
+ const [root, childDir] = segments;
158
+ if (segments.length === 1) {
159
+ const sessionId = root.replace(/\.jsonl$/, "");
160
+ return sessionId !== root && isSessionUuid(sessionId) ? ownTranscriptRoot(sessionId) : null;
161
+ }
162
+ return segments.length > 2 && CLAUDE_SESSION_CHILD_DIRS.has(childDir) && isSessionUuid(root) ? { rootSessionId: root.toLowerCase(), isRootTranscript: false } : null;
163
+ }
112
164
  var claude = {
113
165
  kind: "claude",
114
166
  displayName: "Claude Code",
@@ -119,6 +171,8 @@ var claude = {
119
171
  // registry checks the prefixed agents first, so this is only ever reached
120
172
  // as the terminal arm.
121
173
  owns: (dirName) => typeof dirName === "string" && !dirName.startsWith(CODEX_DIR_PREFIX) && !dirName.startsWith(COPILOT_DIR_PREFIX),
174
+ // An absolute path starts with `/` or `\` (`-`) or a drive (`C:\` → `C--`); a drive root alone is `C-`.
175
+ recognizes: (dirName) => /^(?:(?:-|[A-Za-z]--)[A-Za-z0-9-]*|[A-Za-z]-)$/.test(dirName),
122
176
  /**
123
177
  * Claude Code's own convention: every character outside `[A-Za-z0-9]`
124
178
  * becomes `-`, so `/Users/x/proj` and `C:\Users\x\proj` both collapse into a
@@ -136,10 +190,12 @@ var claude = {
136
190
  return "/" + dirName.replace(/^-/, "").replace(/-/g, "/");
137
191
  }
138
192
  },
193
+ projectWorktreeDir: ".claude/worktrees",
139
194
  sessionFile: {
140
195
  name: (sessionId) => `${sessionId}.jsonl`,
141
196
  sessionId: sessionIdFromJsonlName,
142
- ...jsonlPathUrlCodec
197
+ ...jsonlPathUrlCodec,
198
+ transcriptRoot: claudeTranscriptRoot
143
199
  },
144
200
  metadataFromHead: true,
145
201
  resume: {
@@ -174,6 +230,7 @@ var claude = {
174
230
  bundledBySdk: true,
175
231
  homeDirName: ".claude",
176
232
  homeEnvVar: null,
233
+ sdkHomeEnvVar: "CLAUDE_CONFIG_DIR",
177
234
  installMarker: "projects",
178
235
  homeIsDiscoverable: false,
179
236
  process: {
@@ -185,7 +242,8 @@ var claude = {
185
242
  rootDirName: ".claude",
186
243
  instructions: [
187
244
  { in: "scope", path: "CLAUDE.md", scopes: ["global", "project"] },
188
- { in: "root", path: "CLAUDE.md", label: ".claude/CLAUDE.md", scopes: ["project"] }
245
+ { in: "root", path: "CLAUDE.md", label: ".claude/CLAUDE.md", scopes: ["project"] },
246
+ { in: "scope", path: "AGENTS.md", scopes: ["project"], fallback: true }
189
247
  ],
190
248
  settings: [
191
249
  { in: "root", path: "settings.json", scopes: ["global"] },
@@ -230,7 +288,9 @@ var claude = {
230
288
  nativeRewind: false,
231
289
  fileCheckpoints: true,
232
290
  reasoningEffort: true,
291
+ configurableContextWindow: false,
233
292
  fastTier: true,
293
+ lowPriorityInTerminal: true,
234
294
  imageInput: true,
235
295
  midTurnSteering: false,
236
296
  stopSession: true,
@@ -240,6 +300,7 @@ var claude = {
240
300
  accountSwitching: true,
241
301
  modelFallbackNotices: true,
242
302
  autoPermissionMode: "per-model",
303
+ toolPermissionRules: true,
243
304
  settingsApply: "live",
244
305
  turnLiveness: "transcript",
245
306
  tokenStreaming: true,
@@ -256,6 +317,7 @@ var codex = {
256
317
  displayName: "Codex",
257
318
  binName: "codex",
258
319
  dirName: base64DirNameCodec(CODEX_DIR_PREFIX),
320
+ projectWorktreeDir: null,
259
321
  sessionFile: {
260
322
  /**
261
323
  * Codex nests rollouts by local date:
@@ -272,7 +334,9 @@ var codex = {
272
334
  },
273
335
  sessionId: sessionIdFromJsonlName,
274
336
  // The date nesting is unrecoverable from the id, so the URL keeps the path.
275
- ...jsonlPathUrlCodec
337
+ ...jsonlPathUrlCodec,
338
+ // A sub-agent is a rollout of its own, linked to its parent only in its header.
339
+ transcriptRoot: (relativePath) => plainPathSegments(relativePath) ? ownTranscriptRoot(sessionIdFromJsonlName(relativePath)) : null
276
340
  },
277
341
  // A rollout's header carries no turn count and no last user message; both
278
342
  // only settle at the end of the file.
@@ -285,6 +349,7 @@ var codex = {
285
349
  permissions: (permissions) => {
286
350
  const mode = permissions?.mode || "default";
287
351
  if (mode === "bypassPermissions") return ["--dangerously-bypass-approvals-and-sandbox"];
352
+ if (mode === "auto") return ["--approve-for-me"];
288
353
  return [
289
354
  "--sandbox",
290
355
  mode === "plan" ? "read-only" : "workspace-write",
@@ -344,7 +409,7 @@ var codex = {
344
409
  extendedContext: null
345
410
  },
346
411
  capabilities: {
347
- worktrees: false,
412
+ worktrees: true,
348
413
  mcp: false,
349
414
  slashCommands: false,
350
415
  workflows: false,
@@ -358,7 +423,9 @@ var codex = {
358
423
  nativeRewind: false,
359
424
  fileCheckpoints: false,
360
425
  reasoningEffort: true,
426
+ configurableContextWindow: true,
361
427
  fastTier: true,
428
+ lowPriorityInTerminal: false,
362
429
  imageInput: true,
363
430
  midTurnSteering: true,
364
431
  stopSession: false,
@@ -367,7 +434,8 @@ var codex = {
367
434
  externalProcesses: false,
368
435
  accountSwitching: false,
369
436
  modelFallbackNotices: false,
370
- autoPermissionMode: "never",
437
+ autoPermissionMode: "always",
438
+ toolPermissionRules: true,
371
439
  settingsApply: "next-turn",
372
440
  turnLiveness: "runtime",
373
441
  tokenStreaming: true,
@@ -376,24 +444,25 @@ var codex = {
376
444
  namedSessions: false
377
445
  }
378
446
  };
447
+ function copilotSessionId(fileName) {
448
+ const match = new RegExp(`^(${SESSION_UUID})/events\\.jsonl$`, "i").exec(fileName);
449
+ return match ? match[1] : null;
450
+ }
379
451
  var copilot = {
380
452
  kind: "copilot",
381
453
  displayName: "GitHub Copilot CLI",
382
454
  binName: "copilot",
383
455
  dirName: base64DirNameCodec(COPILOT_DIR_PREFIX),
456
+ projectWorktreeDir: null,
384
457
  sessionFile: {
385
458
  /** Copilot keeps one directory per session: `<uuid>/events.jsonl`. */
386
459
  name: (sessionId) => `${sessionId}/events.jsonl`,
387
- sessionId: (fileName) => {
388
- const match = new RegExp(`^(${SESSION_UUID})/events\\.jsonl$`, "i").exec(fileName);
389
- return match ? match[1] : null;
390
- },
460
+ sessionId: copilotSessionId,
391
461
  // `<id>/events.jsonl` rebuilds from the id, so URLs stay short.
392
- urlId: (fileName) => {
393
- const match = new RegExp(`^(${SESSION_UUID})/events\\.jsonl$`, "i").exec(fileName);
394
- return match ? match[1] : fileName.replace(/\.jsonl$/, "");
395
- },
396
- fileNameFromUrlId: (urlId) => urlId.endsWith(".jsonl") ? urlId : `${urlId}/events.jsonl`
462
+ urlId: (fileName) => copilotSessionId(fileName) ?? fileName.replace(/\.jsonl$/, ""),
463
+ fileNameFromUrlId: (urlId) => urlId.endsWith(".jsonl") ? urlId : `${urlId}/events.jsonl`,
464
+ // Sub-agents are events inside the parent transcript, so every file is a root.
465
+ transcriptRoot: (relativePath) => ownTranscriptRoot(copilotSessionId(relativePath))
397
466
  },
398
467
  metadataFromHead: true,
399
468
  resume: {
@@ -475,7 +544,9 @@ var copilot = {
475
544
  fileCheckpoints: false,
476
545
  // Effort is only offered for models whose catalog entry advertises it.
477
546
  reasoningEffort: false,
547
+ configurableContextWindow: false,
478
548
  fastTier: false,
549
+ lowPriorityInTerminal: false,
479
550
  imageInput: false,
480
551
  midTurnSteering: true,
481
552
  stopSession: true,
@@ -485,6 +556,7 @@ var copilot = {
485
556
  accountSwitching: false,
486
557
  modelFallbackNotices: false,
487
558
  autoPermissionMode: "always",
559
+ toolPermissionRules: true,
488
560
  settingsApply: "next-turn",
489
561
  turnLiveness: "runtime",
490
562
  tokenStreaming: false,
@@ -492,21 +564,56 @@ var copilot = {
492
564
  namedSessions: true
493
565
  }
494
566
  };
567
+ var acp = {
568
+ ...copilot,
569
+ kind: "acp",
570
+ displayName: "ACP agent",
571
+ binName: "acp",
572
+ dirName: base64DirNameCodec("acp__"),
573
+ sessionFile: { name: (id) => `${id}.jsonl`, sessionId: sessionIdFromJsonlName, ...jsonlPathUrlCodec, transcriptRoot: (path) => {
574
+ const segments = plainPathSegments(path);
575
+ return segments?.length === 1 ? ownTranscriptRoot(sessionIdFromJsonlName(path)) : null;
576
+ } },
577
+ resume: { command: () => "Resume this session in Cogpit", args: () => [] },
578
+ launchArgs: { permissions: () => [], model: () => [], effort: () => [], fastTier: () => [] },
579
+ cli: { ...copilot.cli, packageName: "", homebrew: null, wingetId: null, selfUpdate: null, requiresConfiguration: true, homeDirName: ".cogpit-acp", homeEnvVar: "COGPIT_ACP_HOME", homeIsDiscoverable: true, installMarker: "projects" },
580
+ config: { ...copilot.config, rootDirName: ".cogpit-acp", instructions: [], settings: [], skillsDir: null },
581
+ capabilities: { ...copilot.capabilities, undo: false, redo: false, nativeRewind: false, nativeFork: false, midTurnSteering: false, externalProcesses: false, autoPermissionMode: "never", toolPermissionRules: false, namedSessions: false }
582
+ };
495
583
  var DESCRIPTORS = Object.freeze({
496
584
  claude,
497
585
  codex,
498
- copilot
586
+ copilot,
587
+ acp
499
588
  });
500
589
  function descriptorFor(kind) {
501
590
  return DESCRIPTORS[kind];
502
591
  }
503
592
  function descriptorForDirName(dirName) {
593
+ const scoped = splitInstanceDirName(dirName);
594
+ if (scoped.instanceId !== "default") {
595
+ const descriptor = descriptorForDirName(scoped.nativeDirName);
596
+ return {
597
+ ...descriptor,
598
+ dirName: { ...descriptor.dirName, encode: (cwd) => instanceDirName(scoped.instanceId, descriptor.dirName.encode(cwd)), decode: (name) => descriptor.dirName.decode(splitInstanceDirName(name).nativeDirName) },
599
+ sessionFile: { ...descriptor.sessionFile, name: (id) => descriptor.sessionFile.name(splitInstanceSessionId(id).nativeId), sessionId: (file) => {
600
+ const nativeId = descriptor.sessionFile.sessionId(file);
601
+ return nativeId ? instanceSessionId(scoped.instanceId, nativeId) : null;
602
+ }, transcriptRoot: (file) => {
603
+ const root = descriptor.sessionFile.transcriptRoot(file);
604
+ return root ? { ...root, rootSessionId: instanceSessionId(scoped.instanceId, root.rootSessionId) } : null;
605
+ } }
606
+ };
607
+ }
504
608
  for (const kind of AGENT_KINDS) {
505
609
  if (kind === "claude") continue;
506
610
  if (DESCRIPTORS[kind].dirName.owns(dirName)) return DESCRIPTORS[kind];
507
611
  }
508
612
  return claude;
509
613
  }
614
+ function isSessionUuid(value) {
615
+ return SESSION_UUID_RE.test(value);
616
+ }
510
617
 
511
618
  // src/lib/codex-exec.ts
512
619
  var JS_SIMPLE_ESCAPES = {
@@ -682,6 +789,36 @@ function parseAgentEnvelope(text) {
682
789
  return { sender, matched, body: matched ? unwrapped.trim() : text };
683
790
  }
684
791
 
792
+ // src/lib/toolResults.ts
793
+ function record(value) {
794
+ return value && typeof value === "object" ? value : {};
795
+ }
796
+ function toolResultMetadata(name, value) {
797
+ const result = record(value);
798
+ if (name === "Edit" || name === "Write") {
799
+ return result.staged === true ? { awaitingReview: true } : {};
800
+ }
801
+ if (name !== "Bash") return {};
802
+ const diff = record(result.bashEditDiff);
803
+ if (!Array.isArray(diff.files)) return {};
804
+ const files = [];
805
+ for (const raw of diff.files) {
806
+ const file = record(raw);
807
+ if (typeof file.filePath !== "string" || !file.filePath || !Array.isArray(file.hunks)) continue;
808
+ const hunks = [];
809
+ for (const rawHunk of file.hunks) {
810
+ const hunk = record(rawHunk);
811
+ const { oldStart, oldLines, newStart, newLines, lines } = hunk;
812
+ if (![oldStart, oldLines, newStart, newLines].every((n) => typeof n === "number" && Number.isInteger(n) && n >= 0)) continue;
813
+ if (!Array.isArray(lines) || !lines.every((line) => typeof line === "string" && /^[ +\\-]/.test(line))) continue;
814
+ hunks.push({ oldStart, oldLines, newStart, newLines, lines });
815
+ }
816
+ if (hunks.length) files.push({ filePath: file.filePath, hunks });
817
+ }
818
+ const more = typeof diff.moreFiles === "number" && Number.isInteger(diff.moreFiles) && diff.moreFiles > 0 ? diff.moreFiles : 0;
819
+ return { fileDiffs: files, ...more ? { additionalFileDiffs: more } : {} };
820
+ }
821
+
685
822
  // src/lib/turnBuilder.ts
686
823
  function extractTextFromContent(content) {
687
824
  if (typeof content === "string") return content;
@@ -1049,6 +1186,10 @@ function buildTurnsWithStarts(messages) {
1049
1186
  pendingRecap = { content: msg.content, timestamp: msg.timestamp };
1050
1187
  continue;
1051
1188
  }
1189
+ if (isSystemMessage(msg) && msg.subtype === "local_command" && msg.content) {
1190
+ current?.contentBlocks.push({ kind: "local_command", content: msg.content, timestamp: msg.timestamp });
1191
+ continue;
1192
+ }
1052
1193
  if (isQueueOperationMessage(msg)) {
1053
1194
  if (current && msg.operation === "enqueue" && isVisibleQueuedPrompt(msg.content)) {
1054
1195
  const parsed = parseAgentEnvelope(msg.content);
@@ -1100,6 +1241,8 @@ function buildTurnsWithStarts(messages) {
1100
1241
  if (pending) {
1101
1242
  pending.turn.toolCalls[pending.index].result = extractToolResultText(block.content);
1102
1243
  pending.turn.toolCalls[pending.index].isError = block.is_error === true;
1244
+ const call = pending.turn.toolCalls[pending.index];
1245
+ Object.assign(call, toolResultMetadata(call.name, msg.toolUseResult));
1103
1246
  pendingToolUses.delete(block.tool_use_id);
1104
1247
  }
1105
1248
  }
@@ -2016,9 +2159,18 @@ function safeParseLine(line) {
2016
2159
  function isObject3(value) {
2017
2160
  return typeof value === "object" && value !== null;
2018
2161
  }
2019
- function isCodexRecord(record) {
2020
- if (!record || typeof record.type !== "string") return false;
2021
- return record.type === "session_meta" || record.type === "turn_context" || record.type === "event_msg" || record.type === "response_item" || record.type === "inter_agent_communication_metadata" || record.type === "compacted" || record.type === "world_state";
2162
+ function codexParentId(payload) {
2163
+ const source = isObject3(payload.source) ? payload.source : null;
2164
+ const subagent = source && isObject3(source.subagent) ? source.subagent : null;
2165
+ const threadSpawn = subagent && isObject3(subagent.thread_spawn) ? subagent.thread_spawn : null;
2166
+ for (const candidate of [payload.forked_from_id, payload.parent_thread_id, threadSpawn?.parent_thread_id]) {
2167
+ if (typeof candidate === "string" && candidate) return candidate;
2168
+ }
2169
+ return null;
2170
+ }
2171
+ function isCodexRecord(record2) {
2172
+ if (!record2 || typeof record2.type !== "string") return false;
2173
+ return record2.type === "session_meta" || record2.type === "turn_context" || record2.type === "event_msg" || record2.type === "response_item" || record2.type === "inter_agent_communication_metadata" || record2.type === "compacted" || record2.type === "world_state";
2022
2174
  }
2023
2175
  function extractMessageText(payload, blockType) {
2024
2176
  const content = payload?.content;
@@ -2213,22 +2365,22 @@ function createTurn2(turnId, timestamp, model) {
2213
2365
  model
2214
2366
  };
2215
2367
  }
2216
- function extractPromptFromRecord(record) {
2217
- if (record.type === "event_msg" && isObject3(record.payload) && record.payload.type === "user_message" && typeof record.payload.message === "string") {
2218
- return normalizePromptText(record.payload.message);
2368
+ function extractPromptFromRecord(record2) {
2369
+ if (record2.type === "event_msg" && isObject3(record2.payload) && record2.payload.type === "user_message" && typeof record2.payload.message === "string") {
2370
+ return normalizePromptText(record2.payload.message);
2219
2371
  }
2220
- if (record.type === "response_item" && isObject3(record.payload) && record.payload.type === "message" && record.payload.role === "user") {
2221
- return normalizePromptText(extractMessageText(record.payload, "input_text"));
2372
+ if (record2.type === "response_item" && isObject3(record2.payload) && record2.payload.type === "message" && record2.payload.role === "user") {
2373
+ return normalizePromptText(extractMessageText(record2.payload, "input_text"));
2222
2374
  }
2223
2375
  return "";
2224
2376
  }
2225
- function recordOpensTurn(record, payload) {
2226
- if (record.type === "session_meta") return false;
2227
- if (record.type === "event_msg" && payload?.type === "task_started") return false;
2228
- if (record.type === "response_item" && payload?.type === "message") {
2377
+ function recordOpensTurn(record2, payload) {
2378
+ if (record2.type === "session_meta") return false;
2379
+ if (record2.type === "event_msg" && payload?.type === "task_started") return false;
2380
+ if (record2.type === "response_item" && payload?.type === "message") {
2229
2381
  if (payload.role === "developer" || payload.role === "system") return false;
2230
2382
  if (payload.role === "user") {
2231
- return extractPromptFromRecord(record) !== "" || extractResponseMessageImages(payload).length > 0 || extractResponseMessageAudio(payload).length > 0;
2383
+ return extractPromptFromRecord(record2) !== "" || extractResponseMessageImages(payload).length > 0 || extractResponseMessageAudio(payload).length > 0;
2232
2384
  }
2233
2385
  }
2234
2386
  return true;
@@ -2249,22 +2401,22 @@ function extractMetadataFromRecords(records) {
2249
2401
  let parentSessionId = null;
2250
2402
  let agentPath = "/root";
2251
2403
  let previousPrompt = "";
2252
- for (const record of records) {
2253
- if (!isObject3(record.payload)) continue;
2254
- if (record.type === "session_meta") {
2255
- sessionId ||= typeof record.payload.id === "string" ? record.payload.id : "";
2256
- version ||= typeof record.payload.cli_version === "string" ? record.payload.cli_version : "";
2257
- cwd ||= typeof record.payload.cwd === "string" ? record.payload.cwd : "";
2258
- if (!branchedFrom && isObject3(record.payload.branchedFrom)) {
2259
- const sourceId = typeof record.payload.branchedFrom.sessionId === "string" ? record.payload.branchedFrom.sessionId : "";
2404
+ for (const record2 of records) {
2405
+ if (!isObject3(record2.payload)) continue;
2406
+ if (record2.type === "session_meta") {
2407
+ sessionId ||= typeof record2.payload.id === "string" ? record2.payload.id : "";
2408
+ version ||= typeof record2.payload.cli_version === "string" ? record2.payload.cli_version : "";
2409
+ cwd ||= typeof record2.payload.cwd === "string" ? record2.payload.cwd : "";
2410
+ if (!branchedFrom && isObject3(record2.payload.branchedFrom)) {
2411
+ const sourceId = typeof record2.payload.branchedFrom.sessionId === "string" ? record2.payload.branchedFrom.sessionId : "";
2260
2412
  if (sourceId) {
2261
2413
  branchedFrom = {
2262
2414
  sessionId: sourceId,
2263
- turnIndex: typeof record.payload.branchedFrom.turnIndex === "number" ? record.payload.branchedFrom.turnIndex : null
2415
+ turnIndex: typeof record2.payload.branchedFrom.turnIndex === "number" ? record2.payload.branchedFrom.turnIndex : null
2264
2416
  };
2265
2417
  }
2266
2418
  }
2267
- const source = isObject3(record.payload.source) ? record.payload.source : null;
2419
+ const source = isObject3(record2.payload.source) ? record2.payload.source : null;
2268
2420
  if (source && isObject3(source.subagent)) {
2269
2421
  isSubagent = true;
2270
2422
  const threadSpawn = isObject3(source.subagent.thread_spawn) ? source.subagent.thread_spawn : null;
@@ -2272,25 +2424,23 @@ function extractMetadataFromRecords(records) {
2272
2424
  agentPath = threadSpawn.agent_path;
2273
2425
  }
2274
2426
  }
2275
- if (typeof record.payload.forked_from_id === "string" && record.payload.forked_from_id) {
2276
- parentSessionId = record.payload.forked_from_id;
2277
- }
2278
- const git = isObject3(record.payload.git) ? record.payload.git : null;
2427
+ parentSessionId = codexParentId(record2.payload) ?? parentSessionId;
2428
+ const git = isObject3(record2.payload.git) ? record2.payload.git : null;
2279
2429
  gitBranch ||= git && typeof git.branch === "string" ? git.branch : "";
2280
2430
  }
2281
- if (record.type === "turn_context") {
2282
- model ||= typeof record.payload.model === "string" ? record.payload.model : "";
2283
- cwd ||= typeof record.payload.cwd === "string" ? record.payload.cwd : "";
2431
+ if (record2.type === "turn_context") {
2432
+ model ||= typeof record2.payload.model === "string" ? record2.payload.model : "";
2433
+ cwd ||= typeof record2.payload.cwd === "string" ? record2.payload.cwd : "";
2284
2434
  }
2285
- const prompt = extractPromptFromRecord(record);
2435
+ const prompt = extractPromptFromRecord(record2);
2286
2436
  if (!prompt) continue;
2287
2437
  if (prompt === previousPrompt) continue;
2288
2438
  if (!firstUserMessage) firstUserMessage = prompt;
2289
2439
  lastUserMessage = prompt;
2290
2440
  previousPrompt = prompt;
2291
2441
  turnCount++;
2292
- if (!timestamp) timestamp = record.timestamp ?? "";
2293
- lastTimestamp = record.timestamp ?? lastTimestamp;
2442
+ if (!timestamp) timestamp = record2.timestamp ?? "";
2443
+ lastTimestamp = record2.timestamp ?? lastTimestamp;
2294
2444
  }
2295
2445
  if (lastTimestamp === "") lastTimestamp = timestamp;
2296
2446
  return {
@@ -2448,19 +2598,19 @@ function walkCodexRecords(records, options) {
2448
2598
  appendToolCall(targetTurn, toolCall, timestamp);
2449
2599
  }
2450
2600
  for (let index = 0; index < records.length; index++) {
2451
- const record = records[index];
2452
- if (!isCodexRecord(record)) continue;
2601
+ const record2 = records[index];
2602
+ if (!isCodexRecord(record2)) continue;
2453
2603
  recordIndex = index;
2454
- const payload = isObject3(record.payload) ? record.payload : void 0;
2455
- const timestamp = record.timestamp ?? "";
2456
- if (record.type === "compacted") {
2604
+ const payload = isObject3(record2.payload) ? record2.payload : void 0;
2605
+ const timestamp = record2.timestamp ?? "";
2606
+ if (record2.type === "compacted") {
2457
2607
  current = commitTurn(current);
2458
2608
  pendingCompaction = "Conversation compacted";
2459
2609
  lastTurnTimestamp = timestamp;
2460
2610
  continue;
2461
2611
  }
2462
- if (record.type === "world_state") continue;
2463
- if (record.type === "turn_context") {
2612
+ if (record2.type === "world_state") continue;
2613
+ if (record2.type === "turn_context") {
2464
2614
  current = commitTurn(current);
2465
2615
  pendingContextIndex = index;
2466
2616
  currentTurnId = typeof payload?.turn_id === "string" ? payload.turn_id : null;
@@ -2469,7 +2619,7 @@ function walkCodexRecords(records, options) {
2469
2619
  sawTurnStart = true;
2470
2620
  continue;
2471
2621
  }
2472
- if (record.type === "event_msg" && payload?.type === "user_message" && typeof payload.message === "string") {
2622
+ if (record2.type === "event_msg" && payload?.type === "user_message" && typeof payload.message === "string") {
2473
2623
  if (current && (current.assistantText.length > 0 || current.toolCalls.length > 0 || current.thinking.length > 0)) {
2474
2624
  current = commitTurn(current);
2475
2625
  }
@@ -2493,12 +2643,12 @@ function walkCodexRecords(records, options) {
2493
2643
  lastTurnTimestamp = timestamp;
2494
2644
  continue;
2495
2645
  }
2496
- if (!current && !recordOpensTurn(record, payload)) continue;
2646
+ if (!current && !recordOpensTurn(record2, payload)) continue;
2497
2647
  current ??= createActiveTurn(currentTurnId, timestamp, currentModel);
2498
2648
  if (!current.model && currentModel) current.model = currentModel;
2499
2649
  if (!current.timestamp) current.timestamp = timestamp;
2500
2650
  if (timestamp) lastTurnTimestamp = timestamp;
2501
- if (record.type === "event_msg" && payload?.type === "token_count") {
2651
+ if (record2.type === "event_msg" && payload?.type === "token_count") {
2502
2652
  const info = isObject3(payload.info) ? payload.info : null;
2503
2653
  const lastUsage = info ? parseTokenUsage(info.last_token_usage) : null;
2504
2654
  if (lastUsage) {
@@ -2506,14 +2656,14 @@ function walkCodexRecords(records, options) {
2506
2656
  }
2507
2657
  continue;
2508
2658
  }
2509
- if (record.type === "event_msg" && payload?.type === "web_search_end" && typeof payload.call_id === "string") {
2659
+ if (record2.type === "event_msg" && payload?.type === "web_search_end" && typeof payload.call_id === "string") {
2510
2660
  const input = {};
2511
2661
  if (typeof payload.query === "string" && payload.query) input.query = payload.query;
2512
2662
  if (isObject3(payload.action)) input.action = payload.action;
2513
2663
  upsertWebSearchCall(current, payload.call_id, input, "completed", timestamp);
2514
2664
  continue;
2515
2665
  }
2516
- if (record.type === "event_msg" && payload?.type === "mcp_tool_call_end" && typeof payload.call_id === "string" && isObject3(payload.invocation)) {
2666
+ if (record2.type === "event_msg" && payload?.type === "mcp_tool_call_end" && typeof payload.call_id === "string" && isObject3(payload.invocation)) {
2517
2667
  const invocation = payload.invocation;
2518
2668
  const server = typeof invocation.server === "string" ? invocation.server : "mcp";
2519
2669
  const tool = typeof invocation.tool === "string" ? invocation.tool : "tool";
@@ -2538,7 +2688,7 @@ function walkCodexRecords(records, options) {
2538
2688
  }
2539
2689
  continue;
2540
2690
  }
2541
- if (record.type === "event_msg" && payload?.type === "sub_agent_activity" && typeof payload.agent_thread_id === "string" && typeof payload.agent_path === "string") {
2691
+ if (record2.type === "event_msg" && payload?.type === "sub_agent_activity" && typeof payload.agent_thread_id === "string" && typeof payload.agent_path === "string") {
2542
2692
  const agentId = payload.agent_thread_id;
2543
2693
  const eventId2 = typeof payload.event_id === "string" ? payload.event_id : "";
2544
2694
  const kind = typeof payload.kind === "string" ? payload.kind : "started";
@@ -2572,7 +2722,7 @@ function walkCodexRecords(records, options) {
2572
2722
  upsertAgentMessage(agentId, current, timestamp, { status });
2573
2723
  continue;
2574
2724
  }
2575
- if (record.type !== "response_item" || !payload) continue;
2725
+ if (record2.type !== "response_item" || !payload) continue;
2576
2726
  if (payload.type === "reasoning") {
2577
2727
  const summary = Array.isArray(payload.summary) ? payload.summary.filter((item) => typeof item === "string").join("\n") : "";
2578
2728
  appendThinking(current, summary.trim(), timestamp);
@@ -2849,7 +2999,7 @@ function parseCodexSession(jsonlText, options) {
2849
2999
  return session;
2850
3000
  }
2851
3001
  function appendCodexSession(existing, newJsonlText) {
2852
- const prefix = existing.rawMessages.map((record) => JSON.stringify(record)).join("\n");
3002
+ const prefix = existing.rawMessages.map((record2) => JSON.stringify(record2)).join("\n");
2853
3003
  return parseCodexSession(prefix ? `${prefix}
2854
3004
  ${newJsonlText}` : newJsonlText);
2855
3005
  }
@@ -3000,11 +3150,11 @@ function deriveCodexSessionStatus(rawMessages) {
3000
3150
  }
3001
3151
  return { status: "idle" };
3002
3152
  }
3003
- function brandCodexBranch(firstRecord, sessionId, turnIndex) {
3153
+ function brandCodexBranch(firstRecord, sessionId, turnIndex, branchedAt) {
3004
3154
  const payload = isObject3(firstRecord.payload) ? { ...firstRecord.payload } : {};
3005
3155
  const originalId = typeof payload.id === "string" ? payload.id : "";
3006
3156
  payload.id = sessionId;
3007
- payload.branchedFrom = { sessionId: originalId, turnIndex };
3157
+ payload.branchedFrom = { sessionId: originalId, turnIndex, at: new Date(branchedAt).toISOString() };
3008
3158
  return { record: { ...firstRecord, payload }, originalId };
3009
3159
  }
3010
3160
  function codexTurnBoundaries(records) {
@@ -3766,7 +3916,7 @@ function parseCopilotSession(jsonlText, options) {
3766
3916
  };
3767
3917
  }
3768
3918
  function appendCopilotSession(existing, newJsonlText) {
3769
- const prefix = existing.rawMessages.map((record) => JSON.stringify(record)).join("\n");
3919
+ const prefix = existing.rawMessages.map((record2) => JSON.stringify(record2)).join("\n");
3770
3920
  return parseCopilotSession(prefix ? `${prefix}
3771
3921
  ${newJsonlText}` : newJsonlText);
3772
3922
  }
@@ -3874,11 +4024,11 @@ function brandCopilotBranch() {
3874
4024
  function copilotTurnBoundaries(records) {
3875
4025
  const boundaries = [];
3876
4026
  for (let index = 0; index < records.length; index++) {
3877
- const record = records[index];
3878
- if (record.type !== "user.message" || typeof record.agentId === "string") continue;
3879
- const data = record.data;
4027
+ const record2 = records[index];
4028
+ if (record2.type !== "user.message" || typeof record2.agentId === "string") continue;
4029
+ const data = record2.data;
3880
4030
  if (!data || typeof data !== "object" || Array.isArray(data)) continue;
3881
- if (typeof record.id !== "string" || !record.id) continue;
4031
+ if (typeof record2.id !== "string" || !record2.id) continue;
3882
4032
  boundaries.push(index);
3883
4033
  }
3884
4034
  return boundaries;
@@ -3924,6 +4074,15 @@ var claudeFormat = {
3924
4074
  turnBoundaries: claudeTurnBoundaries
3925
4075
  };
3926
4076
  var FORMATS = Object.freeze({
4077
+ acp: {
4078
+ ...claudeFormat,
4079
+ kind: "acp",
4080
+ descriptor: descriptorFor("acp"),
4081
+ detectsText: (text) => text.split("\n", 1)[0]?.includes('"type":"acp_session"') ?? false,
4082
+ detectsRecords: (records) => records[0]?.type === "acp_session",
4083
+ parse: (text, options) => ({ ...parseClaudeSession(text, options), agentKind: "acp" }),
4084
+ append: (session, text) => ({ ...appendClaudeSession(session, text), agentKind: "acp" })
4085
+ },
3927
4086
  claude: claudeFormat,
3928
4087
  codex: codexFormat,
3929
4088
  copilot: copilotFormat
@@ -3952,7 +4111,8 @@ function parseSession(jsonlText, options) {
3952
4111
  function parseSessionAppend(existing, newJsonlText) {
3953
4112
  const byRecords = formatForRecords(existing.rawMessages);
3954
4113
  const format = byRecords.kind === TERMINAL_AGENT_KIND ? formatForText(newJsonlText) : byRecords;
3955
- return format.append(existing, newJsonlText);
4114
+ const appended = format.append(existing, newJsonlText);
4115
+ return existing.instanceId ? scopeParsedSession(appended, existing.instanceId) : appended;
3956
4116
  }
3957
4117
  function getUserMessageText(content) {
3958
4118
  if (content === null) return "";
@@ -3972,8 +4132,8 @@ var import_node_path2 = require("node:path");
3972
4132
  var import_node_path = require("node:path");
3973
4133
  var import_node_os = require("node:os");
3974
4134
  function agentHomeDir(kind, env = process.env, homeDirectory = (0, import_node_os.homedir)()) {
3975
- const { homeEnvVar, homeDirName } = descriptorFor(kind).cli;
3976
- const override = homeEnvVar ? env[homeEnvVar] : void 0;
4135
+ const { homeEnvVar, homeDirName, sdkHomeEnvVar } = descriptorFor(kind).cli;
4136
+ const override = env[homeEnvVar ?? sdkHomeEnvVar ?? ""];
3977
4137
  return override || (0, import_node_path.join)(homeDirectory, homeDirName);
3978
4138
  }
3979
4139
  var dirs = {
@@ -3981,7 +4141,8 @@ var dirs = {
3981
4141
  TEAMS_DIR: (0, import_node_path.join)(agentHomeDir("claude"), "teams"),
3982
4142
  TASKS_DIR: (0, import_node_path.join)(agentHomeDir("claude"), "tasks"),
3983
4143
  CODEX_SESSIONS_DIR: (0, import_node_path.join)(agentHomeDir("codex"), "sessions"),
3984
- COPILOT_SESSIONS_DIR: (0, import_node_path.join)(agentHomeDir("copilot"), "session-state")
4144
+ COPILOT_SESSIONS_DIR: (0, import_node_path.join)(agentHomeDir("copilot"), "session-state"),
4145
+ ACP_PROJECTS_DIR: (0, import_node_path.join)(agentHomeDir("acp"), "projects")
3985
4146
  };
3986
4147
  var DEFAULT_DB_PATH = (0, import_node_path.join)(agentHomeDir("claude"), "cogpit-memory", "search-index.db");
3987
4148
 
@@ -4169,24 +4330,48 @@ var WALKERS = {
4169
4330
  const candidate = copilotTranscript(root, sessionId);
4170
4331
  return mtimeOf(candidate) === null ? null : candidate;
4171
4332
  }
4333
+ },
4334
+ acp: {
4335
+ root: () => dirs.ACP_PROJECTS_DIR,
4336
+ list: walkClaude,
4337
+ identify: claudeIdentify,
4338
+ find: (root, id) => WALKERS.claude.find(root, id)
4172
4339
  }
4173
4340
  };
4174
- function makeStore(kind) {
4341
+ function makeStore(kind, home, configuredId) {
4175
4342
  const walker = WALKERS[kind];
4343
+ const instanceId = configuredId ?? (process.env.COGPIT_WORKER_AGENT === kind ? process.env.COGPIT_AGENT_INSTANCE_ID : void 0) ?? "default";
4344
+ const root = home ? () => (0, import_node_path2.join)(home, kind === "codex" ? "sessions" : kind === "copilot" ? "session-state" : "projects") : walker.root;
4345
+ const identify = (filePath, directory = root()) => {
4346
+ const identity = walker.identify(filePath, directory);
4347
+ return { ...identity, sessionId: instanceSessionId(instanceId, identity.sessionId), parentSessionId: identity.parentSessionId ? instanceSessionId(instanceId, identity.parentSessionId) : null };
4348
+ };
4176
4349
  return {
4177
4350
  kind,
4351
+ instanceId,
4178
4352
  descriptor: descriptorFor(kind),
4179
- root: walker.root,
4180
- ownsPath: (filePath) => isWithin((0, import_node_path2.resolve)(walker.root()), (0, import_node_path2.resolve)(filePath)),
4181
- identify: (filePath, root = walker.root()) => walker.identify(filePath, root),
4182
- list: (cutoffMs, root = walker.root()) => walker.list(root, cutoffMs),
4183
- find: (sessionId) => walker.find(walker.root(), sessionId)
4353
+ root,
4354
+ ownsPath: (filePath) => {
4355
+ if (isWithin((0, import_node_path2.resolve)(root()), (0, import_node_path2.resolve)(filePath))) return true;
4356
+ try {
4357
+ return isWithin((0, import_node_fs.realpathSync)(root()), (0, import_node_fs.realpathSync)(filePath));
4358
+ } catch {
4359
+ return false;
4360
+ }
4361
+ },
4362
+ identify,
4363
+ list: (cutoffMs, directory = root()) => walker.list(directory, cutoffMs).map((file) => ({ ...file, ...identify(file.path, directory) })),
4364
+ find: (sessionId) => {
4365
+ const parts = splitInstanceSessionId(sessionId);
4366
+ return parts.instanceId === instanceId ? walker.find(root(), parts.nativeId) : null;
4367
+ }
4184
4368
  };
4185
4369
  }
4186
4370
  var STORES = {
4187
4371
  claude: makeStore("claude"),
4188
4372
  codex: makeStore("codex"),
4189
- copilot: makeStore("copilot")
4373
+ copilot: makeStore("copilot"),
4374
+ acp: makeStore("acp")
4190
4375
  };
4191
4376
  function storeFor(kind) {
4192
4377
  return STORES[kind];
@@ -4195,7 +4380,20 @@ function defaultStore() {
4195
4380
  return storeFor(descriptorForDirName(null).kind);
4196
4381
  }
4197
4382
  function allStores() {
4198
- return AGENT_KINDS.map((kind) => STORES[kind]);
4383
+ if (process.env.COGPIT_AGENT_INSTANCE_ID && AGENT_KINDS.includes(process.env.COGPIT_WORKER_AGENT)) return [STORES[process.env.COGPIT_WORKER_AGENT]];
4384
+ const stores = AGENT_KINDS.map((kind) => STORES[kind]);
4385
+ const root = process.env.COGPIT_ORCHESTRATION_ROOT;
4386
+ if (!root) return stores;
4387
+ for (const entry of readDirSafe((0, import_node_path2.join)(root, "provider-instances"))) {
4388
+ if (!entry.isDirectory()) continue;
4389
+ const home = (0, import_node_path2.join)(root, "provider-instances", entry.name);
4390
+ try {
4391
+ const config = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path2.join)(home, "config.json"), "utf8"));
4392
+ if (config.providerInstance?.id === entry.name && config.providerInstance.agent && AGENT_KINDS.includes(config.providerInstance.agent)) stores.push(makeStore(config.providerInstance.agent, home, entry.name));
4393
+ } catch {
4394
+ }
4395
+ }
4396
+ return stores;
4199
4397
  }
4200
4398
  function storeForPath(filePath) {
4201
4399
  return allStores().find((store) => store.ownsPath(filePath)) ?? null;
@@ -4594,6 +4792,11 @@ function parseMaxAge(raw) {
4594
4792
  }
4595
4793
  }
4596
4794
 
4795
+ // src/lib/read-session.ts
4796
+ function parseTranscript(content, filePath) {
4797
+ return scopeParsedSession(parseSession(content), storeForPath(filePath)?.instanceId ?? "default");
4798
+ }
4799
+
4597
4800
  // src/commands/search.ts
4598
4801
  async function searchSessions(query, opts, searchIndex) {
4599
4802
  if (!query || query.length < 2) {
@@ -4840,7 +5043,7 @@ async function walkSubagentFiles(parentJsonlPath, query, caseSensitive, currentD
4840
5043
  );
4841
5044
  const hitArrays = await Promise.all(
4842
5045
  matched.filter((m) => m.rawContent).map(async ({ agentId, filePath, rawContent }) => {
4843
- const subSession = parseSession(rawContent);
5046
+ const subSession = parseTranscript(rawContent, filePath);
4844
5047
  const subHits = walkSession(subSession, query, caseSensitive, `agent/${agentId}/`);
4845
5048
  const nestedHits = await walkSubagentFiles(filePath, query, caseSensitive, currentDepth + 1, maxDepth);
4846
5049
  return [...subHits, ...nestedHits];
@@ -4861,7 +5064,7 @@ async function rawScanSearch(query, sessionId, maxAgeMs, limit, caseSensitive, e
4861
5064
  const rawContent = await rawTextMatch(file.path, query, caseSensitive);
4862
5065
  sessionsSearched++;
4863
5066
  if (!rawContent) continue;
4864
- const session = parseSession(rawContent);
5067
+ const session = parseTranscript(rawContent, file.path);
4865
5068
  const sessionHits = walkSession(session, query, caseSensitive);
4866
5069
  const subagentHits = await walkSubagentFiles(file.path, query, caseSensitive, 0, MAX_SUBAGENT_DEPTH);
4867
5070
  const allHits = [...sessionHits, ...subagentHits];
@@ -5113,6 +5316,8 @@ function mapContentBlock(block) {
5113
5316
  return { kind: "recap", content: block.content, timestamp: block.timestamp ?? null };
5114
5317
  case "task_notification":
5115
5318
  return { kind: "task_notification", content: block.content, timestamp: block.timestamp ?? null };
5319
+ case "local_command":
5320
+ return { kind: "local_command", content: block.content, timestamp: block.timestamp ?? null };
5116
5321
  default: {
5117
5322
  const exhaustive = block;
5118
5323
  return exhaustive;
@@ -5202,14 +5407,14 @@ async function getSessionOverview(sessionId) {
5202
5407
  const jsonlPath = await findSessionFile(sessionId);
5203
5408
  if (!jsonlPath) return { error: "Session not found" };
5204
5409
  const content = await (0, import_promises3.readFile)(jsonlPath, "utf-8");
5205
- const session = parseSession(content);
5410
+ const session = parseTranscript(content, jsonlPath);
5206
5411
  return mapSessionToOverview(session);
5207
5412
  }
5208
5413
  async function getTurnDetail(sessionId, turnIndex) {
5209
5414
  const jsonlPath = await findSessionFile(sessionId);
5210
5415
  if (!jsonlPath) return { error: "Session not found" };
5211
5416
  const content = await (0, import_promises3.readFile)(jsonlPath, "utf-8");
5212
- const session = parseSession(content);
5417
+ const session = parseTranscript(content, jsonlPath);
5213
5418
  if (turnIndex < 0 || turnIndex >= session.turns.length) return { error: "Turn not found" };
5214
5419
  return mapTurnToDetail(session, turnIndex);
5215
5420
  }
@@ -5217,11 +5422,11 @@ async function getAgentOverview(sessionId, agentId) {
5217
5422
  const jsonlPath = await findSessionFile(sessionId);
5218
5423
  if (!jsonlPath) return { error: "Session not found" };
5219
5424
  const content = await (0, import_promises3.readFile)(jsonlPath, "utf-8");
5220
- const session = parseSession(content);
5425
+ const session = parseTranscript(content, jsonlPath);
5221
5426
  const subagentFile = await findSubagentFile(jsonlPath, agentId);
5222
5427
  if (!subagentFile) return { error: "Agent not found" };
5223
5428
  const subagentContent = await (0, import_promises3.readFile)(subagentFile.filePath, "utf-8");
5224
- const subagentSession = parseSession(subagentContent);
5429
+ const subagentSession = parseTranscript(subagentContent, subagentFile.filePath);
5225
5430
  const metadata = findAgentMetadata(session, agentId);
5226
5431
  const parentToolCallId = findParentToolCallId(session, agentId);
5227
5432
  const teamContext = await findTeamContext(sessionId, subagentFile.fileName);
@@ -5242,7 +5447,7 @@ async function getAgentTurnDetail(sessionId, agentId, turnIndex) {
5242
5447
  const subagentFile = await findSubagentFile(jsonlPath, agentId);
5243
5448
  if (!subagentFile) return { error: "Agent not found" };
5244
5449
  const subagentContent = await (0, import_promises3.readFile)(subagentFile.filePath, "utf-8");
5245
- const subagentSession = parseSession(subagentContent);
5450
+ const subagentSession = parseTranscript(subagentContent, subagentFile.filePath);
5246
5451
  if (turnIndex < 0 || turnIndex >= subagentSession.turns.length) return { error: "Turn not found" };
5247
5452
  return mapTurnToDetail(subagentSession, turnIndex);
5248
5453
  }
@@ -5276,15 +5481,15 @@ function scanClaudeMessages(lines) {
5276
5481
  turnCount: 0
5277
5482
  };
5278
5483
  for (const line of lines) {
5279
- let record;
5484
+ let record2;
5280
5485
  try {
5281
- record = JSON.parse(line);
5486
+ record2 = JSON.parse(line);
5282
5487
  } catch {
5283
5488
  continue;
5284
5489
  }
5285
- if (record.type !== "user" || record.isMeta) continue;
5286
- if (!meta.timestamp) meta.timestamp = record.timestamp || "";
5287
- const content = record.message?.content;
5490
+ if (record2.type !== "user" || record2.isMeta) continue;
5491
+ if (!meta.timestamp) meta.timestamp = record2.timestamp || "";
5492
+ const content = record2.message?.content;
5288
5493
  let extracted = "";
5289
5494
  if (typeof content === "string") {
5290
5495
  extracted = cleanUserText(content);
@@ -5331,6 +5536,7 @@ async function getSessionMeta(filePath) {
5331
5536
  isPartialRead = false;
5332
5537
  }
5333
5538
  const header = readHeader(format, lines);
5539
+ const instanceId = storeForPath(filePath)?.instanceId ?? "default";
5334
5540
  const conversation = header.turnCount === void 0 ? scanClaudeMessages(lines) : {
5335
5541
  firstUserMessage: header.firstUserMessage ?? "",
5336
5542
  lastUserMessage: header.lastUserMessage ?? "",
@@ -5338,7 +5544,7 @@ async function getSessionMeta(filePath) {
5338
5544
  turnCount: header.turnCount
5339
5545
  };
5340
5546
  return {
5341
- sessionId: header.sessionId,
5547
+ sessionId: instanceSessionId(instanceId, header.sessionId),
5342
5548
  version: header.version,
5343
5549
  gitBranch: header.gitBranch,
5344
5550
  model: header.model,
@@ -5348,7 +5554,7 @@ async function getSessionMeta(filePath) {
5348
5554
  // A head read saw a known number of lines in a known number of bytes;
5349
5555
  // scaling that by the file size is the only estimate available.
5350
5556
  lineCount: isPartialRead ? Math.round(fileStat.size / (HEAD_READ_BYTES / lines.length)) : lines.length,
5351
- branchedFrom: header.branchedFrom
5557
+ branchedFrom: header.branchedFrom ? { ...header.branchedFrom, sessionId: instanceSessionId(instanceId, header.branchedFrom.sessionId) } : void 0
5352
5558
  };
5353
5559
  }
5354
5560
  async function getSessionStatus(filePath) {
@@ -5443,7 +5649,7 @@ async function getSessionStatus(filePath) {
5443
5649
  // src/commands/sessions.ts
5444
5650
  function toSessionSummary(file, meta, status) {
5445
5651
  return {
5446
- sessionId: meta.sessionId,
5652
+ sessionId: file.sessionId || meta.sessionId,
5447
5653
  filePath: file.path,
5448
5654
  timestamp: meta.timestamp,
5449
5655
  model: meta.model,