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/cli.js CHANGED
@@ -48,7 +48,36 @@ var Database = import_better_sqlite3.default;
48
48
  var import_node_fs2 = require("node:fs");
49
49
 
50
50
  // src/lib/types.ts
51
- var AGENT_KINDS = ["codex", "copilot", "claude"];
51
+ var AGENT_KINDS = ["acp", "codex", "copilot", "claude"];
52
+
53
+ // src/lib/instances.ts
54
+ function instanceSessionId(instanceId, nativeId) {
55
+ if (instanceId === "default") return nativeId;
56
+ const bytes = new TextEncoder().encode(nativeId);
57
+ const encoded = btoa(Array.from(bytes, (byte) => String.fromCharCode(byte)).join("")).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
58
+ return `i-${instanceId}__${encoded}`;
59
+ }
60
+ function splitInstanceSessionId(id) {
61
+ const match = /^i-([A-Za-z0-9-]+)__([A-Za-z0-9_-]+)$/.exec(id);
62
+ if (!match) return { instanceId: "default", nativeId: id };
63
+ try {
64
+ const bytes = Uint8Array.from(atob(match[2].replace(/-/g, "+").replace(/_/g, "/")), (character) => character.charCodeAt(0));
65
+ return { instanceId: match[1], nativeId: new TextDecoder("utf-8", { fatal: true }).decode(bytes) };
66
+ } catch {
67
+ throw new Error("Invalid instance session ID");
68
+ }
69
+ }
70
+ function instanceDirName(instanceId, nativeDirName) {
71
+ return instanceId === "default" ? nativeDirName : `instance__${instanceId}__${nativeDirName}`;
72
+ }
73
+ function splitInstanceDirName(dirName) {
74
+ const match = /^instance__([A-Za-z0-9-]+)__(.+)$/.exec(dirName ?? "");
75
+ return match ? { instanceId: match[1], nativeDirName: match[2] } : { instanceId: "default", nativeDirName: dirName ?? "" };
76
+ }
77
+ function scopeParsedSession(session, instanceId) {
78
+ if (instanceId === "default") return session;
79
+ return { ...session, instanceId, sessionId: instanceSessionId(instanceId, splitInstanceSessionId(session.sessionId).nativeId), ...session.branchedFrom ? { branchedFrom: { ...session.branchedFrom, sessionId: instanceSessionId(instanceId, splitInstanceSessionId(session.branchedFrom.sessionId).nativeId) } } : {} };
80
+ }
52
81
 
53
82
  // src/lib/agent-descriptors.ts
54
83
  var SESSION_UUID = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
