cogpit-memory 0.3.0 → 0.4.1
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/README.md +2 -2
- package/dist/cli.js +650 -261
- package/dist/index.js +647 -257
- package/dist/lib/agent-descriptors.d.ts +43 -1
- package/dist/lib/agents.d.ts +37 -12
- package/dist/lib/backgroundAgents.d.ts +16 -0
- package/dist/lib/claude.d.ts +10 -0
- package/dist/lib/codex.d.ts +38 -2
- package/dist/lib/dirs.d.ts +1 -0
- package/dist/lib/instances.d.ts +12 -0
- package/dist/lib/read-session.d.ts +1 -0
- package/dist/lib/stores.d.ts +1 -0
- package/dist/lib/toolResults.d.ts +12 -0
- package/dist/lib/transcriptFile.d.ts +22 -0
- package/dist/lib/types.d.ts +162 -6
- package/package.json +1 -1
- package/skill/SKILL.md +26 -338
package/dist/cli.js
CHANGED
|
@@ -36,8 +36,8 @@ __export(cli_exports, {
|
|
|
36
36
|
module.exports = __toCommonJS(cli_exports);
|
|
37
37
|
|
|
38
38
|
// src/commands/search.ts
|
|
39
|
-
var
|
|
40
|
-
var
|
|
39
|
+
var import_node_fs4 = require("node:fs");
|
|
40
|
+
var import_promises2 = require("node:fs/promises");
|
|
41
41
|
var import_node_path3 = require("node:path");
|
|
42
42
|
|
|
43
43
|
// src/lib/sqlite-node-shim.ts
|
|
@@ -45,10 +45,39 @@ var import_better_sqlite3 = __toESM(require("better-sqlite3"));
|
|
|
45
45
|
var Database = import_better_sqlite3.default;
|
|
46
46
|
|
|
47
47
|
// src/lib/search-index.ts
|
|
48
|
-
var
|
|
48
|
+
var import_node_fs3 = 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,14 +104,26 @@ 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
|
};
|
|
81
114
|
}
|
|
82
115
|
function sessionIdFromJsonlName(fileName) {
|
|
83
|
-
const match = new RegExp(`(${SESSION_UUID})\\.jsonl
|
|
116
|
+
const match = new RegExp(`(${SESSION_UUID})\\.jsonl(?:\\.zst)?$`, "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,9 +279,12 @@ 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
|
-
midTurnSteering:
|
|
286
|
+
midTurnSteering: true,
|
|
287
|
+
activeNotifications: true,
|
|
227
288
|
stopSession: true,
|
|
228
289
|
ultracode: true,
|
|
229
290
|
contextWindow: true,
|
|
@@ -231,6 +292,7 @@ var claude = {
|
|
|
231
292
|
accountSwitching: true,
|
|
232
293
|
modelFallbackNotices: true,
|
|
233
294
|
autoPermissionMode: "per-model",
|
|
295
|
+
toolPermissionRules: true,
|
|
234
296
|
settingsApply: "live",
|
|
235
297
|
turnLiveness: "transcript",
|
|
236
298
|
tokenStreaming: true,
|
|
@@ -247,6 +309,7 @@ var codex = {
|
|
|
247
309
|
displayName: "Codex",
|
|
248
310
|
binName: "codex",
|
|
249
311
|
dirName: base64DirNameCodec(CODEX_DIR_PREFIX),
|
|
312
|
+
projectWorktreeDir: null,
|
|
250
313
|
sessionFile: {
|
|
251
314
|
/**
|
|
252
315
|
* Codex nests rollouts by local date:
|
|
@@ -263,7 +326,9 @@ var codex = {
|
|
|
263
326
|
},
|
|
264
327
|
sessionId: sessionIdFromJsonlName,
|
|
265
328
|
// The date nesting is unrecoverable from the id, so the URL keeps the path.
|
|
266
|
-
...jsonlPathUrlCodec
|
|
329
|
+
...jsonlPathUrlCodec,
|
|
330
|
+
// A sub-agent is a rollout of its own, linked to its parent only in its header.
|
|
331
|
+
transcriptRoot: (relativePath) => plainPathSegments(relativePath) ? ownTranscriptRoot(sessionIdFromJsonlName(relativePath)) : null
|
|
267
332
|
},
|
|
268
333
|
// A rollout's header carries no turn count and no last user message; both
|
|
269
334
|
// only settle at the end of the file.
|
|
@@ -276,6 +341,7 @@ var codex = {
|
|
|
276
341
|
permissions: (permissions) => {
|
|
277
342
|
const mode = permissions?.mode || "default";
|
|
278
343
|
if (mode === "bypassPermissions") return ["--dangerously-bypass-approvals-and-sandbox"];
|
|
344
|
+
if (mode === "auto") return ["--approve-for-me"];
|
|
279
345
|
return [
|
|
280
346
|
"--sandbox",
|
|
281
347
|
mode === "plan" ? "read-only" : "workspace-write",
|
|
@@ -304,6 +370,7 @@ var codex = {
|
|
|
304
370
|
bundledBySdk: false,
|
|
305
371
|
homeDirName: ".codex",
|
|
306
372
|
homeEnvVar: "CODEX_HOME",
|
|
373
|
+
sessionIdEnvVar: "CODEX_THREAD_ID",
|
|
307
374
|
installMarker: "",
|
|
308
375
|
homeIsDiscoverable: true,
|
|
309
376
|
process: {
|
|
@@ -335,7 +402,7 @@ var codex = {
|
|
|
335
402
|
extendedContext: null
|
|
336
403
|
},
|
|
337
404
|
capabilities: {
|
|
338
|
-
worktrees:
|
|
405
|
+
worktrees: true,
|
|
339
406
|
mcp: false,
|
|
340
407
|
slashCommands: false,
|
|
341
408
|
workflows: false,
|
|
@@ -349,16 +416,20 @@ var codex = {
|
|
|
349
416
|
nativeRewind: false,
|
|
350
417
|
fileCheckpoints: false,
|
|
351
418
|
reasoningEffort: true,
|
|
419
|
+
configurableContextWindow: true,
|
|
352
420
|
fastTier: true,
|
|
421
|
+
lowPriorityInTerminal: false,
|
|
353
422
|
imageInput: true,
|
|
354
423
|
midTurnSteering: true,
|
|
424
|
+
activeNotifications: true,
|
|
355
425
|
stopSession: false,
|
|
356
426
|
ultracode: false,
|
|
357
427
|
contextWindow: false,
|
|
358
428
|
externalProcesses: false,
|
|
359
429
|
accountSwitching: false,
|
|
360
430
|
modelFallbackNotices: false,
|
|
361
|
-
autoPermissionMode: "
|
|
431
|
+
autoPermissionMode: "always",
|
|
432
|
+
toolPermissionRules: true,
|
|
362
433
|
settingsApply: "next-turn",
|
|
363
434
|
turnLiveness: "runtime",
|
|
364
435
|
tokenStreaming: true,
|
|
@@ -367,24 +438,25 @@ var codex = {
|
|
|
367
438
|
namedSessions: false
|
|
368
439
|
}
|
|
369
440
|
};
|
|
441
|
+
function copilotSessionId(fileName) {
|
|
442
|
+
const match = new RegExp(`^(${SESSION_UUID})/events\\.jsonl$`, "i").exec(fileName);
|
|
443
|
+
return match ? match[1] : null;
|
|
444
|
+
}
|
|
370
445
|
var copilot = {
|
|
371
446
|
kind: "copilot",
|
|
372
447
|
displayName: "GitHub Copilot CLI",
|
|
373
448
|
binName: "copilot",
|
|
374
449
|
dirName: base64DirNameCodec(COPILOT_DIR_PREFIX),
|
|
450
|
+
projectWorktreeDir: null,
|
|
375
451
|
sessionFile: {
|
|
376
452
|
/** Copilot keeps one directory per session: `<uuid>/events.jsonl`. */
|
|
377
453
|
name: (sessionId) => `${sessionId}/events.jsonl`,
|
|
378
|
-
sessionId:
|
|
379
|
-
const match = new RegExp(`^(${SESSION_UUID})/events\\.jsonl$`, "i").exec(fileName);
|
|
380
|
-
return match ? match[1] : null;
|
|
381
|
-
},
|
|
454
|
+
sessionId: copilotSessionId,
|
|
382
455
|
// `<id>/events.jsonl` rebuilds from the id, so URLs stay short.
|
|
383
|
-
urlId: (fileName) =>
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
fileNameFromUrlId: (urlId) => urlId.endsWith(".jsonl") ? urlId : `${urlId}/events.jsonl`
|
|
456
|
+
urlId: (fileName) => copilotSessionId(fileName) ?? fileName.replace(/\.jsonl$/, ""),
|
|
457
|
+
fileNameFromUrlId: (urlId) => urlId.endsWith(".jsonl") ? urlId : `${urlId}/events.jsonl`,
|
|
458
|
+
// Sub-agents are events inside the parent transcript, so every file is a root.
|
|
459
|
+
transcriptRoot: (relativePath) => ownTranscriptRoot(copilotSessionId(relativePath))
|
|
388
460
|
},
|
|
389
461
|
metadataFromHead: true,
|
|
390
462
|
resume: {
|
|
@@ -466,9 +538,12 @@ var copilot = {
|
|
|
466
538
|
fileCheckpoints: false,
|
|
467
539
|
// Effort is only offered for models whose catalog entry advertises it.
|
|
468
540
|
reasoningEffort: false,
|
|
541
|
+
configurableContextWindow: false,
|
|
469
542
|
fastTier: false,
|
|
543
|
+
lowPriorityInTerminal: false,
|
|
470
544
|
imageInput: false,
|
|
471
545
|
midTurnSteering: true,
|
|
546
|
+
activeNotifications: true,
|
|
472
547
|
stopSession: true,
|
|
473
548
|
ultracode: false,
|
|
474
549
|
contextWindow: false,
|
|
@@ -476,6 +551,7 @@ var copilot = {
|
|
|
476
551
|
accountSwitching: false,
|
|
477
552
|
modelFallbackNotices: false,
|
|
478
553
|
autoPermissionMode: "always",
|
|
554
|
+
toolPermissionRules: true,
|
|
479
555
|
settingsApply: "next-turn",
|
|
480
556
|
turnLiveness: "runtime",
|
|
481
557
|
tokenStreaming: false,
|
|
@@ -483,21 +559,56 @@ var copilot = {
|
|
|
483
559
|
namedSessions: true
|
|
484
560
|
}
|
|
485
561
|
};
|
|
562
|
+
var acp = {
|
|
563
|
+
...copilot,
|
|
564
|
+
kind: "acp",
|
|
565
|
+
displayName: "ACP agent",
|
|
566
|
+
binName: "acp",
|
|
567
|
+
dirName: base64DirNameCodec("acp__"),
|
|
568
|
+
sessionFile: { name: (id) => `${id}.jsonl`, sessionId: sessionIdFromJsonlName, ...jsonlPathUrlCodec, transcriptRoot: (path) => {
|
|
569
|
+
const segments = plainPathSegments(path);
|
|
570
|
+
return segments?.length === 1 ? ownTranscriptRoot(sessionIdFromJsonlName(path)) : null;
|
|
571
|
+
} },
|
|
572
|
+
resume: { command: () => "Resume this session in Cogpit", args: () => [] },
|
|
573
|
+
launchArgs: { permissions: () => [], model: () => [], effort: () => [], fastTier: () => [] },
|
|
574
|
+
cli: { ...copilot.cli, packageName: "", homebrew: null, wingetId: null, selfUpdate: null, requiresConfiguration: true, homeDirName: ".cogpit-acp", homeEnvVar: "COGPIT_ACP_HOME", homeIsDiscoverable: true, installMarker: "projects" },
|
|
575
|
+
config: { ...copilot.config, rootDirName: ".cogpit-acp", instructions: [], settings: [], skillsDir: null },
|
|
576
|
+
capabilities: { ...copilot.capabilities, undo: false, redo: false, nativeRewind: false, nativeFork: false, midTurnSteering: false, activeNotifications: false, externalProcesses: false, autoPermissionMode: "never", toolPermissionRules: false, namedSessions: false }
|
|
577
|
+
};
|
|
486
578
|
var DESCRIPTORS = Object.freeze({
|
|
487
579
|
claude,
|
|
488
580
|
codex,
|
|
489
|
-
copilot
|
|
581
|
+
copilot,
|
|
582
|
+
acp
|
|
490
583
|
});
|
|
491
584
|
function descriptorFor(kind) {
|
|
492
585
|
return DESCRIPTORS[kind];
|
|
493
586
|
}
|
|
494
587
|
function descriptorForDirName(dirName) {
|
|
588
|
+
const scoped = splitInstanceDirName(dirName);
|
|
589
|
+
if (scoped.instanceId !== "default") {
|
|
590
|
+
const descriptor = descriptorForDirName(scoped.nativeDirName);
|
|
591
|
+
return {
|
|
592
|
+
...descriptor,
|
|
593
|
+
dirName: { ...descriptor.dirName, encode: (cwd) => instanceDirName(scoped.instanceId, descriptor.dirName.encode(cwd)), decode: (name) => descriptor.dirName.decode(splitInstanceDirName(name).nativeDirName) },
|
|
594
|
+
sessionFile: { ...descriptor.sessionFile, name: (id) => descriptor.sessionFile.name(splitInstanceSessionId(id).nativeId), sessionId: (file) => {
|
|
595
|
+
const nativeId = descriptor.sessionFile.sessionId(file);
|
|
596
|
+
return nativeId ? instanceSessionId(scoped.instanceId, nativeId) : null;
|
|
597
|
+
}, transcriptRoot: (file) => {
|
|
598
|
+
const root = descriptor.sessionFile.transcriptRoot(file);
|
|
599
|
+
return root ? { ...root, rootSessionId: instanceSessionId(scoped.instanceId, root.rootSessionId) } : null;
|
|
600
|
+
} }
|
|
601
|
+
};
|
|
602
|
+
}
|
|
495
603
|
for (const kind of AGENT_KINDS) {
|
|
496
604
|
if (kind === "claude") continue;
|
|
497
605
|
if (DESCRIPTORS[kind].dirName.owns(dirName)) return DESCRIPTORS[kind];
|
|
498
606
|
}
|
|
499
607
|
return claude;
|
|
500
608
|
}
|
|
609
|
+
function isSessionUuid(value) {
|
|
610
|
+
return SESSION_UUID_RE.test(value);
|
|
611
|
+
}
|
|
501
612
|
|
|
502
613
|
// src/lib/codex-exec.ts
|
|
503
614
|
var JS_SIMPLE_ESCAPES = {
|
|
@@ -566,6 +677,27 @@ function readJsStringLiteral(source, startIndex) {
|
|
|
566
677
|
return null;
|
|
567
678
|
}
|
|
568
679
|
|
|
680
|
+
// src/lib/backgroundAgents.ts
|
|
681
|
+
function awaitingAgentsStatus(launches, pendingQueue = 0) {
|
|
682
|
+
if (!launches.some((launch) => launch.endedAt === void 0)) return null;
|
|
683
|
+
const ordered = [...launches].sort((a, b) => a.launchedAt - b.launchedAt);
|
|
684
|
+
let batchStart = 0;
|
|
685
|
+
let busyUntil = -Infinity;
|
|
686
|
+
ordered.forEach((launch, index) => {
|
|
687
|
+
if (busyUntil < launch.launchedAt) batchStart = index;
|
|
688
|
+
busyUntil = Math.max(busyUntil, launch.endedAt ?? Infinity);
|
|
689
|
+
});
|
|
690
|
+
const batch = ordered.slice(batchStart).map(({ agent, endedAt }) => ({ ...agent, done: endedAt !== void 0 }));
|
|
691
|
+
const running = batch.filter((agent) => !agent.done);
|
|
692
|
+
return {
|
|
693
|
+
status: "awaiting_agents",
|
|
694
|
+
pendingQueue,
|
|
695
|
+
pendingAgents: running.length,
|
|
696
|
+
pendingAgentDescriptions: running.map((agent) => agent.description).filter((description) => description.length > 0),
|
|
697
|
+
backgroundAgents: batch
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
|
|
569
701
|
// src/lib/messageTypeGuards.ts
|
|
570
702
|
function isUserMessage(msg) {
|
|
571
703
|
return msg.type === "user";
|
|
@@ -673,6 +805,36 @@ function parseAgentEnvelope(text) {
|
|
|
673
805
|
return { sender, matched, body: matched ? unwrapped.trim() : text };
|
|
674
806
|
}
|
|
675
807
|
|
|
808
|
+
// src/lib/toolResults.ts
|
|
809
|
+
function record(value) {
|
|
810
|
+
return value && typeof value === "object" ? value : {};
|
|
811
|
+
}
|
|
812
|
+
function toolResultMetadata(name, value) {
|
|
813
|
+
const result = record(value);
|
|
814
|
+
if (name === "Edit" || name === "Write") {
|
|
815
|
+
return result.staged === true ? { awaitingReview: true } : {};
|
|
816
|
+
}
|
|
817
|
+
if (name !== "Bash") return {};
|
|
818
|
+
const diff = record(result.bashEditDiff);
|
|
819
|
+
if (!Array.isArray(diff.files)) return {};
|
|
820
|
+
const files = [];
|
|
821
|
+
for (const raw of diff.files) {
|
|
822
|
+
const file = record(raw);
|
|
823
|
+
if (typeof file.filePath !== "string" || !file.filePath || !Array.isArray(file.hunks)) continue;
|
|
824
|
+
const hunks = [];
|
|
825
|
+
for (const rawHunk of file.hunks) {
|
|
826
|
+
const hunk = record(rawHunk);
|
|
827
|
+
const { oldStart, oldLines, newStart, newLines, lines } = hunk;
|
|
828
|
+
if (![oldStart, oldLines, newStart, newLines].every((n) => typeof n === "number" && Number.isInteger(n) && n >= 0)) continue;
|
|
829
|
+
if (!Array.isArray(lines) || !lines.every((line) => typeof line === "string" && /^[ +\\-]/.test(line))) continue;
|
|
830
|
+
hunks.push({ oldStart, oldLines, newStart, newLines, lines });
|
|
831
|
+
}
|
|
832
|
+
if (hunks.length) files.push({ filePath: file.filePath, hunks });
|
|
833
|
+
}
|
|
834
|
+
const more = typeof diff.moreFiles === "number" && Number.isInteger(diff.moreFiles) && diff.moreFiles > 0 ? diff.moreFiles : 0;
|
|
835
|
+
return { fileDiffs: files, ...more ? { additionalFileDiffs: more } : {} };
|
|
836
|
+
}
|
|
837
|
+
|
|
676
838
|
// src/lib/turnBuilder.ts
|
|
677
839
|
function extractTextFromContent(content) {
|
|
678
840
|
if (typeof content === "string") return content;
|
|
@@ -700,6 +862,17 @@ function isTaskNotificationRecord(msg) {
|
|
|
700
862
|
if (!text.includes("<task-notification>")) return false;
|
|
701
863
|
return text.replace(TASK_NOTIFICATION_BLOCK_RE, "").replace(ANY_TAG_RE, "").trim() === "";
|
|
702
864
|
}
|
|
865
|
+
function isTaskToolName(name) {
|
|
866
|
+
return name === "Task" || name === "Agent";
|
|
867
|
+
}
|
|
868
|
+
function readTaskMeta(input) {
|
|
869
|
+
const effort = input.effort;
|
|
870
|
+
return {
|
|
871
|
+
name: typeof input.name === "string" ? input.name : null,
|
|
872
|
+
subagentType: typeof input.subagent_type === "string" ? input.subagent_type : null,
|
|
873
|
+
effort: typeof effort === "string" || typeof effort === "number" ? String(effort) : null
|
|
874
|
+
};
|
|
875
|
+
}
|
|
703
876
|
function queuedCommandPrompt(msg) {
|
|
704
877
|
if (!isAttachmentMessage(msg)) return null;
|
|
705
878
|
const attachment = msg.attachment;
|
|
@@ -1040,6 +1213,16 @@ function buildTurnsWithStarts(messages) {
|
|
|
1040
1213
|
pendingRecap = { content: msg.content, timestamp: msg.timestamp };
|
|
1041
1214
|
continue;
|
|
1042
1215
|
}
|
|
1216
|
+
if (isSystemMessage(msg) && msg.subtype === "local_command" && msg.content) {
|
|
1217
|
+
current?.contentBlocks.push({
|
|
1218
|
+
kind: "local_command",
|
|
1219
|
+
content: msg.content,
|
|
1220
|
+
timestamp: msg.timestamp,
|
|
1221
|
+
...msg.contextUsage ? { contextUsage: msg.contextUsage } : {},
|
|
1222
|
+
...msg.usageReport ? { usageReport: msg.usageReport } : {}
|
|
1223
|
+
});
|
|
1224
|
+
continue;
|
|
1225
|
+
}
|
|
1043
1226
|
if (isQueueOperationMessage(msg)) {
|
|
1044
1227
|
if (current && msg.operation === "enqueue" && isVisibleQueuedPrompt(msg.content)) {
|
|
1045
1228
|
const parsed = parseAgentEnvelope(msg.content);
|
|
@@ -1091,6 +1274,8 @@ function buildTurnsWithStarts(messages) {
|
|
|
1091
1274
|
if (pending) {
|
|
1092
1275
|
pending.turn.toolCalls[pending.index].result = extractToolResultText(block.content);
|
|
1093
1276
|
pending.turn.toolCalls[pending.index].isError = block.is_error === true;
|
|
1277
|
+
const call = pending.turn.toolCalls[pending.index];
|
|
1278
|
+
Object.assign(call, toolResultMetadata(call.name, msg.toolUseResult));
|
|
1094
1279
|
pendingToolUses.delete(block.tool_use_id);
|
|
1095
1280
|
}
|
|
1096
1281
|
}
|
|
@@ -1111,6 +1296,7 @@ function buildTurnsWithStarts(messages) {
|
|
|
1111
1296
|
parentToolUseId: toolUseId || void 0,
|
|
1112
1297
|
agentName: taskMeta?.name ?? null,
|
|
1113
1298
|
subagentType: taskMeta?.subagentType ?? null,
|
|
1299
|
+
...taskMeta?.effort ? { effort: taskMeta.effort } : {},
|
|
1114
1300
|
type: "assistant",
|
|
1115
1301
|
content: toolUseResult.content,
|
|
1116
1302
|
toolCalls: [],
|
|
@@ -1249,15 +1435,12 @@ function buildTurnsWithStarts(messages) {
|
|
|
1249
1435
|
current.toolCalls.push(tc);
|
|
1250
1436
|
msgToolCalls.push(tc);
|
|
1251
1437
|
pendingToolUses.set(block.id, { turn: current, index: idx });
|
|
1252
|
-
if (block.name
|
|
1438
|
+
if (isTaskToolName(block.name)) {
|
|
1253
1439
|
const input = block.input;
|
|
1254
1440
|
if (input.run_in_background === true) {
|
|
1255
1441
|
backgroundAgentParentIds.add(block.id);
|
|
1256
1442
|
}
|
|
1257
|
-
taskMetaMap.set(block.id,
|
|
1258
|
-
name: input.name ?? null,
|
|
1259
|
-
subagentType: input.subagent_type ?? null
|
|
1260
|
-
});
|
|
1443
|
+
taskMetaMap.set(block.id, readTaskMeta(input));
|
|
1261
1444
|
}
|
|
1262
1445
|
}
|
|
1263
1446
|
}
|
|
@@ -1268,6 +1451,7 @@ function buildTurnsWithStarts(messages) {
|
|
|
1268
1451
|
if (isProgressMessage(msg) && msg.data.type === "agent_progress") {
|
|
1269
1452
|
const data = msg.data;
|
|
1270
1453
|
const parentId = msg.parentToolUseID ?? "";
|
|
1454
|
+
const ownToolUseId = data.toolUseId ?? parentId;
|
|
1271
1455
|
let subAgentUsage = null;
|
|
1272
1456
|
if (data.message.type === "assistant") {
|
|
1273
1457
|
const innerMsg = data.message.message;
|
|
@@ -1284,12 +1468,14 @@ function buildTurnsWithStarts(messages) {
|
|
|
1284
1468
|
}
|
|
1285
1469
|
}
|
|
1286
1470
|
const innerModel = data.message.type === "assistant" ? data.message.message.model ?? null : null;
|
|
1287
|
-
const taskMeta = taskMetaMap.get(
|
|
1471
|
+
const taskMeta = taskMetaMap.get(ownToolUseId);
|
|
1288
1472
|
const agentMsg = {
|
|
1289
1473
|
agentId: data.agentId,
|
|
1290
|
-
parentToolUseId:
|
|
1474
|
+
parentToolUseId: ownToolUseId || void 0,
|
|
1475
|
+
...data.parentAgentId ? { parentAgentId: data.parentAgentId } : {},
|
|
1476
|
+
...taskMeta?.effort ? { effort: taskMeta.effort } : {},
|
|
1291
1477
|
agentName: taskMeta?.name ?? null,
|
|
1292
|
-
subagentType: taskMeta?.subagentType ?? null,
|
|
1478
|
+
subagentType: taskMeta?.subagentType ?? data.subagentType ?? null,
|
|
1293
1479
|
type: data.message.type,
|
|
1294
1480
|
content: data.message.message.content,
|
|
1295
1481
|
toolCalls: [],
|
|
@@ -1309,6 +1495,7 @@ function buildTurnsWithStarts(messages) {
|
|
|
1309
1495
|
} else if (block.type === "text") {
|
|
1310
1496
|
agentMsg.text.push(block.text);
|
|
1311
1497
|
} else if (block.type === "tool_use") {
|
|
1498
|
+
if (isTaskToolName(block.name)) taskMetaMap.set(block.id, readTaskMeta(block.input));
|
|
1312
1499
|
agentMsg.toolCalls.push({
|
|
1313
1500
|
id: block.id,
|
|
1314
1501
|
name: block.name,
|
|
@@ -1401,11 +1588,19 @@ function buildTurns(messages) {
|
|
|
1401
1588
|
function isClaudeSessionText() {
|
|
1402
1589
|
return true;
|
|
1403
1590
|
}
|
|
1591
|
+
var API_REQUEST_TYPE = '"type":"api-request';
|
|
1592
|
+
var RECORD_TYPE_WINDOW = 256;
|
|
1593
|
+
function isClaudeNoiseLine(line) {
|
|
1594
|
+
if (line.startsWith(`{${API_REQUEST_TYPE}`)) return true;
|
|
1595
|
+
const head = line.slice(0, RECORD_TYPE_WINDOW);
|
|
1596
|
+
const at = head.indexOf(API_REQUEST_TYPE);
|
|
1597
|
+
return at > 0 && head.lastIndexOf("{", at) === 0;
|
|
1598
|
+
}
|
|
1404
1599
|
function parseRecords(lines) {
|
|
1405
1600
|
const messages = [];
|
|
1406
1601
|
for (const line of lines) {
|
|
1407
1602
|
const trimmed = line.trim();
|
|
1408
|
-
if (!trimmed) continue;
|
|
1603
|
+
if (!trimmed || isClaudeNoiseLine(trimmed)) continue;
|
|
1409
1604
|
try {
|
|
1410
1605
|
messages.push(JSON.parse(trimmed));
|
|
1411
1606
|
} catch {
|
|
@@ -1510,21 +1705,24 @@ function userMessageTexts(msg) {
|
|
|
1510
1705
|
}
|
|
1511
1706
|
function collectClaudeBackgroundWork(rawMessages) {
|
|
1512
1707
|
const launches = [];
|
|
1513
|
-
const
|
|
1708
|
+
const resolvedAt = /* @__PURE__ */ new Map();
|
|
1514
1709
|
const queuedIds = /* @__PURE__ */ new Set();
|
|
1515
1710
|
const deliveredIds = /* @__PURE__ */ new Set();
|
|
1516
|
-
function
|
|
1711
|
+
function resolve2(id, index) {
|
|
1712
|
+
if (!resolvedAt.has(id)) resolvedAt.set(id, index);
|
|
1713
|
+
}
|
|
1714
|
+
function noteNotifications(text, index, sink) {
|
|
1517
1715
|
if (!text.includes(TASK_NOTIFICATION_MARKER)) return;
|
|
1518
1716
|
for (const match of text.matchAll(TASK_ID_RE)) {
|
|
1519
|
-
|
|
1717
|
+
resolve2(match[1], index);
|
|
1520
1718
|
sink?.add(match[1]);
|
|
1521
1719
|
}
|
|
1522
1720
|
for (const match of text.matchAll(TOOL_USE_ID_RE)) {
|
|
1523
|
-
|
|
1721
|
+
resolve2(match[1], index);
|
|
1524
1722
|
sink?.add(match[1]);
|
|
1525
1723
|
}
|
|
1526
1724
|
}
|
|
1527
|
-
for (const msg of rawMessages) {
|
|
1725
|
+
for (const [index, msg] of rawMessages.entries()) {
|
|
1528
1726
|
if (msg.type === "user") {
|
|
1529
1727
|
const toolUseResult = isObject(msg.toolUseResult) ? msg.toolUseResult : null;
|
|
1530
1728
|
if (toolUseResult?.status === "async_launched") {
|
|
@@ -1533,9 +1731,11 @@ function collectClaudeBackgroundWork(rawMessages) {
|
|
|
1533
1731
|
const toolUseIds = blocks.filter((block) => isObject(block) && block.type === "tool_result").map((block) => block.tool_use_id);
|
|
1534
1732
|
const ids = [toolUseResult.agentId, toolUseResult.taskId, ...toolUseIds].filter((id) => typeof id === "string" && id.length > 0);
|
|
1535
1733
|
const description = [toolUseResult.description, toolUseResult.workflowName, toolUseResult.summary].find((value) => typeof value === "string" && value.length > 0) ?? "";
|
|
1536
|
-
|
|
1734
|
+
const toolUseId = toolUseIds.find((id) => typeof id === "string" && id.length > 0);
|
|
1735
|
+
const startedAt = typeof msg.timestamp === "string" ? msg.timestamp : void 0;
|
|
1736
|
+
if (ids.length > 0) launches.push({ ids, launch: { agent: { description, startedAt, toolUseId }, launchedAt: index } });
|
|
1537
1737
|
}
|
|
1538
|
-
for (const text of userMessageTexts(msg)) noteNotifications(text, deliveredIds);
|
|
1738
|
+
for (const text of userMessageTexts(msg)) noteNotifications(text, index, deliveredIds);
|
|
1539
1739
|
continue;
|
|
1540
1740
|
}
|
|
1541
1741
|
if (msg.type === "assistant") {
|
|
@@ -1545,33 +1745,28 @@ function collectClaudeBackgroundWork(rawMessages) {
|
|
|
1545
1745
|
if (!isObject(block) || block.type !== "tool_use" || block.name !== "TaskStop") continue;
|
|
1546
1746
|
const input = isObject(block.input) ? block.input : null;
|
|
1547
1747
|
const taskId = input?.task_id ?? input?.shell_id;
|
|
1548
|
-
if (typeof taskId === "string")
|
|
1748
|
+
if (typeof taskId === "string") resolve2(taskId, index);
|
|
1549
1749
|
}
|
|
1550
1750
|
continue;
|
|
1551
1751
|
}
|
|
1552
1752
|
if (msg.type === "attachment") {
|
|
1553
1753
|
const attachment = isObject(msg.attachment) ? msg.attachment : null;
|
|
1554
|
-
if (typeof attachment?.prompt === "string") noteNotifications(attachment.prompt, deliveredIds);
|
|
1754
|
+
if (typeof attachment?.prompt === "string") noteNotifications(attachment.prompt, index, deliveredIds);
|
|
1555
1755
|
continue;
|
|
1556
1756
|
}
|
|
1557
1757
|
if (msg.type === "queue-operation" && typeof msg.content === "string") {
|
|
1558
1758
|
const op = msg.operation;
|
|
1559
|
-
noteNotifications(msg.content, op === "enqueue" ? queuedIds : deliveredIds);
|
|
1759
|
+
noteNotifications(msg.content, index, op === "enqueue" ? queuedIds : deliveredIds);
|
|
1560
1760
|
}
|
|
1561
1761
|
}
|
|
1562
1762
|
return {
|
|
1563
|
-
|
|
1763
|
+
launches: launches.map(({ ids, launch }) => {
|
|
1764
|
+
const ends = ids.flatMap((id) => resolvedAt.get(id) ?? []);
|
|
1765
|
+
return ends.length > 0 ? { ...launch, endedAt: Math.min(...ends) } : launch;
|
|
1766
|
+
}),
|
|
1564
1767
|
undeliveredNotifications: [...queuedIds].filter((id) => !deliveredIds.has(id)).length
|
|
1565
1768
|
};
|
|
1566
1769
|
}
|
|
1567
|
-
function awaitingAgentsResult(pendingDescriptions, pendingQueue) {
|
|
1568
|
-
return {
|
|
1569
|
-
status: "awaiting_agents",
|
|
1570
|
-
pendingQueue,
|
|
1571
|
-
pendingAgents: pendingDescriptions.length,
|
|
1572
|
-
pendingAgentDescriptions: pendingDescriptions.filter((description) => description.length > 0)
|
|
1573
|
-
};
|
|
1574
|
-
}
|
|
1575
1770
|
function deriveClaudeSessionStatus(rawMessages) {
|
|
1576
1771
|
let pendingEnqueues = 0;
|
|
1577
1772
|
let backgroundWork = null;
|
|
@@ -1617,9 +1812,8 @@ function deriveClaudeSessionStatus(rawMessages) {
|
|
|
1617
1812
|
if (stopReason === "end_turn") {
|
|
1618
1813
|
const work = getBackgroundWork();
|
|
1619
1814
|
if (deliveringNotification || work.undeliveredNotifications > 0) return result("processing");
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
}
|
|
1815
|
+
const awaiting = awaitingAgentsStatus(work.launches, Math.max(0, pendingEnqueues));
|
|
1816
|
+
if (awaiting) return awaiting;
|
|
1623
1817
|
let hasActivity = false;
|
|
1624
1818
|
for (let j = i - 1; j >= 0; j--) {
|
|
1625
1819
|
const m = rawMessages[j];
|
|
@@ -2007,9 +2201,60 @@ function safeParseLine(line) {
|
|
|
2007
2201
|
function isObject3(value) {
|
|
2008
2202
|
return typeof value === "object" && value !== null;
|
|
2009
2203
|
}
|
|
2010
|
-
function
|
|
2011
|
-
|
|
2012
|
-
|
|
2204
|
+
function codexParentId(payload) {
|
|
2205
|
+
const source = isObject3(payload.source) ? payload.source : null;
|
|
2206
|
+
const subagent = source && isObject3(source.subagent) ? source.subagent : null;
|
|
2207
|
+
const threadSpawn = subagent && isObject3(subagent.thread_spawn) ? subagent.thread_spawn : null;
|
|
2208
|
+
for (const candidate of [payload.forked_from_id, payload.parent_thread_id, threadSpawn?.parent_thread_id]) {
|
|
2209
|
+
if (typeof candidate === "string" && candidate) return candidate;
|
|
2210
|
+
}
|
|
2211
|
+
return null;
|
|
2212
|
+
}
|
|
2213
|
+
function inheritedHistoryFilter(records) {
|
|
2214
|
+
const meta = records.find((record2) => record2?.type === "session_meta");
|
|
2215
|
+
const start = meta?.payload?.subagent_history_start_ordinal;
|
|
2216
|
+
if (typeof start !== "number") return null;
|
|
2217
|
+
const reachesPast = (ordinal) => typeof ordinal === "number" && ordinal >= start;
|
|
2218
|
+
if (!reachesPast(meta?.rolloutLastOrdinal) && !records.some((record2) => reachesPast(record2?.ordinal))) return null;
|
|
2219
|
+
return (record2) => typeof record2.ordinal === "number" && record2.ordinal > 0 && record2.ordinal < start;
|
|
2220
|
+
}
|
|
2221
|
+
async function codexPageHeaderLine(firstLine, lastLines) {
|
|
2222
|
+
if (!firstLine.includes('"subagent_history_start_ordinal"')) return firstLine;
|
|
2223
|
+
const meta = safeParseLine(firstLine);
|
|
2224
|
+
if (meta?.type !== "session_meta") return firstLine;
|
|
2225
|
+
const lines = await lastLines();
|
|
2226
|
+
for (let index = lines.length - 1; index >= 0; index--) {
|
|
2227
|
+
const ordinal = safeParseLine(lines[index])?.ordinal;
|
|
2228
|
+
if (typeof ordinal === "number") return JSON.stringify({ ...meta, rolloutLastOrdinal: ordinal });
|
|
2229
|
+
}
|
|
2230
|
+
return firstLine;
|
|
2231
|
+
}
|
|
2232
|
+
function subAgentActivitySource(record2) {
|
|
2233
|
+
const payload = record2.payload;
|
|
2234
|
+
if (record2.type !== "event_msg" || !isObject3(payload)) return null;
|
|
2235
|
+
if (payload.type === "sub_agent_activity") return payload;
|
|
2236
|
+
if (payload.type === "item_completed" && isObject3(payload.item) && payload.item.type === "SubAgentActivity") return payload.item;
|
|
2237
|
+
return null;
|
|
2238
|
+
}
|
|
2239
|
+
function readSubAgentActivity(record2) {
|
|
2240
|
+
const source = subAgentActivitySource(record2);
|
|
2241
|
+
if (!source || typeof source.agent_thread_id !== "string" || typeof source.agent_path !== "string") return null;
|
|
2242
|
+
return {
|
|
2243
|
+
agentId: source.agent_thread_id,
|
|
2244
|
+
agentPath: source.agent_path,
|
|
2245
|
+
kind: typeof source.kind === "string" ? source.kind : "started",
|
|
2246
|
+
eventId: findString(source.event_id, source.id) ?? "",
|
|
2247
|
+
model: findString(source.model) || null,
|
|
2248
|
+
effort: findString(source.reasoning_effort) || null
|
|
2249
|
+
};
|
|
2250
|
+
}
|
|
2251
|
+
function turnErrorMessage(payload) {
|
|
2252
|
+
if (payload.type !== "task_complete" || !isObject3(payload.error)) return "";
|
|
2253
|
+
return typeof payload.error.message === "string" ? payload.error.message.trim() : "";
|
|
2254
|
+
}
|
|
2255
|
+
function isCodexRecord(record2) {
|
|
2256
|
+
if (!record2 || typeof record2.type !== "string") return false;
|
|
2257
|
+
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
2258
|
}
|
|
2014
2259
|
function extractMessageText(payload, blockType) {
|
|
2015
2260
|
const content = payload?.content;
|
|
@@ -2204,22 +2449,39 @@ function createTurn2(turnId, timestamp, model) {
|
|
|
2204
2449
|
model
|
|
2205
2450
|
};
|
|
2206
2451
|
}
|
|
2207
|
-
function
|
|
2208
|
-
|
|
2209
|
-
|
|
2452
|
+
function codexPromptText(record2) {
|
|
2453
|
+
const payload = isObject3(record2.payload) ? record2.payload : null;
|
|
2454
|
+
if (record2.type === "event_msg" && payload?.type === "user_message" && typeof payload.message === "string") {
|
|
2455
|
+
return normalizePromptText(payload.message);
|
|
2210
2456
|
}
|
|
2211
|
-
if (
|
|
2212
|
-
return normalizePromptText(extractMessageText(
|
|
2457
|
+
if (record2.type === "response_item" && payload?.type === "message" && payload.role === "user") {
|
|
2458
|
+
return normalizePromptText(extractMessageText(payload, "input_text"));
|
|
2213
2459
|
}
|
|
2214
2460
|
return "";
|
|
2215
2461
|
}
|
|
2216
|
-
function
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2462
|
+
function createCodexPromptReader() {
|
|
2463
|
+
let echoPending = false;
|
|
2464
|
+
return (record2) => {
|
|
2465
|
+
const payload = isObject3(record2.payload) ? record2.payload : null;
|
|
2466
|
+
if (record2.type === "event_msg" && payload?.type === "user_message") {
|
|
2467
|
+
const isEcho = echoPending;
|
|
2468
|
+
echoPending = false;
|
|
2469
|
+
return isEcho ? "" : codexPromptText(record2);
|
|
2470
|
+
}
|
|
2471
|
+
if (record2.type !== "response_item") return "";
|
|
2472
|
+
const prompt = codexPromptText(record2);
|
|
2473
|
+
if (prompt) echoPending = true;
|
|
2474
|
+
else if (payload?.type !== "message" || payload.role !== "user") echoPending = false;
|
|
2475
|
+
return prompt;
|
|
2476
|
+
};
|
|
2477
|
+
}
|
|
2478
|
+
function recordOpensTurn(record2, payload) {
|
|
2479
|
+
if (record2.type === "session_meta") return false;
|
|
2480
|
+
if (record2.type === "event_msg" && payload?.type === "task_started") return false;
|
|
2481
|
+
if (record2.type === "response_item" && payload?.type === "message") {
|
|
2220
2482
|
if (payload.role === "developer" || payload.role === "system") return false;
|
|
2221
2483
|
if (payload.role === "user") {
|
|
2222
|
-
return
|
|
2484
|
+
return codexPromptText(record2) !== "" || extractResponseMessageImages(payload).length > 0 || extractResponseMessageAudio(payload).length > 0;
|
|
2223
2485
|
}
|
|
2224
2486
|
}
|
|
2225
2487
|
return true;
|
|
@@ -2239,23 +2501,24 @@ function extractMetadataFromRecords(records) {
|
|
|
2239
2501
|
let isSubagent = false;
|
|
2240
2502
|
let parentSessionId = null;
|
|
2241
2503
|
let agentPath = "/root";
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
if (
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2504
|
+
const readPrompt = createCodexPromptReader();
|
|
2505
|
+
const isInherited = inheritedHistoryFilter(records);
|
|
2506
|
+
for (const record2 of records) {
|
|
2507
|
+
if (!isObject3(record2.payload) || isInherited?.(record2)) continue;
|
|
2508
|
+
if (record2.type === "session_meta") {
|
|
2509
|
+
sessionId ||= typeof record2.payload.id === "string" ? record2.payload.id : "";
|
|
2510
|
+
version ||= typeof record2.payload.cli_version === "string" ? record2.payload.cli_version : "";
|
|
2511
|
+
cwd ||= typeof record2.payload.cwd === "string" ? record2.payload.cwd : "";
|
|
2512
|
+
if (!branchedFrom && isObject3(record2.payload.branchedFrom)) {
|
|
2513
|
+
const sourceId = typeof record2.payload.branchedFrom.sessionId === "string" ? record2.payload.branchedFrom.sessionId : "";
|
|
2251
2514
|
if (sourceId) {
|
|
2252
2515
|
branchedFrom = {
|
|
2253
2516
|
sessionId: sourceId,
|
|
2254
|
-
turnIndex: typeof
|
|
2517
|
+
turnIndex: typeof record2.payload.branchedFrom.turnIndex === "number" ? record2.payload.branchedFrom.turnIndex : null
|
|
2255
2518
|
};
|
|
2256
2519
|
}
|
|
2257
2520
|
}
|
|
2258
|
-
const source = isObject3(
|
|
2521
|
+
const source = isObject3(record2.payload.source) ? record2.payload.source : null;
|
|
2259
2522
|
if (source && isObject3(source.subagent)) {
|
|
2260
2523
|
isSubagent = true;
|
|
2261
2524
|
const threadSpawn = isObject3(source.subagent.thread_spawn) ? source.subagent.thread_spawn : null;
|
|
@@ -2263,25 +2526,21 @@ function extractMetadataFromRecords(records) {
|
|
|
2263
2526
|
agentPath = threadSpawn.agent_path;
|
|
2264
2527
|
}
|
|
2265
2528
|
}
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
}
|
|
2269
|
-
const git = isObject3(record.payload.git) ? record.payload.git : null;
|
|
2529
|
+
parentSessionId = codexParentId(record2.payload) ?? parentSessionId;
|
|
2530
|
+
const git = isObject3(record2.payload.git) ? record2.payload.git : null;
|
|
2270
2531
|
gitBranch ||= git && typeof git.branch === "string" ? git.branch : "";
|
|
2271
2532
|
}
|
|
2272
|
-
if (
|
|
2273
|
-
model ||= typeof
|
|
2274
|
-
cwd ||= typeof
|
|
2533
|
+
if (record2.type === "turn_context") {
|
|
2534
|
+
model ||= typeof record2.payload.model === "string" ? record2.payload.model : "";
|
|
2535
|
+
cwd ||= typeof record2.payload.cwd === "string" ? record2.payload.cwd : "";
|
|
2275
2536
|
}
|
|
2276
|
-
const prompt =
|
|
2537
|
+
const prompt = readPrompt(record2);
|
|
2277
2538
|
if (!prompt) continue;
|
|
2278
|
-
if (prompt === previousPrompt) continue;
|
|
2279
2539
|
if (!firstUserMessage) firstUserMessage = prompt;
|
|
2280
2540
|
lastUserMessage = prompt;
|
|
2281
|
-
previousPrompt = prompt;
|
|
2282
2541
|
turnCount++;
|
|
2283
|
-
if (!timestamp) timestamp =
|
|
2284
|
-
lastTimestamp =
|
|
2542
|
+
if (!timestamp) timestamp = record2.timestamp ?? "";
|
|
2543
|
+
lastTimestamp = record2.timestamp ?? lastTimestamp;
|
|
2285
2544
|
}
|
|
2286
2545
|
if (lastTimestamp === "") lastTimestamp = timestamp;
|
|
2287
2546
|
return {
|
|
@@ -2348,6 +2607,7 @@ function walkCodexRecords(records, options) {
|
|
|
2348
2607
|
timestamp: info?.timestamp ?? eventTimestamp2,
|
|
2349
2608
|
tokenUsage: null,
|
|
2350
2609
|
model: info?.model ?? null,
|
|
2610
|
+
...info?.effort ? { effort: info.effort } : {},
|
|
2351
2611
|
isBackground: false,
|
|
2352
2612
|
prompt: info?.message || void 0,
|
|
2353
2613
|
status: update.status ?? "running"
|
|
@@ -2368,6 +2628,7 @@ function walkCodexRecords(records, options) {
|
|
|
2368
2628
|
agentMessage.agentName = info.nickname ?? (info.agentPath ? agentNameFromPath(info.agentPath) : agentMessage.agentName);
|
|
2369
2629
|
agentMessage.subagentType = info.agentType;
|
|
2370
2630
|
agentMessage.model = info.model;
|
|
2631
|
+
if (info.effort) agentMessage.effort = info.effort;
|
|
2371
2632
|
agentMessage.prompt = info.message || agentMessage.prompt;
|
|
2372
2633
|
}
|
|
2373
2634
|
if (update.text && !agentMessage.text.includes(update.text)) {
|
|
@@ -2438,20 +2699,21 @@ function walkCodexRecords(records, options) {
|
|
|
2438
2699
|
webSearchCalls.set(id, toolCall);
|
|
2439
2700
|
appendToolCall(targetTurn, toolCall, timestamp);
|
|
2440
2701
|
}
|
|
2702
|
+
const isInherited = inheritedHistoryFilter(records);
|
|
2441
2703
|
for (let index = 0; index < records.length; index++) {
|
|
2442
|
-
const
|
|
2443
|
-
if (!isCodexRecord(
|
|
2704
|
+
const record2 = records[index];
|
|
2705
|
+
if (!isCodexRecord(record2) || isInherited?.(record2)) continue;
|
|
2444
2706
|
recordIndex = index;
|
|
2445
|
-
const payload = isObject3(
|
|
2446
|
-
const timestamp =
|
|
2447
|
-
if (
|
|
2707
|
+
const payload = isObject3(record2.payload) ? record2.payload : void 0;
|
|
2708
|
+
const timestamp = record2.timestamp ?? "";
|
|
2709
|
+
if (record2.type === "compacted") {
|
|
2448
2710
|
current = commitTurn(current);
|
|
2449
2711
|
pendingCompaction = "Conversation compacted";
|
|
2450
2712
|
lastTurnTimestamp = timestamp;
|
|
2451
2713
|
continue;
|
|
2452
2714
|
}
|
|
2453
|
-
if (
|
|
2454
|
-
if (
|
|
2715
|
+
if (record2.type === "world_state") continue;
|
|
2716
|
+
if (record2.type === "turn_context") {
|
|
2455
2717
|
current = commitTurn(current);
|
|
2456
2718
|
pendingContextIndex = index;
|
|
2457
2719
|
currentTurnId = typeof payload?.turn_id === "string" ? payload.turn_id : null;
|
|
@@ -2460,7 +2722,7 @@ function walkCodexRecords(records, options) {
|
|
|
2460
2722
|
sawTurnStart = true;
|
|
2461
2723
|
continue;
|
|
2462
2724
|
}
|
|
2463
|
-
if (
|
|
2725
|
+
if (record2.type === "event_msg" && payload?.type === "user_message" && typeof payload.message === "string") {
|
|
2464
2726
|
if (current && (current.assistantText.length > 0 || current.toolCalls.length > 0 || current.thinking.length > 0)) {
|
|
2465
2727
|
current = commitTurn(current);
|
|
2466
2728
|
}
|
|
@@ -2484,12 +2746,12 @@ function walkCodexRecords(records, options) {
|
|
|
2484
2746
|
lastTurnTimestamp = timestamp;
|
|
2485
2747
|
continue;
|
|
2486
2748
|
}
|
|
2487
|
-
if (!current && !recordOpensTurn(
|
|
2749
|
+
if (!current && !recordOpensTurn(record2, payload)) continue;
|
|
2488
2750
|
current ??= createActiveTurn(currentTurnId, timestamp, currentModel);
|
|
2489
2751
|
if (!current.model && currentModel) current.model = currentModel;
|
|
2490
2752
|
if (!current.timestamp) current.timestamp = timestamp;
|
|
2491
2753
|
if (timestamp) lastTurnTimestamp = timestamp;
|
|
2492
|
-
if (
|
|
2754
|
+
if (record2.type === "event_msg" && payload?.type === "token_count") {
|
|
2493
2755
|
const info = isObject3(payload.info) ? payload.info : null;
|
|
2494
2756
|
const lastUsage = info ? parseTokenUsage(info.last_token_usage) : null;
|
|
2495
2757
|
if (lastUsage) {
|
|
@@ -2497,14 +2759,14 @@ function walkCodexRecords(records, options) {
|
|
|
2497
2759
|
}
|
|
2498
2760
|
continue;
|
|
2499
2761
|
}
|
|
2500
|
-
if (
|
|
2762
|
+
if (record2.type === "event_msg" && payload?.type === "web_search_end" && typeof payload.call_id === "string") {
|
|
2501
2763
|
const input = {};
|
|
2502
2764
|
if (typeof payload.query === "string" && payload.query) input.query = payload.query;
|
|
2503
2765
|
if (isObject3(payload.action)) input.action = payload.action;
|
|
2504
2766
|
upsertWebSearchCall(current, payload.call_id, input, "completed", timestamp);
|
|
2505
2767
|
continue;
|
|
2506
2768
|
}
|
|
2507
|
-
if (
|
|
2769
|
+
if (record2.type === "event_msg" && payload?.type === "mcp_tool_call_end" && typeof payload.call_id === "string" && isObject3(payload.invocation)) {
|
|
2508
2770
|
const invocation = payload.invocation;
|
|
2509
2771
|
const server = typeof invocation.server === "string" ? invocation.server : "mcp";
|
|
2510
2772
|
const tool = typeof invocation.tool === "string" ? invocation.tool : "tool";
|
|
@@ -2529,12 +2791,18 @@ function walkCodexRecords(records, options) {
|
|
|
2529
2791
|
}
|
|
2530
2792
|
continue;
|
|
2531
2793
|
}
|
|
2532
|
-
if (
|
|
2533
|
-
const
|
|
2534
|
-
|
|
2535
|
-
|
|
2794
|
+
if (record2.type === "event_msg" && payload) {
|
|
2795
|
+
const turnError = turnErrorMessage(payload);
|
|
2796
|
+
if (turnError) {
|
|
2797
|
+
appendAssistantText(current, turnError, timestamp);
|
|
2798
|
+
continue;
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
const activity = readSubAgentActivity(record2);
|
|
2802
|
+
if (activity) {
|
|
2803
|
+
const { agentId, agentPath, eventId: eventId2, kind } = activity;
|
|
2536
2804
|
const spawnInfo = eventId2 ? spawnAgentCalls.get(eventId2) : void 0;
|
|
2537
|
-
const existingId = agentIdByPath.get(
|
|
2805
|
+
const existingId = agentIdByPath.get(agentPath);
|
|
2538
2806
|
const existing = agentRegistry.get(agentId) ?? (existingId ? agentRegistry.get(existingId) : void 0);
|
|
2539
2807
|
if (kind !== "started" && !existing && !spawnInfo) continue;
|
|
2540
2808
|
if (existingId && existingId !== agentId && !agentMessages.has(agentId)) {
|
|
@@ -2548,22 +2816,23 @@ function walkCodexRecords(records, options) {
|
|
|
2548
2816
|
}
|
|
2549
2817
|
const info = {
|
|
2550
2818
|
agentId,
|
|
2551
|
-
nickname: existing?.nickname ?? agentNameFromPath(
|
|
2552
|
-
agentPath
|
|
2819
|
+
nickname: existing?.nickname ?? agentNameFromPath(agentPath) ?? spawnInfo?.taskName ?? null,
|
|
2820
|
+
agentPath,
|
|
2553
2821
|
message: existing?.message ?? spawnInfo?.message ?? "",
|
|
2554
|
-
model: existing?.model ?? spawnInfo?.model ?? null,
|
|
2822
|
+
model: activity.model ?? existing?.model ?? spawnInfo?.model ?? null,
|
|
2823
|
+
effort: activity.effort ?? existing?.effort ?? null,
|
|
2555
2824
|
agentType: existing?.agentType ?? spawnInfo?.agentType ?? null,
|
|
2556
2825
|
timestamp: existing?.timestamp ?? (timestamp || spawnInfo?.timestamp || ""),
|
|
2557
2826
|
parentToolCallId: existing?.parentToolCallId ?? eventId2
|
|
2558
2827
|
};
|
|
2559
2828
|
agentRegistry.set(agentId, info);
|
|
2560
|
-
agentIdByPath.set(
|
|
2829
|
+
agentIdByPath.set(agentPath, agentId);
|
|
2561
2830
|
if (eventId2 && (kind === "started" || spawnInfo)) agentIdBySpawnCall.set(eventId2, agentId);
|
|
2562
|
-
const status = kind === "interrupted"
|
|
2831
|
+
const status = kind === "interrupted" || kind === "completed" ? kind : "running";
|
|
2563
2832
|
upsertAgentMessage(agentId, current, timestamp, { status });
|
|
2564
2833
|
continue;
|
|
2565
2834
|
}
|
|
2566
|
-
if (
|
|
2835
|
+
if (record2.type !== "response_item" || !payload) continue;
|
|
2567
2836
|
if (payload.type === "reasoning") {
|
|
2568
2837
|
const summary = Array.isArray(payload.summary) ? payload.summary.filter((item) => typeof item === "string").join("\n") : "";
|
|
2569
2838
|
appendThinking(current, summary.trim(), timestamp);
|
|
@@ -2715,6 +2984,7 @@ function walkCodexRecords(records, options) {
|
|
|
2715
2984
|
agentPath: agentPath ?? existing?.agentPath ?? null,
|
|
2716
2985
|
message: existing?.message ?? spawnInfo?.message ?? "",
|
|
2717
2986
|
model: existing?.model ?? spawnInfo?.model ?? null,
|
|
2987
|
+
effort: existing?.effort ?? null,
|
|
2718
2988
|
agentType: existing?.agentType ?? spawnInfo?.agentType ?? null,
|
|
2719
2989
|
timestamp: existing?.timestamp ?? spawnInfo?.timestamp ?? timestamp,
|
|
2720
2990
|
parentToolCallId: existing?.parentToolCallId ?? toolCall.id
|
|
@@ -2833,14 +3103,16 @@ function walkCodexRecords(records, options) {
|
|
|
2833
3103
|
}
|
|
2834
3104
|
function parseCodexSession(jsonlText, options) {
|
|
2835
3105
|
const records = jsonlText.split("\n").map((line) => line.trim()).filter(Boolean).map(safeParseLine).filter(isCodexRecord);
|
|
3106
|
+
const isInherited = inheritedHistoryFilter(records);
|
|
3107
|
+
const ownRecords = isInherited ? records.filter((record2) => !isInherited(record2)) : records;
|
|
2836
3108
|
const { turnStartIndices: _starts, ...session } = walkCodexRecords(
|
|
2837
|
-
|
|
3109
|
+
ownRecords,
|
|
2838
3110
|
options
|
|
2839
3111
|
);
|
|
2840
3112
|
return session;
|
|
2841
3113
|
}
|
|
2842
3114
|
function appendCodexSession(existing, newJsonlText) {
|
|
2843
|
-
const prefix = existing.rawMessages.map((
|
|
3115
|
+
const prefix = existing.rawMessages.map((record2) => JSON.stringify(record2)).join("\n");
|
|
2844
3116
|
return parseCodexSession(prefix ? `${prefix}
|
|
2845
3117
|
${newJsonlText}` : newJsonlText);
|
|
2846
3118
|
}
|
|
@@ -2859,40 +3131,48 @@ function parseJsonObject(value) {
|
|
|
2859
3131
|
return null;
|
|
2860
3132
|
}
|
|
2861
3133
|
}
|
|
2862
|
-
function
|
|
2863
|
-
const
|
|
2864
|
-
const
|
|
3134
|
+
function collectCodexBackgroundAgents(rawMessages) {
|
|
3135
|
+
const launched = /* @__PURE__ */ new Map();
|
|
3136
|
+
const alive = /* @__PURE__ */ new Set();
|
|
3137
|
+
const endedAt = /* @__PURE__ */ new Map();
|
|
2865
3138
|
const agentIdByPath = /* @__PURE__ */ new Map();
|
|
2866
3139
|
const spawnCalls = /* @__PURE__ */ new Map();
|
|
2867
3140
|
const waitCalls = /* @__PURE__ */ new Set();
|
|
2868
3141
|
const interruptCalls = /* @__PURE__ */ new Map();
|
|
2869
|
-
function markAlive(agentId, description) {
|
|
2870
|
-
if (
|
|
2871
|
-
|
|
2872
|
-
|
|
3142
|
+
function markAlive(agentId, description, index, startedAt) {
|
|
3143
|
+
if (endedAt.has(agentId)) return;
|
|
3144
|
+
alive.add(agentId);
|
|
3145
|
+
const launch = launched.get(agentId);
|
|
3146
|
+
if (!launch) launched.set(agentId, { launchedAt: index, description, startedAt });
|
|
3147
|
+
else if (!launch.description) launch.description = description;
|
|
3148
|
+
}
|
|
3149
|
+
function markEnded(agentId, index) {
|
|
3150
|
+
if (!endedAt.has(agentId)) endedAt.set(agentId, index);
|
|
3151
|
+
alive.delete(agentId);
|
|
3152
|
+
}
|
|
3153
|
+
for (const [index, msg] of rawMessages.entries()) {
|
|
2873
3154
|
const payload = isObject3(msg.payload) ? msg.payload : null;
|
|
2874
3155
|
if (!payload) continue;
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
const
|
|
2879
|
-
const existingId = agentIdByPath.get(
|
|
2880
|
-
const known = alive.has(agentId) ||
|
|
3156
|
+
const timestamp = typeof msg.timestamp === "string" ? msg.timestamp : void 0;
|
|
3157
|
+
const activity = readSubAgentActivity(msg);
|
|
3158
|
+
if (activity) {
|
|
3159
|
+
const { agentId, agentPath, kind, eventId: eventId2 } = activity;
|
|
3160
|
+
const existingId = agentIdByPath.get(agentPath);
|
|
3161
|
+
const known = alive.has(agentId) || endedAt.has(agentId) || existingId !== void 0 || spawnCalls.has(eventId2);
|
|
2881
3162
|
if (kind !== "started" && !known) continue;
|
|
2882
3163
|
if (existingId && existingId !== agentId) {
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
if (
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
done.add(agentId);
|
|
2892
|
-
alive.delete(agentId);
|
|
2893
|
-
} else {
|
|
2894
|
-
markAlive(agentId, spawnCalls.get(eventId2) ?? "");
|
|
3164
|
+
const prior = launched.get(existingId);
|
|
3165
|
+
launched.delete(existingId);
|
|
3166
|
+
const current = launched.get(agentId);
|
|
3167
|
+
if (prior && !current) launched.set(agentId, prior);
|
|
3168
|
+
else if (prior && current && !current.description) current.description = prior.description;
|
|
3169
|
+
if (alive.delete(existingId)) markAlive(agentId, "", index, timestamp);
|
|
3170
|
+
const priorEnd = endedAt.get(existingId);
|
|
3171
|
+
if (priorEnd !== void 0) markEnded(agentId, priorEnd);
|
|
2895
3172
|
}
|
|
3173
|
+
agentIdByPath.set(agentPath, agentId);
|
|
3174
|
+
if (kind === "interrupted" || kind === "completed") markEnded(agentId, index);
|
|
3175
|
+
else markAlive(agentId, spawnCalls.get(eventId2) ?? "", index, timestamp);
|
|
2896
3176
|
continue;
|
|
2897
3177
|
}
|
|
2898
3178
|
if (msg.type !== "response_item") continue;
|
|
@@ -2916,7 +3196,7 @@ function collectCodexPendingAgents(rawMessages) {
|
|
|
2916
3196
|
const output = parseJsonObject(payload.output);
|
|
2917
3197
|
const agentId = typeof output?.agent_id === "string" ? output.agent_id : "";
|
|
2918
3198
|
if (agentId) {
|
|
2919
|
-
markAlive(agentId, spawnCalls.get(callId) ?? "");
|
|
3199
|
+
markAlive(agentId, spawnCalls.get(callId) ?? "", index, timestamp);
|
|
2920
3200
|
const agentPath = typeof output?.task_name === "string" ? output.task_name : "";
|
|
2921
3201
|
if (agentPath && !agentIdByPath.has(agentPath)) agentIdByPath.set(agentPath, agentId);
|
|
2922
3202
|
}
|
|
@@ -2927,16 +3207,10 @@ function collectCodexPendingAgents(rawMessages) {
|
|
|
2927
3207
|
const statusValue = isObject3(status) ? status : {};
|
|
2928
3208
|
const lifecycle = typeof statusValue.status === "string" ? statusValue.status : "";
|
|
2929
3209
|
const isTerminal = typeof statusValue.completed === "string" || typeof statusValue.failed === "string" || typeof statusValue.error === "string" || typeof statusValue.interrupted === "string" || lifecycle === "completed" || lifecycle === "failed" || lifecycle === "interrupted";
|
|
2930
|
-
if (isTerminal)
|
|
2931
|
-
done.add(agentId);
|
|
2932
|
-
alive.delete(agentId);
|
|
2933
|
-
}
|
|
3210
|
+
if (isTerminal) markEnded(agentId, index);
|
|
2934
3211
|
}
|
|
2935
3212
|
} else if (interruptCalls.has(callId)) {
|
|
2936
|
-
for (const agentId of interruptCalls.get(callId) ?? [])
|
|
2937
|
-
done.add(agentId);
|
|
2938
|
-
alive.delete(agentId);
|
|
2939
|
-
}
|
|
3213
|
+
for (const agentId of interruptCalls.get(callId) ?? []) markEnded(agentId, index);
|
|
2940
3214
|
}
|
|
2941
3215
|
continue;
|
|
2942
3216
|
}
|
|
@@ -2944,12 +3218,14 @@ function collectCodexPendingAgents(rawMessages) {
|
|
|
2944
3218
|
const text = codexPayloadText(payload);
|
|
2945
3219
|
const messageType = text.match(/^Message Type:\s*([^\r\n]+)/)?.[1]?.trim().toUpperCase() ?? "";
|
|
2946
3220
|
if (!TERMINAL_INTER_AGENT_TYPES.has(messageType)) continue;
|
|
2947
|
-
|
|
2948
|
-
done.add(agentId);
|
|
2949
|
-
alive.delete(agentId);
|
|
3221
|
+
markEnded(agentIdByPath.get(payload.author) ?? payload.author, index);
|
|
2950
3222
|
}
|
|
2951
3223
|
}
|
|
2952
|
-
return [...
|
|
3224
|
+
return [...launched.entries()].map(([agentId, { description, startedAt, launchedAt }]) => ({
|
|
3225
|
+
agent: { description, startedAt },
|
|
3226
|
+
launchedAt,
|
|
3227
|
+
endedAt: alive.has(agentId) ? void 0 : endedAt.get(agentId) ?? launchedAt
|
|
3228
|
+
}));
|
|
2953
3229
|
}
|
|
2954
3230
|
function deriveCodexSessionStatus(rawMessages) {
|
|
2955
3231
|
for (let i = rawMessages.length - 1; i >= 0; i--) {
|
|
@@ -2958,16 +3234,7 @@ function deriveCodexSessionStatus(rawMessages) {
|
|
|
2958
3234
|
const payload = msg.payload;
|
|
2959
3235
|
switch (payload?.type) {
|
|
2960
3236
|
case "task_complete": {
|
|
2961
|
-
|
|
2962
|
-
if (pending.length > 0) {
|
|
2963
|
-
return {
|
|
2964
|
-
status: "awaiting_agents",
|
|
2965
|
-
pendingQueue: 0,
|
|
2966
|
-
pendingAgents: pending.length,
|
|
2967
|
-
pendingAgentDescriptions: pending.filter((description) => description.length > 0)
|
|
2968
|
-
};
|
|
2969
|
-
}
|
|
2970
|
-
return { status: "completed" };
|
|
3237
|
+
return awaitingAgentsStatus(collectCodexBackgroundAgents(rawMessages)) ?? { status: "completed" };
|
|
2971
3238
|
}
|
|
2972
3239
|
case "task_started":
|
|
2973
3240
|
return { status: "processing" };
|
|
@@ -2991,11 +3258,11 @@ function deriveCodexSessionStatus(rawMessages) {
|
|
|
2991
3258
|
}
|
|
2992
3259
|
return { status: "idle" };
|
|
2993
3260
|
}
|
|
2994
|
-
function brandCodexBranch(firstRecord, sessionId, turnIndex) {
|
|
3261
|
+
function brandCodexBranch(firstRecord, sessionId, turnIndex, branchedAt) {
|
|
2995
3262
|
const payload = isObject3(firstRecord.payload) ? { ...firstRecord.payload } : {};
|
|
2996
3263
|
const originalId = typeof payload.id === "string" ? payload.id : "";
|
|
2997
3264
|
payload.id = sessionId;
|
|
2998
|
-
payload.branchedFrom = { sessionId: originalId, turnIndex };
|
|
3265
|
+
payload.branchedFrom = { sessionId: originalId, turnIndex, at: new Date(branchedAt).toISOString() };
|
|
2999
3266
|
return { record: { ...firstRecord, payload }, originalId };
|
|
3000
3267
|
}
|
|
3001
3268
|
function codexTurnBoundaries(records) {
|
|
@@ -3757,7 +4024,7 @@ function parseCopilotSession(jsonlText, options) {
|
|
|
3757
4024
|
};
|
|
3758
4025
|
}
|
|
3759
4026
|
function appendCopilotSession(existing, newJsonlText) {
|
|
3760
|
-
const prefix = existing.rawMessages.map((
|
|
4027
|
+
const prefix = existing.rawMessages.map((record2) => JSON.stringify(record2)).join("\n");
|
|
3761
4028
|
return parseCopilotSession(prefix ? `${prefix}
|
|
3762
4029
|
${newJsonlText}` : newJsonlText);
|
|
3763
4030
|
}
|
|
@@ -3765,33 +4032,32 @@ function isCopilotEventType(type) {
|
|
|
3765
4032
|
return typeof type === "string" && (type === "abort" || type === "binary_asset" || type === "user.message" || type.startsWith("assistant.") || type.startsWith("permission.") || type.startsWith("session.") || type.startsWith("subagent.") || type.startsWith("tool.") || type.startsWith("user_input."));
|
|
3766
4033
|
}
|
|
3767
4034
|
function deriveCopilotSessionStatus(rawMessages) {
|
|
3768
|
-
const
|
|
3769
|
-
for (const event of rawMessages) {
|
|
4035
|
+
const agents = /* @__PURE__ */ new Map();
|
|
4036
|
+
for (const [index, event] of rawMessages.entries()) {
|
|
3770
4037
|
const agentId = typeof event.agentId === "string" ? event.agentId : "";
|
|
3771
4038
|
if (!agentId && (event.type === "abort" || event.type === "session.shutdown" || event.type === "session.error")) {
|
|
3772
|
-
|
|
4039
|
+
agents.clear();
|
|
3773
4040
|
continue;
|
|
3774
4041
|
}
|
|
3775
4042
|
if (!agentId || !event.type.startsWith("subagent.")) continue;
|
|
3776
4043
|
const data = isObject4(event.data) ? event.data : {};
|
|
4044
|
+
const launch = agents.get(agentId);
|
|
3777
4045
|
if (event.type === "subagent.completed" || event.type === "subagent.failed") {
|
|
3778
|
-
|
|
4046
|
+
if (launch) launch.endedAt ??= index;
|
|
3779
4047
|
continue;
|
|
3780
4048
|
}
|
|
3781
4049
|
if (event.type === "subagent.started" || event.type === "subagent.configured") {
|
|
3782
4050
|
const description = [data.agentDisplayName, data.description, data.agentName].find((value) => typeof value === "string") ?? "";
|
|
3783
|
-
|
|
4051
|
+
if (!launch || launch.endedAt !== void 0) {
|
|
4052
|
+
const startedAt = typeof event.timestamp === "string" ? event.timestamp : void 0;
|
|
4053
|
+
agents.set(agentId, { agent: { description, startedAt }, launchedAt: index });
|
|
4054
|
+
} else if (description) {
|
|
4055
|
+
launch.agent.description = description;
|
|
4056
|
+
}
|
|
3784
4057
|
}
|
|
3785
4058
|
}
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
return {
|
|
3789
|
-
status: "awaiting_agents",
|
|
3790
|
-
pendingQueue: 0,
|
|
3791
|
-
pendingAgents: descriptions.length,
|
|
3792
|
-
pendingAgentDescriptions: descriptions.filter((description) => description.length > 0)
|
|
3793
|
-
};
|
|
3794
|
-
}
|
|
4059
|
+
const awaiting = awaitingAgentsStatus([...agents.values()]);
|
|
4060
|
+
if (awaiting) return awaiting;
|
|
3795
4061
|
let sawUserActivity = false;
|
|
3796
4062
|
const completedPermissionRequests = /* @__PURE__ */ new Set();
|
|
3797
4063
|
for (let i = rawMessages.length - 1; i >= 0; i--) {
|
|
@@ -3865,11 +4131,11 @@ function brandCopilotBranch() {
|
|
|
3865
4131
|
function copilotTurnBoundaries(records) {
|
|
3866
4132
|
const boundaries = [];
|
|
3867
4133
|
for (let index = 0; index < records.length; index++) {
|
|
3868
|
-
const
|
|
3869
|
-
if (
|
|
3870
|
-
const data =
|
|
4134
|
+
const record2 = records[index];
|
|
4135
|
+
if (record2.type !== "user.message" || typeof record2.agentId === "string") continue;
|
|
4136
|
+
const data = record2.data;
|
|
3871
4137
|
if (!data || typeof data !== "object" || Array.isArray(data)) continue;
|
|
3872
|
-
if (typeof
|
|
4138
|
+
if (typeof record2.id !== "string" || !record2.id) continue;
|
|
3873
4139
|
boundaries.push(index);
|
|
3874
4140
|
}
|
|
3875
4141
|
return boundaries;
|
|
@@ -3877,6 +4143,7 @@ function copilotTurnBoundaries(records) {
|
|
|
3877
4143
|
|
|
3878
4144
|
// src/lib/agents.ts
|
|
3879
4145
|
var TERMINAL_AGENT_KIND = AGENT_KINDS[AGENT_KINDS.length - 1];
|
|
4146
|
+
var hasNoNoiseLines = () => false;
|
|
3880
4147
|
var codexFormat = {
|
|
3881
4148
|
kind: "codex",
|
|
3882
4149
|
descriptor: descriptorFor("codex"),
|
|
@@ -3886,6 +4153,8 @@ var codexFormat = {
|
|
|
3886
4153
|
append: appendCodexSession,
|
|
3887
4154
|
metadataFromLines: extractCodexMetadataFromLines,
|
|
3888
4155
|
status: deriveCodexSessionStatus,
|
|
4156
|
+
isNoiseLine: hasNoNoiseLines,
|
|
4157
|
+
pageHeaderLine: codexPageHeaderLine,
|
|
3889
4158
|
brandBranch: brandCodexBranch,
|
|
3890
4159
|
turnBoundaries: codexTurnBoundaries
|
|
3891
4160
|
};
|
|
@@ -3898,6 +4167,7 @@ var copilotFormat = {
|
|
|
3898
4167
|
append: appendCopilotSession,
|
|
3899
4168
|
metadataFromLines: extractCopilotMetadataFromLines,
|
|
3900
4169
|
status: deriveCopilotSessionStatus,
|
|
4170
|
+
isNoiseLine: hasNoNoiseLines,
|
|
3901
4171
|
brandBranch: brandCopilotBranch,
|
|
3902
4172
|
turnBoundaries: copilotTurnBoundaries
|
|
3903
4173
|
};
|
|
@@ -3911,10 +4181,20 @@ var claudeFormat = {
|
|
|
3911
4181
|
append: appendClaudeSession,
|
|
3912
4182
|
metadataFromLines: extractClaudeMetadataFromLines,
|
|
3913
4183
|
status: deriveClaudeSessionStatus,
|
|
4184
|
+
isNoiseLine: isClaudeNoiseLine,
|
|
3914
4185
|
brandBranch: brandClaudeBranch,
|
|
3915
4186
|
turnBoundaries: claudeTurnBoundaries
|
|
3916
4187
|
};
|
|
3917
4188
|
var FORMATS = Object.freeze({
|
|
4189
|
+
acp: {
|
|
4190
|
+
...claudeFormat,
|
|
4191
|
+
kind: "acp",
|
|
4192
|
+
descriptor: descriptorFor("acp"),
|
|
4193
|
+
detectsText: (text) => text.split("\n", 1)[0]?.includes('"type":"acp_session"') ?? false,
|
|
4194
|
+
detectsRecords: (records) => records[0]?.type === "acp_session",
|
|
4195
|
+
parse: (text, options) => ({ ...parseClaudeSession(text, options), agentKind: "acp" }),
|
|
4196
|
+
append: (session, text) => ({ ...appendClaudeSession(session, text), agentKind: "acp" })
|
|
4197
|
+
},
|
|
3918
4198
|
claude: claudeFormat,
|
|
3919
4199
|
codex: codexFormat,
|
|
3920
4200
|
copilot: copilotFormat
|
|
@@ -3931,6 +4211,9 @@ function formatForText(jsonlText) {
|
|
|
3931
4211
|
function formatForRecords(records) {
|
|
3932
4212
|
return firstMatch((format) => format.detectsRecords(records));
|
|
3933
4213
|
}
|
|
4214
|
+
function isAnyAgentNoiseLine(line) {
|
|
4215
|
+
return AGENT_KINDS.some((kind) => FORMATS[kind].isNoiseLine(line));
|
|
4216
|
+
}
|
|
3934
4217
|
|
|
3935
4218
|
// src/lib/parser.ts
|
|
3936
4219
|
function extractTextFromContent2(content) {
|
|
@@ -3946,16 +4229,63 @@ function getUserMessageText(content) {
|
|
|
3946
4229
|
return extractTextFromContent2(content);
|
|
3947
4230
|
}
|
|
3948
4231
|
|
|
3949
|
-
// src/lib/
|
|
4232
|
+
// src/lib/transcriptFile.ts
|
|
3950
4233
|
var import_node_fs = require("node:fs");
|
|
4234
|
+
var import_promises = require("node:fs/promises");
|
|
4235
|
+
var zlib = __toESM(require("node:zlib"));
|
|
4236
|
+
var COMPRESSED_SUFFIX = ".zst";
|
|
4237
|
+
var canReadCompressedTranscripts = typeof zlib.zstdDecompressSync === "function";
|
|
4238
|
+
function isCompressedTranscript(filePath) {
|
|
4239
|
+
return filePath.endsWith(COMPRESSED_SUFFIX);
|
|
4240
|
+
}
|
|
4241
|
+
function uncompressedTranscriptName(fileName) {
|
|
4242
|
+
return isCompressedTranscript(fileName) ? fileName.slice(0, -COMPRESSED_SUFFIX.length) : fileName;
|
|
4243
|
+
}
|
|
4244
|
+
function isListableTranscript(name, siblings) {
|
|
4245
|
+
const plainName = uncompressedTranscriptName(name);
|
|
4246
|
+
if (!plainName.endsWith(".jsonl")) return false;
|
|
4247
|
+
if (plainName === name) return true;
|
|
4248
|
+
return canReadCompressedTranscripts && !siblings.has(plainName);
|
|
4249
|
+
}
|
|
4250
|
+
function inflate(data) {
|
|
4251
|
+
if (!canReadCompressedTranscripts) {
|
|
4252
|
+
throw new Error("Reading a .jsonl.zst transcript needs Node.js 22.15 or newer, or Bun");
|
|
4253
|
+
}
|
|
4254
|
+
return zlib.zstdDecompressSync(data);
|
|
4255
|
+
}
|
|
4256
|
+
function readTranscriptTextSync(filePath) {
|
|
4257
|
+
const data = (0, import_node_fs.readFileSync)(filePath);
|
|
4258
|
+
return (isCompressedTranscript(filePath) ? inflate(data) : data).toString("utf-8");
|
|
4259
|
+
}
|
|
4260
|
+
async function readTranscriptText(filePath) {
|
|
4261
|
+
const data = await (0, import_promises.readFile)(filePath);
|
|
4262
|
+
return (isCompressedTranscript(filePath) ? inflate(data) : data).toString("utf-8");
|
|
4263
|
+
}
|
|
4264
|
+
async function openTranscript(filePath) {
|
|
4265
|
+
if (!isCompressedTranscript(filePath)) return (0, import_promises.open)(filePath, "r");
|
|
4266
|
+
const [fileStat, compressed] = await Promise.all([(0, import_promises.stat)(filePath), (0, import_promises.readFile)(filePath)]);
|
|
4267
|
+
const data = inflate(compressed);
|
|
4268
|
+
return {
|
|
4269
|
+
async read(buffer, offset, length, position) {
|
|
4270
|
+
const start = Math.min(Math.max(position, 0), data.length);
|
|
4271
|
+
return { bytesRead: data.copy(buffer, offset, start, Math.min(start + length, data.length)) };
|
|
4272
|
+
},
|
|
4273
|
+
stat: async () => ({ size: data.length, mtimeMs: fileStat.mtimeMs }),
|
|
4274
|
+
close: async () => {
|
|
4275
|
+
}
|
|
4276
|
+
};
|
|
4277
|
+
}
|
|
4278
|
+
|
|
4279
|
+
// src/lib/stores.ts
|
|
4280
|
+
var import_node_fs2 = require("node:fs");
|
|
3951
4281
|
var import_node_path2 = require("node:path");
|
|
3952
4282
|
|
|
3953
4283
|
// src/lib/dirs.ts
|
|
3954
4284
|
var import_node_path = require("node:path");
|
|
3955
4285
|
var import_node_os = require("node:os");
|
|
3956
4286
|
function agentHomeDir(kind, env = process.env, homeDirectory = (0, import_node_os.homedir)()) {
|
|
3957
|
-
const { homeEnvVar, homeDirName } = descriptorFor(kind).cli;
|
|
3958
|
-
const override =
|
|
4287
|
+
const { homeEnvVar, homeDirName, sdkHomeEnvVar } = descriptorFor(kind).cli;
|
|
4288
|
+
const override = env[homeEnvVar ?? sdkHomeEnvVar ?? ""];
|
|
3959
4289
|
return override || (0, import_node_path.join)(homeDirectory, homeDirName);
|
|
3960
4290
|
}
|
|
3961
4291
|
var dirs = {
|
|
@@ -3963,7 +4293,8 @@ var dirs = {
|
|
|
3963
4293
|
TEAMS_DIR: (0, import_node_path.join)(agentHomeDir("claude"), "teams"),
|
|
3964
4294
|
TASKS_DIR: (0, import_node_path.join)(agentHomeDir("claude"), "tasks"),
|
|
3965
4295
|
CODEX_SESSIONS_DIR: (0, import_node_path.join)(agentHomeDir("codex"), "sessions"),
|
|
3966
|
-
COPILOT_SESSIONS_DIR: (0, import_node_path.join)(agentHomeDir("copilot"), "session-state")
|
|
4296
|
+
COPILOT_SESSIONS_DIR: (0, import_node_path.join)(agentHomeDir("copilot"), "session-state"),
|
|
4297
|
+
ACP_PROJECTS_DIR: (0, import_node_path.join)(agentHomeDir("acp"), "projects")
|
|
3967
4298
|
};
|
|
3968
4299
|
var DEFAULT_DB_PATH = (0, import_node_path.join)(agentHomeDir("claude"), "cogpit-memory", "search-index.db");
|
|
3969
4300
|
|
|
@@ -3973,14 +4304,14 @@ var MAX_WALK_DEPTH = 4;
|
|
|
3973
4304
|
var SUBAGENTS_DIR = "subagents";
|
|
3974
4305
|
function mtimeOf(filePath) {
|
|
3975
4306
|
try {
|
|
3976
|
-
return (0,
|
|
4307
|
+
return (0, import_node_fs2.statSync)(filePath).mtimeMs;
|
|
3977
4308
|
} catch {
|
|
3978
4309
|
return null;
|
|
3979
4310
|
}
|
|
3980
4311
|
}
|
|
3981
4312
|
function readDirSafe(dir) {
|
|
3982
4313
|
try {
|
|
3983
|
-
return (0,
|
|
4314
|
+
return (0, import_node_fs2.readdirSync)(dir, { withFileTypes: true });
|
|
3984
4315
|
} catch {
|
|
3985
4316
|
return [];
|
|
3986
4317
|
}
|
|
@@ -3991,8 +4322,8 @@ function isWithin(root, target) {
|
|
|
3991
4322
|
}
|
|
3992
4323
|
function canonicalWithinRoot(root, filePath) {
|
|
3993
4324
|
try {
|
|
3994
|
-
const realRoot = (0,
|
|
3995
|
-
const real = (0,
|
|
4325
|
+
const realRoot = (0, import_node_fs2.realpathSync)(root);
|
|
4326
|
+
const real = (0, import_node_fs2.realpathSync)(filePath);
|
|
3996
4327
|
return isWithin(realRoot, real) ? real : null;
|
|
3997
4328
|
} catch {
|
|
3998
4329
|
return null;
|
|
@@ -4058,13 +4389,15 @@ function walkClaude(root, cutoffMs) {
|
|
|
4058
4389
|
function walkCodex(root, cutoffMs, dir = root, depth = 0) {
|
|
4059
4390
|
if (depth > MAX_WALK_DEPTH) return [];
|
|
4060
4391
|
const found = [];
|
|
4061
|
-
|
|
4392
|
+
const entries = readDirSafe(dir);
|
|
4393
|
+
const names = new Set(entries.map((entry) => entry.name));
|
|
4394
|
+
for (const entry of entries) {
|
|
4062
4395
|
const filePath = (0, import_node_path2.join)(dir, entry.name);
|
|
4063
4396
|
if (entry.isDirectory()) {
|
|
4064
4397
|
found.push(...walkCodex(root, cutoffMs, filePath, depth + 1));
|
|
4065
4398
|
continue;
|
|
4066
4399
|
}
|
|
4067
|
-
if (!entry.name
|
|
4400
|
+
if (!isListableTranscript(entry.name, names)) continue;
|
|
4068
4401
|
const mtimeMs = mtimeOf(filePath);
|
|
4069
4402
|
if (mtimeMs === null || mtimeMs < cutoffMs) continue;
|
|
4070
4403
|
found.push({
|
|
@@ -4125,14 +4458,18 @@ var WALKERS = {
|
|
|
4125
4458
|
find(root, sessionId) {
|
|
4126
4459
|
const walk = (dir, depth) => {
|
|
4127
4460
|
if (depth > MAX_WALK_DEPTH) return null;
|
|
4128
|
-
|
|
4461
|
+
const entries = readDirSafe(dir);
|
|
4462
|
+
const names = new Set(entries.map((entry) => entry.name));
|
|
4463
|
+
for (const entry of entries) {
|
|
4129
4464
|
const filePath = (0, import_node_path2.join)(dir, entry.name);
|
|
4130
4465
|
if (entry.isDirectory()) {
|
|
4131
4466
|
const match = walk(filePath, depth + 1);
|
|
4132
4467
|
if (match) return match;
|
|
4133
4468
|
continue;
|
|
4134
4469
|
}
|
|
4135
|
-
if (entry.name.endsWith(`${sessionId}.jsonl`))
|
|
4470
|
+
if (uncompressedTranscriptName(entry.name).endsWith(`${sessionId}.jsonl`) && isListableTranscript(entry.name, names)) {
|
|
4471
|
+
return filePath;
|
|
4472
|
+
}
|
|
4136
4473
|
}
|
|
4137
4474
|
return null;
|
|
4138
4475
|
};
|
|
@@ -4151,24 +4488,48 @@ var WALKERS = {
|
|
|
4151
4488
|
const candidate = copilotTranscript(root, sessionId);
|
|
4152
4489
|
return mtimeOf(candidate) === null ? null : candidate;
|
|
4153
4490
|
}
|
|
4491
|
+
},
|
|
4492
|
+
acp: {
|
|
4493
|
+
root: () => dirs.ACP_PROJECTS_DIR,
|
|
4494
|
+
list: walkClaude,
|
|
4495
|
+
identify: claudeIdentify,
|
|
4496
|
+
find: (root, id) => WALKERS.claude.find(root, id)
|
|
4154
4497
|
}
|
|
4155
4498
|
};
|
|
4156
|
-
function makeStore(kind) {
|
|
4499
|
+
function makeStore(kind, home, configuredId) {
|
|
4157
4500
|
const walker = WALKERS[kind];
|
|
4501
|
+
const instanceId = configuredId ?? (process.env.COGPIT_WORKER_AGENT === kind ? process.env.COGPIT_AGENT_INSTANCE_ID : void 0) ?? "default";
|
|
4502
|
+
const root = home ? () => (0, import_node_path2.join)(home, kind === "codex" ? "sessions" : kind === "copilot" ? "session-state" : "projects") : walker.root;
|
|
4503
|
+
const identify = (filePath, directory = root()) => {
|
|
4504
|
+
const identity = walker.identify(filePath, directory);
|
|
4505
|
+
return { ...identity, sessionId: instanceSessionId(instanceId, identity.sessionId), parentSessionId: identity.parentSessionId ? instanceSessionId(instanceId, identity.parentSessionId) : null };
|
|
4506
|
+
};
|
|
4158
4507
|
return {
|
|
4159
4508
|
kind,
|
|
4509
|
+
instanceId,
|
|
4160
4510
|
descriptor: descriptorFor(kind),
|
|
4161
|
-
root
|
|
4162
|
-
ownsPath: (filePath) =>
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4511
|
+
root,
|
|
4512
|
+
ownsPath: (filePath) => {
|
|
4513
|
+
if (isWithin((0, import_node_path2.resolve)(root()), (0, import_node_path2.resolve)(filePath))) return true;
|
|
4514
|
+
try {
|
|
4515
|
+
return isWithin((0, import_node_fs2.realpathSync)(root()), (0, import_node_fs2.realpathSync)(filePath));
|
|
4516
|
+
} catch {
|
|
4517
|
+
return false;
|
|
4518
|
+
}
|
|
4519
|
+
},
|
|
4520
|
+
identify,
|
|
4521
|
+
list: (cutoffMs, directory = root()) => walker.list(directory, cutoffMs).map((file) => ({ ...file, ...identify(file.path, directory) })),
|
|
4522
|
+
find: (sessionId) => {
|
|
4523
|
+
const parts = splitInstanceSessionId(sessionId);
|
|
4524
|
+
return parts.instanceId === instanceId ? walker.find(root(), parts.nativeId) : null;
|
|
4525
|
+
}
|
|
4166
4526
|
};
|
|
4167
4527
|
}
|
|
4168
4528
|
var STORES = {
|
|
4169
4529
|
claude: makeStore("claude"),
|
|
4170
4530
|
codex: makeStore("codex"),
|
|
4171
|
-
copilot: makeStore("copilot")
|
|
4531
|
+
copilot: makeStore("copilot"),
|
|
4532
|
+
acp: makeStore("acp")
|
|
4172
4533
|
};
|
|
4173
4534
|
function storeFor(kind) {
|
|
4174
4535
|
return STORES[kind];
|
|
@@ -4177,7 +4538,20 @@ function defaultStore() {
|
|
|
4177
4538
|
return storeFor(descriptorForDirName(null).kind);
|
|
4178
4539
|
}
|
|
4179
4540
|
function allStores() {
|
|
4180
|
-
|
|
4541
|
+
if (process.env.COGPIT_AGENT_INSTANCE_ID && AGENT_KINDS.includes(process.env.COGPIT_WORKER_AGENT)) return [STORES[process.env.COGPIT_WORKER_AGENT]];
|
|
4542
|
+
const stores = AGENT_KINDS.map((kind) => STORES[kind]);
|
|
4543
|
+
const root = process.env.COGPIT_ORCHESTRATION_ROOT;
|
|
4544
|
+
if (!root) return stores;
|
|
4545
|
+
for (const entry of readDirSafe((0, import_node_path2.join)(root, "provider-instances"))) {
|
|
4546
|
+
if (!entry.isDirectory()) continue;
|
|
4547
|
+
const home = (0, import_node_path2.join)(root, "provider-instances", entry.name);
|
|
4548
|
+
try {
|
|
4549
|
+
const config = JSON.parse((0, import_node_fs2.readFileSync)((0, import_node_path2.join)(home, "config.json"), "utf8"));
|
|
4550
|
+
if (config.providerInstance?.id === entry.name && config.providerInstance.agent && AGENT_KINDS.includes(config.providerInstance.agent)) stores.push(makeStore(config.providerInstance.agent, home, entry.name));
|
|
4551
|
+
} catch {
|
|
4552
|
+
}
|
|
4553
|
+
}
|
|
4554
|
+
return stores;
|
|
4181
4555
|
}
|
|
4182
4556
|
function storeForPath(filePath) {
|
|
4183
4557
|
return allStores().find((store) => store.ownsPath(filePath)) ?? null;
|
|
@@ -4247,7 +4621,7 @@ var SearchIndex = class {
|
|
|
4247
4621
|
const { count: totalRows } = this.db.prepare("SELECT COUNT(*) as count FROM search_content").get();
|
|
4248
4622
|
let dbSizeBytes = 0;
|
|
4249
4623
|
try {
|
|
4250
|
-
dbSizeBytes = (0,
|
|
4624
|
+
dbSizeBytes = (0, import_node_fs3.statSync)(this.dbPath).size;
|
|
4251
4625
|
} catch {
|
|
4252
4626
|
}
|
|
4253
4627
|
return {
|
|
@@ -4320,9 +4694,9 @@ var SearchIndex = class {
|
|
|
4320
4694
|
indexFile(filePath, sessionId, mtimeMs, opts) {
|
|
4321
4695
|
const resolvedSessionId = sessionId ?? identifyTranscript(filePath).sessionId;
|
|
4322
4696
|
if (mtimeMs == null) {
|
|
4323
|
-
mtimeMs = (0,
|
|
4697
|
+
mtimeMs = (0, import_node_fs3.statSync)(filePath).mtimeMs;
|
|
4324
4698
|
}
|
|
4325
|
-
const content = (
|
|
4699
|
+
const content = readTranscriptTextSync(filePath);
|
|
4326
4700
|
const session = parseSession(content, { skipStats: true });
|
|
4327
4701
|
const isSubagent = opts?.isSubagent ? 1 : 0;
|
|
4328
4702
|
const parentSessionId = opts?.parentSessionId ?? null;
|
|
@@ -4351,7 +4725,7 @@ var SearchIndex = class {
|
|
|
4351
4725
|
const files = this.db.prepare("SELECT file_path FROM indexed_files").all();
|
|
4352
4726
|
const missing = files.filter(({ file_path }) => {
|
|
4353
4727
|
try {
|
|
4354
|
-
(0,
|
|
4728
|
+
(0, import_node_fs3.statSync)(file_path);
|
|
4355
4729
|
return false;
|
|
4356
4730
|
} catch (error) {
|
|
4357
4731
|
const code = error.code;
|
|
@@ -4468,7 +4842,7 @@ var SearchIndex = class {
|
|
|
4468
4842
|
this.db.close();
|
|
4469
4843
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
4470
4844
|
try {
|
|
4471
|
-
(0,
|
|
4845
|
+
(0, import_node_fs3.unlinkSync)(this.dbPath + suffix);
|
|
4472
4846
|
} catch {
|
|
4473
4847
|
}
|
|
4474
4848
|
}
|
|
@@ -4492,7 +4866,7 @@ var SearchIndex = class {
|
|
|
4492
4866
|
const txn = this.db.transaction(() => {
|
|
4493
4867
|
for (const file of files) {
|
|
4494
4868
|
try {
|
|
4495
|
-
const content = (
|
|
4869
|
+
const content = readTranscriptTextSync(file.path);
|
|
4496
4870
|
const session = parseSession(content, { skipStats: true });
|
|
4497
4871
|
this.insertSessionContent(insert, file.sessionId, file.path, session);
|
|
4498
4872
|
insertFile.run(file.path, file.mtimeMs, file.sessionId, file.isSubagent ? 1 : 0, file.parentSessionId);
|
|
@@ -4571,6 +4945,11 @@ function parseMaxAge(raw) {
|
|
|
4571
4945
|
}
|
|
4572
4946
|
}
|
|
4573
4947
|
|
|
4948
|
+
// src/lib/read-session.ts
|
|
4949
|
+
function parseTranscript(content, filePath) {
|
|
4950
|
+
return scopeParsedSession(parseSession(content), storeForPath(filePath)?.instanceId ?? "default");
|
|
4951
|
+
}
|
|
4952
|
+
|
|
4574
4953
|
// src/commands/search.ts
|
|
4575
4954
|
async function searchSessions(query, opts, searchIndex) {
|
|
4576
4955
|
if (!query || query.length < 2) {
|
|
@@ -4583,9 +4962,9 @@ async function searchSessions(query, opts, searchIndex) {
|
|
|
4583
4962
|
let ownedIndex = false;
|
|
4584
4963
|
if (!index && searchIndex === void 0) {
|
|
4585
4964
|
try {
|
|
4586
|
-
const dbExists = (0,
|
|
4965
|
+
const dbExists = (0, import_node_fs4.existsSync)(DEFAULT_DB_PATH);
|
|
4587
4966
|
if (!dbExists) {
|
|
4588
|
-
(0,
|
|
4967
|
+
(0, import_node_fs4.mkdirSync)((0, import_node_path3.dirname)(DEFAULT_DB_PATH), { recursive: true });
|
|
4589
4968
|
}
|
|
4590
4969
|
index = new SearchIndex(DEFAULT_DB_PATH);
|
|
4591
4970
|
ownedIndex = true;
|
|
@@ -4659,7 +5038,7 @@ async function cwdFromFilePath(filePath) {
|
|
|
4659
5038
|
const cached = cwdCache.get(filePath);
|
|
4660
5039
|
if (cached !== void 0) return cached;
|
|
4661
5040
|
try {
|
|
4662
|
-
const fh = await (
|
|
5041
|
+
const fh = await openTranscript(filePath);
|
|
4663
5042
|
try {
|
|
4664
5043
|
const buf = Buffer.alloc(CWD_READ_BYTES);
|
|
4665
5044
|
const { bytesRead } = await fh.read(buf, 0, CWD_READ_BYTES, 0);
|
|
@@ -4717,7 +5096,7 @@ function searchField(text, location, query, caseSensitive, extras) {
|
|
|
4717
5096
|
async function discoverSingleSession(sessionId) {
|
|
4718
5097
|
const jsonlPath = await findSessionFile(sessionId);
|
|
4719
5098
|
if (!jsonlPath) return [];
|
|
4720
|
-
const s = await (0,
|
|
5099
|
+
const s = await (0, import_promises2.stat)(jsonlPath);
|
|
4721
5100
|
return [{ path: jsonlPath, mtimeMs: s.mtimeMs, sessionId }];
|
|
4722
5101
|
}
|
|
4723
5102
|
function discoverAllSessions(maxAgeMs) {
|
|
@@ -4725,7 +5104,7 @@ function discoverAllSessions(maxAgeMs) {
|
|
|
4725
5104
|
}
|
|
4726
5105
|
async function rawTextMatch(filePath, query, caseSensitive) {
|
|
4727
5106
|
try {
|
|
4728
|
-
const content = await (
|
|
5107
|
+
const content = await readTranscriptText(filePath);
|
|
4729
5108
|
const haystack = caseSensitive ? content : content.toLowerCase();
|
|
4730
5109
|
const needle = caseSensitive ? query : query.toLowerCase();
|
|
4731
5110
|
if (haystack.includes(needle)) return content;
|
|
@@ -4800,7 +5179,7 @@ async function walkSubagentFiles(parentJsonlPath, query, caseSensitive, currentD
|
|
|
4800
5179
|
const subDir = parentJsonlPath.replace(/\.jsonl$/, "") + "/subagents";
|
|
4801
5180
|
let files;
|
|
4802
5181
|
try {
|
|
4803
|
-
files = await (0,
|
|
5182
|
+
files = await (0, import_promises2.readdir)(subDir);
|
|
4804
5183
|
} catch {
|
|
4805
5184
|
return [];
|
|
4806
5185
|
}
|
|
@@ -4817,7 +5196,7 @@ async function walkSubagentFiles(parentJsonlPath, query, caseSensitive, currentD
|
|
|
4817
5196
|
);
|
|
4818
5197
|
const hitArrays = await Promise.all(
|
|
4819
5198
|
matched.filter((m) => m.rawContent).map(async ({ agentId, filePath, rawContent }) => {
|
|
4820
|
-
const subSession =
|
|
5199
|
+
const subSession = parseTranscript(rawContent, filePath);
|
|
4821
5200
|
const subHits = walkSession(subSession, query, caseSensitive, `agent/${agentId}/`);
|
|
4822
5201
|
const nestedHits = await walkSubagentFiles(filePath, query, caseSensitive, currentDepth + 1, maxDepth);
|
|
4823
5202
|
return [...subHits, ...nestedHits];
|
|
@@ -4838,7 +5217,7 @@ async function rawScanSearch(query, sessionId, maxAgeMs, limit, caseSensitive, e
|
|
|
4838
5217
|
const rawContent = await rawTextMatch(file.path, query, caseSensitive);
|
|
4839
5218
|
sessionsSearched++;
|
|
4840
5219
|
if (!rawContent) continue;
|
|
4841
|
-
const session =
|
|
5220
|
+
const session = parseTranscript(rawContent, file.path);
|
|
4842
5221
|
const sessionHits = walkSession(session, query, caseSensitive);
|
|
4843
5222
|
const subagentHits = await walkSubagentFiles(file.path, query, caseSensitive, 0, MAX_SUBAGENT_DEPTH);
|
|
4844
5223
|
const allHits = [...sessionHits, ...subagentHits];
|
|
@@ -4878,14 +5257,14 @@ async function rawScanSearch(query, sessionId, maxAgeMs, limit, caseSensitive, e
|
|
|
4878
5257
|
}
|
|
4879
5258
|
|
|
4880
5259
|
// src/commands/context.ts
|
|
4881
|
-
var
|
|
5260
|
+
var import_promises4 = require("node:fs/promises");
|
|
4882
5261
|
var import_node_path5 = require("node:path");
|
|
4883
5262
|
|
|
4884
5263
|
// src/lib/helpers.ts
|
|
4885
|
-
var
|
|
5264
|
+
var import_promises3 = require("node:fs/promises");
|
|
4886
5265
|
var import_node_path4 = require("node:path");
|
|
4887
5266
|
async function matchSubagentToMember(leadSessionId, subagentFileName, members) {
|
|
4888
|
-
const entries = await (0,
|
|
5267
|
+
const entries = await (0, import_promises3.readdir)(dirs.PROJECTS_DIR, { withFileTypes: true });
|
|
4889
5268
|
for (const entry of entries) {
|
|
4890
5269
|
if (!entry.isDirectory() || entry.name === "memory") continue;
|
|
4891
5270
|
const filePath = (0, import_node_path4.join)(
|
|
@@ -4896,7 +5275,7 @@ async function matchSubagentToMember(leadSessionId, subagentFileName, members) {
|
|
|
4896
5275
|
subagentFileName
|
|
4897
5276
|
);
|
|
4898
5277
|
try {
|
|
4899
|
-
const fh = await (0,
|
|
5278
|
+
const fh = await (0, import_promises3.open)(filePath, "r");
|
|
4900
5279
|
try {
|
|
4901
5280
|
const buf = Buffer.alloc(16384);
|
|
4902
5281
|
const { bytesRead } = await fh.read(buf, 0, 16384, 0);
|
|
@@ -5090,6 +5469,14 @@ function mapContentBlock(block) {
|
|
|
5090
5469
|
return { kind: "recap", content: block.content, timestamp: block.timestamp ?? null };
|
|
5091
5470
|
case "task_notification":
|
|
5092
5471
|
return { kind: "task_notification", content: block.content, timestamp: block.timestamp ?? null };
|
|
5472
|
+
case "local_command":
|
|
5473
|
+
return {
|
|
5474
|
+
kind: "local_command",
|
|
5475
|
+
content: block.content,
|
|
5476
|
+
timestamp: block.timestamp ?? null,
|
|
5477
|
+
...block.contextUsage ? { contextUsage: block.contextUsage } : {},
|
|
5478
|
+
...block.usageReport ? { usageReport: block.usageReport } : {}
|
|
5479
|
+
};
|
|
5093
5480
|
default: {
|
|
5094
5481
|
const exhaustive = block;
|
|
5095
5482
|
return exhaustive;
|
|
@@ -5099,7 +5486,7 @@ function mapContentBlock(block) {
|
|
|
5099
5486
|
async function findSubagentFile(parentJsonlPath, agentId) {
|
|
5100
5487
|
const subagentsDir = parentJsonlPath.replace(/\.jsonl$/, "") + "/subagents";
|
|
5101
5488
|
try {
|
|
5102
|
-
const files = await (0,
|
|
5489
|
+
const files = await (0, import_promises4.readdir)(subagentsDir);
|
|
5103
5490
|
for (const f of files) {
|
|
5104
5491
|
if (!f.startsWith("agent-") || !f.endsWith(".jsonl")) continue;
|
|
5105
5492
|
const fileAgentId = f.replace("agent-", "").replace(".jsonl", "");
|
|
@@ -5113,12 +5500,12 @@ async function findSubagentFile(parentJsonlPath, agentId) {
|
|
|
5113
5500
|
}
|
|
5114
5501
|
async function findTeamContext(sessionId, subagentFileName) {
|
|
5115
5502
|
try {
|
|
5116
|
-
const teamEntries = await (0,
|
|
5503
|
+
const teamEntries = await (0, import_promises4.readdir)(dirs.TEAMS_DIR, { withFileTypes: true });
|
|
5117
5504
|
for (const entry of teamEntries) {
|
|
5118
5505
|
if (!entry.isDirectory()) continue;
|
|
5119
5506
|
const configPath = (0, import_node_path5.join)(dirs.TEAMS_DIR, entry.name, "config.json");
|
|
5120
5507
|
try {
|
|
5121
|
-
const raw = await (0,
|
|
5508
|
+
const raw = await (0, import_promises4.readFile)(configPath, "utf-8");
|
|
5122
5509
|
const config = JSON.parse(raw);
|
|
5123
5510
|
if (config.leadSessionId !== sessionId) continue;
|
|
5124
5511
|
const members = config.members ?? [];
|
|
@@ -5127,10 +5514,10 @@ async function findTeamContext(sessionId, subagentFileName) {
|
|
|
5127
5514
|
let currentTask = null;
|
|
5128
5515
|
try {
|
|
5129
5516
|
const taskDir = (0, import_node_path5.join)(dirs.TASKS_DIR, entry.name);
|
|
5130
|
-
const taskFiles = await (0,
|
|
5517
|
+
const taskFiles = await (0, import_promises4.readdir)(taskDir);
|
|
5131
5518
|
for (const tf of taskFiles) {
|
|
5132
5519
|
if (!tf.endsWith(".json")) continue;
|
|
5133
|
-
const taskRaw = await (0,
|
|
5520
|
+
const taskRaw = await (0, import_promises4.readFile)((0, import_node_path5.join)(taskDir, tf), "utf-8");
|
|
5134
5521
|
const task = JSON.parse(taskRaw);
|
|
5135
5522
|
if (task.owner === memberName && task.status === "in_progress") {
|
|
5136
5523
|
currentTask = { id: task.id, subject: task.subject, status: task.status };
|
|
@@ -5178,27 +5565,27 @@ function findAgentMetadata(session, agentId) {
|
|
|
5178
5565
|
async function getSessionOverview(sessionId) {
|
|
5179
5566
|
const jsonlPath = await findSessionFile(sessionId);
|
|
5180
5567
|
if (!jsonlPath) return { error: "Session not found" };
|
|
5181
|
-
const content = await (
|
|
5182
|
-
const session =
|
|
5568
|
+
const content = await readTranscriptText(jsonlPath);
|
|
5569
|
+
const session = parseTranscript(content, jsonlPath);
|
|
5183
5570
|
return mapSessionToOverview(session);
|
|
5184
5571
|
}
|
|
5185
5572
|
async function getTurnDetail(sessionId, turnIndex) {
|
|
5186
5573
|
const jsonlPath = await findSessionFile(sessionId);
|
|
5187
5574
|
if (!jsonlPath) return { error: "Session not found" };
|
|
5188
|
-
const content = await (
|
|
5189
|
-
const session =
|
|
5575
|
+
const content = await readTranscriptText(jsonlPath);
|
|
5576
|
+
const session = parseTranscript(content, jsonlPath);
|
|
5190
5577
|
if (turnIndex < 0 || turnIndex >= session.turns.length) return { error: "Turn not found" };
|
|
5191
5578
|
return mapTurnToDetail(session, turnIndex);
|
|
5192
5579
|
}
|
|
5193
5580
|
async function getAgentOverview(sessionId, agentId) {
|
|
5194
5581
|
const jsonlPath = await findSessionFile(sessionId);
|
|
5195
5582
|
if (!jsonlPath) return { error: "Session not found" };
|
|
5196
|
-
const content = await (
|
|
5197
|
-
const session =
|
|
5583
|
+
const content = await readTranscriptText(jsonlPath);
|
|
5584
|
+
const session = parseTranscript(content, jsonlPath);
|
|
5198
5585
|
const subagentFile = await findSubagentFile(jsonlPath, agentId);
|
|
5199
5586
|
if (!subagentFile) return { error: "Agent not found" };
|
|
5200
|
-
const subagentContent = await (
|
|
5201
|
-
const subagentSession =
|
|
5587
|
+
const subagentContent = await readTranscriptText(subagentFile.filePath);
|
|
5588
|
+
const subagentSession = parseTranscript(subagentContent, subagentFile.filePath);
|
|
5202
5589
|
const metadata = findAgentMetadata(session, agentId);
|
|
5203
5590
|
const parentToolCallId = findParentToolCallId(session, agentId);
|
|
5204
5591
|
const teamContext = await findTeamContext(sessionId, subagentFile.fileName);
|
|
@@ -5218,8 +5605,8 @@ async function getAgentTurnDetail(sessionId, agentId, turnIndex) {
|
|
|
5218
5605
|
if (!jsonlPath) return { error: "Session not found" };
|
|
5219
5606
|
const subagentFile = await findSubagentFile(jsonlPath, agentId);
|
|
5220
5607
|
if (!subagentFile) return { error: "Agent not found" };
|
|
5221
|
-
const subagentContent = await (
|
|
5222
|
-
const subagentSession =
|
|
5608
|
+
const subagentContent = await readTranscriptText(subagentFile.filePath);
|
|
5609
|
+
const subagentSession = parseTranscript(subagentContent, subagentFile.filePath);
|
|
5223
5610
|
if (turnIndex < 0 || turnIndex >= subagentSession.turns.length) return { error: "Turn not found" };
|
|
5224
5611
|
return mapTurnToDetail(subagentSession, turnIndex);
|
|
5225
5612
|
}
|
|
@@ -5228,7 +5615,7 @@ async function getAgentTurnDetail(sessionId, agentId, turnIndex) {
|
|
|
5228
5615
|
var import_node_path6 = require("node:path");
|
|
5229
5616
|
|
|
5230
5617
|
// src/lib/metadata.ts
|
|
5231
|
-
var
|
|
5618
|
+
var import_promises5 = require("node:fs/promises");
|
|
5232
5619
|
|
|
5233
5620
|
// src/lib/sessionStatus.ts
|
|
5234
5621
|
function deriveSessionStatus(rawMessages) {
|
|
@@ -5253,15 +5640,16 @@ function scanClaudeMessages(lines) {
|
|
|
5253
5640
|
turnCount: 0
|
|
5254
5641
|
};
|
|
5255
5642
|
for (const line of lines) {
|
|
5256
|
-
|
|
5643
|
+
if (isAnyAgentNoiseLine(line)) continue;
|
|
5644
|
+
let record2;
|
|
5257
5645
|
try {
|
|
5258
|
-
|
|
5646
|
+
record2 = JSON.parse(line);
|
|
5259
5647
|
} catch {
|
|
5260
5648
|
continue;
|
|
5261
5649
|
}
|
|
5262
|
-
if (
|
|
5263
|
-
if (!meta.timestamp) meta.timestamp =
|
|
5264
|
-
const content =
|
|
5650
|
+
if (record2.type !== "user" || record2.isMeta) continue;
|
|
5651
|
+
if (!meta.timestamp) meta.timestamp = record2.timestamp || "";
|
|
5652
|
+
const content = record2.message?.content;
|
|
5265
5653
|
let extracted = "";
|
|
5266
5654
|
if (typeof content === "string") {
|
|
5267
5655
|
extracted = cleanUserText(content);
|
|
@@ -5281,11 +5669,11 @@ function scanClaudeMessages(lines) {
|
|
|
5281
5669
|
return meta;
|
|
5282
5670
|
}
|
|
5283
5671
|
async function readLines(filePath, size) {
|
|
5284
|
-
if (size <= FULL_READ_LIMIT) {
|
|
5285
|
-
const content = await (
|
|
5672
|
+
if (size <= FULL_READ_LIMIT || isCompressedTranscript(filePath)) {
|
|
5673
|
+
const content = await readTranscriptText(filePath);
|
|
5286
5674
|
return { lines: content.split("\n").filter(Boolean), isPartialRead: false };
|
|
5287
5675
|
}
|
|
5288
|
-
const handle = await (0,
|
|
5676
|
+
const handle = await (0, import_promises5.open)(filePath, "r");
|
|
5289
5677
|
try {
|
|
5290
5678
|
const buffer = Buffer.alloc(HEAD_READ_BYTES);
|
|
5291
5679
|
const { bytesRead } = await handle.read(buffer, 0, HEAD_READ_BYTES, 0);
|
|
@@ -5300,14 +5688,15 @@ async function readLines(filePath, size) {
|
|
|
5300
5688
|
}
|
|
5301
5689
|
}
|
|
5302
5690
|
async function getSessionMeta(filePath) {
|
|
5303
|
-
const fileStat = await (0,
|
|
5691
|
+
const fileStat = await (0, import_promises5.stat)(filePath);
|
|
5304
5692
|
let { lines, isPartialRead } = await readLines(filePath, fileStat.size);
|
|
5305
5693
|
const format = formatForText(lines.join("\n"));
|
|
5306
5694
|
if (isPartialRead && !format.descriptor.metadataFromHead) {
|
|
5307
|
-
lines = (await (0,
|
|
5695
|
+
lines = (await (0, import_promises5.readFile)(filePath, "utf-8")).split("\n").filter(Boolean);
|
|
5308
5696
|
isPartialRead = false;
|
|
5309
5697
|
}
|
|
5310
5698
|
const header = readHeader(format, lines);
|
|
5699
|
+
const instanceId = storeForPath(filePath)?.instanceId ?? "default";
|
|
5311
5700
|
const conversation = header.turnCount === void 0 ? scanClaudeMessages(lines) : {
|
|
5312
5701
|
firstUserMessage: header.firstUserMessage ?? "",
|
|
5313
5702
|
lastUserMessage: header.lastUserMessage ?? "",
|
|
@@ -5315,7 +5704,7 @@ async function getSessionMeta(filePath) {
|
|
|
5315
5704
|
turnCount: header.turnCount
|
|
5316
5705
|
};
|
|
5317
5706
|
return {
|
|
5318
|
-
sessionId: header.sessionId,
|
|
5707
|
+
sessionId: instanceSessionId(instanceId, header.sessionId),
|
|
5319
5708
|
version: header.version,
|
|
5320
5709
|
gitBranch: header.gitBranch,
|
|
5321
5710
|
model: header.model,
|
|
@@ -5325,17 +5714,17 @@ async function getSessionMeta(filePath) {
|
|
|
5325
5714
|
// A head read saw a known number of lines in a known number of bytes;
|
|
5326
5715
|
// scaling that by the file size is the only estimate available.
|
|
5327
5716
|
lineCount: isPartialRead ? Math.round(fileStat.size / (HEAD_READ_BYTES / lines.length)) : lines.length,
|
|
5328
|
-
branchedFrom: header.branchedFrom
|
|
5717
|
+
branchedFrom: header.branchedFrom ? { ...header.branchedFrom, sessionId: instanceSessionId(instanceId, header.branchedFrom.sessionId) } : void 0
|
|
5329
5718
|
};
|
|
5330
5719
|
}
|
|
5331
5720
|
async function getSessionStatus(filePath) {
|
|
5332
5721
|
const CHUNK = 4096;
|
|
5333
5722
|
const MAX_CHUNKS = 64;
|
|
5334
5723
|
try {
|
|
5335
|
-
const
|
|
5336
|
-
if (fileStat.size === 0) return { status: "idle" };
|
|
5337
|
-
const fh = await (0, import_promises4.open)(filePath, "r");
|
|
5724
|
+
const fh = await openTranscript(filePath);
|
|
5338
5725
|
try {
|
|
5726
|
+
const fileStat = await fh.stat();
|
|
5727
|
+
if (fileStat.size === 0) return { status: "idle" };
|
|
5339
5728
|
const meaningful = [];
|
|
5340
5729
|
let cursor = fileStat.size;
|
|
5341
5730
|
let leftover = "";
|
|
@@ -5351,7 +5740,7 @@ async function getSessionStatus(filePath) {
|
|
|
5351
5740
|
const startIdx = cursor > 0 ? 1 : 0;
|
|
5352
5741
|
for (let i = lines.length - 1; i >= startIdx; i--) {
|
|
5353
5742
|
const line = lines[i];
|
|
5354
|
-
if (!line) continue;
|
|
5743
|
+
if (!line || isAnyAgentNoiseLine(line)) continue;
|
|
5355
5744
|
let obj;
|
|
5356
5745
|
try {
|
|
5357
5746
|
obj = JSON.parse(line);
|
|
@@ -5420,7 +5809,7 @@ async function getSessionStatus(filePath) {
|
|
|
5420
5809
|
// src/commands/sessions.ts
|
|
5421
5810
|
function toSessionSummary(file, meta, status) {
|
|
5422
5811
|
return {
|
|
5423
|
-
sessionId: meta.sessionId,
|
|
5812
|
+
sessionId: file.sessionId || meta.sessionId,
|
|
5424
5813
|
filePath: file.path,
|
|
5425
5814
|
timestamp: meta.timestamp,
|
|
5426
5815
|
model: meta.model,
|
|
@@ -5482,11 +5871,11 @@ async function currentSession(cwd) {
|
|
|
5482
5871
|
}
|
|
5483
5872
|
|
|
5484
5873
|
// src/commands/index-cmd.ts
|
|
5485
|
-
var
|
|
5874
|
+
var import_node_fs5 = require("node:fs");
|
|
5486
5875
|
var import_node_path7 = require("node:path");
|
|
5487
5876
|
async function indexStats(dbPath) {
|
|
5488
5877
|
const path = dbPath ?? DEFAULT_DB_PATH;
|
|
5489
|
-
if (!(0,
|
|
5878
|
+
if (!(0, import_node_fs5.existsSync)(path)) {
|
|
5490
5879
|
return { error: `Database not found at ${path}. Run 'cogpit-memory index rebuild' to create it.` };
|
|
5491
5880
|
}
|
|
5492
5881
|
const index = new SearchIndex(path);
|
|
@@ -5496,7 +5885,7 @@ async function indexStats(dbPath) {
|
|
|
5496
5885
|
}
|
|
5497
5886
|
async function indexRebuild(dbPath) {
|
|
5498
5887
|
const path = dbPath ?? DEFAULT_DB_PATH;
|
|
5499
|
-
(0,
|
|
5888
|
+
(0, import_node_fs5.mkdirSync)((0, import_node_path7.dirname)(path), { recursive: true });
|
|
5500
5889
|
const index = new SearchIndex(path);
|
|
5501
5890
|
index.buildFull();
|
|
5502
5891
|
const stats = index.getStats();
|
|
@@ -5505,7 +5894,7 @@ async function indexRebuild(dbPath) {
|
|
|
5505
5894
|
}
|
|
5506
5895
|
|
|
5507
5896
|
// src/commands/install-skill.ts
|
|
5508
|
-
var
|
|
5897
|
+
var import_node_fs6 = require("node:fs");
|
|
5509
5898
|
var import_node_path8 = require("node:path");
|
|
5510
5899
|
function findSkillContent() {
|
|
5511
5900
|
const candidates = [];
|
|
@@ -5515,8 +5904,8 @@ function findSkillContent() {
|
|
|
5515
5904
|
}
|
|
5516
5905
|
for (const candidate of candidates) {
|
|
5517
5906
|
try {
|
|
5518
|
-
if ((0,
|
|
5519
|
-
return (0,
|
|
5907
|
+
if ((0, import_node_fs6.existsSync)(candidate)) {
|
|
5908
|
+
return (0, import_node_fs6.readFileSync)(candidate, "utf-8");
|
|
5520
5909
|
}
|
|
5521
5910
|
} catch {
|
|
5522
5911
|
}
|
|
@@ -5526,10 +5915,10 @@ function findSkillContent() {
|
|
|
5526
5915
|
function installSkill(cwd, global) {
|
|
5527
5916
|
const root = global ? (0, import_node_path8.join)(process.env.HOME ?? process.env.USERPROFILE ?? "~", ".claude") : (0, import_node_path8.join)(cwd ?? process.cwd(), ".claude");
|
|
5528
5917
|
const skillDir = (0, import_node_path8.join)(root, "skills", "cogpit-memory");
|
|
5529
|
-
(0,
|
|
5918
|
+
(0, import_node_fs6.mkdirSync)(skillDir, { recursive: true });
|
|
5530
5919
|
const content = findSkillContent();
|
|
5531
5920
|
const dest = (0, import_node_path8.join)(skillDir, "SKILL.md");
|
|
5532
|
-
(0,
|
|
5921
|
+
(0, import_node_fs6.writeFileSync)(dest, content);
|
|
5533
5922
|
return { installed: true, path: skillDir };
|
|
5534
5923
|
}
|
|
5535
5924
|
|