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