@@ -75,6 +104,10 @@ function base64DirNameCodec(prefix) {
75
104
  return {
76
105
  lossy: false,
77
106
  owns: (dirName) => typeof dirName === "string" && dirName.startsWith(prefix),
107
+ recognizes: (dirName) => {
108
+ const cwd = decodeBase64DirName(prefix, dirName);
109
+ return cwd !== null && cwd !== "" && encodeBase64DirName(prefix, cwd) === dirName;
110
+ },
78
111
  encode: (cwd) => encodeBase64DirName(prefix, cwd),
79
112
  decode: (dirName) => decodeBase64DirName(prefix, dirName)
80
113
  };
@@ -83,6 +116,14 @@ function sessionIdFromJsonlName(fileName) {
83
116
  const match = new RegExp(`(${SESSION_UUID})\\.jsonl$`, "i").exec(fileName);
84
117
  return match ? match[1] : null;
85
118
  }
119
+ function ownTranscriptRoot(sessionId) {
120
+ return sessionId ? { rootSessionId: sessionId.toLowerCase(), isRootTranscript: true } : null;
121
+ }
122
+ function plainPathSegments(relativePath) {
123
+ const segments = relativePath.split("/");
124
+ const plain = segments.every((segment) => segment !== "" && segment !== "." && segment !== ".." && !segment.includes("\\"));
125
+ return plain ? segments : null;
126
+ }
86
127
  var jsonlPathUrlCodec = {
87
128
  urlId: (fileName) => fileName.replace(/\.jsonl$/, ""),
88
129
  fileNameFromUrlId: (urlId) => urlId.endsWith(".jsonl") ? urlId : `${urlId}.jsonl`
@@ -100,6 +141,17 @@ var ARGV_SESSION_ID = "[0-9a-f-]{36}";
100
141
  var CODEX_DIR_PREFIX = "codex__";
101
142
  var COPILOT_DIR_PREFIX = "copilot__";
102
143
  var CLAUDE_PERMISSION_MODES = /* @__PURE__ */ new Set(["default", "plan", "acceptEdits", "dontAsk", "auto"]);
144
+ var CLAUDE_SESSION_CHILD_DIRS = /* @__PURE__ */ new Set(["subagents", "workflows"]);
145
+ function claudeTranscriptRoot(relativePath) {
146
+ const segments = plainPathSegments(relativePath);
147
+ if (!segments) return null;
148
+ const [root, childDir] = segments;
149
+ if (segments.length === 1) {
150
+ const sessionId = root.replace(/\.jsonl$/, "");
151
+ return sessionId !== root && isSessionUuid(sessionId) ? ownTranscriptRoot(sessionId) : null;
152
+ }
153
+ return segments.length > 2 && CLAUDE_SESSION_CHILD_DIRS.has(childDir) && isSessionUuid(root) ? { rootSessionId: root.toLowerCase(), isRootTranscript: false } : null;
154
+ }
103
155
  var claude = {
104
156
  kind: "claude",
105
157
  displayName: "Claude Code",
@@ -110,6 +162,8 @@ var claude = {
110
162
  // registry checks the prefixed agents first, so this is only ever reached
111
163
  // as the terminal arm.
112
164
  owns: (dirName) => typeof dirName === "string" && !dirName.startsWith(CODEX_DIR_PREFIX) && !dirName.startsWith(COPILOT_DIR_PREFIX),
165
+ // An absolute path starts with `/` or `\` (`-`) or a drive (`C:\` → `C--`); a drive root alone is `C-`.
166
+ recognizes: (dirName) => /^(?:(?:-|[A-Za-z]--)[A-Za-z0-9-]*|[A-Za-z]-)$/.test(dirName),
113
167
  /**
114
168
  * Claude Code's own convention: every character outside `[A-Za-z0-9]`
115
169
  * becomes `-`, so `/Users/x/proj` and `C:\Users\x\proj` both collapse into a
@@ -127,10 +181,12 @@ var claude = {
127
181
  return "/" + dirName.replace(/^-/, "").replace(/-/g, "/");
128
182
  }
129
183
  },
184
+ projectWorktreeDir: ".claude/worktrees",
130
185
  sessionFile: {
131
186
  name: (sessionId) => `${sessionId}.jsonl`,
132
187
  sessionId: sessionIdFromJsonlName,
133
- ...jsonlPathUrlCodec
188
+ ...jsonlPathUrlCodec,
189
+ transcriptRoot: claudeTranscriptRoot
134
190
  },
135
191
  metadataFromHead: true,
136
192
  resume: {
@@ -165,6 +221,7 @@ var claude = {
165
221
  bundledBySdk: true,
166
222
  homeDirName: ".claude",
167
223
  homeEnvVar: null,
224
+ sdkHomeEnvVar: "CLAUDE_CONFIG_DIR",
168
225
  installMarker: "projects",
169
226
  homeIsDiscoverable: false,
170
227
  process: {
@@ -176,7 +233,8 @@ var claude = {
176
233
  rootDirName: ".claude",
177
234
  instructions: [
178
235
  { in: "scope", path: "CLAUDE.md", scopes: ["global", "project"] },
179
- { in: "root", path: "CLAUDE.md", label: ".claude/CLAUDE.md", scopes: ["project"] }
236
+ { in: "root", path: "CLAUDE.md", label: ".claude/CLAUDE.md", scopes: ["project"] },
237
+ { in: "scope", path: "AGENTS.md", scopes: ["project"], fallback: true }
180
238
  ],
181
239
  settings: [
182
240
  { in: "root", path: "settings.json", scopes: ["global"] },
@@ -221,7 +279,9 @@ var claude = {
221
279
  nativeRewind: false,
222
280
  fileCheckpoints: true,
223
281
  reasoningEffort: true,
282
+ configurableContextWindow: false,
224
283
  fastTier: true,
284
+ lowPriorityInTerminal: true,
225
285
  imageInput: true,
226
286
  midTurnSteering: false,
227
287
  stopSession: true,
@@ -231,6 +291,7 @@ var claude = {
231
291
  accountSwitching: true,
232
292
  modelFallbackNotices: true,
233
293
  autoPermissionMode: "per-model",
294
+ toolPermissionRules: true,
234
295
  settingsApply: "live",
235
296
  turnLiveness: "transcript",
236
297
  tokenStreaming: true,
@@ -247,6 +308,7 @@ var codex = {
247
308
  displayName: "Codex",
248
309
  binName: "codex",
249
310
  dirName: base64DirNameCodec(CODEX_DIR_PREFIX),
311
+ projectWorktreeDir: null,
250
312
  sessionFile: {
251
313
  /**
252
314
  * Codex nests rollouts by local date:
@@ -263,7 +325,9 @@ var codex = {
263
325
  },
264
326
  sessionId: sessionIdFromJsonlName,
265
327
  // The date nesting is unrecoverable from the id, so the URL keeps the path.
266
- ...jsonlPathUrlCodec
328
+ ...jsonlPathUrlCodec,
329
+ // A sub-agent is a rollout of its own, linked to its parent only in its header.
330
+ transcriptRoot: (relativePath) => plainPathSegments(relativePath) ? ownTranscriptRoot(sessionIdFromJsonlName(relativePath)) : null
267
331
  },
268
332
  // A rollout's header carries no turn count and no last user message; both
269
333
  // only settle at the end of the file.
@@ -276,6 +340,7 @@ var codex = {
276
340
  permissions: (permissions) => {
277
341
  const mode = permissions?.mode || "default";
278
342
  if (mode === "bypassPermissions") return ["--dangerously-bypass-approvals-and-sandbox"];
343
+ if (mode === "auto") return ["--approve-for-me"];
279
344
  return [
280
345
  "--sandbox",
281
346
  mode === "plan" ? "read-only" : "workspace-write",
@@ -335,7 +400,7 @@ var codex = {
335
400
  extendedContext: null
336
401
  },
337
402
  capabilities: {
338
- worktrees: false,
403
+ worktrees: true,
339
404
  mcp: false,
340
405
  slashCommands: false,
341
406
  workflows: false,
@@ -349,7 +414,9 @@ var codex = {
349
414
  nativeRewind: false,
350
415
  fileCheckpoints: false,
351
416
  reasoningEffort: true,
417
+ configurableContextWindow: true,
352
418
  fastTier: true,
419
+ lowPriorityInTerminal: false,
353
420
  imageInput: true,
354
421
  midTurnSteering: true,
355
422
  stopSession: false,
@@ -358,7 +425,8 @@ var codex = {
358
425
  externalProcesses: false,
359
426
  accountSwitching: false,
360
427
  modelFallbackNotices: false,
361
- autoPermissionMode: "never",
428
+ autoPermissionMode: "always",
429
+ toolPermissionRules: true,
362
430
  settingsApply: "next-turn",
363
431
  turnLiveness: "runtime",
364
432
  tokenStreaming: true,
@@ -367,24 +435,25 @@ var codex = {
367
435
  namedSessions: false
368
436
  }
369
437
  };
438
+ function copilotSessionId(fileName) {
439
+ const match = new RegExp(`^(${SESSION_UUID})/events\\.jsonl$`, "i").exec(fileName);
440
+ return match ? match[1] : null;
441
+ }
370
442
  var copilot = {
371
443
  kind: "copilot",
372
444
  displayName: "GitHub Copilot CLI",
373
445
  binName: "copilot",
374
446
  dirName: base64DirNameCodec(COPILOT_DIR_PREFIX),
447
+ projectWorktreeDir: null,
375
448
  sessionFile: {
376
449
  /** Copilot keeps one directory per session: `<uuid>/events.jsonl`. */
377
450
  name: (sessionId) => `${sessionId}/events.jsonl`,
378
- sessionId: (fileName) => {
379
- const match = new RegExp(`^(${SESSION_UUID})/events\\.jsonl$`, "i").exec(fileName);
380
- return match ? match[1] : null;
381
- },
451
+ sessionId: copilotSessionId,
382
452
  // `<id>/events.jsonl` rebuilds from the id, so URLs stay short.
383
- urlId: (fileName) => {
384
- const match = new RegExp(`^(${SESSION_UUID})/events\\.jsonl$`, "i").exec(fileName);
385
- return match ? match[1] : fileName.replace(/\.jsonl$/, "");
386
- },
387
- fileNameFromUrlId: (urlId) => urlId.endsWith(".jsonl") ? urlId : `${urlId}/events.jsonl`
453
+ urlId: (fileName) => copilotSessionId(fileName) ?? fileName.replace(/\.jsonl$/, ""),
454
+ fileNameFromUrlId: (urlId) => urlId.endsWith(".jsonl") ? urlId : `${urlId}/events.jsonl`,
455
+ // Sub-agents are events inside the parent transcript, so every file is a root.
456
+ transcriptRoot: (relativePath) => ownTranscriptRoot(copilotSessionId(relativePath))
388
457
  },
389
458
  metadataFromHead: true,
390
459
  resume: {
@@ -466,7 +535,9 @@ var copilot = {
466
535
  fileCheckpoints: false,
467
536
  // Effort is only offered for models whose catalog entry advertises it.
468
537
  reasoningEffort: false,
538
+ configurableContextWindow: false,
469
539
  fastTier: false,
540
+ lowPriorityInTerminal: false,
470
541
  imageInput: false,
471
542
  midTurnSteering: true,
472
543
  stopSession: true,
@@ -476,6 +547,7 @@ var copilot = {
476
547
  accountSwitching: false,
477
548
  modelFallbackNotices: false,
478
549
  autoPermissionMode: "always",
550
+ toolPermissionRules: true,
479
551
  settingsApply: "next-turn",
480
552
  turnLiveness: "runtime",
481
553
  tokenStreaming: false,
@@ -483,21 +555,56 @@ var copilot = {
483
555
  namedSessions: true
484
556
  }
485
557
  };
558
+ var acp = {
559
+ ...copilot,
560
+ kind: "acp",
561
+ displayName: "ACP agent",
562
+ binName: "acp",
563
+ dirName: base64DirNameCodec("acp__"),
564
+ sessionFile: { name: (id) => `${id}.jsonl`, sessionId: sessionIdFromJsonlName, ...jsonlPathUrlCodec, transcriptRoot: (path) => {
565
+ const segments = plainPathSegments(path);
566
+ return segments?.length === 1 ? ownTranscriptRoot(sessionIdFromJsonlName(path)) : null;
567
+ } },
568
+ resume: { command: () => "Resume this session in Cogpit", args: () => [] },
569
+ launchArgs: { permissions: () => [], model: () => [], effort: () => [], fastTier: () => [] },
570
+ cli: { ...copilot.cli, packageName: "", homebrew: null, wingetId: null, selfUpdate: null, requiresConfiguration: true, homeDirName: ".cogpit-acp", homeEnvVar: "COGPIT_ACP_HOME", homeIsDiscoverable: true, installMarker: "projects" },
571
+ config: { ...copilot.config, rootDirName: ".cogpit-acp", instructions: [], settings: [], skillsDir: null },
572
+ capabilities: { ...copilot.capabilities, undo: false, redo: false, nativeRewind: false, nativeFork: false, midTurnSteering: false, externalProcesses: false, autoPermissionMode: "never", toolPermissionRules: false, namedSessions: false }
573
+ };
486
574
  var DESCRIPTORS = Object.freeze({
487
575
  claude,
488
576
  codex,
489
- copilot
577
+ copilot,
578
+ acp
490
579
  });
491
580
  function descriptorFor(kind) {
492
581
  return DESCRIPTORS[kind];
493
582
  }
494
583
  function descriptorForDirName(dirName) {
584
+ const scoped = splitInstanceDirName(dirName);
585
+ if (scoped.instanceId !== "default") {
586
+ const descriptor = descriptorForDirName(scoped.nativeDirName);
587
+ return {
588
+ ...descriptor,
589
+ dirName: { ...descriptor.dirName, encode: (cwd) => instanceDirName(scoped.instanceId, descriptor.dirName.encode(cwd)), decode: (name) => descriptor.dirName.decode(splitInstanceDirName(name).nativeDirName) },
590
+ sessionFile: { ...descriptor.sessionFile, name: (id) => descriptor.sessionFile.name(splitInstanceSessionId(id).nativeId), sessionId: (file) => {
591
+ const nativeId = descriptor.sessionFile.sessionId(file);
592
+ return nativeId ? instanceSessionId(scoped.instanceId, nativeId) : null;
593
+ }, transcriptRoot: (file) => {
594
+ const root = descriptor.sessionFile.transcriptRoot(file);
595
+ return root ? { ...root, rootSessionId: instanceSessionId(scoped.instanceId, root.rootSessionId) } : null;
596
+ } }
597
+ };
598
+ }
495
599
  for (const kind of AGENT_KINDS) {
496
600
  if (kind === "claude") continue;
497
601
  if (DESCRIPTORS[kind].dirName.owns(dirName)) return DESCRIPTORS[kind];
498
602
  }
499
603
  return claude;
500
604
  }
605
+ function isSessionUuid(value) {
606
+ return SESSION_UUID_RE.test(value);
607
+ }
501
608
 
502
609
  // src/lib/codex-exec.ts
503
610
  var JS_SIMPLE_ESCAPES = {
@@ -673,6 +780,36 @@ function parseAgentEnvelope(text) {
673
780
  return { sender, matched, body: matched ? unwrapped.trim() : text };
674
781
  }
675
782
 
783
+ // src/lib/toolResults.ts
784
+ function record(value) {
785
+ return value && typeof value === "object" ? value : {};
786
+ }
787
+ function toolResultMetadata(name, value) {
788
+ const result = record(value);
789
+ if (name === "Edit" || name === "Write") {
790
+ return result.staged === true ? { awaitingReview: true } : {};
791
+ }
792
+ if (name !== "Bash") return {};
793
+ const diff = record(result.bashEditDiff);
794
+ if (!Array.isArray(diff.files)) return {};
795
+ const files = [];
796
+ for (const raw of diff.files) {
797
+ const file = record(raw);
798
+ if (typeof file.filePath !== "string" || !file.filePath || !Array.isArray(file.hunks)) continue;
799
+ const hunks = [];
800
+ for (const rawHunk of file.hunks) {
801
+ const hunk = record(rawHunk);
802
+ const { oldStart, oldLines, newStart, newLines, lines } = hunk;
803
+ if (![oldStart, oldLines, newStart, newLines].every((n) => typeof n === "number" && Number.isInteger(n) && n >= 0)) continue;
804
+ if (!Array.isArray(lines) || !lines.every((line) => typeof line === "string" && /^[ +\\-]/.test(line))) continue;
805
+ hunks.push({ oldStart, oldLines, newStart, newLines, lines });
806
+ }
807
+ if (hunks.length) files.push({ filePath: file.filePath, hunks });
808
+ }
809
+ const more = typeof diff.moreFiles === "number" && Number.isInteger(diff.moreFiles) && diff.moreFiles > 0 ? diff.moreFiles : 0;
810
+ return { fileDiffs: files, ...more ? { additionalFileDiffs: more } : {} };
811
+ }
812
+
676
813
  // src/lib/turnBuilder.ts
677
814
  function extractTextFromContent(content) {
678
815
  if (typeof content === "string") return content;
@@ -1040,6 +1177,10 @@ function buildTurnsWithStarts(messages) {
1040
1177
  pendingRecap = { content: msg.content, timestamp: msg.timestamp };
1041
1178
  continue;
1042
1179
  }
1180
+ if (isSystemMessage(msg) && msg.subtype === "local_command" && msg.content) {
1181
+ current?.contentBlocks.push({ kind: "local_command", content: msg.content, timestamp: msg.timestamp });
1182
+ continue;
1183
+ }
1043
1184
  if (isQueueOperationMessage(msg)) {
1044
1185
  if (current && msg.operation === "enqueue" && isVisibleQueuedPrompt(msg.content)) {
1045
1186
  const parsed = parseAgentEnvelope(msg.content);
@@ -1091,6 +1232,8 @@ function buildTurnsWithStarts(messages) {
1091
1232
  if (pending) {
1092
1233
  pending.turn.toolCalls[pending.index].result = extractToolResultText(block.content);
1093
1234
  pending.turn.toolCalls[pending.index].isError = block.is_error === true;
1235
+ const call = pending.turn.toolCalls[pending.index];
1236
+ Object.assign(call, toolResultMetadata(call.name, msg.toolUseResult));
1094
1237
  pendingToolUses.delete(block.tool_use_id);
1095
1238
  }
1096
1239
  }
@@ -2007,9 +2150,18 @@ function safeParseLine(line) {
2007
2150
  function isObject3(value) {
2008
2151
  return typeof value === "object" && value !== null;
2009
2152
  }
2010
- function isCodexRecord(record) {
2011
- if (!record || typeof record.type !== "string") return false;
2012
- 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";
2153
+ function codexParentId(payload) {
2154
+ const source = isObject3(payload.source) ? payload.source : null;
2155
+ const subagent = source && isObject3(source.subagent) ? source.subagent : null;
2156
+ const threadSpawn = subagent && isObject3(subagent.thread_spawn) ? subagent.thread_spawn : null;
2157
+ for (const candidate of [payload.forked_from_id, payload.parent_thread_id, threadSpawn?.parent_thread_id]) {
2158
+ if (typeof candidate === "string" && candidate) return candidate;
2159
+ }
2160
+ return null;
2161
+ }
2162
+ function isCodexRecord(record2) {
2163
+ if (!record2 || typeof record2.type !== "string") return false;
2164
+ 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";
2013
2165
  }
2014
2166
  function extractMessageText(payload, blockType) {
2015
2167
  const content = payload?.content;
@@ -2204,22 +2356,22 @@ function createTurn2(turnId, timestamp, model) {
2204
2356
  model
2205
2357
  };
2206
2358
  }
2207
- function extractPromptFromRecord(record) {
2208
- if (record.type === "event_msg" && isObject3(record.payload) && record.payload.type === "user_message" && typeof record.payload.message === "string") {
2209
- return normalizePromptText(record.payload.message);
2359
+ function extractPromptFromRecord(record2) {
2360
+ if (record2.type === "event_msg" && isObject3(record2.payload) && record2.payload.type === "user_message" && typeof record2.payload.message === "string") {
2361
+ return normalizePromptText(record2.payload.message);
2210
2362
  }
2211
- if (record.type === "response_item" && isObject3(record.payload) && record.payload.type === "message" && record.payload.role === "user") {
2212
- return normalizePromptText(extractMessageText(record.payload, "input_text"));
2363
+ if (record2.type === "response_item" && isObject3(record2.payload) && record2.payload.type === "message" && record2.payload.role === "user") {
2364
+ return normalizePromptText(extractMessageText(record2.payload, "input_text"));
2213
2365
  }
2214
2366
  return "";
2215
2367
  }
2216
- function recordOpensTurn(record, payload) {
2217
- if (record.type === "session_meta") return false;
2218
- if (record.type === "event_msg" && payload?.type === "task_started") return false;
2219
- if (record.type === "response_item" && payload?.type === "message") {
2368
+ function recordOpensTurn(record2, payload) {
2369
+ if (record2.type === "session_meta") return false;
2370
+ if (record2.type === "event_msg" && payload?.type === "task_started") return false;
2371
+ if (record2.type === "response_item" && payload?.type === "message") {
2220
2372
  if (payload.role === "developer" || payload.role === "system") return false;
2221
2373
  if (payload.role === "user") {
2222
- return extractPromptFromRecord(record) !== "" || extractResponseMessageImages(payload).length > 0 || extractResponseMessageAudio(payload).length > 0;
2374
+ return extractPromptFromRecord(record2) !== "" || extractResponseMessageImages(payload).length > 0 || extractResponseMessageAudio(payload).length > 0;
2223
2375
  }
2224
2376
  }
2225
2377
  return true;
@@ -2240,22 +2392,22 @@ function extractMetadataFromRecords(records) {
2240
2392
  let parentSessionId = null;
2241
2393
  let agentPath = "/root";
2242
2394
  let previousPrompt = "";
2243
- for (const record of records) {
2244
- if (!isObject3(record.payload)) continue;
2245
- if (record.type === "session_meta") {
2246
- sessionId ||= typeof record.payload.id === "string" ? record.payload.id : "";
2247
- version ||= typeof record.payload.cli_version === "string" ? record.payload.cli_version : "";
2248
- cwd ||= typeof record.payload.cwd === "string" ? record.payload.cwd : "";
2249
- if (!branchedFrom && isObject3(record.payload.branchedFrom)) {
2250
- const sourceId = typeof record.payload.branchedFrom.sessionId === "string" ? record.payload.branchedFrom.sessionId : "";
2395
+ for (const record2 of records) {
2396
+ if (!isObject3(record2.payload)) continue;
2397
+ if (record2.type === "session_meta") {
2398
+ sessionId ||= typeof record2.payload.id === "string" ? record2.payload.id : "";
2399
+ version ||= typeof record2.payload.cli_version === "string" ? record2.payload.cli_version : "";
2400
+ cwd ||= typeof record2.payload.cwd === "string" ? record2.payload.cwd : "";
2401
+ if (!branchedFrom && isObject3(record2.payload.branchedFrom)) {
2402
+ const sourceId = typeof record2.payload.branchedFrom.sessionId === "string" ? record2.payload.branchedFrom.sessionId : "";
2251
2403
  if (sourceId) {
2252
2404
  branchedFrom = {
2253
2405
  sessionId: sourceId,
2254
- turnIndex: typeof record.payload.branchedFrom.turnIndex === "number" ? record.payload.branchedFrom.turnIndex : null
2406
+ turnIndex: typeof record2.payload.branchedFrom.turnIndex === "number" ? record2.payload.branchedFrom.turnIndex : null
2255
2407
  };
2256
2408
  }
2257
2409
  }
2258
- const source = isObject3(record.payload.source) ? record.payload.source : null;
2410
+ const source = isObject3(record2.payload.source) ? record2.payload.source : null;
2259
2411
  if (source && isObject3(source.subagent)) {
2260
2412
  isSubagent = true;
2261
2413
  const threadSpawn = isObject3(source.subagent.thread_spawn) ? source.subagent.thread_spawn : null;
@@ -2263,25 +2415,23 @@ function extractMetadataFromRecords(records) {
2263
2415
  agentPath = threadSpawn.agent_path;
2264
2416
  }
2265
2417
  }
2266
- if (typeof record.payload.forked_from_id === "string" && record.payload.forked_from_id) {
2267
- parentSessionId = record.payload.forked_from_id;
2268
- }
2269
- const git = isObject3(record.payload.git) ? record.payload.git : null;
2418
+ parentSessionId = codexParentId(record2.payload) ?? parentSessionId;
2419
+ const git = isObject3(record2.payload.git) ? record2.payload.git : null;
2270
2420
  gitBranch ||= git && typeof git.branch === "string" ? git.branch : "";
2271
2421
  }
2272
- if (record.type === "turn_context") {
2273
- model ||= typeof record.payload.model === "string" ? record.payload.model : "";
2274
- cwd ||= typeof record.payload.cwd === "string" ? record.payload.cwd : "";
2422
+ if (record2.type === "turn_context") {
2423
+ model ||= typeof record2.payload.model === "string" ? record2.payload.model : "";
2424
+ cwd ||= typeof record2.payload.cwd === "string" ? record2.payload.cwd : "";
2275
2425
  }
2276
- const prompt = extractPromptFromRecord(record);
2426
+ const prompt = extractPromptFromRecord(record2);
2277
2427
  if (!prompt) continue;
2278
2428
  if (prompt === previousPrompt) continue;
2279
2429
  if (!firstUserMessage) firstUserMessage = prompt;
2280
2430
  lastUserMessage = prompt;
2281
2431
  previousPrompt = prompt;
2282
2432
  turnCount++;
2283
- if (!timestamp) timestamp = record.timestamp ?? "";
2284
- lastTimestamp = record.timestamp ?? lastTimestamp;
2433
+ if (!timestamp) timestamp = record2.timestamp ?? "";
2434
+ lastTimestamp = record2.timestamp ?? lastTimestamp;
2285
2435
  }
2286
2436
  if (lastTimestamp === "") lastTimestamp = timestamp;
2287
2437
  return {
@@ -2439,19 +2589,19 @@ function walkCodexRecords(records, options) {
2439
2589
  appendToolCall(targetTurn, toolCall, timestamp);
2440
2590
  }
2441
2591
  for (let index = 0; index < records.length; index++) {
2442
- const record = records[index];
2443
- if (!isCodexRecord(record)) continue;
2592
+ const record2 = records[index];
2593
+ if (!isCodexRecord(record2)) continue;
2444
2594
  recordIndex = index;
2445
- const payload = isObject3(record.payload) ? record.payload : void 0;
2446
- const timestamp = record.timestamp ?? "";
2447
- if (record.type === "compacted") {
2595
+ const payload = isObject3(record2.payload) ? record2.payload : void 0;
2596
+ const timestamp = record2.timestamp ?? "";
2597
+ if (record2.type === "compacted") {
2448
2598
  current = commitTurn(current);
2449
2599
  pendingCompaction = "Conversation compacted";
2450
2600
  lastTurnTimestamp = timestamp;
2451
2601
  continue;
2452
2602
  }
2453
- if (record.type === "world_state") continue;
2454
- if (record.type === "turn_context") {
2603
+ if (record2.type === "world_state") continue;
2604
+ if (record2.type === "turn_context") {
2455
2605
  current = commitTurn(current);
2456
2606
  pendingContextIndex = index;
2457
2607
  currentTurnId = typeof payload?.turn_id === "string" ? payload.turn_id : null;
@@ -2460,7 +2610,7 @@ function walkCodexRecords(records, options) {
2460
2610
  sawTurnStart = true;
2461
2611
  continue;
2462
2612
  }
2463
- if (record.type === "event_msg" && payload?.type === "user_message" && typeof payload.message === "string") {
2613
+ if (record2.type === "event_msg" && payload?.type === "user_message" && typeof payload.message === "string") {
2464
2614
  if (current && (current.assistantText.length > 0 || current.toolCalls.length > 0 || current.thinking.length > 0)) {
2465
2615
  current = commitTurn(current);
2466
2616
  }
@@ -2484,12 +2634,12 @@ function walkCodexRecords(records, options) {
2484
2634
  lastTurnTimestamp = timestamp;
2485
2635
  continue;
2486
2636
  }
2487
- if (!current && !recordOpensTurn(record, payload)) continue;
2637
+ if (!current && !recordOpensTurn(record2, payload)) continue;
2488
2638
  current ??= createActiveTurn(currentTurnId, timestamp, currentModel);
2489
2639
  if (!current.model && currentModel) current.model = currentModel;
2490
2640
  if (!current.timestamp) current.timestamp = timestamp;
2491
2641
  if (timestamp) lastTurnTimestamp = timestamp;
2492
- if (record.type === "event_msg" && payload?.type === "token_count") {
2642
+ if (record2.type === "event_msg" && payload?.type === "token_count") {
2493
2643
  const info = isObject3(payload.info) ? payload.info : null;
2494
2644
  const lastUsage = info ? parseTokenUsage(info.last_token_usage) : null;
2495
2645
  if (lastUsage) {
@@ -2497,14 +2647,14 @@ function walkCodexRecords(records, options) {
2497
2647
  }
2498
2648
  continue;
2499
2649
  }
2500
- if (record.type === "event_msg" && payload?.type === "web_search_end" && typeof payload.call_id === "string") {
2650
+ if (record2.type === "event_msg" && payload?.type === "web_search_end" && typeof payload.call_id === "string") {
2501
2651
  const input = {};
2502
2652
  if (typeof payload.query === "string" && payload.query) input.query = payload.query;
2503
2653
  if (isObject3(payload.action)) input.action = payload.action;
2504
2654
  upsertWebSearchCall(current, payload.call_id, input, "completed", timestamp);
2505
2655
  continue;
2506
2656
  }
2507
- if (record.type === "event_msg" && payload?.type === "mcp_tool_call_end" && typeof payload.call_id === "string" && isObject3(payload.invocation)) {
2657
+ if (record2.type === "event_msg" && payload?.type === "mcp_tool_call_end" && typeof payload.call_id === "string" && isObject3(payload.invocation)) {
2508
2658
  const invocation = payload.invocation;
2509
2659
  const server = typeof invocation.server === "string" ? invocation.server : "mcp";
2510
2660
  const tool = typeof invocation.tool === "string" ? invocation.tool : "tool";
@@ -2529,7 +2679,7 @@ function walkCodexRecords(records, options) {
2529
2679
  }
2530
2680
  continue;
2531
2681
  }
2532
- if (record.type === "event_msg" && payload?.type === "sub_agent_activity" && typeof payload.agent_thread_id === "string" && typeof payload.agent_path === "string") {
2682
+ if (record2.type === "event_msg" && payload?.type === "sub_agent_activity" && typeof payload.agent_thread_id === "string" && typeof payload.agent_path === "string") {
2533
2683
  const agentId = payload.agent_thread_id;
2534
2684
  const eventId2 = typeof payload.event_id === "string" ? payload.event_id : "";
2535
2685
  const kind = typeof payload.kind === "string" ? payload.kind : "started";
@@ -2563,7 +2713,7 @@ function walkCodexRecords(records, options) {
2563
2713
  upsertAgentMessage(agentId, current, timestamp, { status });
2564
2714
  continue;
2565
2715
  }
2566
- if (record.type !== "response_item" || !payload) continue;
2716
+ if (record2.type !== "response_item" || !payload) continue;
2567
2717
  if (payload.type === "reasoning") {
2568
2718
  const summary = Array.isArray(payload.summary) ? payload.summary.filter((item) => typeof item === "string").join("\n") : "";
2569
2719
  appendThinking(current, summary.trim(), timestamp);
@@ -2840,7 +2990,7 @@ function parseCodexSession(jsonlText, options) {
2840
2990
  return session;
2841
2991
  }
2842
2992
  function appendCodexSession(existing, newJsonlText) {
2843
- const prefix = existing.rawMessages.map((record) => JSON.stringify(record)).join("\n");
2993
+ const prefix = existing.rawMessages.map((record2) => JSON.stringify(record2)).join("\n");
2844
2994
  return parseCodexSession(prefix ? `${prefix}
2845
2995
  ${newJsonlText}` : newJsonlText);
2846
2996
  }
@@ -2991,11 +3141,11 @@ function deriveCodexSessionStatus(rawMessages) {
2991
3141
  }
2992
3142
  return { status: "idle" };
2993
3143
  }
2994
- function brandCodexBranch(firstRecord, sessionId, turnIndex) {
3144
+ function brandCodexBranch(firstRecord, sessionId, turnIndex, branchedAt) {
2995
3145
  const payload = isObject3(firstRecord.payload) ? { ...firstRecord.payload } : {};
2996
3146
  const originalId = typeof payload.id === "string" ? payload.id : "";
2997
3147
  payload.id = sessionId;
2998
- payload.branchedFrom = { sessionId: originalId, turnIndex };
3148
+ payload.branchedFrom = { sessionId: originalId, turnIndex, at: new Date(branchedAt).toISOString() };
2999
3149
  return { record: { ...firstRecord, payload }, originalId };
3000
3150
  }
3001
3151
  function codexTurnBoundaries(records) {
@@ -3757,7 +3907,7 @@ function parseCopilotSession(jsonlText, options) {
3757
3907
  };
3758
3908
  }
3759
3909
  function appendCopilotSession(existing, newJsonlText) {
3760
- const prefix = existing.rawMessages.map((record) => JSON.stringify(record)).join("\n");
3910
+ const prefix = existing.rawMessages.map((record2) => JSON.stringify(record2)).join("\n");
3761
3911
  return parseCopilotSession(prefix ? `${prefix}
3762
3912
  ${newJsonlText}` : newJsonlText);
3763
3913
  }
@@ -3865,11 +4015,11 @@ function brandCopilotBranch() {
3865
4015
  function copilotTurnBoundaries(records) {
3866
4016
  const boundaries = [];
3867
4017
  for (let index = 0; index < records.length; index++) {
3868
- const record = records[index];
3869
- if (record.type !== "user.message" || typeof record.agentId === "string") continue;
3870
- const data = record.data;
4018
+ const record2 = records[index];
4019
+ if (record2.type !== "user.message" || typeof record2.agentId === "string") continue;
4020
+ const data = record2.data;
3871
4021
  if (!data || typeof data !== "object" || Array.isArray(data)) continue;
3872
- if (typeof record.id !== "string" || !record.id) continue;
4022
+ if (typeof record2.id !== "string" || !record2.id) continue;
3873
4023
  boundaries.push(index);
3874
4024
  }
3875
4025
  return boundaries;
@@ -3915,6 +4065,15 @@ var claudeFormat = {
3915
4065
  turnBoundaries: claudeTurnBoundaries
3916
4066
  };
3917
4067
  var FORMATS = Object.freeze({
4068
+ acp: {
4069
+ ...claudeFormat,
4070
+ kind: "acp",
4071
+ descriptor: descriptorFor("acp"),
4072
+ detectsText: (text) => text.split("\n", 1)[0]?.includes('"type":"acp_session"') ?? false,
4073
+ detectsRecords: (records) => records[0]?.type === "acp_session",
4074
+ parse: (text, options) => ({ ...parseClaudeSession(text, options), agentKind: "acp" }),
4075
+ append: (session, text) => ({ ...appendClaudeSession(session, text), agentKind: "acp" })
4076
+ },
3918
4077
  claude: claudeFormat,
3919
4078
  codex: codexFormat,
3920
4079
  copilot: copilotFormat
@@ -3954,8 +4113,8 @@ var import_node_path2 = require("node:path");
3954
4113
  var import_node_path = require("node:path");
3955
4114
  var import_node_os = require("node:os");
3956
4115
  function agentHomeDir(kind, env = process.env, homeDirectory = (0, import_node_os.homedir)()) {
3957
- const { homeEnvVar, homeDirName } = descriptorFor(kind).cli;
3958
- const override = homeEnvVar ? env[homeEnvVar] : void 0;
4116
+ const { homeEnvVar, homeDirName, sdkHomeEnvVar } = descriptorFor(kind).cli;
4117
+ const override = env[homeEnvVar ?? sdkHomeEnvVar ?? ""];
3959
4118
  return override || (0, import_node_path.join)(homeDirectory, homeDirName);
3960
4119
  }
3961
4120
  var dirs = {
@@ -3963,7 +4122,8 @@ var dirs = {
3963
4122
  TEAMS_DIR: (0, import_node_path.join)(agentHomeDir("claude"), "teams"),
3964
4123
  TASKS_DIR: (0, import_node_path.join)(agentHomeDir("claude"), "tasks"),
3965
4124
  CODEX_SESSIONS_DIR: (0, import_node_path.join)(agentHomeDir("codex"), "sessions"),
3966
- COPILOT_SESSIONS_DIR: (0, import_node_path.join)(agentHomeDir("copilot"), "session-state")
4125
+ COPILOT_SESSIONS_DIR: (0, import_node_path.join)(agentHomeDir("copilot"), "session-state"),
4126
+ ACP_PROJECTS_DIR: (0, import_node_path.join)(agentHomeDir("acp"), "projects")
3967
4127
  };
3968
4128
  var DEFAULT_DB_PATH = (0, import_node_path.join)(agentHomeDir("claude"), "cogpit-memory", "search-index.db");
3969
4129
 
@@ -4151,24 +4311,48 @@ var WALKERS = {
4151
4311
  const candidate = copilotTranscript(root, sessionId);
4152
4312
  return mtimeOf(candidate) === null ? null : candidate;
4153
4313
  }
4314
+ },
4315
+ acp: {
4316
+ root: () => dirs.ACP_PROJECTS_DIR,
4317
+ list: walkClaude,
4318
+ identify: claudeIdentify,
4319
+ find: (root, id) => WALKERS.claude.find(root, id)
4154
4320
  }
4155
4321
  };
4156
- function makeStore(kind) {
4322
+ function makeStore(kind, home, configuredId) {
4157
4323
  const walker = WALKERS[kind];
4324
+ const instanceId = configuredId ?? (process.env.COGPIT_WORKER_AGENT === kind ? process.env.COGPIT_AGENT_INSTANCE_ID : void 0) ?? "default";
4325
+ const root = home ? () => (0, import_node_path2.join)(home, kind === "codex" ? "sessions" : kind === "copilot" ? "session-state" : "projects") : walker.root;
4326
+ const identify = (filePath, directory = root()) => {
4327
+ const identity = walker.identify(filePath, directory);
4328
+ return { ...identity, sessionId: instanceSessionId(instanceId, identity.sessionId), parentSessionId: identity.parentSessionId ? instanceSessionId(instanceId, identity.parentSessionId) : null };
4329
+ };
4158
4330
  return {
4159
4331
  kind,
4332
+ instanceId,
4160
4333
  descriptor: descriptorFor(kind),
4161
- root: walker.root,
4162
- ownsPath: (filePath) => isWithin((0, import_node_path2.resolve)(walker.root()), (0, import_node_path2.resolve)(filePath)),
4163
- identify: (filePath, root = walker.root()) => walker.identify(filePath, root),
4164
- list: (cutoffMs, root = walker.root()) => walker.list(root, cutoffMs),
4165
- find: (sessionId) => walker.find(walker.root(), sessionId)
4334
+ root,
4335
+ ownsPath: (filePath) => {
4336
+ if (isWithin((0, import_node_path2.resolve)(root()), (0, import_node_path2.resolve)(filePath))) return true;
4337
+ try {
4338
+ return isWithin((0, import_node_fs.realpathSync)(root()), (0, import_node_fs.realpathSync)(filePath));
4339
+ } catch {
4340
+ return false;
4341
+ }
4342
+ },
4343
+ identify,
4344
+ list: (cutoffMs, directory = root()) => walker.list(directory, cutoffMs).map((file) => ({ ...file, ...identify(file.path, directory) })),
4345
+ find: (sessionId) => {
4346
+ const parts = splitInstanceSessionId(sessionId);
4347
+ return parts.instanceId === instanceId ? walker.find(root(), parts.nativeId) : null;
4348
+ }
4166
4349
  };
4167
4350
  }
4168
4351
  var STORES = {
4169
4352
  claude: makeStore("claude"),
4170
4353
  codex: makeStore("codex"),
4171
- copilot: makeStore("copilot")
4354
+ copilot: makeStore("copilot"),
4355
+ acp: makeStore("acp")
4172
4356
  };
4173
4357
  function storeFor(kind) {
4174
4358
  return STORES[kind];
@@ -4177,7 +4361,20 @@ function defaultStore() {
4177
4361
  return storeFor(descriptorForDirName(null).kind);
4178
4362
  }
4179
4363
  function allStores() {
4180
- return AGENT_KINDS.map((kind) => STORES[kind]);
4364
+ if (process.env.COGPIT_AGENT_INSTANCE_ID && AGENT_KINDS.includes(process.env.COGPIT_WORKER_AGENT)) return [STORES[process.env.COGPIT_WORKER_AGENT]];
4365
+ const stores = AGENT_KINDS.map((kind) => STORES[kind]);
4366
+ const root = process.env.COGPIT_ORCHESTRATION_ROOT;
4367
+ if (!root) return stores;
4368
+ for (const entry of readDirSafe((0, import_node_path2.join)(root, "provider-instances"))) {
4369
+ if (!entry.isDirectory()) continue;
4370
+ const home = (0, import_node_path2.join)(root, "provider-instances", entry.name);
4371
+ try {
4372
+ const config = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path2.join)(home, "config.json"), "utf8"));
4373
+ if (config.providerInstance?.id === entry.name && config.providerInstance.agent && AGENT_KINDS.includes(config.providerInstance.agent)) stores.push(makeStore(config.providerInstance.agent, home, entry.name));
4374
+ } catch {
4375
+ }
4376
+ }
4377
+ return stores;
4181
4378
  }
4182
4379
  function storeForPath(filePath) {
4183
4380
  return allStores().find((store) => store.ownsPath(filePath)) ?? null;
@@ -4571,6 +4768,11 @@ function parseMaxAge(raw) {
4571
4768
  }
4572
4769
  }
4573
4770
 
4771
+ // src/lib/read-session.ts
4772
+ function parseTranscript(content, filePath) {
4773
+ return scopeParsedSession(parseSession(content), storeForPath(filePath)?.instanceId ?? "default");
4774
+ }
4775
+
4574
4776
  // src/commands/search.ts
4575
4777
  async function searchSessions(query, opts, searchIndex) {
4576
4778
  if (!query || query.length < 2) {
@@ -4817,7 +5019,7 @@ async function walkSubagentFiles(parentJsonlPath, query, caseSensitive, currentD
4817
5019
  );
4818
5020
  const hitArrays = await Promise.all(
4819
5021
  matched.filter((m) => m.rawContent).map(async ({ agentId, filePath, rawContent }) => {
4820
- const subSession = parseSession(rawContent);
5022
+ const subSession = parseTranscript(rawContent, filePath);
4821
5023
  const subHits = walkSession(subSession, query, caseSensitive, `agent/${agentId}/`);
4822
5024
  const nestedHits = await walkSubagentFiles(filePath, query, caseSensitive, currentDepth + 1, maxDepth);
4823
5025
  return [...subHits, ...nestedHits];
@@ -4838,7 +5040,7 @@ async function rawScanSearch(query, sessionId, maxAgeMs, limit, caseSensitive, e
4838
5040
  const rawContent = await rawTextMatch(file.path, query, caseSensitive);
4839
5041
  sessionsSearched++;
4840
5042
  if (!rawContent) continue;
4841
- const session = parseSession(rawContent);
5043
+ const session = parseTranscript(rawContent, file.path);
4842
5044
  const sessionHits = walkSession(session, query, caseSensitive);
4843
5045
  const subagentHits = await walkSubagentFiles(file.path, query, caseSensitive, 0, MAX_SUBAGENT_DEPTH);
4844
5046
  const allHits = [...sessionHits, ...subagentHits];
@@ -5090,6 +5292,8 @@ function mapContentBlock(block) {
5090
5292
  return { kind: "recap", content: block.content, timestamp: block.timestamp ?? null };
5091
5293
  case "task_notification":
5092
5294
  return { kind: "task_notification", content: block.content, timestamp: block.timestamp ?? null };
5295
+ case "local_command":
5296
+ return { kind: "local_command", content: block.content, timestamp: block.timestamp ?? null };
5093
5297
  default: {
5094
5298
  const exhaustive = block;
5095
5299
  return exhaustive;
@@ -5179,14 +5383,14 @@ async function getSessionOverview(sessionId) {
5179
5383
  const jsonlPath = await findSessionFile(sessionId);
5180
5384
  if (!jsonlPath) return { error: "Session not found" };
5181
5385
  const content = await (0, import_promises3.readFile)(jsonlPath, "utf-8");
5182
- const session = parseSession(content);
5386
+ const session = parseTranscript(content, jsonlPath);
5183
5387
  return mapSessionToOverview(session);
5184
5388
  }
5185
5389
  async function getTurnDetail(sessionId, turnIndex) {
5186
5390
  const jsonlPath = await findSessionFile(sessionId);
5187
5391
  if (!jsonlPath) return { error: "Session not found" };
5188
5392
  const content = await (0, import_promises3.readFile)(jsonlPath, "utf-8");
5189
- const session = parseSession(content);
5393
+ const session = parseTranscript(content, jsonlPath);
5190
5394
  if (turnIndex < 0 || turnIndex >= session.turns.length) return { error: "Turn not found" };
5191
5395
  return mapTurnToDetail(session, turnIndex);
5192
5396
  }
@@ -5194,11 +5398,11 @@ async function getAgentOverview(sessionId, agentId) {
5194
5398
  const jsonlPath = await findSessionFile(sessionId);
5195
5399
  if (!jsonlPath) return { error: "Session not found" };
5196
5400
  const content = await (0, import_promises3.readFile)(jsonlPath, "utf-8");
5197
- const session = parseSession(content);
5401
+ const session = parseTranscript(content, jsonlPath);
5198
5402
  const subagentFile = await findSubagentFile(jsonlPath, agentId);
5199
5403
  if (!subagentFile) return { error: "Agent not found" };
5200
5404
  const subagentContent = await (0, import_promises3.readFile)(subagentFile.filePath, "utf-8");
5201
- const subagentSession = parseSession(subagentContent);
5405
+ const subagentSession = parseTranscript(subagentContent, subagentFile.filePath);
5202
5406
  const metadata = findAgentMetadata(session, agentId);
5203
5407
  const parentToolCallId = findParentToolCallId(session, agentId);
5204
5408
  const teamContext = await findTeamContext(sessionId, subagentFile.fileName);
@@ -5219,7 +5423,7 @@ async function getAgentTurnDetail(sessionId, agentId, turnIndex) {
5219
5423
  const subagentFile = await findSubagentFile(jsonlPath, agentId);
5220
5424
  if (!subagentFile) return { error: "Agent not found" };
5221
5425
  const subagentContent = await (0, import_promises3.readFile)(subagentFile.filePath, "utf-8");
5222
- const subagentSession = parseSession(subagentContent);
5426
+ const subagentSession = parseTranscript(subagentContent, subagentFile.filePath);
5223
5427
  if (turnIndex < 0 || turnIndex >= subagentSession.turns.length) return { error: "Turn not found" };
5224
5428
  return mapTurnToDetail(subagentSession, turnIndex);
5225
5429
  }
@@ -5253,15 +5457,15 @@ function scanClaudeMessages(lines) {
5253
5457
  turnCount: 0
5254
5458
  };
5255
5459
  for (const line of lines) {
5256
- let record;
5460
+ let record2;
5257
5461
  try {
5258
- record = JSON.parse(line);
5462
+ record2 = JSON.parse(line);
5259
5463
  } catch {
5260
5464
  continue;
5261
5465
  }
5262
- if (record.type !== "user" || record.isMeta) continue;
5263
- if (!meta.timestamp) meta.timestamp = record.timestamp || "";
5264
- const content = record.message?.content;
5466
+ if (record2.type !== "user" || record2.isMeta) continue;
5467
+ if (!meta.timestamp) meta.timestamp = record2.timestamp || "";
5468
+ const content = record2.message?.content;
5265
5469
  let extracted = "";
5266
5470
  if (typeof content === "string") {
5267
5471
  extracted = cleanUserText(content);
@@ -5308,6 +5512,7 @@ async function getSessionMeta(filePath) {
5308
5512
  isPartialRead = false;
5309
5513
  }
5310
5514
  const header = readHeader(format, lines);
5515
+ const instanceId = storeForPath(filePath)?.instanceId ?? "default";
5311
5516
  const conversation = header.turnCount === void 0 ? scanClaudeMessages(lines) : {
5312
5517
  firstUserMessage: header.firstUserMessage ?? "",
5313
5518
  lastUserMessage: header.lastUserMessage ?? "",
@@ -5315,7 +5520,7 @@ async function getSessionMeta(filePath) {
5315
5520
  turnCount: header.turnCount
5316
5521
  };
5317
5522
  return {
5318
- sessionId: header.sessionId,
5523
+ sessionId: instanceSessionId(instanceId, header.sessionId),
5319
5524
  version: header.version,
5320
5525
  gitBranch: header.gitBranch,
5321
5526
  model: header.model,
@@ -5325,7 +5530,7 @@ async function getSessionMeta(filePath) {
5325
5530
  // A head read saw a known number of lines in a known number of bytes;
5326
5531
  // scaling that by the file size is the only estimate available.
5327
5532
  lineCount: isPartialRead ? Math.round(fileStat.size / (HEAD_READ_BYTES / lines.length)) : lines.length,
5328
- branchedFrom: header.branchedFrom
5533
+ branchedFrom: header.branchedFrom ? { ...header.branchedFrom, sessionId: instanceSessionId(instanceId, header.branchedFrom.sessionId) } : void 0
5329
5534
  };
5330
5535
  }
5331
5536
  async function getSessionStatus(filePath) {
@@ -5420,7 +5625,7 @@ async function getSessionStatus(filePath) {
5420
5625
  // src/commands/sessions.ts
5421
5626
  function toSessionSummary(file, meta, status) {
5422
5627
  return {
5423
- sessionId: meta.sessionId,
5628
+ sessionId: file.sessionId || meta.sessionId,
5424
5629
  filePath: file.path,
5425
5630
  timestamp: meta.timestamp,
5426
5631
  model: meta.model,