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/index.js
CHANGED
|
@@ -54,10 +54,39 @@ var import_better_sqlite3 = __toESM(require("better-sqlite3"));
|
|
|
54
54
|
var Database = import_better_sqlite3.default;
|
|
55
55
|
|
|
56
56
|
// src/lib/search-index.ts
|
|
57
|
-
var
|
|
57
|
+
var import_node_fs3 = require("node:fs");
|
|
58
58
|
|
|
59
59
|
// src/lib/types.ts
|
|
60
|
-
var AGENT_KINDS = ["codex", "copilot", "claude"];
|
|
60
|
+
var AGENT_KINDS = ["acp", "codex", "copilot", "claude"];
|
|
61
|
+
|
|
62
|
+
// src/lib/instances.ts
|
|
63
|
+
function instanceSessionId(instanceId, nativeId) {
|
|
64
|
+
if (instanceId === "default") return nativeId;
|
|
65
|
+
const bytes = new TextEncoder().encode(nativeId);
|
|
66
|
+
const encoded = btoa(Array.from(bytes, (byte) => String.fromCharCode(byte)).join("")).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
67
|
+
return `i-${instanceId}__${encoded}`;
|
|
68
|
+
}
|
|
69
|
+
function splitInstanceSessionId(id) {
|
|
70
|
+
const match = /^i-([A-Za-z0-9-]+)__([A-Za-z0-9_-]+)$/.exec(id);
|
|
71
|
+
if (!match) return { instanceId: "default", nativeId: id };
|
|
72
|
+
try {
|
|
73
|
+
const bytes = Uint8Array.from(atob(match[2].replace(/-/g, "+").replace(/_/g, "/")), (character) => character.charCodeAt(0));
|
|
74
|
+
return { instanceId: match[1], nativeId: new TextDecoder("utf-8", { fatal: true }).decode(bytes) };
|
|
75
|
+
} catch {
|
|
76
|
+
throw new Error("Invalid instance session ID");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function instanceDirName(instanceId, nativeDirName) {
|
|
80
|
+
return instanceId === "default" ? nativeDirName : `instance__${instanceId}__${nativeDirName}`;
|
|
81
|
+
}
|
|
82
|
+
function splitInstanceDirName(dirName) {
|
|
83
|
+
const match = /^instance__([A-Za-z0-9-]+)__(.+)$/.exec(dirName ?? "");
|
|
84
|
+
return match ? { instanceId: match[1], nativeDirName: match[2] } : { instanceId: "default", nativeDirName: dirName ?? "" };
|
|
85
|
+
}
|
|
86
|
+
function scopeParsedSession(session, instanceId) {
|
|
87
|
+
if (instanceId === "default") return session;
|
|
88
|
+
return { ...session, instanceId, sessionId: instanceSessionId(instanceId, splitInstanceSessionId(session.sessionId).nativeId), ...session.branchedFrom ? { branchedFrom: { ...session.branchedFrom, sessionId: instanceSessionId(instanceId, splitInstanceSessionId(session.branchedFrom.sessionId).nativeId) } } : {} };
|
|
89
|
+
}
|
|
61
90
|
|
|
62
91
|
// src/lib/agent-descriptors.ts
|
|
63
92
|
var SESSION_UUID = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
|
|
@@ -84,14 +113,26 @@ function base64DirNameCodec(prefix) {
|
|
|
84
113
|
return {
|
|
85
114
|
lossy: false,
|
|
86
115
|
owns: (dirName) => typeof dirName === "string" && dirName.startsWith(prefix),
|
|
116
|
+
recognizes: (dirName) => {
|
|
117
|
+
const cwd = decodeBase64DirName(prefix, dirName);
|
|
118
|
+
return cwd !== null && cwd !== "" && encodeBase64DirName(prefix, cwd) === dirName;
|
|
119
|
+
},
|
|
87
120
|
encode: (cwd) => encodeBase64DirName(prefix, cwd),
|
|
88
121
|
decode: (dirName) => decodeBase64DirName(prefix, dirName)
|
|
89
122
|
};
|
|
90
123
|
}
|
|
91
124
|
function sessionIdFromJsonlName(fileName) {
|
|
92
|
-
const match = new RegExp(`(${SESSION_UUID})\\.jsonl
|
|
125
|
+
const match = new RegExp(`(${SESSION_UUID})\\.jsonl(?:\\.zst)?$`, "i").exec(fileName);
|
|
93
126
|
return match ? match[1] : null;
|
|
94
127
|
}
|
|
128
|
+
function ownTranscriptRoot(sessionId) {
|
|
129
|
+
return sessionId ? { rootSessionId: sessionId.toLowerCase(), isRootTranscript: true } : null;
|
|
130
|
+
}
|
|
131
|
+
function plainPathSegments(relativePath) {
|
|
132
|
+
const segments = relativePath.split("/");
|
|
133
|
+
const plain = segments.every((segment) => segment !== "" && segment !== "." && segment !== ".." && !segment.includes("\\"));
|
|
134
|
+
return plain ? segments : null;
|
|
135
|
+
}
|
|
95
136
|
var jsonlPathUrlCodec = {
|
|
96
137
|
urlId: (fileName) => fileName.replace(/\.jsonl$/, ""),
|
|
97
138
|
fileNameFromUrlId: (urlId) => urlId.endsWith(".jsonl") ? urlId : `${urlId}.jsonl`
|
|
@@ -109,6 +150,17 @@ var ARGV_SESSION_ID = "[0-9a-f-]{36}";
|
|
|
109
150
|
var CODEX_DIR_PREFIX = "codex__";
|
|
110
151
|
var COPILOT_DIR_PREFIX = "copilot__";
|
|
111
152
|
var CLAUDE_PERMISSION_MODES = /* @__PURE__ */ new Set(["default", "plan", "acceptEdits", "dontAsk", "auto"]);
|
|
153
|
+
var CLAUDE_SESSION_CHILD_DIRS = /* @__PURE__ */ new Set(["subagents", "workflows"]);
|
|
154
|
+
function claudeTranscriptRoot(relativePath) {
|
|
155
|
+
const segments = plainPathSegments(relativePath);
|
|
156
|
+
if (!segments) return null;
|
|
157
|
+
const [root, childDir] = segments;
|
|
158
|
+
if (segments.length === 1) {
|
|
159
|
+
const sessionId = root.replace(/\.jsonl$/, "");
|
|
160
|
+
return sessionId !== root && isSessionUuid(sessionId) ? ownTranscriptRoot(sessionId) : null;
|
|
161
|
+
}
|
|
162
|
+
return segments.length > 2 && CLAUDE_SESSION_CHILD_DIRS.has(childDir) && isSessionUuid(root) ? { rootSessionId: root.toLowerCase(), isRootTranscript: false } : null;
|
|
163
|
+
}
|
|
112
164
|
var claude = {
|
|
113
165
|
kind: "claude",
|
|
114
166
|
displayName: "Claude Code",
|
|
@@ -119,6 +171,8 @@ var claude = {
|
|
|
119
171
|
// registry checks the prefixed agents first, so this is only ever reached
|
|
120
172
|
// as the terminal arm.
|
|
121
173
|
owns: (dirName) => typeof dirName === "string" && !dirName.startsWith(CODEX_DIR_PREFIX) && !dirName.startsWith(COPILOT_DIR_PREFIX),
|
|
174
|
+
// An absolute path starts with `/` or `\` (`-`) or a drive (`C:\` → `C--`); a drive root alone is `C-`.
|
|
175
|
+
recognizes: (dirName) => /^(?:(?:-|[A-Za-z]--)[A-Za-z0-9-]*|[A-Za-z]-)$/.test(dirName),
|
|
122
176
|
/**
|
|
123
177
|
* Claude Code's own convention: every character outside `[A-Za-z0-9]`
|
|
124
178
|
* becomes `-`, so `/Users/x/proj` and `C:\Users\x\proj` both collapse into a
|
|
@@ -136,10 +190,12 @@ var claude = {
|
|
|
136
190
|
return "/" + dirName.replace(/^-/, "").replace(/-/g, "/");
|
|
137
191
|
}
|
|
138
192
|
},
|
|
193
|
+
projectWorktreeDir: ".claude/worktrees",
|
|
139
194
|
sessionFile: {
|
|
140
195
|
name: (sessionId) => `${sessionId}.jsonl`,
|
|
141
196
|
sessionId: sessionIdFromJsonlName,
|
|
142
|
-
...jsonlPathUrlCodec
|
|
197
|
+
...jsonlPathUrlCodec,
|
|
198
|
+
transcriptRoot: claudeTranscriptRoot
|
|
143
199
|
},
|
|
144
200
|
metadataFromHead: true,
|
|
145
201
|
resume: {
|
|
@@ -174,6 +230,7 @@ var claude = {
|
|
|
174
230
|
bundledBySdk: true,
|
|
175
231
|
homeDirName: ".claude",
|
|
176
232
|
homeEnvVar: null,
|
|
233
|
+
sdkHomeEnvVar: "CLAUDE_CONFIG_DIR",
|
|
177
234
|
installMarker: "projects",
|
|
178
235
|
homeIsDiscoverable: false,
|
|
179
236
|
process: {
|
|
@@ -185,7 +242,8 @@ var claude = {
|
|
|
185
242
|
rootDirName: ".claude",
|
|
186
243
|
instructions: [
|
|
187
244
|
{ in: "scope", path: "CLAUDE.md", scopes: ["global", "project"] },
|
|
188
|
-
{ in: "root", path: "CLAUDE.md", label: ".claude/CLAUDE.md", scopes: ["project"] }
|
|
245
|
+
{ in: "root", path: "CLAUDE.md", label: ".claude/CLAUDE.md", scopes: ["project"] },
|
|
246
|
+
{ in: "scope", path: "AGENTS.md", scopes: ["project"], fallback: true }
|
|
189
247
|
],
|
|
190
248
|
settings: [
|
|
191
249
|
{ in: "root", path: "settings.json", scopes: ["global"] },
|
|
@@ -230,9 +288,12 @@ var claude = {
|
|
|
230
288
|
nativeRewind: false,
|
|
231
289
|
fileCheckpoints: true,
|
|
232
290
|
reasoningEffort: true,
|
|
291
|
+
configurableContextWindow: false,
|
|
233
292
|
fastTier: true,
|
|
293
|
+
lowPriorityInTerminal: true,
|
|
234
294
|
imageInput: true,
|
|
235
|
-
midTurnSteering:
|
|
295
|
+
midTurnSteering: true,
|
|
296
|
+
activeNotifications: true,
|
|
236
297
|
stopSession: true,
|
|
237
298
|
ultracode: true,
|
|
238
299
|
contextWindow: true,
|
|
@@ -240,6 +301,7 @@ var claude = {
|
|
|
240
301
|
accountSwitching: true,
|
|
241
302
|
modelFallbackNotices: true,
|
|
242
303
|
autoPermissionMode: "per-model",
|
|
304
|
+
toolPermissionRules: true,
|
|
243
305
|
settingsApply: "live",
|
|
244
306
|
turnLiveness: "transcript",
|
|
245
307
|
tokenStreaming: true,
|
|
@@ -256,6 +318,7 @@ var codex = {
|
|
|
256
318
|
displayName: "Codex",
|
|
257
319
|
binName: "codex",
|
|
258
320
|
dirName: base64DirNameCodec(CODEX_DIR_PREFIX),
|
|
321
|
+
projectWorktreeDir: null,
|
|
259
322
|
sessionFile: {
|
|
260
323
|
/**
|
|
261
324
|
* Codex nests rollouts by local date:
|
|
@@ -272,7 +335,9 @@ var codex = {
|
|
|
272
335
|
},
|
|
273
336
|
sessionId: sessionIdFromJsonlName,
|
|
274
337
|
// The date nesting is unrecoverable from the id, so the URL keeps the path.
|
|
275
|
-
...jsonlPathUrlCodec
|
|
338
|
+
...jsonlPathUrlCodec,
|
|
339
|
+
// A sub-agent is a rollout of its own, linked to its parent only in its header.
|
|
340
|
+
transcriptRoot: (relativePath) => plainPathSegments(relativePath) ? ownTranscriptRoot(sessionIdFromJsonlName(relativePath)) : null
|
|
276
341
|
},
|
|
277
342
|
// A rollout's header carries no turn count and no last user message; both
|
|
278
343
|
// only settle at the end of the file.
|
|
@@ -285,6 +350,7 @@ var codex = {
|
|
|
285
350
|
permissions: (permissions) => {
|
|
286
351
|
const mode = permissions?.mode || "default";
|
|
287
352
|
if (mode === "bypassPermissions") return ["--dangerously-bypass-approvals-and-sandbox"];
|
|
353
|
+
if (mode === "auto") return ["--approve-for-me"];
|
|
288
354
|
return [
|
|
289
355
|
"--sandbox",
|
|
290
356
|
mode === "plan" ? "read-only" : "workspace-write",
|
|
@@ -313,6 +379,7 @@ var codex = {
|
|
|
313
379
|
bundledBySdk: false,
|
|
314
380
|
homeDirName: ".codex",
|
|
315
381
|
homeEnvVar: "CODEX_HOME",
|
|
382
|
+
sessionIdEnvVar: "CODEX_THREAD_ID",
|
|
316
383
|
installMarker: "",
|
|
317
384
|
homeIsDiscoverable: true,
|
|
318
385
|
process: {
|
|
@@ -344,7 +411,7 @@ var codex = {
|
|
|
344
411
|
extendedContext: null
|
|
345
412
|
},
|
|
346
413
|
capabilities: {
|
|
347
|
-
worktrees:
|
|
414
|
+
worktrees: true,
|
|
348
415
|
mcp: false,
|
|
349
416
|
slashCommands: false,
|
|
350
417
|
workflows: false,
|
|
@@ -358,16 +425,20 @@ var codex = {
|
|
|
358
425
|
nativeRewind: false,
|
|
359
426
|
fileCheckpoints: false,
|
|
360
427
|
reasoningEffort: true,
|
|
428
|
+
configurableContextWindow: true,
|
|
361
429
|
fastTier: true,
|
|
430
|
+
lowPriorityInTerminal: false,
|
|
362
431
|
imageInput: true,
|
|
363
432
|
midTurnSteering: true,
|
|
433
|
+
activeNotifications: true,
|
|
364
434
|
stopSession: false,
|
|
365
435
|
ultracode: false,
|
|
366
436
|
contextWindow: false,
|
|
367
437
|
externalProcesses: false,
|
|
368
438
|
accountSwitching: false,
|
|
369
439
|
modelFallbackNotices: false,
|
|
370
|
-
autoPermissionMode: "
|
|
440
|
+
autoPermissionMode: "always",
|
|
441
|
+
toolPermissionRules: true,
|
|
371
442
|
settingsApply: "next-turn",
|
|
372
443
|
turnLiveness: "runtime",
|
|
373
444
|
tokenStreaming: true,
|
|
@@ -376,24 +447,25 @@ var codex = {
|
|
|
376
447
|
namedSessions: false
|
|
377
448
|
}
|
|
378
449
|
};
|
|
450
|
+
function copilotSessionId(fileName) {
|
|
451
|
+
const match = new RegExp(`^(${SESSION_UUID})/events\\.jsonl$`, "i").exec(fileName);
|
|
452
|
+
return match ? match[1] : null;
|
|
453
|
+
}
|
|
379
454
|
var copilot = {
|
|
380
455
|
kind: "copilot",
|
|
381
456
|
displayName: "GitHub Copilot CLI",
|
|
382
457
|
binName: "copilot",
|
|
383
458
|
dirName: base64DirNameCodec(COPILOT_DIR_PREFIX),
|
|
459
|
+
projectWorktreeDir: null,
|
|
384
460
|
sessionFile: {
|
|
385
461
|
/** Copilot keeps one directory per session: `<uuid>/events.jsonl`. */
|
|
386
462
|
name: (sessionId) => `${sessionId}/events.jsonl`,
|
|
387
|
-
sessionId:
|
|
388
|
-
const match = new RegExp(`^(${SESSION_UUID})/events\\.jsonl$`, "i").exec(fileName);
|
|
389
|
-
return match ? match[1] : null;
|
|
390
|
-
},
|
|
463
|
+
sessionId: copilotSessionId,
|
|
391
464
|
// `<id>/events.jsonl` rebuilds from the id, so URLs stay short.
|
|
392
|
-
urlId: (fileName) =>
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
fileNameFromUrlId: (urlId) => urlId.endsWith(".jsonl") ? urlId : `${urlId}/events.jsonl`
|
|
465
|
+
urlId: (fileName) => copilotSessionId(fileName) ?? fileName.replace(/\.jsonl$/, ""),
|
|
466
|
+
fileNameFromUrlId: (urlId) => urlId.endsWith(".jsonl") ? urlId : `${urlId}/events.jsonl`,
|
|
467
|
+
// Sub-agents are events inside the parent transcript, so every file is a root.
|
|
468
|
+
transcriptRoot: (relativePath) => ownTranscriptRoot(copilotSessionId(relativePath))
|
|
397
469
|
},
|
|
398
470
|
metadataFromHead: true,
|
|
399
471
|
resume: {
|
|
@@ -475,9 +547,12 @@ var copilot = {
|
|
|
475
547
|
fileCheckpoints: false,
|
|
476
548
|
// Effort is only offered for models whose catalog entry advertises it.
|
|
477
549
|
reasoningEffort: false,
|
|
550
|
+
configurableContextWindow: false,
|
|
478
551
|
fastTier: false,
|
|
552
|
+
lowPriorityInTerminal: false,
|
|
479
553
|
imageInput: false,
|
|
480
554
|
midTurnSteering: true,
|
|
555
|
+
activeNotifications: true,
|
|
481
556
|
stopSession: true,
|
|
482
557
|
ultracode: false,
|
|
483
558
|
contextWindow: false,
|
|
@@ -485,6 +560,7 @@ var copilot = {
|
|
|
485
560
|
accountSwitching: false,
|
|
486
561
|
modelFallbackNotices: false,
|
|
487
562
|
autoPermissionMode: "always",
|
|
563
|
+
toolPermissionRules: true,
|
|
488
564
|
settingsApply: "next-turn",
|
|
489
565
|
turnLiveness: "runtime",
|
|
490
566
|
tokenStreaming: false,
|
|
@@ -492,21 +568,56 @@ var copilot = {
|
|
|
492
568
|
namedSessions: true
|
|
493
569
|
}
|
|
494
570
|
};
|
|
571
|
+
var acp = {
|
|
572
|
+
...copilot,
|
|
573
|
+
kind: "acp",
|
|
574
|
+
displayName: "ACP agent",
|
|
575
|
+
binName: "acp",
|
|
576
|
+
dirName: base64DirNameCodec("acp__"),
|
|
577
|
+
sessionFile: { name: (id) => `${id}.jsonl`, sessionId: sessionIdFromJsonlName, ...jsonlPathUrlCodec, transcriptRoot: (path) => {
|
|
578
|
+
const segments = plainPathSegments(path);
|
|
579
|
+
return segments?.length === 1 ? ownTranscriptRoot(sessionIdFromJsonlName(path)) : null;
|
|
580
|
+
} },
|
|
581
|
+
resume: { command: () => "Resume this session in Cogpit", args: () => [] },
|
|
582
|
+
launchArgs: { permissions: () => [], model: () => [], effort: () => [], fastTier: () => [] },
|
|
583
|
+
cli: { ...copilot.cli, packageName: "", homebrew: null, wingetId: null, selfUpdate: null, requiresConfiguration: true, homeDirName: ".cogpit-acp", homeEnvVar: "COGPIT_ACP_HOME", homeIsDiscoverable: true, installMarker: "projects" },
|
|
584
|
+
config: { ...copilot.config, rootDirName: ".cogpit-acp", instructions: [], settings: [], skillsDir: null },
|
|
585
|
+
capabilities: { ...copilot.capabilities, undo: false, redo: false, nativeRewind: false, nativeFork: false, midTurnSteering: false, activeNotifications: false, externalProcesses: false, autoPermissionMode: "never", toolPermissionRules: false, namedSessions: false }
|
|
586
|
+
};
|
|
495
587
|
var DESCRIPTORS = Object.freeze({
|
|
496
588
|
claude,
|
|
497
589
|
codex,
|
|
498
|
-
copilot
|
|
590
|
+
copilot,
|
|
591
|
+
acp
|
|
499
592
|
});
|
|
500
593
|
function descriptorFor(kind) {
|
|
501
594
|
return DESCRIPTORS[kind];
|
|
502
595
|
}
|
|
503
596
|
function descriptorForDirName(dirName) {
|
|
597
|
+
const scoped = splitInstanceDirName(dirName);
|
|
598
|
+
if (scoped.instanceId !== "default") {
|
|
599
|
+
const descriptor = descriptorForDirName(scoped.nativeDirName);
|
|
600
|
+
return {
|
|
601
|
+
...descriptor,
|
|
602
|
+
dirName: { ...descriptor.dirName, encode: (cwd) => instanceDirName(scoped.instanceId, descriptor.dirName.encode(cwd)), decode: (name) => descriptor.dirName.decode(splitInstanceDirName(name).nativeDirName) },
|
|
603
|
+
sessionFile: { ...descriptor.sessionFile, name: (id) => descriptor.sessionFile.name(splitInstanceSessionId(id).nativeId), sessionId: (file) => {
|
|
604
|
+
const nativeId = descriptor.sessionFile.sessionId(file);
|
|
605
|
+
return nativeId ? instanceSessionId(scoped.instanceId, nativeId) : null;
|
|
606
|
+
}, transcriptRoot: (file) => {
|
|
607
|
+
const root = descriptor.sessionFile.transcriptRoot(file);
|
|
608
|
+
return root ? { ...root, rootSessionId: instanceSessionId(scoped.instanceId, root.rootSessionId) } : null;
|
|
609
|
+
} }
|
|
610
|
+
};
|
|
611
|
+
}
|
|
504
612
|
for (const kind of AGENT_KINDS) {
|
|
505
613
|
if (kind === "claude") continue;
|
|
506
614
|
if (DESCRIPTORS[kind].dirName.owns(dirName)) return DESCRIPTORS[kind];
|
|
507
615
|
}
|
|
508
616
|
return claude;
|
|
509
617
|
}
|
|
618
|
+
function isSessionUuid(value) {
|
|
619
|
+
return SESSION_UUID_RE.test(value);
|
|
620
|
+
}
|
|
510
621
|
|
|
511
622
|
// src/lib/codex-exec.ts
|
|
512
623
|
var JS_SIMPLE_ESCAPES = {
|
|
@@ -575,6 +686,27 @@ function readJsStringLiteral(source, startIndex) {
|
|
|
575
686
|
return null;
|
|
576
687
|
}
|
|
577
688
|
|
|
689
|
+
// src/lib/backgroundAgents.ts
|
|
690
|
+
function awaitingAgentsStatus(launches, pendingQueue = 0) {
|
|
691
|
+
if (!launches.some((launch) => launch.endedAt === void 0)) return null;
|
|
692
|
+
const ordered = [...launches].sort((a, b) => a.launchedAt - b.launchedAt);
|
|
693
|
+
let batchStart = 0;
|
|
694
|
+
let busyUntil = -Infinity;
|
|
695
|
+
ordered.forEach((launch, index) => {
|
|
696
|
+
if (busyUntil < launch.launchedAt) batchStart = index;
|
|
697
|
+
busyUntil = Math.max(busyUntil, launch.endedAt ?? Infinity);
|
|
698
|
+
});
|
|
699
|
+
const batch = ordered.slice(batchStart).map(({ agent, endedAt }) => ({ ...agent, done: endedAt !== void 0 }));
|
|
700
|
+
const running = batch.filter((agent) => !agent.done);
|
|
701
|
+
return {
|
|
702
|
+
status: "awaiting_agents",
|
|
703
|
+
pendingQueue,
|
|
704
|
+
pendingAgents: running.length,
|
|
705
|
+
pendingAgentDescriptions: running.map((agent) => agent.description).filter((description) => description.length > 0),
|
|
706
|
+
backgroundAgents: batch
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
|
|
578
710
|
// src/lib/messageTypeGuards.ts
|
|
579
711
|
function isUserMessage(msg) {
|
|
580
712
|
return msg.type === "user";
|
|
@@ -682,6 +814,36 @@ function parseAgentEnvelope(text) {
|
|
|
682
814
|
return { sender, matched, body: matched ? unwrapped.trim() : text };
|
|
683
815
|
}
|
|
684
816
|
|
|
817
|
+
// src/lib/toolResults.ts
|
|
818
|
+
function record(value) {
|
|
819
|
+
return value && typeof value === "object" ? value : {};
|
|
820
|
+
}
|
|
821
|
+
function toolResultMetadata(name, value) {
|
|
822
|
+
const result = record(value);
|
|
823
|
+
if (name === "Edit" || name === "Write") {
|
|
824
|
+
return result.staged === true ? { awaitingReview: true } : {};
|
|
825
|
+
}
|
|
826
|
+
if (name !== "Bash") return {};
|
|
827
|
+
const diff = record(result.bashEditDiff);
|
|
828
|
+
if (!Array.isArray(diff.files)) return {};
|
|
829
|
+
const files = [];
|
|
830
|
+
for (const raw of diff.files) {
|
|
831
|
+
const file = record(raw);
|
|
832
|
+
if (typeof file.filePath !== "string" || !file.filePath || !Array.isArray(file.hunks)) continue;
|
|
833
|
+
const hunks = [];
|
|
834
|
+
for (const rawHunk of file.hunks) {
|
|
835
|
+
const hunk = record(rawHunk);
|
|
836
|
+
const { oldStart, oldLines, newStart, newLines, lines } = hunk;
|
|
837
|
+
if (![oldStart, oldLines, newStart, newLines].every((n) => typeof n === "number" && Number.isInteger(n) && n >= 0)) continue;
|
|
838
|
+
if (!Array.isArray(lines) || !lines.every((line) => typeof line === "string" && /^[ +\\-]/.test(line))) continue;
|
|
839
|
+
hunks.push({ oldStart, oldLines, newStart, newLines, lines });
|
|
840
|
+
}
|
|
841
|
+
if (hunks.length) files.push({ filePath: file.filePath, hunks });
|
|
842
|
+
}
|
|
843
|
+
const more = typeof diff.moreFiles === "number" && Number.isInteger(diff.moreFiles) && diff.moreFiles > 0 ? diff.moreFiles : 0;
|
|
844
|
+
return { fileDiffs: files, ...more ? { additionalFileDiffs: more } : {} };
|
|
845
|
+
}
|
|
846
|
+
|
|
685
847
|
// src/lib/turnBuilder.ts
|
|
686
848
|
function extractTextFromContent(content) {
|
|
687
849
|
if (typeof content === "string") return content;
|
|
@@ -709,6 +871,17 @@ function isTaskNotificationRecord(msg) {
|
|
|
709
871
|
if (!text.includes("<task-notification>")) return false;
|
|
710
872
|
return text.replace(TASK_NOTIFICATION_BLOCK_RE, "").replace(ANY_TAG_RE, "").trim() === "";
|
|
711
873
|
}
|
|
874
|
+
function isTaskToolName(name) {
|
|
875
|
+
return name === "Task" || name === "Agent";
|
|
876
|
+
}
|
|
877
|
+
function readTaskMeta(input) {
|
|
878
|
+
const effort = input.effort;
|
|
879
|
+
return {
|
|
880
|
+
name: typeof input.name === "string" ? input.name : null,
|
|
881
|
+
subagentType: typeof input.subagent_type === "string" ? input.subagent_type : null,
|
|
882
|
+
effort: typeof effort === "string" || typeof effort === "number" ? String(effort) : null
|
|
883
|
+
};
|
|
884
|
+
}
|
|
712
885
|
function queuedCommandPrompt(msg) {
|
|
713
886
|
if (!isAttachmentMessage(msg)) return null;
|
|
714
887
|
const attachment = msg.attachment;
|
|
@@ -1049,6 +1222,16 @@ function buildTurnsWithStarts(messages) {
|
|
|
1049
1222
|
pendingRecap = { content: msg.content, timestamp: msg.timestamp };
|
|
1050
1223
|
continue;
|
|
1051
1224
|
}
|
|
1225
|
+
if (isSystemMessage(msg) && msg.subtype === "local_command" && msg.content) {
|
|
1226
|
+
current?.contentBlocks.push({
|
|
1227
|
+
kind: "local_command",
|
|
1228
|
+
content: msg.content,
|
|
1229
|
+
timestamp: msg.timestamp,
|
|
1230
|
+
...msg.contextUsage ? { contextUsage: msg.contextUsage } : {},
|
|
1231
|
+
...msg.usageReport ? { usageReport: msg.usageReport } : {}
|
|
1232
|
+
});
|
|
1233
|
+
continue;
|
|
1234
|
+
}
|
|
1052
1235
|
if (isQueueOperationMessage(msg)) {
|
|
1053
1236
|
if (current && msg.operation === "enqueue" && isVisibleQueuedPrompt(msg.content)) {
|
|
1054
1237
|
const parsed = parseAgentEnvelope(msg.content);
|
|
@@ -1100,6 +1283,8 @@ function buildTurnsWithStarts(messages) {
|
|
|
1100
1283
|
if (pending) {
|
|
1101
1284
|
pending.turn.toolCalls[pending.index].result = extractToolResultText(block.content);
|
|
1102
1285
|
pending.turn.toolCalls[pending.index].isError = block.is_error === true;
|
|
1286
|
+
const call = pending.turn.toolCalls[pending.index];
|
|
1287
|
+
Object.assign(call, toolResultMetadata(call.name, msg.toolUseResult));
|
|
1103
1288
|
pendingToolUses.delete(block.tool_use_id);
|
|
1104
1289
|
}
|
|
1105
1290
|
}
|
|
@@ -1120,6 +1305,7 @@ function buildTurnsWithStarts(messages) {
|
|
|
1120
1305
|
parentToolUseId: toolUseId || void 0,
|
|
1121
1306
|
agentName: taskMeta?.name ?? null,
|
|
1122
1307
|
subagentType: taskMeta?.subagentType ?? null,
|
|
1308
|
+
...taskMeta?.effort ? { effort: taskMeta.effort } : {},
|
|
1123
1309
|
type: "assistant",
|
|
1124
1310
|
content: toolUseResult.content,
|
|
1125
1311
|
toolCalls: [],
|
|
@@ -1258,15 +1444,12 @@ function buildTurnsWithStarts(messages) {
|
|
|
1258
1444
|
current.toolCalls.push(tc);
|
|
1259
1445
|
msgToolCalls.push(tc);
|
|
1260
1446
|
pendingToolUses.set(block.id, { turn: current, index: idx });
|
|
1261
|
-
if (block.name
|
|
1447
|
+
if (isTaskToolName(block.name)) {
|
|
1262
1448
|
const input = block.input;
|
|
1263
1449
|
if (input.run_in_background === true) {
|
|
1264
1450
|
backgroundAgentParentIds.add(block.id);
|
|
1265
1451
|
}
|
|
1266
|
-
taskMetaMap.set(block.id,
|
|
1267
|
-
name: input.name ?? null,
|
|
1268
|
-
subagentType: input.subagent_type ?? null
|
|
1269
|
-
});
|
|
1452
|
+
taskMetaMap.set(block.id, readTaskMeta(input));
|
|
1270
1453
|
}
|
|
1271
1454
|
}
|
|
1272
1455
|
}
|
|
@@ -1277,6 +1460,7 @@ function buildTurnsWithStarts(messages) {
|
|
|
1277
1460
|
if (isProgressMessage(msg) && msg.data.type === "agent_progress") {
|
|
1278
1461
|
const data = msg.data;
|
|
1279
1462
|
const parentId = msg.parentToolUseID ?? "";
|
|
1463
|
+
const ownToolUseId = data.toolUseId ?? parentId;
|
|
1280
1464
|
let subAgentUsage = null;
|
|
1281
1465
|
if (data.message.type === "assistant") {
|
|
1282
1466
|
const innerMsg = data.message.message;
|
|
@@ -1293,12 +1477,14 @@ function buildTurnsWithStarts(messages) {
|
|
|
1293
1477
|
}
|
|
1294
1478
|
}
|
|
1295
1479
|
const innerModel = data.message.type === "assistant" ? data.message.message.model ?? null : null;
|
|
1296
|
-
const taskMeta = taskMetaMap.get(
|
|
1480
|
+
const taskMeta = taskMetaMap.get(ownToolUseId);
|
|
1297
1481
|
const agentMsg = {
|
|
1298
1482
|
agentId: data.agentId,
|
|
1299
|
-
parentToolUseId:
|
|
1483
|
+
parentToolUseId: ownToolUseId || void 0,
|
|
1484
|
+
...data.parentAgentId ? { parentAgentId: data.parentAgentId } : {},
|
|
1485
|
+
...taskMeta?.effort ? { effort: taskMeta.effort } : {},
|
|
1300
1486
|
agentName: taskMeta?.name ?? null,
|
|
1301
|
-
subagentType: taskMeta?.subagentType ?? null,
|
|
1487
|
+
subagentType: taskMeta?.subagentType ?? data.subagentType ?? null,
|
|
1302
1488
|
type: data.message.type,
|
|
1303
1489
|
content: data.message.message.content,
|
|
1304
1490
|
toolCalls: [],
|
|
@@ -1318,6 +1504,7 @@ function buildTurnsWithStarts(messages) {
|
|
|
1318
1504
|
} else if (block.type === "text") {
|
|
1319
1505
|
agentMsg.text.push(block.text);
|
|
1320
1506
|
} else if (block.type === "tool_use") {
|
|
1507
|
+
if (isTaskToolName(block.name)) taskMetaMap.set(block.id, readTaskMeta(block.input));
|
|
1321
1508
|
agentMsg.toolCalls.push({
|
|
1322
1509
|
id: block.id,
|
|
1323
1510
|
name: block.name,
|
|
@@ -1410,11 +1597,19 @@ function buildTurns(messages) {
|
|
|
1410
1597
|
function isClaudeSessionText() {
|
|
1411
1598
|
return true;
|
|
1412
1599
|
}
|
|
1600
|
+
var API_REQUEST_TYPE = '"type":"api-request';
|
|
1601
|
+
var RECORD_TYPE_WINDOW = 256;
|
|
1602
|
+
function isClaudeNoiseLine(line) {
|
|
1603
|
+
if (line.startsWith(`{${API_REQUEST_TYPE}`)) return true;
|
|
1604
|
+
const head = line.slice(0, RECORD_TYPE_WINDOW);
|
|
1605
|
+
const at = head.indexOf(API_REQUEST_TYPE);
|
|
1606
|
+
return at > 0 && head.lastIndexOf("{", at) === 0;
|
|
1607
|
+
}
|
|
1413
1608
|
function parseRecords(lines) {
|
|
1414
1609
|
const messages = [];
|
|
1415
1610
|
for (const line of lines) {
|
|
1416
1611
|
const trimmed = line.trim();
|
|
1417
|
-
if (!trimmed) continue;
|
|
1612
|
+
if (!trimmed || isClaudeNoiseLine(trimmed)) continue;
|
|
1418
1613
|
try {
|
|
1419
1614
|
messages.push(JSON.parse(trimmed));
|
|
1420
1615
|
} catch {
|
|
@@ -1519,21 +1714,24 @@ function userMessageTexts(msg) {
|
|
|
1519
1714
|
}
|
|
1520
1715
|
function collectClaudeBackgroundWork(rawMessages) {
|
|
1521
1716
|
const launches = [];
|
|
1522
|
-
const
|
|
1717
|
+
const resolvedAt = /* @__PURE__ */ new Map();
|
|
1523
1718
|
const queuedIds = /* @__PURE__ */ new Set();
|
|
1524
1719
|
const deliveredIds = /* @__PURE__ */ new Set();
|
|
1525
|
-
function
|
|
1720
|
+
function resolve2(id, index) {
|
|
1721
|
+
if (!resolvedAt.has(id)) resolvedAt.set(id, index);
|
|
1722
|
+
}
|
|
1723
|
+
function noteNotifications(text, index, sink) {
|
|
1526
1724
|
if (!text.includes(TASK_NOTIFICATION_MARKER)) return;
|
|
1527
1725
|
for (const match of text.matchAll(TASK_ID_RE)) {
|
|
1528
|
-
|
|
1726
|
+
resolve2(match[1], index);
|
|
1529
1727
|
sink?.add(match[1]);
|
|
1530
1728
|
}
|
|
1531
1729
|
for (const match of text.matchAll(TOOL_USE_ID_RE)) {
|
|
1532
|
-
|
|
1730
|
+
resolve2(match[1], index);
|
|
1533
1731
|
sink?.add(match[1]);
|
|
1534
1732
|
}
|
|
1535
1733
|
}
|
|
1536
|
-
for (const msg of rawMessages) {
|
|
1734
|
+
for (const [index, msg] of rawMessages.entries()) {
|
|
1537
1735
|
if (msg.type === "user") {
|
|
1538
1736
|
const toolUseResult = isObject(msg.toolUseResult) ? msg.toolUseResult : null;
|
|
1539
1737
|
if (toolUseResult?.status === "async_launched") {
|
|
@@ -1542,9 +1740,11 @@ function collectClaudeBackgroundWork(rawMessages) {
|
|
|
1542
1740
|
const toolUseIds = blocks.filter((block) => isObject(block) && block.type === "tool_result").map((block) => block.tool_use_id);
|
|
1543
1741
|
const ids = [toolUseResult.agentId, toolUseResult.taskId, ...toolUseIds].filter((id) => typeof id === "string" && id.length > 0);
|
|
1544
1742
|
const description = [toolUseResult.description, toolUseResult.workflowName, toolUseResult.summary].find((value) => typeof value === "string" && value.length > 0) ?? "";
|
|
1545
|
-
|
|
1743
|
+
const toolUseId = toolUseIds.find((id) => typeof id === "string" && id.length > 0);
|
|
1744
|
+
const startedAt = typeof msg.timestamp === "string" ? msg.timestamp : void 0;
|
|
1745
|
+
if (ids.length > 0) launches.push({ ids, launch: { agent: { description, startedAt, toolUseId }, launchedAt: index } });
|
|
1546
1746
|
}
|
|
1547
|
-
for (const text of userMessageTexts(msg)) noteNotifications(text, deliveredIds);
|
|
1747
|
+
for (const text of userMessageTexts(msg)) noteNotifications(text, index, deliveredIds);
|
|
1548
1748
|
continue;
|
|
1549
1749
|
}
|
|
1550
1750
|
if (msg.type === "assistant") {
|
|
@@ -1554,33 +1754,28 @@ function collectClaudeBackgroundWork(rawMessages) {
|
|
|
1554
1754
|
if (!isObject(block) || block.type !== "tool_use" || block.name !== "TaskStop") continue;
|
|
1555
1755
|
const input = isObject(block.input) ? block.input : null;
|
|
1556
1756
|
const taskId = input?.task_id ?? input?.shell_id;
|
|
1557
|
-
if (typeof taskId === "string")
|
|
1757
|
+
if (typeof taskId === "string") resolve2(taskId, index);
|
|
1558
1758
|
}
|
|
1559
1759
|
continue;
|
|
1560
1760
|
}
|
|
1561
1761
|
if (msg.type === "attachment") {
|
|
1562
1762
|
const attachment = isObject(msg.attachment) ? msg.attachment : null;
|
|
1563
|
-
if (typeof attachment?.prompt === "string") noteNotifications(attachment.prompt, deliveredIds);
|
|
1763
|
+
if (typeof attachment?.prompt === "string") noteNotifications(attachment.prompt, index, deliveredIds);
|
|
1564
1764
|
continue;
|
|
1565
1765
|
}
|
|
1566
1766
|
if (msg.type === "queue-operation" && typeof msg.content === "string") {
|
|
1567
1767
|
const op = msg.operation;
|
|
1568
|
-
noteNotifications(msg.content, op === "enqueue" ? queuedIds : deliveredIds);
|
|
1768
|
+
noteNotifications(msg.content, index, op === "enqueue" ? queuedIds : deliveredIds);
|
|
1569
1769
|
}
|
|
1570
1770
|
}
|
|
1571
1771
|
return {
|
|
1572
|
-
|
|
1772
|
+
launches: launches.map(({ ids, launch }) => {
|
|
1773
|
+
const ends = ids.flatMap((id) => resolvedAt.get(id) ?? []);
|
|
1774
|
+
return ends.length > 0 ? { ...launch, endedAt: Math.min(...ends) } : launch;
|
|
1775
|
+
}),
|
|
1573
1776
|
undeliveredNotifications: [...queuedIds].filter((id) => !deliveredIds.has(id)).length
|
|
1574
1777
|
};
|
|
1575
1778
|
}
|
|
1576
|
-
function awaitingAgentsResult(pendingDescriptions, pendingQueue) {
|
|
1577
|
-
return {
|
|
1578
|
-
status: "awaiting_agents",
|
|
1579
|
-
pendingQueue,
|
|
1580
|
-
pendingAgents: pendingDescriptions.length,
|
|
1581
|
-
pendingAgentDescriptions: pendingDescriptions.filter((description) => description.length > 0)
|
|
1582
|
-
};
|
|
1583
|
-
}
|
|
1584
1779
|
function deriveClaudeSessionStatus(rawMessages) {
|
|
1585
1780
|
let pendingEnqueues = 0;
|
|
1586
1781
|
let backgroundWork = null;
|
|
@@ -1626,9 +1821,8 @@ function deriveClaudeSessionStatus(rawMessages) {
|
|
|
1626
1821
|
if (stopReason === "end_turn") {
|
|
1627
1822
|
const work = getBackgroundWork();
|
|
1628
1823
|
if (deliveringNotification || work.undeliveredNotifications > 0) return result("processing");
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
}
|
|
1824
|
+
const awaiting = awaitingAgentsStatus(work.launches, Math.max(0, pendingEnqueues));
|
|
1825
|
+
if (awaiting) return awaiting;
|
|
1632
1826
|
let hasActivity = false;
|
|
1633
1827
|
for (let j = i - 1; j >= 0; j--) {
|
|
1634
1828
|
const m = rawMessages[j];
|
|
@@ -2016,9 +2210,60 @@ function safeParseLine(line) {
|
|
|
2016
2210
|
function isObject3(value) {
|
|
2017
2211
|
return typeof value === "object" && value !== null;
|
|
2018
2212
|
}
|
|
2019
|
-
function
|
|
2020
|
-
|
|
2021
|
-
|
|
2213
|
+
function codexParentId(payload) {
|
|
2214
|
+
const source = isObject3(payload.source) ? payload.source : null;
|
|
2215
|
+
const subagent = source && isObject3(source.subagent) ? source.subagent : null;
|
|
2216
|
+
const threadSpawn = subagent && isObject3(subagent.thread_spawn) ? subagent.thread_spawn : null;
|
|
2217
|
+
for (const candidate of [payload.forked_from_id, payload.parent_thread_id, threadSpawn?.parent_thread_id]) {
|
|
2218
|
+
if (typeof candidate === "string" && candidate) return candidate;
|
|
2219
|
+
}
|
|
2220
|
+
return null;
|
|
2221
|
+
}
|
|
2222
|
+
function inheritedHistoryFilter(records) {
|
|
2223
|
+
const meta = records.find((record2) => record2?.type === "session_meta");
|
|
2224
|
+
const start = meta?.payload?.subagent_history_start_ordinal;
|
|
2225
|
+
if (typeof start !== "number") return null;
|
|
2226
|
+
const reachesPast = (ordinal) => typeof ordinal === "number" && ordinal >= start;
|
|
2227
|
+
if (!reachesPast(meta?.rolloutLastOrdinal) && !records.some((record2) => reachesPast(record2?.ordinal))) return null;
|
|
2228
|
+
return (record2) => typeof record2.ordinal === "number" && record2.ordinal > 0 && record2.ordinal < start;
|
|
2229
|
+
}
|
|
2230
|
+
async function codexPageHeaderLine(firstLine, lastLines) {
|
|
2231
|
+
if (!firstLine.includes('"subagent_history_start_ordinal"')) return firstLine;
|
|
2232
|
+
const meta = safeParseLine(firstLine);
|
|
2233
|
+
if (meta?.type !== "session_meta") return firstLine;
|
|
2234
|
+
const lines = await lastLines();
|
|
2235
|
+
for (let index = lines.length - 1; index >= 0; index--) {
|
|
2236
|
+
const ordinal = safeParseLine(lines[index])?.ordinal;
|
|
2237
|
+
if (typeof ordinal === "number") return JSON.stringify({ ...meta, rolloutLastOrdinal: ordinal });
|
|
2238
|
+
}
|
|
2239
|
+
return firstLine;
|
|
2240
|
+
}
|
|
2241
|
+
function subAgentActivitySource(record2) {
|
|
2242
|
+
const payload = record2.payload;
|
|
2243
|
+
if (record2.type !== "event_msg" || !isObject3(payload)) return null;
|
|
2244
|
+
if (payload.type === "sub_agent_activity") return payload;
|
|
2245
|
+
if (payload.type === "item_completed" && isObject3(payload.item) && payload.item.type === "SubAgentActivity") return payload.item;
|
|
2246
|
+
return null;
|
|
2247
|
+
}
|
|
2248
|
+
function readSubAgentActivity(record2) {
|
|
2249
|
+
const source = subAgentActivitySource(record2);
|
|
2250
|
+
if (!source || typeof source.agent_thread_id !== "string" || typeof source.agent_path !== "string") return null;
|
|
2251
|
+
return {
|
|
2252
|
+
agentId: source.agent_thread_id,
|
|
2253
|
+
agentPath: source.agent_path,
|
|
2254
|
+
kind: typeof source.kind === "string" ? source.kind : "started",
|
|
2255
|
+
eventId: findString(source.event_id, source.id) ?? "",
|
|
2256
|
+
model: findString(source.model) || null,
|
|
2257
|
+
effort: findString(source.reasoning_effort) || null
|
|
2258
|
+
};
|
|
2259
|
+
}
|
|
2260
|
+
function turnErrorMessage(payload) {
|
|
2261
|
+
if (payload.type !== "task_complete" || !isObject3(payload.error)) return "";
|
|
2262
|
+
return typeof payload.error.message === "string" ? payload.error.message.trim() : "";
|
|
2263
|
+
}
|
|
2264
|
+
function isCodexRecord(record2) {
|
|
2265
|
+
if (!record2 || typeof record2.type !== "string") return false;
|
|
2266
|
+
return record2.type === "session_meta" || record2.type === "turn_context" || record2.type === "event_msg" || record2.type === "response_item" || record2.type === "inter_agent_communication_metadata" || record2.type === "compacted" || record2.type === "world_state";
|
|
2022
2267
|
}
|
|
2023
2268
|
function extractMessageText(payload, blockType) {
|
|
2024
2269
|
const content = payload?.content;
|
|
@@ -2213,22 +2458,39 @@ function createTurn2(turnId, timestamp, model) {
|
|
|
2213
2458
|
model
|
|
2214
2459
|
};
|
|
2215
2460
|
}
|
|
2216
|
-
function
|
|
2217
|
-
|
|
2218
|
-
|
|
2461
|
+
function codexPromptText(record2) {
|
|
2462
|
+
const payload = isObject3(record2.payload) ? record2.payload : null;
|
|
2463
|
+
if (record2.type === "event_msg" && payload?.type === "user_message" && typeof payload.message === "string") {
|
|
2464
|
+
return normalizePromptText(payload.message);
|
|
2219
2465
|
}
|
|
2220
|
-
if (
|
|
2221
|
-
return normalizePromptText(extractMessageText(
|
|
2466
|
+
if (record2.type === "response_item" && payload?.type === "message" && payload.role === "user") {
|
|
2467
|
+
return normalizePromptText(extractMessageText(payload, "input_text"));
|
|
2222
2468
|
}
|
|
2223
2469
|
return "";
|
|
2224
2470
|
}
|
|
2225
|
-
function
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2471
|
+
function createCodexPromptReader() {
|
|
2472
|
+
let echoPending = false;
|
|
2473
|
+
return (record2) => {
|
|
2474
|
+
const payload = isObject3(record2.payload) ? record2.payload : null;
|
|
2475
|
+
if (record2.type === "event_msg" && payload?.type === "user_message") {
|
|
2476
|
+
const isEcho = echoPending;
|
|
2477
|
+
echoPending = false;
|
|
2478
|
+
return isEcho ? "" : codexPromptText(record2);
|
|
2479
|
+
}
|
|
2480
|
+
if (record2.type !== "response_item") return "";
|
|
2481
|
+
const prompt = codexPromptText(record2);
|
|
2482
|
+
if (prompt) echoPending = true;
|
|
2483
|
+
else if (payload?.type !== "message" || payload.role !== "user") echoPending = false;
|
|
2484
|
+
return prompt;
|
|
2485
|
+
};
|
|
2486
|
+
}
|
|
2487
|
+
function recordOpensTurn(record2, payload) {
|
|
2488
|
+
if (record2.type === "session_meta") return false;
|
|
2489
|
+
if (record2.type === "event_msg" && payload?.type === "task_started") return false;
|
|
2490
|
+
if (record2.type === "response_item" && payload?.type === "message") {
|
|
2229
2491
|
if (payload.role === "developer" || payload.role === "system") return false;
|
|
2230
2492
|
if (payload.role === "user") {
|
|
2231
|
-
return
|
|
2493
|
+
return codexPromptText(record2) !== "" || extractResponseMessageImages(payload).length > 0 || extractResponseMessageAudio(payload).length > 0;
|
|
2232
2494
|
}
|
|
2233
2495
|
}
|
|
2234
2496
|
return true;
|
|
@@ -2248,23 +2510,24 @@ function extractMetadataFromRecords(records) {
|
|
|
2248
2510
|
let isSubagent = false;
|
|
2249
2511
|
let parentSessionId = null;
|
|
2250
2512
|
let agentPath = "/root";
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
if (
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2513
|
+
const readPrompt = createCodexPromptReader();
|
|
2514
|
+
const isInherited = inheritedHistoryFilter(records);
|
|
2515
|
+
for (const record2 of records) {
|
|
2516
|
+
if (!isObject3(record2.payload) || isInherited?.(record2)) continue;
|
|
2517
|
+
if (record2.type === "session_meta") {
|
|
2518
|
+
sessionId ||= typeof record2.payload.id === "string" ? record2.payload.id : "";
|
|
2519
|
+
version ||= typeof record2.payload.cli_version === "string" ? record2.payload.cli_version : "";
|
|
2520
|
+
cwd ||= typeof record2.payload.cwd === "string" ? record2.payload.cwd : "";
|
|
2521
|
+
if (!branchedFrom && isObject3(record2.payload.branchedFrom)) {
|
|
2522
|
+
const sourceId = typeof record2.payload.branchedFrom.sessionId === "string" ? record2.payload.branchedFrom.sessionId : "";
|
|
2260
2523
|
if (sourceId) {
|
|
2261
2524
|
branchedFrom = {
|
|
2262
2525
|
sessionId: sourceId,
|
|
2263
|
-
turnIndex: typeof
|
|
2526
|
+
turnIndex: typeof record2.payload.branchedFrom.turnIndex === "number" ? record2.payload.branchedFrom.turnIndex : null
|
|
2264
2527
|
};
|
|
2265
2528
|
}
|
|
2266
2529
|
}
|
|
2267
|
-
const source = isObject3(
|
|
2530
|
+
const source = isObject3(record2.payload.source) ? record2.payload.source : null;
|
|
2268
2531
|
if (source && isObject3(source.subagent)) {
|
|
2269
2532
|
isSubagent = true;
|
|
2270
2533
|
const threadSpawn = isObject3(source.subagent.thread_spawn) ? source.subagent.thread_spawn : null;
|
|
@@ -2272,25 +2535,21 @@ function extractMetadataFromRecords(records) {
|
|
|
2272
2535
|
agentPath = threadSpawn.agent_path;
|
|
2273
2536
|
}
|
|
2274
2537
|
}
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
}
|
|
2278
|
-
const git = isObject3(record.payload.git) ? record.payload.git : null;
|
|
2538
|
+
parentSessionId = codexParentId(record2.payload) ?? parentSessionId;
|
|
2539
|
+
const git = isObject3(record2.payload.git) ? record2.payload.git : null;
|
|
2279
2540
|
gitBranch ||= git && typeof git.branch === "string" ? git.branch : "";
|
|
2280
2541
|
}
|
|
2281
|
-
if (
|
|
2282
|
-
model ||= typeof
|
|
2283
|
-
cwd ||= typeof
|
|
2542
|
+
if (record2.type === "turn_context") {
|
|
2543
|
+
model ||= typeof record2.payload.model === "string" ? record2.payload.model : "";
|
|
2544
|
+
cwd ||= typeof record2.payload.cwd === "string" ? record2.payload.cwd : "";
|
|
2284
2545
|
}
|
|
2285
|
-
const prompt =
|
|
2546
|
+
const prompt = readPrompt(record2);
|
|
2286
2547
|
if (!prompt) continue;
|
|
2287
|
-
if (prompt === previousPrompt) continue;
|
|
2288
2548
|
if (!firstUserMessage) firstUserMessage = prompt;
|
|
2289
2549
|
lastUserMessage = prompt;
|
|
2290
|
-
previousPrompt = prompt;
|
|
2291
2550
|
turnCount++;
|
|
2292
|
-
if (!timestamp) timestamp =
|
|
2293
|
-
lastTimestamp =
|
|
2551
|
+
if (!timestamp) timestamp = record2.timestamp ?? "";
|
|
2552
|
+
lastTimestamp = record2.timestamp ?? lastTimestamp;
|
|
2294
2553
|
}
|
|
2295
2554
|
if (lastTimestamp === "") lastTimestamp = timestamp;
|
|
2296
2555
|
return {
|
|
@@ -2357,6 +2616,7 @@ function walkCodexRecords(records, options) {
|
|
|
2357
2616
|
timestamp: info?.timestamp ?? eventTimestamp2,
|
|
2358
2617
|
tokenUsage: null,
|
|
2359
2618
|
model: info?.model ?? null,
|
|
2619
|
+
...info?.effort ? { effort: info.effort } : {},
|
|
2360
2620
|
isBackground: false,
|
|
2361
2621
|
prompt: info?.message || void 0,
|
|
2362
2622
|
status: update.status ?? "running"
|
|
@@ -2377,6 +2637,7 @@ function walkCodexRecords(records, options) {
|
|
|
2377
2637
|
agentMessage.agentName = info.nickname ?? (info.agentPath ? agentNameFromPath(info.agentPath) : agentMessage.agentName);
|
|
2378
2638
|
agentMessage.subagentType = info.agentType;
|
|
2379
2639
|
agentMessage.model = info.model;
|
|
2640
|
+
if (info.effort) agentMessage.effort = info.effort;
|
|
2380
2641
|
agentMessage.prompt = info.message || agentMessage.prompt;
|
|
2381
2642
|
}
|
|
2382
2643
|
if (update.text && !agentMessage.text.includes(update.text)) {
|
|
@@ -2447,20 +2708,21 @@ function walkCodexRecords(records, options) {
|
|
|
2447
2708
|
webSearchCalls.set(id, toolCall);
|
|
2448
2709
|
appendToolCall(targetTurn, toolCall, timestamp);
|
|
2449
2710
|
}
|
|
2711
|
+
const isInherited = inheritedHistoryFilter(records);
|
|
2450
2712
|
for (let index = 0; index < records.length; index++) {
|
|
2451
|
-
const
|
|
2452
|
-
if (!isCodexRecord(
|
|
2713
|
+
const record2 = records[index];
|
|
2714
|
+
if (!isCodexRecord(record2) || isInherited?.(record2)) continue;
|
|
2453
2715
|
recordIndex = index;
|
|
2454
|
-
const payload = isObject3(
|
|
2455
|
-
const timestamp =
|
|
2456
|
-
if (
|
|
2716
|
+
const payload = isObject3(record2.payload) ? record2.payload : void 0;
|
|
2717
|
+
const timestamp = record2.timestamp ?? "";
|
|
2718
|
+
if (record2.type === "compacted") {
|
|
2457
2719
|
current = commitTurn(current);
|
|
2458
2720
|
pendingCompaction = "Conversation compacted";
|
|
2459
2721
|
lastTurnTimestamp = timestamp;
|
|
2460
2722
|
continue;
|
|
2461
2723
|
}
|
|
2462
|
-
if (
|
|
2463
|
-
if (
|
|
2724
|
+
if (record2.type === "world_state") continue;
|
|
2725
|
+
if (record2.type === "turn_context") {
|
|
2464
2726
|
current = commitTurn(current);
|
|
2465
2727
|
pendingContextIndex = index;
|
|
2466
2728
|
currentTurnId = typeof payload?.turn_id === "string" ? payload.turn_id : null;
|
|
@@ -2469,7 +2731,7 @@ function walkCodexRecords(records, options) {
|
|
|
2469
2731
|
sawTurnStart = true;
|
|
2470
2732
|
continue;
|
|
2471
2733
|
}
|
|
2472
|
-
if (
|
|
2734
|
+
if (record2.type === "event_msg" && payload?.type === "user_message" && typeof payload.message === "string") {
|
|
2473
2735
|
if (current && (current.assistantText.length > 0 || current.toolCalls.length > 0 || current.thinking.length > 0)) {
|
|
2474
2736
|
current = commitTurn(current);
|
|
2475
2737
|
}
|
|
@@ -2493,12 +2755,12 @@ function walkCodexRecords(records, options) {
|
|
|
2493
2755
|
lastTurnTimestamp = timestamp;
|
|
2494
2756
|
continue;
|
|
2495
2757
|
}
|
|
2496
|
-
if (!current && !recordOpensTurn(
|
|
2758
|
+
if (!current && !recordOpensTurn(record2, payload)) continue;
|
|
2497
2759
|
current ??= createActiveTurn(currentTurnId, timestamp, currentModel);
|
|
2498
2760
|
if (!current.model && currentModel) current.model = currentModel;
|
|
2499
2761
|
if (!current.timestamp) current.timestamp = timestamp;
|
|
2500
2762
|
if (timestamp) lastTurnTimestamp = timestamp;
|
|
2501
|
-
if (
|
|
2763
|
+
if (record2.type === "event_msg" && payload?.type === "token_count") {
|
|
2502
2764
|
const info = isObject3(payload.info) ? payload.info : null;
|
|
2503
2765
|
const lastUsage = info ? parseTokenUsage(info.last_token_usage) : null;
|
|
2504
2766
|
if (lastUsage) {
|
|
@@ -2506,14 +2768,14 @@ function walkCodexRecords(records, options) {
|
|
|
2506
2768
|
}
|
|
2507
2769
|
continue;
|
|
2508
2770
|
}
|
|
2509
|
-
if (
|
|
2771
|
+
if (record2.type === "event_msg" && payload?.type === "web_search_end" && typeof payload.call_id === "string") {
|
|
2510
2772
|
const input = {};
|
|
2511
2773
|
if (typeof payload.query === "string" && payload.query) input.query = payload.query;
|
|
2512
2774
|
if (isObject3(payload.action)) input.action = payload.action;
|
|
2513
2775
|
upsertWebSearchCall(current, payload.call_id, input, "completed", timestamp);
|
|
2514
2776
|
continue;
|
|
2515
2777
|
}
|
|
2516
|
-
if (
|
|
2778
|
+
if (record2.type === "event_msg" && payload?.type === "mcp_tool_call_end" && typeof payload.call_id === "string" && isObject3(payload.invocation)) {
|
|
2517
2779
|
const invocation = payload.invocation;
|
|
2518
2780
|
const server = typeof invocation.server === "string" ? invocation.server : "mcp";
|
|
2519
2781
|
const tool = typeof invocation.tool === "string" ? invocation.tool : "tool";
|
|
@@ -2538,12 +2800,18 @@ function walkCodexRecords(records, options) {
|
|
|
2538
2800
|
}
|
|
2539
2801
|
continue;
|
|
2540
2802
|
}
|
|
2541
|
-
if (
|
|
2542
|
-
const
|
|
2543
|
-
|
|
2544
|
-
|
|
2803
|
+
if (record2.type === "event_msg" && payload) {
|
|
2804
|
+
const turnError = turnErrorMessage(payload);
|
|
2805
|
+
if (turnError) {
|
|
2806
|
+
appendAssistantText(current, turnError, timestamp);
|
|
2807
|
+
continue;
|
|
2808
|
+
}
|
|
2809
|
+
}
|
|
2810
|
+
const activity = readSubAgentActivity(record2);
|
|
2811
|
+
if (activity) {
|
|
2812
|
+
const { agentId, agentPath, eventId: eventId2, kind } = activity;
|
|
2545
2813
|
const spawnInfo = eventId2 ? spawnAgentCalls.get(eventId2) : void 0;
|
|
2546
|
-
const existingId = agentIdByPath.get(
|
|
2814
|
+
const existingId = agentIdByPath.get(agentPath);
|
|
2547
2815
|
const existing = agentRegistry.get(agentId) ?? (existingId ? agentRegistry.get(existingId) : void 0);
|
|
2548
2816
|
if (kind !== "started" && !existing && !spawnInfo) continue;
|
|
2549
2817
|
if (existingId && existingId !== agentId && !agentMessages.has(agentId)) {
|
|
@@ -2557,22 +2825,23 @@ function walkCodexRecords(records, options) {
|
|
|
2557
2825
|
}
|
|
2558
2826
|
const info = {
|
|
2559
2827
|
agentId,
|
|
2560
|
-
nickname: existing?.nickname ?? agentNameFromPath(
|
|
2561
|
-
agentPath
|
|
2828
|
+
nickname: existing?.nickname ?? agentNameFromPath(agentPath) ?? spawnInfo?.taskName ?? null,
|
|
2829
|
+
agentPath,
|
|
2562
2830
|
message: existing?.message ?? spawnInfo?.message ?? "",
|
|
2563
|
-
model: existing?.model ?? spawnInfo?.model ?? null,
|
|
2831
|
+
model: activity.model ?? existing?.model ?? spawnInfo?.model ?? null,
|
|
2832
|
+
effort: activity.effort ?? existing?.effort ?? null,
|
|
2564
2833
|
agentType: existing?.agentType ?? spawnInfo?.agentType ?? null,
|
|
2565
2834
|
timestamp: existing?.timestamp ?? (timestamp || spawnInfo?.timestamp || ""),
|
|
2566
2835
|
parentToolCallId: existing?.parentToolCallId ?? eventId2
|
|
2567
2836
|
};
|
|
2568
2837
|
agentRegistry.set(agentId, info);
|
|
2569
|
-
agentIdByPath.set(
|
|
2838
|
+
agentIdByPath.set(agentPath, agentId);
|
|
2570
2839
|
if (eventId2 && (kind === "started" || spawnInfo)) agentIdBySpawnCall.set(eventId2, agentId);
|
|
2571
|
-
const status = kind === "interrupted"
|
|
2840
|
+
const status = kind === "interrupted" || kind === "completed" ? kind : "running";
|
|
2572
2841
|
upsertAgentMessage(agentId, current, timestamp, { status });
|
|
2573
2842
|
continue;
|
|
2574
2843
|
}
|
|
2575
|
-
if (
|
|
2844
|
+
if (record2.type !== "response_item" || !payload) continue;
|
|
2576
2845
|
if (payload.type === "reasoning") {
|
|
2577
2846
|
const summary = Array.isArray(payload.summary) ? payload.summary.filter((item) => typeof item === "string").join("\n") : "";
|
|
2578
2847
|
appendThinking(current, summary.trim(), timestamp);
|
|
@@ -2724,6 +2993,7 @@ function walkCodexRecords(records, options) {
|
|
|
2724
2993
|
agentPath: agentPath ?? existing?.agentPath ?? null,
|
|
2725
2994
|
message: existing?.message ?? spawnInfo?.message ?? "",
|
|
2726
2995
|
model: existing?.model ?? spawnInfo?.model ?? null,
|
|
2996
|
+
effort: existing?.effort ?? null,
|
|
2727
2997
|
agentType: existing?.agentType ?? spawnInfo?.agentType ?? null,
|
|
2728
2998
|
timestamp: existing?.timestamp ?? spawnInfo?.timestamp ?? timestamp,
|
|
2729
2999
|
parentToolCallId: existing?.parentToolCallId ?? toolCall.id
|
|
@@ -2842,14 +3112,16 @@ function walkCodexRecords(records, options) {
|
|
|
2842
3112
|
}
|
|
2843
3113
|
function parseCodexSession(jsonlText, options) {
|
|
2844
3114
|
const records = jsonlText.split("\n").map((line) => line.trim()).filter(Boolean).map(safeParseLine).filter(isCodexRecord);
|
|
3115
|
+
const isInherited = inheritedHistoryFilter(records);
|
|
3116
|
+
const ownRecords = isInherited ? records.filter((record2) => !isInherited(record2)) : records;
|
|
2845
3117
|
const { turnStartIndices: _starts, ...session } = walkCodexRecords(
|
|
2846
|
-
|
|
3118
|
+
ownRecords,
|
|
2847
3119
|
options
|
|
2848
3120
|
);
|
|
2849
3121
|
return session;
|
|
2850
3122
|
}
|
|
2851
3123
|
function appendCodexSession(existing, newJsonlText) {
|
|
2852
|
-
const prefix = existing.rawMessages.map((
|
|
3124
|
+
const prefix = existing.rawMessages.map((record2) => JSON.stringify(record2)).join("\n");
|
|
2853
3125
|
return parseCodexSession(prefix ? `${prefix}
|
|
2854
3126
|
${newJsonlText}` : newJsonlText);
|
|
2855
3127
|
}
|
|
@@ -2868,40 +3140,48 @@ function parseJsonObject(value) {
|
|
|
2868
3140
|
return null;
|
|
2869
3141
|
}
|
|
2870
3142
|
}
|
|
2871
|
-
function
|
|
2872
|
-
const
|
|
2873
|
-
const
|
|
3143
|
+
function collectCodexBackgroundAgents(rawMessages) {
|
|
3144
|
+
const launched = /* @__PURE__ */ new Map();
|
|
3145
|
+
const alive = /* @__PURE__ */ new Set();
|
|
3146
|
+
const endedAt = /* @__PURE__ */ new Map();
|
|
2874
3147
|
const agentIdByPath = /* @__PURE__ */ new Map();
|
|
2875
3148
|
const spawnCalls = /* @__PURE__ */ new Map();
|
|
2876
3149
|
const waitCalls = /* @__PURE__ */ new Set();
|
|
2877
3150
|
const interruptCalls = /* @__PURE__ */ new Map();
|
|
2878
|
-
function markAlive(agentId, description) {
|
|
2879
|
-
if (
|
|
2880
|
-
|
|
2881
|
-
|
|
3151
|
+
function markAlive(agentId, description, index, startedAt) {
|
|
3152
|
+
if (endedAt.has(agentId)) return;
|
|
3153
|
+
alive.add(agentId);
|
|
3154
|
+
const launch = launched.get(agentId);
|
|
3155
|
+
if (!launch) launched.set(agentId, { launchedAt: index, description, startedAt });
|
|
3156
|
+
else if (!launch.description) launch.description = description;
|
|
3157
|
+
}
|
|
3158
|
+
function markEnded(agentId, index) {
|
|
3159
|
+
if (!endedAt.has(agentId)) endedAt.set(agentId, index);
|
|
3160
|
+
alive.delete(agentId);
|
|
3161
|
+
}
|
|
3162
|
+
for (const [index, msg] of rawMessages.entries()) {
|
|
2882
3163
|
const payload = isObject3(msg.payload) ? msg.payload : null;
|
|
2883
3164
|
if (!payload) continue;
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
const
|
|
2888
|
-
const existingId = agentIdByPath.get(
|
|
2889
|
-
const known = alive.has(agentId) ||
|
|
3165
|
+
const timestamp = typeof msg.timestamp === "string" ? msg.timestamp : void 0;
|
|
3166
|
+
const activity = readSubAgentActivity(msg);
|
|
3167
|
+
if (activity) {
|
|
3168
|
+
const { agentId, agentPath, kind, eventId: eventId2 } = activity;
|
|
3169
|
+
const existingId = agentIdByPath.get(agentPath);
|
|
3170
|
+
const known = alive.has(agentId) || endedAt.has(agentId) || existingId !== void 0 || spawnCalls.has(eventId2);
|
|
2890
3171
|
if (kind !== "started" && !known) continue;
|
|
2891
3172
|
if (existingId && existingId !== agentId) {
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
if (
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
done.add(agentId);
|
|
2901
|
-
alive.delete(agentId);
|
|
2902
|
-
} else {
|
|
2903
|
-
markAlive(agentId, spawnCalls.get(eventId2) ?? "");
|
|
3173
|
+
const prior = launched.get(existingId);
|
|
3174
|
+
launched.delete(existingId);
|
|
3175
|
+
const current = launched.get(agentId);
|
|
3176
|
+
if (prior && !current) launched.set(agentId, prior);
|
|
3177
|
+
else if (prior && current && !current.description) current.description = prior.description;
|
|
3178
|
+
if (alive.delete(existingId)) markAlive(agentId, "", index, timestamp);
|
|
3179
|
+
const priorEnd = endedAt.get(existingId);
|
|
3180
|
+
if (priorEnd !== void 0) markEnded(agentId, priorEnd);
|
|
2904
3181
|
}
|
|
3182
|
+
agentIdByPath.set(agentPath, agentId);
|
|
3183
|
+
if (kind === "interrupted" || kind === "completed") markEnded(agentId, index);
|
|
3184
|
+
else markAlive(agentId, spawnCalls.get(eventId2) ?? "", index, timestamp);
|
|
2905
3185
|
continue;
|
|
2906
3186
|
}
|
|
2907
3187
|
if (msg.type !== "response_item") continue;
|
|
@@ -2925,7 +3205,7 @@ function collectCodexPendingAgents(rawMessages) {
|
|
|
2925
3205
|
const output = parseJsonObject(payload.output);
|
|
2926
3206
|
const agentId = typeof output?.agent_id === "string" ? output.agent_id : "";
|
|
2927
3207
|
if (agentId) {
|
|
2928
|
-
markAlive(agentId, spawnCalls.get(callId) ?? "");
|
|
3208
|
+
markAlive(agentId, spawnCalls.get(callId) ?? "", index, timestamp);
|
|
2929
3209
|
const agentPath = typeof output?.task_name === "string" ? output.task_name : "";
|
|
2930
3210
|
if (agentPath && !agentIdByPath.has(agentPath)) agentIdByPath.set(agentPath, agentId);
|
|
2931
3211
|
}
|
|
@@ -2936,16 +3216,10 @@ function collectCodexPendingAgents(rawMessages) {
|
|
|
2936
3216
|
const statusValue = isObject3(status) ? status : {};
|
|
2937
3217
|
const lifecycle = typeof statusValue.status === "string" ? statusValue.status : "";
|
|
2938
3218
|
const isTerminal = typeof statusValue.completed === "string" || typeof statusValue.failed === "string" || typeof statusValue.error === "string" || typeof statusValue.interrupted === "string" || lifecycle === "completed" || lifecycle === "failed" || lifecycle === "interrupted";
|
|
2939
|
-
if (isTerminal)
|
|
2940
|
-
done.add(agentId);
|
|
2941
|
-
alive.delete(agentId);
|
|
2942
|
-
}
|
|
3219
|
+
if (isTerminal) markEnded(agentId, index);
|
|
2943
3220
|
}
|
|
2944
3221
|
} else if (interruptCalls.has(callId)) {
|
|
2945
|
-
for (const agentId of interruptCalls.get(callId) ?? [])
|
|
2946
|
-
done.add(agentId);
|
|
2947
|
-
alive.delete(agentId);
|
|
2948
|
-
}
|
|
3222
|
+
for (const agentId of interruptCalls.get(callId) ?? []) markEnded(agentId, index);
|
|
2949
3223
|
}
|
|
2950
3224
|
continue;
|
|
2951
3225
|
}
|
|
@@ -2953,12 +3227,14 @@ function collectCodexPendingAgents(rawMessages) {
|
|
|
2953
3227
|
const text = codexPayloadText(payload);
|
|
2954
3228
|
const messageType = text.match(/^Message Type:\s*([^\r\n]+)/)?.[1]?.trim().toUpperCase() ?? "";
|
|
2955
3229
|
if (!TERMINAL_INTER_AGENT_TYPES.has(messageType)) continue;
|
|
2956
|
-
|
|
2957
|
-
done.add(agentId);
|
|
2958
|
-
alive.delete(agentId);
|
|
3230
|
+
markEnded(agentIdByPath.get(payload.author) ?? payload.author, index);
|
|
2959
3231
|
}
|
|
2960
3232
|
}
|
|
2961
|
-
return [...
|
|
3233
|
+
return [...launched.entries()].map(([agentId, { description, startedAt, launchedAt }]) => ({
|
|
3234
|
+
agent: { description, startedAt },
|
|
3235
|
+
launchedAt,
|
|
3236
|
+
endedAt: alive.has(agentId) ? void 0 : endedAt.get(agentId) ?? launchedAt
|
|
3237
|
+
}));
|
|
2962
3238
|
}
|
|
2963
3239
|
function deriveCodexSessionStatus(rawMessages) {
|
|
2964
3240
|
for (let i = rawMessages.length - 1; i >= 0; i--) {
|
|
@@ -2967,16 +3243,7 @@ function deriveCodexSessionStatus(rawMessages) {
|
|
|
2967
3243
|
const payload = msg.payload;
|
|
2968
3244
|
switch (payload?.type) {
|
|
2969
3245
|
case "task_complete": {
|
|
2970
|
-
|
|
2971
|
-
if (pending.length > 0) {
|
|
2972
|
-
return {
|
|
2973
|
-
status: "awaiting_agents",
|
|
2974
|
-
pendingQueue: 0,
|
|
2975
|
-
pendingAgents: pending.length,
|
|
2976
|
-
pendingAgentDescriptions: pending.filter((description) => description.length > 0)
|
|
2977
|
-
};
|
|
2978
|
-
}
|
|
2979
|
-
return { status: "completed" };
|
|
3246
|
+
return awaitingAgentsStatus(collectCodexBackgroundAgents(rawMessages)) ?? { status: "completed" };
|
|
2980
3247
|
}
|
|
2981
3248
|
case "task_started":
|
|
2982
3249
|
return { status: "processing" };
|
|
@@ -3000,11 +3267,11 @@ function deriveCodexSessionStatus(rawMessages) {
|
|
|
3000
3267
|
}
|
|
3001
3268
|
return { status: "idle" };
|
|
3002
3269
|
}
|
|
3003
|
-
function brandCodexBranch(firstRecord, sessionId, turnIndex) {
|
|
3270
|
+
function brandCodexBranch(firstRecord, sessionId, turnIndex, branchedAt) {
|
|
3004
3271
|
const payload = isObject3(firstRecord.payload) ? { ...firstRecord.payload } : {};
|
|
3005
3272
|
const originalId = typeof payload.id === "string" ? payload.id : "";
|
|
3006
3273
|
payload.id = sessionId;
|
|
3007
|
-
payload.branchedFrom = { sessionId: originalId, turnIndex };
|
|
3274
|
+
payload.branchedFrom = { sessionId: originalId, turnIndex, at: new Date(branchedAt).toISOString() };
|
|
3008
3275
|
return { record: { ...firstRecord, payload }, originalId };
|
|
3009
3276
|
}
|
|
3010
3277
|
function codexTurnBoundaries(records) {
|
|
@@ -3766,7 +4033,7 @@ function parseCopilotSession(jsonlText, options) {
|
|
|
3766
4033
|
};
|
|
3767
4034
|
}
|
|
3768
4035
|
function appendCopilotSession(existing, newJsonlText) {
|
|
3769
|
-
const prefix = existing.rawMessages.map((
|
|
4036
|
+
const prefix = existing.rawMessages.map((record2) => JSON.stringify(record2)).join("\n");
|
|
3770
4037
|
return parseCopilotSession(prefix ? `${prefix}
|
|
3771
4038
|
${newJsonlText}` : newJsonlText);
|
|
3772
4039
|
}
|
|
@@ -3774,33 +4041,32 @@ function isCopilotEventType(type) {
|
|
|
3774
4041
|
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."));
|
|
3775
4042
|
}
|
|
3776
4043
|
function deriveCopilotSessionStatus(rawMessages) {
|
|
3777
|
-
const
|
|
3778
|
-
for (const event of rawMessages) {
|
|
4044
|
+
const agents = /* @__PURE__ */ new Map();
|
|
4045
|
+
for (const [index, event] of rawMessages.entries()) {
|
|
3779
4046
|
const agentId = typeof event.agentId === "string" ? event.agentId : "";
|
|
3780
4047
|
if (!agentId && (event.type === "abort" || event.type === "session.shutdown" || event.type === "session.error")) {
|
|
3781
|
-
|
|
4048
|
+
agents.clear();
|
|
3782
4049
|
continue;
|
|
3783
4050
|
}
|
|
3784
4051
|
if (!agentId || !event.type.startsWith("subagent.")) continue;
|
|
3785
4052
|
const data = isObject4(event.data) ? event.data : {};
|
|
4053
|
+
const launch = agents.get(agentId);
|
|
3786
4054
|
if (event.type === "subagent.completed" || event.type === "subagent.failed") {
|
|
3787
|
-
|
|
4055
|
+
if (launch) launch.endedAt ??= index;
|
|
3788
4056
|
continue;
|
|
3789
4057
|
}
|
|
3790
4058
|
if (event.type === "subagent.started" || event.type === "subagent.configured") {
|
|
3791
4059
|
const description = [data.agentDisplayName, data.description, data.agentName].find((value) => typeof value === "string") ?? "";
|
|
3792
|
-
|
|
4060
|
+
if (!launch || launch.endedAt !== void 0) {
|
|
4061
|
+
const startedAt = typeof event.timestamp === "string" ? event.timestamp : void 0;
|
|
4062
|
+
agents.set(agentId, { agent: { description, startedAt }, launchedAt: index });
|
|
4063
|
+
} else if (description) {
|
|
4064
|
+
launch.agent.description = description;
|
|
4065
|
+
}
|
|
3793
4066
|
}
|
|
3794
4067
|
}
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
return {
|
|
3798
|
-
status: "awaiting_agents",
|
|
3799
|
-
pendingQueue: 0,
|
|
3800
|
-
pendingAgents: descriptions.length,
|
|
3801
|
-
pendingAgentDescriptions: descriptions.filter((description) => description.length > 0)
|
|
3802
|
-
};
|
|
3803
|
-
}
|
|
4068
|
+
const awaiting = awaitingAgentsStatus([...agents.values()]);
|
|
4069
|
+
if (awaiting) return awaiting;
|
|
3804
4070
|
let sawUserActivity = false;
|
|
3805
4071
|
const completedPermissionRequests = /* @__PURE__ */ new Set();
|
|
3806
4072
|
for (let i = rawMessages.length - 1; i >= 0; i--) {
|
|
@@ -3874,11 +4140,11 @@ function brandCopilotBranch() {
|
|
|
3874
4140
|
function copilotTurnBoundaries(records) {
|
|
3875
4141
|
const boundaries = [];
|
|
3876
4142
|
for (let index = 0; index < records.length; index++) {
|
|
3877
|
-
const
|
|
3878
|
-
if (
|
|
3879
|
-
const data =
|
|
4143
|
+
const record2 = records[index];
|
|
4144
|
+
if (record2.type !== "user.message" || typeof record2.agentId === "string") continue;
|
|
4145
|
+
const data = record2.data;
|
|
3880
4146
|
if (!data || typeof data !== "object" || Array.isArray(data)) continue;
|
|
3881
|
-
if (typeof
|
|
4147
|
+
if (typeof record2.id !== "string" || !record2.id) continue;
|
|
3882
4148
|
boundaries.push(index);
|
|
3883
4149
|
}
|
|
3884
4150
|
return boundaries;
|
|
@@ -3886,6 +4152,7 @@ function copilotTurnBoundaries(records) {
|
|
|
3886
4152
|
|
|
3887
4153
|
// src/lib/agents.ts
|
|
3888
4154
|
var TERMINAL_AGENT_KIND = AGENT_KINDS[AGENT_KINDS.length - 1];
|
|
4155
|
+
var hasNoNoiseLines = () => false;
|
|
3889
4156
|
var codexFormat = {
|
|
3890
4157
|
kind: "codex",
|
|
3891
4158
|
descriptor: descriptorFor("codex"),
|
|
@@ -3895,6 +4162,8 @@ var codexFormat = {
|
|
|
3895
4162
|
append: appendCodexSession,
|
|
3896
4163
|
metadataFromLines: extractCodexMetadataFromLines,
|
|
3897
4164
|
status: deriveCodexSessionStatus,
|
|
4165
|
+
isNoiseLine: hasNoNoiseLines,
|
|
4166
|
+
pageHeaderLine: codexPageHeaderLine,
|
|
3898
4167
|
brandBranch: brandCodexBranch,
|
|
3899
4168
|
turnBoundaries: codexTurnBoundaries
|
|
3900
4169
|
};
|
|
@@ -3907,6 +4176,7 @@ var copilotFormat = {
|
|
|
3907
4176
|
append: appendCopilotSession,
|
|
3908
4177
|
metadataFromLines: extractCopilotMetadataFromLines,
|
|
3909
4178
|
status: deriveCopilotSessionStatus,
|
|
4179
|
+
isNoiseLine: hasNoNoiseLines,
|
|
3910
4180
|
brandBranch: brandCopilotBranch,
|
|
3911
4181
|
turnBoundaries: copilotTurnBoundaries
|
|
3912
4182
|
};
|
|
@@ -3920,10 +4190,20 @@ var claudeFormat = {
|
|
|
3920
4190
|
append: appendClaudeSession,
|
|
3921
4191
|
metadataFromLines: extractClaudeMetadataFromLines,
|
|
3922
4192
|
status: deriveClaudeSessionStatus,
|
|
4193
|
+
isNoiseLine: isClaudeNoiseLine,
|
|
3923
4194
|
brandBranch: brandClaudeBranch,
|
|
3924
4195
|
turnBoundaries: claudeTurnBoundaries
|
|
3925
4196
|
};
|
|
3926
4197
|
var FORMATS = Object.freeze({
|
|
4198
|
+
acp: {
|
|
4199
|
+
...claudeFormat,
|
|
4200
|
+
kind: "acp",
|
|
4201
|
+
descriptor: descriptorFor("acp"),
|
|
4202
|
+
detectsText: (text) => text.split("\n", 1)[0]?.includes('"type":"acp_session"') ?? false,
|
|
4203
|
+
detectsRecords: (records) => records[0]?.type === "acp_session",
|
|
4204
|
+
parse: (text, options) => ({ ...parseClaudeSession(text, options), agentKind: "acp" }),
|
|
4205
|
+
append: (session, text) => ({ ...appendClaudeSession(session, text), agentKind: "acp" })
|
|
4206
|
+
},
|
|
3927
4207
|
claude: claudeFormat,
|
|
3928
4208
|
codex: codexFormat,
|
|
3929
4209
|
copilot: copilotFormat
|
|
@@ -3940,6 +4220,9 @@ function formatForText(jsonlText) {
|
|
|
3940
4220
|
function formatForRecords(records) {
|
|
3941
4221
|
return firstMatch((format) => format.detectsRecords(records));
|
|
3942
4222
|
}
|
|
4223
|
+
function isAnyAgentNoiseLine(line) {
|
|
4224
|
+
return AGENT_KINDS.some((kind) => FORMATS[kind].isNoiseLine(line));
|
|
4225
|
+
}
|
|
3943
4226
|
|
|
3944
4227
|
// src/lib/parser.ts
|
|
3945
4228
|
function extractTextFromContent2(content) {
|
|
@@ -3952,7 +4235,8 @@ function parseSession(jsonlText, options) {
|
|
|
3952
4235
|
function parseSessionAppend(existing, newJsonlText) {
|
|
3953
4236
|
const byRecords = formatForRecords(existing.rawMessages);
|
|
3954
4237
|
const format = byRecords.kind === TERMINAL_AGENT_KIND ? formatForText(newJsonlText) : byRecords;
|
|
3955
|
-
|
|
4238
|
+
const appended = format.append(existing, newJsonlText);
|
|
4239
|
+
return existing.instanceId ? scopeParsedSession(appended, existing.instanceId) : appended;
|
|
3956
4240
|
}
|
|
3957
4241
|
function getUserMessageText(content) {
|
|
3958
4242
|
if (content === null) return "";
|
|
@@ -3964,16 +4248,63 @@ function getUserMessageImages(content) {
|
|
|
3964
4248
|
return content.filter((b) => b.type === "image");
|
|
3965
4249
|
}
|
|
3966
4250
|
|
|
3967
|
-
// src/lib/
|
|
4251
|
+
// src/lib/transcriptFile.ts
|
|
3968
4252
|
var import_node_fs = require("node:fs");
|
|
4253
|
+
var import_promises = require("node:fs/promises");
|
|
4254
|
+
var zlib = __toESM(require("node:zlib"));
|
|
4255
|
+
var COMPRESSED_SUFFIX = ".zst";
|
|
4256
|
+
var canReadCompressedTranscripts = typeof zlib.zstdDecompressSync === "function";
|
|
4257
|
+
function isCompressedTranscript(filePath) {
|
|
4258
|
+
return filePath.endsWith(COMPRESSED_SUFFIX);
|
|
4259
|
+
}
|
|
4260
|
+
function uncompressedTranscriptName(fileName) {
|
|
4261
|
+
return isCompressedTranscript(fileName) ? fileName.slice(0, -COMPRESSED_SUFFIX.length) : fileName;
|
|
4262
|
+
}
|
|
4263
|
+
function isListableTranscript(name, siblings) {
|
|
4264
|
+
const plainName = uncompressedTranscriptName(name);
|
|
4265
|
+
if (!plainName.endsWith(".jsonl")) return false;
|
|
4266
|
+
if (plainName === name) return true;
|
|
4267
|
+
return canReadCompressedTranscripts && !siblings.has(plainName);
|
|
4268
|
+
}
|
|
4269
|
+
function inflate(data) {
|
|
4270
|
+
if (!canReadCompressedTranscripts) {
|
|
4271
|
+
throw new Error("Reading a .jsonl.zst transcript needs Node.js 22.15 or newer, or Bun");
|
|
4272
|
+
}
|
|
4273
|
+
return zlib.zstdDecompressSync(data);
|
|
4274
|
+
}
|
|
4275
|
+
function readTranscriptTextSync(filePath) {
|
|
4276
|
+
const data = (0, import_node_fs.readFileSync)(filePath);
|
|
4277
|
+
return (isCompressedTranscript(filePath) ? inflate(data) : data).toString("utf-8");
|
|
4278
|
+
}
|
|
4279
|
+
async function readTranscriptText(filePath) {
|
|
4280
|
+
const data = await (0, import_promises.readFile)(filePath);
|
|
4281
|
+
return (isCompressedTranscript(filePath) ? inflate(data) : data).toString("utf-8");
|
|
4282
|
+
}
|
|
4283
|
+
async function openTranscript(filePath) {
|
|
4284
|
+
if (!isCompressedTranscript(filePath)) return (0, import_promises.open)(filePath, "r");
|
|
4285
|
+
const [fileStat, compressed] = await Promise.all([(0, import_promises.stat)(filePath), (0, import_promises.readFile)(filePath)]);
|
|
4286
|
+
const data = inflate(compressed);
|
|
4287
|
+
return {
|
|
4288
|
+
async read(buffer, offset, length, position) {
|
|
4289
|
+
const start = Math.min(Math.max(position, 0), data.length);
|
|
4290
|
+
return { bytesRead: data.copy(buffer, offset, start, Math.min(start + length, data.length)) };
|
|
4291
|
+
},
|
|
4292
|
+
stat: async () => ({ size: data.length, mtimeMs: fileStat.mtimeMs }),
|
|
4293
|
+
close: async () => {
|
|
4294
|
+
}
|
|
4295
|
+
};
|
|
4296
|
+
}
|
|
4297
|
+
|
|
4298
|
+
// src/lib/stores.ts
|
|
4299
|
+
var import_node_fs2 = require("node:fs");
|
|
3969
4300
|
var import_node_path2 = require("node:path");
|
|
3970
4301
|
|
|
3971
4302
|
// src/lib/dirs.ts
|
|
3972
4303
|
var import_node_path = require("node:path");
|
|
3973
4304
|
var import_node_os = require("node:os");
|
|
3974
4305
|
function agentHomeDir(kind, env = process.env, homeDirectory = (0, import_node_os.homedir)()) {
|
|
3975
|
-
const { homeEnvVar, homeDirName } = descriptorFor(kind).cli;
|
|
3976
|
-
const override =
|
|
4306
|
+
const { homeEnvVar, homeDirName, sdkHomeEnvVar } = descriptorFor(kind).cli;
|
|
4307
|
+
const override = env[homeEnvVar ?? sdkHomeEnvVar ?? ""];
|
|
3977
4308
|
return override || (0, import_node_path.join)(homeDirectory, homeDirName);
|
|
3978
4309
|
}
|
|
3979
4310
|
var dirs = {
|
|
@@ -3981,7 +4312,8 @@ var dirs = {
|
|
|
3981
4312
|
TEAMS_DIR: (0, import_node_path.join)(agentHomeDir("claude"), "teams"),
|
|
3982
4313
|
TASKS_DIR: (0, import_node_path.join)(agentHomeDir("claude"), "tasks"),
|
|
3983
4314
|
CODEX_SESSIONS_DIR: (0, import_node_path.join)(agentHomeDir("codex"), "sessions"),
|
|
3984
|
-
COPILOT_SESSIONS_DIR: (0, import_node_path.join)(agentHomeDir("copilot"), "session-state")
|
|
4315
|
+
COPILOT_SESSIONS_DIR: (0, import_node_path.join)(agentHomeDir("copilot"), "session-state"),
|
|
4316
|
+
ACP_PROJECTS_DIR: (0, import_node_path.join)(agentHomeDir("acp"), "projects")
|
|
3985
4317
|
};
|
|
3986
4318
|
var DEFAULT_DB_PATH = (0, import_node_path.join)(agentHomeDir("claude"), "cogpit-memory", "search-index.db");
|
|
3987
4319
|
|
|
@@ -3991,14 +4323,14 @@ var MAX_WALK_DEPTH = 4;
|
|
|
3991
4323
|
var SUBAGENTS_DIR = "subagents";
|
|
3992
4324
|
function mtimeOf(filePath) {
|
|
3993
4325
|
try {
|
|
3994
|
-
return (0,
|
|
4326
|
+
return (0, import_node_fs2.statSync)(filePath).mtimeMs;
|
|
3995
4327
|
} catch {
|
|
3996
4328
|
return null;
|
|
3997
4329
|
}
|
|
3998
4330
|
}
|
|
3999
4331
|
function readDirSafe(dir) {
|
|
4000
4332
|
try {
|
|
4001
|
-
return (0,
|
|
4333
|
+
return (0, import_node_fs2.readdirSync)(dir, { withFileTypes: true });
|
|
4002
4334
|
} catch {
|
|
4003
4335
|
return [];
|
|
4004
4336
|
}
|
|
@@ -4009,8 +4341,8 @@ function isWithin(root, target) {
|
|
|
4009
4341
|
}
|
|
4010
4342
|
function canonicalWithinRoot(root, filePath) {
|
|
4011
4343
|
try {
|
|
4012
|
-
const realRoot = (0,
|
|
4013
|
-
const real = (0,
|
|
4344
|
+
const realRoot = (0, import_node_fs2.realpathSync)(root);
|
|
4345
|
+
const real = (0, import_node_fs2.realpathSync)(filePath);
|
|
4014
4346
|
return isWithin(realRoot, real) ? real : null;
|
|
4015
4347
|
} catch {
|
|
4016
4348
|
return null;
|
|
@@ -4076,13 +4408,15 @@ function walkClaude(root, cutoffMs) {
|
|
|
4076
4408
|
function walkCodex(root, cutoffMs, dir = root, depth = 0) {
|
|
4077
4409
|
if (depth > MAX_WALK_DEPTH) return [];
|
|
4078
4410
|
const found = [];
|
|
4079
|
-
|
|
4411
|
+
const entries = readDirSafe(dir);
|
|
4412
|
+
const names = new Set(entries.map((entry) => entry.name));
|
|
4413
|
+
for (const entry of entries) {
|
|
4080
4414
|
const filePath = (0, import_node_path2.join)(dir, entry.name);
|
|
4081
4415
|
if (entry.isDirectory()) {
|
|
4082
4416
|
found.push(...walkCodex(root, cutoffMs, filePath, depth + 1));
|
|
4083
4417
|
continue;
|
|
4084
4418
|
}
|
|
4085
|
-
if (!entry.name
|
|
4419
|
+
if (!isListableTranscript(entry.name, names)) continue;
|
|
4086
4420
|
const mtimeMs = mtimeOf(filePath);
|
|
4087
4421
|
if (mtimeMs === null || mtimeMs < cutoffMs) continue;
|
|
4088
4422
|
found.push({
|
|
@@ -4143,14 +4477,18 @@ var WALKERS = {
|
|
|
4143
4477
|
find(root, sessionId) {
|
|
4144
4478
|
const walk = (dir, depth) => {
|
|
4145
4479
|
if (depth > MAX_WALK_DEPTH) return null;
|
|
4146
|
-
|
|
4480
|
+
const entries = readDirSafe(dir);
|
|
4481
|
+
const names = new Set(entries.map((entry) => entry.name));
|
|
4482
|
+
for (const entry of entries) {
|
|
4147
4483
|
const filePath = (0, import_node_path2.join)(dir, entry.name);
|
|
4148
4484
|
if (entry.isDirectory()) {
|
|
4149
4485
|
const match = walk(filePath, depth + 1);
|
|
4150
4486
|
if (match) return match;
|
|
4151
4487
|
continue;
|
|
4152
4488
|
}
|
|
4153
|
-
if (entry.name.endsWith(`${sessionId}.jsonl`))
|
|
4489
|
+
if (uncompressedTranscriptName(entry.name).endsWith(`${sessionId}.jsonl`) && isListableTranscript(entry.name, names)) {
|
|
4490
|
+
return filePath;
|
|
4491
|
+
}
|
|
4154
4492
|
}
|
|
4155
4493
|
return null;
|
|
4156
4494
|
};
|
|
@@ -4169,24 +4507,48 @@ var WALKERS = {
|
|
|
4169
4507
|
const candidate = copilotTranscript(root, sessionId);
|
|
4170
4508
|
return mtimeOf(candidate) === null ? null : candidate;
|
|
4171
4509
|
}
|
|
4510
|
+
},
|
|
4511
|
+
acp: {
|
|
4512
|
+
root: () => dirs.ACP_PROJECTS_DIR,
|
|
4513
|
+
list: walkClaude,
|
|
4514
|
+
identify: claudeIdentify,
|
|
4515
|
+
find: (root, id) => WALKERS.claude.find(root, id)
|
|
4172
4516
|
}
|
|
4173
4517
|
};
|
|
4174
|
-
function makeStore(kind) {
|
|
4518
|
+
function makeStore(kind, home, configuredId) {
|
|
4175
4519
|
const walker = WALKERS[kind];
|
|
4520
|
+
const instanceId = configuredId ?? (process.env.COGPIT_WORKER_AGENT === kind ? process.env.COGPIT_AGENT_INSTANCE_ID : void 0) ?? "default";
|
|
4521
|
+
const root = home ? () => (0, import_node_path2.join)(home, kind === "codex" ? "sessions" : kind === "copilot" ? "session-state" : "projects") : walker.root;
|
|
4522
|
+
const identify = (filePath, directory = root()) => {
|
|
4523
|
+
const identity = walker.identify(filePath, directory);
|
|
4524
|
+
return { ...identity, sessionId: instanceSessionId(instanceId, identity.sessionId), parentSessionId: identity.parentSessionId ? instanceSessionId(instanceId, identity.parentSessionId) : null };
|
|
4525
|
+
};
|
|
4176
4526
|
return {
|
|
4177
4527
|
kind,
|
|
4528
|
+
instanceId,
|
|
4178
4529
|
descriptor: descriptorFor(kind),
|
|
4179
|
-
root
|
|
4180
|
-
ownsPath: (filePath) =>
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4530
|
+
root,
|
|
4531
|
+
ownsPath: (filePath) => {
|
|
4532
|
+
if (isWithin((0, import_node_path2.resolve)(root()), (0, import_node_path2.resolve)(filePath))) return true;
|
|
4533
|
+
try {
|
|
4534
|
+
return isWithin((0, import_node_fs2.realpathSync)(root()), (0, import_node_fs2.realpathSync)(filePath));
|
|
4535
|
+
} catch {
|
|
4536
|
+
return false;
|
|
4537
|
+
}
|
|
4538
|
+
},
|
|
4539
|
+
identify,
|
|
4540
|
+
list: (cutoffMs, directory = root()) => walker.list(directory, cutoffMs).map((file) => ({ ...file, ...identify(file.path, directory) })),
|
|
4541
|
+
find: (sessionId) => {
|
|
4542
|
+
const parts = splitInstanceSessionId(sessionId);
|
|
4543
|
+
return parts.instanceId === instanceId ? walker.find(root(), parts.nativeId) : null;
|
|
4544
|
+
}
|
|
4184
4545
|
};
|
|
4185
4546
|
}
|
|
4186
4547
|
var STORES = {
|
|
4187
4548
|
claude: makeStore("claude"),
|
|
4188
4549
|
codex: makeStore("codex"),
|
|
4189
|
-
copilot: makeStore("copilot")
|
|
4550
|
+
copilot: makeStore("copilot"),
|
|
4551
|
+
acp: makeStore("acp")
|
|
4190
4552
|
};
|
|
4191
4553
|
function storeFor(kind) {
|
|
4192
4554
|
return STORES[kind];
|
|
@@ -4195,7 +4557,20 @@ function defaultStore() {
|
|
|
4195
4557
|
return storeFor(descriptorForDirName(null).kind);
|
|
4196
4558
|
}
|
|
4197
4559
|
function allStores() {
|
|
4198
|
-
|
|
4560
|
+
if (process.env.COGPIT_AGENT_INSTANCE_ID && AGENT_KINDS.includes(process.env.COGPIT_WORKER_AGENT)) return [STORES[process.env.COGPIT_WORKER_AGENT]];
|
|
4561
|
+
const stores = AGENT_KINDS.map((kind) => STORES[kind]);
|
|
4562
|
+
const root = process.env.COGPIT_ORCHESTRATION_ROOT;
|
|
4563
|
+
if (!root) return stores;
|
|
4564
|
+
for (const entry of readDirSafe((0, import_node_path2.join)(root, "provider-instances"))) {
|
|
4565
|
+
if (!entry.isDirectory()) continue;
|
|
4566
|
+
const home = (0, import_node_path2.join)(root, "provider-instances", entry.name);
|
|
4567
|
+
try {
|
|
4568
|
+
const config = JSON.parse((0, import_node_fs2.readFileSync)((0, import_node_path2.join)(home, "config.json"), "utf8"));
|
|
4569
|
+
if (config.providerInstance?.id === entry.name && config.providerInstance.agent && AGENT_KINDS.includes(config.providerInstance.agent)) stores.push(makeStore(config.providerInstance.agent, home, entry.name));
|
|
4570
|
+
} catch {
|
|
4571
|
+
}
|
|
4572
|
+
}
|
|
4573
|
+
return stores;
|
|
4199
4574
|
}
|
|
4200
4575
|
function storeForPath(filePath) {
|
|
4201
4576
|
return allStores().find((store) => store.ownsPath(filePath)) ?? null;
|
|
@@ -4265,7 +4640,7 @@ var SearchIndex = class {
|
|
|
4265
4640
|
const { count: totalRows } = this.db.prepare("SELECT COUNT(*) as count FROM search_content").get();
|
|
4266
4641
|
let dbSizeBytes = 0;
|
|
4267
4642
|
try {
|
|
4268
|
-
dbSizeBytes = (0,
|
|
4643
|
+
dbSizeBytes = (0, import_node_fs3.statSync)(this.dbPath).size;
|
|
4269
4644
|
} catch {
|
|
4270
4645
|
}
|
|
4271
4646
|
return {
|
|
@@ -4338,9 +4713,9 @@ var SearchIndex = class {
|
|
|
4338
4713
|
indexFile(filePath, sessionId, mtimeMs, opts) {
|
|
4339
4714
|
const resolvedSessionId = sessionId ?? identifyTranscript(filePath).sessionId;
|
|
4340
4715
|
if (mtimeMs == null) {
|
|
4341
|
-
mtimeMs = (0,
|
|
4716
|
+
mtimeMs = (0, import_node_fs3.statSync)(filePath).mtimeMs;
|
|
4342
4717
|
}
|
|
4343
|
-
const content = (
|
|
4718
|
+
const content = readTranscriptTextSync(filePath);
|
|
4344
4719
|
const session = parseSession(content, { skipStats: true });
|
|
4345
4720
|
const isSubagent = opts?.isSubagent ? 1 : 0;
|
|
4346
4721
|
const parentSessionId = opts?.parentSessionId ?? null;
|
|
@@ -4369,7 +4744,7 @@ var SearchIndex = class {
|
|
|
4369
4744
|
const files = this.db.prepare("SELECT file_path FROM indexed_files").all();
|
|
4370
4745
|
const missing = files.filter(({ file_path }) => {
|
|
4371
4746
|
try {
|
|
4372
|
-
(0,
|
|
4747
|
+
(0, import_node_fs3.statSync)(file_path);
|
|
4373
4748
|
return false;
|
|
4374
4749
|
} catch (error) {
|
|
4375
4750
|
const code = error.code;
|
|
@@ -4486,7 +4861,7 @@ var SearchIndex = class {
|
|
|
4486
4861
|
this.db.close();
|
|
4487
4862
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
4488
4863
|
try {
|
|
4489
|
-
(0,
|
|
4864
|
+
(0, import_node_fs3.unlinkSync)(this.dbPath + suffix);
|
|
4490
4865
|
} catch {
|
|
4491
4866
|
}
|
|
4492
4867
|
}
|
|
@@ -4510,7 +4885,7 @@ var SearchIndex = class {
|
|
|
4510
4885
|
const txn = this.db.transaction(() => {
|
|
4511
4886
|
for (const file of files) {
|
|
4512
4887
|
try {
|
|
4513
|
-
const content = (
|
|
4888
|
+
const content = readTranscriptTextSync(file.path);
|
|
4514
4889
|
const session = parseSession(content, { skipStats: true });
|
|
4515
4890
|
this.insertSessionContent(insert, file.sessionId, file.path, session);
|
|
4516
4891
|
insertFile.run(file.path, file.mtimeMs, file.sessionId, file.isSubagent ? 1 : 0, file.parentSessionId);
|
|
@@ -4572,8 +4947,8 @@ var SearchIndex = class {
|
|
|
4572
4947
|
};
|
|
4573
4948
|
|
|
4574
4949
|
// src/commands/search.ts
|
|
4575
|
-
var
|
|
4576
|
-
var
|
|
4950
|
+
var import_node_fs4 = require("node:fs");
|
|
4951
|
+
var import_promises2 = require("node:fs/promises");
|
|
4577
4952
|
var import_node_path3 = require("node:path");
|
|
4578
4953
|
|
|
4579
4954
|
// src/lib/response.ts
|
|
@@ -4594,6 +4969,11 @@ function parseMaxAge(raw) {
|
|
|
4594
4969
|
}
|
|
4595
4970
|
}
|
|
4596
4971
|
|
|
4972
|
+
// src/lib/read-session.ts
|
|
4973
|
+
function parseTranscript(content, filePath) {
|
|
4974
|
+
return scopeParsedSession(parseSession(content), storeForPath(filePath)?.instanceId ?? "default");
|
|
4975
|
+
}
|
|
4976
|
+
|
|
4597
4977
|
// src/commands/search.ts
|
|
4598
4978
|
async function searchSessions(query, opts, searchIndex) {
|
|
4599
4979
|
if (!query || query.length < 2) {
|
|
@@ -4606,9 +4986,9 @@ async function searchSessions(query, opts, searchIndex) {
|
|
|
4606
4986
|
let ownedIndex = false;
|
|
4607
4987
|
if (!index && searchIndex === void 0) {
|
|
4608
4988
|
try {
|
|
4609
|
-
const dbExists = (0,
|
|
4989
|
+
const dbExists = (0, import_node_fs4.existsSync)(DEFAULT_DB_PATH);
|
|
4610
4990
|
if (!dbExists) {
|
|
4611
|
-
(0,
|
|
4991
|
+
(0, import_node_fs4.mkdirSync)((0, import_node_path3.dirname)(DEFAULT_DB_PATH), { recursive: true });
|
|
4612
4992
|
}
|
|
4613
4993
|
index = new SearchIndex(DEFAULT_DB_PATH);
|
|
4614
4994
|
ownedIndex = true;
|
|
@@ -4682,7 +5062,7 @@ async function cwdFromFilePath(filePath) {
|
|
|
4682
5062
|
const cached = cwdCache.get(filePath);
|
|
4683
5063
|
if (cached !== void 0) return cached;
|
|
4684
5064
|
try {
|
|
4685
|
-
const fh = await (
|
|
5065
|
+
const fh = await openTranscript(filePath);
|
|
4686
5066
|
try {
|
|
4687
5067
|
const buf = Buffer.alloc(CWD_READ_BYTES);
|
|
4688
5068
|
const { bytesRead } = await fh.read(buf, 0, CWD_READ_BYTES, 0);
|
|
@@ -4740,7 +5120,7 @@ function searchField(text, location, query, caseSensitive, extras) {
|
|
|
4740
5120
|
async function discoverSingleSession(sessionId) {
|
|
4741
5121
|
const jsonlPath = await findSessionFile(sessionId);
|
|
4742
5122
|
if (!jsonlPath) return [];
|
|
4743
|
-
const s = await (0,
|
|
5123
|
+
const s = await (0, import_promises2.stat)(jsonlPath);
|
|
4744
5124
|
return [{ path: jsonlPath, mtimeMs: s.mtimeMs, sessionId }];
|
|
4745
5125
|
}
|
|
4746
5126
|
function discoverAllSessions(maxAgeMs) {
|
|
@@ -4748,7 +5128,7 @@ function discoverAllSessions(maxAgeMs) {
|
|
|
4748
5128
|
}
|
|
4749
5129
|
async function rawTextMatch(filePath, query, caseSensitive) {
|
|
4750
5130
|
try {
|
|
4751
|
-
const content = await (
|
|
5131
|
+
const content = await readTranscriptText(filePath);
|
|
4752
5132
|
const haystack = caseSensitive ? content : content.toLowerCase();
|
|
4753
5133
|
const needle = caseSensitive ? query : query.toLowerCase();
|
|
4754
5134
|
if (haystack.includes(needle)) return content;
|
|
@@ -4823,7 +5203,7 @@ async function walkSubagentFiles(parentJsonlPath, query, caseSensitive, currentD
|
|
|
4823
5203
|
const subDir = parentJsonlPath.replace(/\.jsonl$/, "") + "/subagents";
|
|
4824
5204
|
let files;
|
|
4825
5205
|
try {
|
|
4826
|
-
files = await (0,
|
|
5206
|
+
files = await (0, import_promises2.readdir)(subDir);
|
|
4827
5207
|
} catch {
|
|
4828
5208
|
return [];
|
|
4829
5209
|
}
|
|
@@ -4840,7 +5220,7 @@ async function walkSubagentFiles(parentJsonlPath, query, caseSensitive, currentD
|
|
|
4840
5220
|
);
|
|
4841
5221
|
const hitArrays = await Promise.all(
|
|
4842
5222
|
matched.filter((m) => m.rawContent).map(async ({ agentId, filePath, rawContent }) => {
|
|
4843
|
-
const subSession =
|
|
5223
|
+
const subSession = parseTranscript(rawContent, filePath);
|
|
4844
5224
|
const subHits = walkSession(subSession, query, caseSensitive, `agent/${agentId}/`);
|
|
4845
5225
|
const nestedHits = await walkSubagentFiles(filePath, query, caseSensitive, currentDepth + 1, maxDepth);
|
|
4846
5226
|
return [...subHits, ...nestedHits];
|
|
@@ -4861,7 +5241,7 @@ async function rawScanSearch(query, sessionId, maxAgeMs, limit, caseSensitive, e
|
|
|
4861
5241
|
const rawContent = await rawTextMatch(file.path, query, caseSensitive);
|
|
4862
5242
|
sessionsSearched++;
|
|
4863
5243
|
if (!rawContent) continue;
|
|
4864
|
-
const session =
|
|
5244
|
+
const session = parseTranscript(rawContent, file.path);
|
|
4865
5245
|
const sessionHits = walkSession(session, query, caseSensitive);
|
|
4866
5246
|
const subagentHits = await walkSubagentFiles(file.path, query, caseSensitive, 0, MAX_SUBAGENT_DEPTH);
|
|
4867
5247
|
const allHits = [...sessionHits, ...subagentHits];
|
|
@@ -4901,14 +5281,14 @@ async function rawScanSearch(query, sessionId, maxAgeMs, limit, caseSensitive, e
|
|
|
4901
5281
|
}
|
|
4902
5282
|
|
|
4903
5283
|
// src/commands/context.ts
|
|
4904
|
-
var
|
|
5284
|
+
var import_promises4 = require("node:fs/promises");
|
|
4905
5285
|
var import_node_path5 = require("node:path");
|
|
4906
5286
|
|
|
4907
5287
|
// src/lib/helpers.ts
|
|
4908
|
-
var
|
|
5288
|
+
var import_promises3 = require("node:fs/promises");
|
|
4909
5289
|
var import_node_path4 = require("node:path");
|
|
4910
5290
|
async function matchSubagentToMember(leadSessionId, subagentFileName, members) {
|
|
4911
|
-
const entries = await (0,
|
|
5291
|
+
const entries = await (0, import_promises3.readdir)(dirs.PROJECTS_DIR, { withFileTypes: true });
|
|
4912
5292
|
for (const entry of entries) {
|
|
4913
5293
|
if (!entry.isDirectory() || entry.name === "memory") continue;
|
|
4914
5294
|
const filePath = (0, import_node_path4.join)(
|
|
@@ -4919,7 +5299,7 @@ async function matchSubagentToMember(leadSessionId, subagentFileName, members) {
|
|
|
4919
5299
|
subagentFileName
|
|
4920
5300
|
);
|
|
4921
5301
|
try {
|
|
4922
|
-
const fh = await (0,
|
|
5302
|
+
const fh = await (0, import_promises3.open)(filePath, "r");
|
|
4923
5303
|
try {
|
|
4924
5304
|
const buf = Buffer.alloc(16384);
|
|
4925
5305
|
const { bytesRead } = await fh.read(buf, 0, 16384, 0);
|
|
@@ -5113,6 +5493,14 @@ function mapContentBlock(block) {
|
|
|
5113
5493
|
return { kind: "recap", content: block.content, timestamp: block.timestamp ?? null };
|
|
5114
5494
|
case "task_notification":
|
|
5115
5495
|
return { kind: "task_notification", content: block.content, timestamp: block.timestamp ?? null };
|
|
5496
|
+
case "local_command":
|
|
5497
|
+
return {
|
|
5498
|
+
kind: "local_command",
|
|
5499
|
+
content: block.content,
|
|
5500
|
+
timestamp: block.timestamp ?? null,
|
|
5501
|
+
...block.contextUsage ? { contextUsage: block.contextUsage } : {},
|
|
5502
|
+
...block.usageReport ? { usageReport: block.usageReport } : {}
|
|
5503
|
+
};
|
|
5116
5504
|
default: {
|
|
5117
5505
|
const exhaustive = block;
|
|
5118
5506
|
return exhaustive;
|
|
@@ -5122,7 +5510,7 @@ function mapContentBlock(block) {
|
|
|
5122
5510
|
async function findSubagentFile(parentJsonlPath, agentId) {
|
|
5123
5511
|
const subagentsDir = parentJsonlPath.replace(/\.jsonl$/, "") + "/subagents";
|
|
5124
5512
|
try {
|
|
5125
|
-
const files = await (0,
|
|
5513
|
+
const files = await (0, import_promises4.readdir)(subagentsDir);
|
|
5126
5514
|
for (const f of files) {
|
|
5127
5515
|
if (!f.startsWith("agent-") || !f.endsWith(".jsonl")) continue;
|
|
5128
5516
|
const fileAgentId = f.replace("agent-", "").replace(".jsonl", "");
|
|
@@ -5136,12 +5524,12 @@ async function findSubagentFile(parentJsonlPath, agentId) {
|
|
|
5136
5524
|
}
|
|
5137
5525
|
async function findTeamContext(sessionId, subagentFileName) {
|
|
5138
5526
|
try {
|
|
5139
|
-
const teamEntries = await (0,
|
|
5527
|
+
const teamEntries = await (0, import_promises4.readdir)(dirs.TEAMS_DIR, { withFileTypes: true });
|
|
5140
5528
|
for (const entry of teamEntries) {
|
|
5141
5529
|
if (!entry.isDirectory()) continue;
|
|
5142
5530
|
const configPath = (0, import_node_path5.join)(dirs.TEAMS_DIR, entry.name, "config.json");
|
|
5143
5531
|
try {
|
|
5144
|
-
const raw = await (0,
|
|
5532
|
+
const raw = await (0, import_promises4.readFile)(configPath, "utf-8");
|
|
5145
5533
|
const config = JSON.parse(raw);
|
|
5146
5534
|
if (config.leadSessionId !== sessionId) continue;
|
|
5147
5535
|
const members = config.members ?? [];
|
|
@@ -5150,10 +5538,10 @@ async function findTeamContext(sessionId, subagentFileName) {
|
|
|
5150
5538
|
let currentTask = null;
|
|
5151
5539
|
try {
|
|
5152
5540
|
const taskDir = (0, import_node_path5.join)(dirs.TASKS_DIR, entry.name);
|
|
5153
|
-
const taskFiles = await (0,
|
|
5541
|
+
const taskFiles = await (0, import_promises4.readdir)(taskDir);
|
|
5154
5542
|
for (const tf of taskFiles) {
|
|
5155
5543
|
if (!tf.endsWith(".json")) continue;
|
|
5156
|
-
const taskRaw = await (0,
|
|
5544
|
+
const taskRaw = await (0, import_promises4.readFile)((0, import_node_path5.join)(taskDir, tf), "utf-8");
|
|
5157
5545
|
const task = JSON.parse(taskRaw);
|
|
5158
5546
|
if (task.owner === memberName && task.status === "in_progress") {
|
|
5159
5547
|
currentTask = { id: task.id, subject: task.subject, status: task.status };
|
|
@@ -5201,27 +5589,27 @@ function findAgentMetadata(session, agentId) {
|
|
|
5201
5589
|
async function getSessionOverview(sessionId) {
|
|
5202
5590
|
const jsonlPath = await findSessionFile(sessionId);
|
|
5203
5591
|
if (!jsonlPath) return { error: "Session not found" };
|
|
5204
|
-
const content = await (
|
|
5205
|
-
const session =
|
|
5592
|
+
const content = await readTranscriptText(jsonlPath);
|
|
5593
|
+
const session = parseTranscript(content, jsonlPath);
|
|
5206
5594
|
return mapSessionToOverview(session);
|
|
5207
5595
|
}
|
|
5208
5596
|
async function getTurnDetail(sessionId, turnIndex) {
|
|
5209
5597
|
const jsonlPath = await findSessionFile(sessionId);
|
|
5210
5598
|
if (!jsonlPath) return { error: "Session not found" };
|
|
5211
|
-
const content = await (
|
|
5212
|
-
const session =
|
|
5599
|
+
const content = await readTranscriptText(jsonlPath);
|
|
5600
|
+
const session = parseTranscript(content, jsonlPath);
|
|
5213
5601
|
if (turnIndex < 0 || turnIndex >= session.turns.length) return { error: "Turn not found" };
|
|
5214
5602
|
return mapTurnToDetail(session, turnIndex);
|
|
5215
5603
|
}
|
|
5216
5604
|
async function getAgentOverview(sessionId, agentId) {
|
|
5217
5605
|
const jsonlPath = await findSessionFile(sessionId);
|
|
5218
5606
|
if (!jsonlPath) return { error: "Session not found" };
|
|
5219
|
-
const content = await (
|
|
5220
|
-
const session =
|
|
5607
|
+
const content = await readTranscriptText(jsonlPath);
|
|
5608
|
+
const session = parseTranscript(content, jsonlPath);
|
|
5221
5609
|
const subagentFile = await findSubagentFile(jsonlPath, agentId);
|
|
5222
5610
|
if (!subagentFile) return { error: "Agent not found" };
|
|
5223
|
-
const subagentContent = await (
|
|
5224
|
-
const subagentSession =
|
|
5611
|
+
const subagentContent = await readTranscriptText(subagentFile.filePath);
|
|
5612
|
+
const subagentSession = parseTranscript(subagentContent, subagentFile.filePath);
|
|
5225
5613
|
const metadata = findAgentMetadata(session, agentId);
|
|
5226
5614
|
const parentToolCallId = findParentToolCallId(session, agentId);
|
|
5227
5615
|
const teamContext = await findTeamContext(sessionId, subagentFile.fileName);
|
|
@@ -5241,8 +5629,8 @@ async function getAgentTurnDetail(sessionId, agentId, turnIndex) {
|
|
|
5241
5629
|
if (!jsonlPath) return { error: "Session not found" };
|
|
5242
5630
|
const subagentFile = await findSubagentFile(jsonlPath, agentId);
|
|
5243
5631
|
if (!subagentFile) return { error: "Agent not found" };
|
|
5244
|
-
const subagentContent = await (
|
|
5245
|
-
const subagentSession =
|
|
5632
|
+
const subagentContent = await readTranscriptText(subagentFile.filePath);
|
|
5633
|
+
const subagentSession = parseTranscript(subagentContent, subagentFile.filePath);
|
|
5246
5634
|
if (turnIndex < 0 || turnIndex >= subagentSession.turns.length) return { error: "Turn not found" };
|
|
5247
5635
|
return mapTurnToDetail(subagentSession, turnIndex);
|
|
5248
5636
|
}
|
|
@@ -5251,7 +5639,7 @@ async function getAgentTurnDetail(sessionId, agentId, turnIndex) {
|
|
|
5251
5639
|
var import_node_path6 = require("node:path");
|
|
5252
5640
|
|
|
5253
5641
|
// src/lib/metadata.ts
|
|
5254
|
-
var
|
|
5642
|
+
var import_promises5 = require("node:fs/promises");
|
|
5255
5643
|
|
|
5256
5644
|
// src/lib/sessionStatus.ts
|
|
5257
5645
|
function deriveSessionStatus(rawMessages) {
|
|
@@ -5276,15 +5664,16 @@ function scanClaudeMessages(lines) {
|
|
|
5276
5664
|
turnCount: 0
|
|
5277
5665
|
};
|
|
5278
5666
|
for (const line of lines) {
|
|
5279
|
-
|
|
5667
|
+
if (isAnyAgentNoiseLine(line)) continue;
|
|
5668
|
+
let record2;
|
|
5280
5669
|
try {
|
|
5281
|
-
|
|
5670
|
+
record2 = JSON.parse(line);
|
|
5282
5671
|
} catch {
|
|
5283
5672
|
continue;
|
|
5284
5673
|
}
|
|
5285
|
-
if (
|
|
5286
|
-
if (!meta.timestamp) meta.timestamp =
|
|
5287
|
-
const content =
|
|
5674
|
+
if (record2.type !== "user" || record2.isMeta) continue;
|
|
5675
|
+
if (!meta.timestamp) meta.timestamp = record2.timestamp || "";
|
|
5676
|
+
const content = record2.message?.content;
|
|
5288
5677
|
let extracted = "";
|
|
5289
5678
|
if (typeof content === "string") {
|
|
5290
5679
|
extracted = cleanUserText(content);
|
|
@@ -5304,11 +5693,11 @@ function scanClaudeMessages(lines) {
|
|
|
5304
5693
|
return meta;
|
|
5305
5694
|
}
|
|
5306
5695
|
async function readLines(filePath, size) {
|
|
5307
|
-
if (size <= FULL_READ_LIMIT) {
|
|
5308
|
-
const content = await (
|
|
5696
|
+
if (size <= FULL_READ_LIMIT || isCompressedTranscript(filePath)) {
|
|
5697
|
+
const content = await readTranscriptText(filePath);
|
|
5309
5698
|
return { lines: content.split("\n").filter(Boolean), isPartialRead: false };
|
|
5310
5699
|
}
|
|
5311
|
-
const handle = await (0,
|
|
5700
|
+
const handle = await (0, import_promises5.open)(filePath, "r");
|
|
5312
5701
|
try {
|
|
5313
5702
|
const buffer = Buffer.alloc(HEAD_READ_BYTES);
|
|
5314
5703
|
const { bytesRead } = await handle.read(buffer, 0, HEAD_READ_BYTES, 0);
|
|
@@ -5323,14 +5712,15 @@ async function readLines(filePath, size) {
|
|
|
5323
5712
|
}
|
|
5324
5713
|
}
|
|
5325
5714
|
async function getSessionMeta(filePath) {
|
|
5326
|
-
const fileStat = await (0,
|
|
5715
|
+
const fileStat = await (0, import_promises5.stat)(filePath);
|
|
5327
5716
|
let { lines, isPartialRead } = await readLines(filePath, fileStat.size);
|
|
5328
5717
|
const format = formatForText(lines.join("\n"));
|
|
5329
5718
|
if (isPartialRead && !format.descriptor.metadataFromHead) {
|
|
5330
|
-
lines = (await (0,
|
|
5719
|
+
lines = (await (0, import_promises5.readFile)(filePath, "utf-8")).split("\n").filter(Boolean);
|
|
5331
5720
|
isPartialRead = false;
|
|
5332
5721
|
}
|
|
5333
5722
|
const header = readHeader(format, lines);
|
|
5723
|
+
const instanceId = storeForPath(filePath)?.instanceId ?? "default";
|
|
5334
5724
|
const conversation = header.turnCount === void 0 ? scanClaudeMessages(lines) : {
|
|
5335
5725
|
firstUserMessage: header.firstUserMessage ?? "",
|
|
5336
5726
|
lastUserMessage: header.lastUserMessage ?? "",
|
|
@@ -5338,7 +5728,7 @@ async function getSessionMeta(filePath) {
|
|
|
5338
5728
|
turnCount: header.turnCount
|
|
5339
5729
|
};
|
|
5340
5730
|
return {
|
|
5341
|
-
sessionId: header.sessionId,
|
|
5731
|
+
sessionId: instanceSessionId(instanceId, header.sessionId),
|
|
5342
5732
|
version: header.version,
|
|
5343
5733
|
gitBranch: header.gitBranch,
|
|
5344
5734
|
model: header.model,
|
|
@@ -5348,17 +5738,17 @@ async function getSessionMeta(filePath) {
|
|
|
5348
5738
|
// A head read saw a known number of lines in a known number of bytes;
|
|
5349
5739
|
// scaling that by the file size is the only estimate available.
|
|
5350
5740
|
lineCount: isPartialRead ? Math.round(fileStat.size / (HEAD_READ_BYTES / lines.length)) : lines.length,
|
|
5351
|
-
branchedFrom: header.branchedFrom
|
|
5741
|
+
branchedFrom: header.branchedFrom ? { ...header.branchedFrom, sessionId: instanceSessionId(instanceId, header.branchedFrom.sessionId) } : void 0
|
|
5352
5742
|
};
|
|
5353
5743
|
}
|
|
5354
5744
|
async function getSessionStatus(filePath) {
|
|
5355
5745
|
const CHUNK = 4096;
|
|
5356
5746
|
const MAX_CHUNKS = 64;
|
|
5357
5747
|
try {
|
|
5358
|
-
const
|
|
5359
|
-
if (fileStat.size === 0) return { status: "idle" };
|
|
5360
|
-
const fh = await (0, import_promises4.open)(filePath, "r");
|
|
5748
|
+
const fh = await openTranscript(filePath);
|
|
5361
5749
|
try {
|
|
5750
|
+
const fileStat = await fh.stat();
|
|
5751
|
+
if (fileStat.size === 0) return { status: "idle" };
|
|
5362
5752
|
const meaningful = [];
|
|
5363
5753
|
let cursor = fileStat.size;
|
|
5364
5754
|
let leftover = "";
|
|
@@ -5374,7 +5764,7 @@ async function getSessionStatus(filePath) {
|
|
|
5374
5764
|
const startIdx = cursor > 0 ? 1 : 0;
|
|
5375
5765
|
for (let i = lines.length - 1; i >= startIdx; i--) {
|
|
5376
5766
|
const line = lines[i];
|
|
5377
|
-
if (!line) continue;
|
|
5767
|
+
if (!line || isAnyAgentNoiseLine(line)) continue;
|
|
5378
5768
|
let obj;
|
|
5379
5769
|
try {
|
|
5380
5770
|
obj = JSON.parse(line);
|
|
@@ -5443,7 +5833,7 @@ async function getSessionStatus(filePath) {
|
|
|
5443
5833
|
// src/commands/sessions.ts
|
|
5444
5834
|
function toSessionSummary(file, meta, status) {
|
|
5445
5835
|
return {
|
|
5446
|
-
sessionId: meta.sessionId,
|
|
5836
|
+
sessionId: file.sessionId || meta.sessionId,
|
|
5447
5837
|
filePath: file.path,
|
|
5448
5838
|
timestamp: meta.timestamp,
|
|
5449
5839
|
model: meta.model,
|
|
@@ -5505,11 +5895,11 @@ async function currentSession(cwd) {
|
|
|
5505
5895
|
}
|
|
5506
5896
|
|
|
5507
5897
|
// src/commands/index-cmd.ts
|
|
5508
|
-
var
|
|
5898
|
+
var import_node_fs5 = require("node:fs");
|
|
5509
5899
|
var import_node_path7 = require("node:path");
|
|
5510
5900
|
async function indexStats(dbPath) {
|
|
5511
5901
|
const path = dbPath ?? DEFAULT_DB_PATH;
|
|
5512
|
-
if (!(0,
|
|
5902
|
+
if (!(0, import_node_fs5.existsSync)(path)) {
|
|
5513
5903
|
return { error: `Database not found at ${path}. Run 'cogpit-memory index rebuild' to create it.` };
|
|
5514
5904
|
}
|
|
5515
5905
|
const index = new SearchIndex(path);
|
|
@@ -5519,7 +5909,7 @@ async function indexStats(dbPath) {
|
|
|
5519
5909
|
}
|
|
5520
5910
|
async function indexRebuild(dbPath) {
|
|
5521
5911
|
const path = dbPath ?? DEFAULT_DB_PATH;
|
|
5522
|
-
(0,
|
|
5912
|
+
(0, import_node_fs5.mkdirSync)((0, import_node_path7.dirname)(path), { recursive: true });
|
|
5523
5913
|
const index = new SearchIndex(path);
|
|
5524
5914
|
index.buildFull();
|
|
5525
5915
|
const stats = index.getStats();
|