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