cogpit-memory 0.1.9 → 0.1.10
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 +26 -5
- package/dist/cli.d.ts +30 -0
- package/dist/cli.js +1114 -178
- package/dist/commands/context.d.ts +20 -0
- package/dist/commands/index-cmd.d.ts +15 -0
- package/dist/commands/install-skill.d.ts +4 -0
- package/dist/commands/search.d.ts +39 -0
- package/dist/commands/sessions.d.ts +41 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +1117 -180
- package/dist/lib/codex-patches.d.ts +9 -0
- package/dist/lib/codex-tool-normalization.d.ts +8 -0
- package/dist/lib/codex.d.ts +30 -0
- package/dist/lib/dirs.d.ts +8 -0
- package/dist/lib/helpers.d.ts +11 -0
- package/dist/lib/interactiveState.d.ts +35 -0
- package/dist/lib/messageTypeGuards.d.ts +17 -0
- package/dist/lib/metadata.d.ts +31 -0
- package/dist/lib/parser.d.ts +14 -0
- package/dist/lib/pricingTiers.d.ts +30 -0
- package/dist/lib/response.d.ts +2 -0
- package/dist/lib/search-index.d.ts +154 -0
- package/dist/lib/sessionStats.d.ts +6 -0
- package/dist/lib/sessionStatus.d.ts +36 -0
- package/dist/lib/token-costs.d.ts +48 -0
- package/dist/lib/turnBuilder.d.ts +5 -0
- package/dist/lib/types.d.ts +401 -0
- package/package.json +13 -7
- package/skill/SKILL.md +4 -4
package/dist/cli.js
CHANGED
|
@@ -48,7 +48,7 @@ var Database = import_better_sqlite3.default;
|
|
|
48
48
|
var import_node_fs = require("node:fs");
|
|
49
49
|
var import_node_path = require("node:path");
|
|
50
50
|
|
|
51
|
-
// src/lib/
|
|
51
|
+
// src/lib/messageTypeGuards.ts
|
|
52
52
|
function isUserMessage(msg) {
|
|
53
53
|
return msg.type === "user";
|
|
54
54
|
}
|
|
@@ -64,9 +64,14 @@ function isSystemMessage(msg) {
|
|
|
64
64
|
function isSummaryMessage(msg) {
|
|
65
65
|
return msg.type === "summary";
|
|
66
66
|
}
|
|
67
|
+
function isQueueOperationMessage(msg) {
|
|
68
|
+
return msg.type === "queue-operation";
|
|
69
|
+
}
|
|
67
70
|
function isCompactBoundary(msg) {
|
|
68
71
|
return msg.type === "system" && msg.subtype === "compact_boundary";
|
|
69
72
|
}
|
|
73
|
+
|
|
74
|
+
// src/lib/turnBuilder.ts
|
|
70
75
|
function extractTextFromContent(content) {
|
|
71
76
|
if (typeof content === "string") return content;
|
|
72
77
|
return content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
|
|
@@ -79,6 +84,11 @@ function extractToolResultText(content) {
|
|
|
79
84
|
return "";
|
|
80
85
|
}).filter(Boolean).join("\n");
|
|
81
86
|
}
|
|
87
|
+
function isVisibleQueuedPrompt(content) {
|
|
88
|
+
if (!content?.trim()) return false;
|
|
89
|
+
const trimmed = content.trimStart();
|
|
90
|
+
return !trimmed.startsWith("<task-notification>") && !trimmed.startsWith("<local-command-");
|
|
91
|
+
}
|
|
82
92
|
function mergeTokenUsage(existing, incoming) {
|
|
83
93
|
if (!existing) {
|
|
84
94
|
return { ...incoming };
|
|
@@ -87,7 +97,8 @@ function mergeTokenUsage(existing, incoming) {
|
|
|
87
97
|
input_tokens: existing.input_tokens + incoming.input_tokens,
|
|
88
98
|
output_tokens: existing.output_tokens + incoming.output_tokens,
|
|
89
99
|
cache_creation_input_tokens: (existing.cache_creation_input_tokens ?? 0) + (incoming.cache_creation_input_tokens ?? 0),
|
|
90
|
-
cache_read_input_tokens: (existing.cache_read_input_tokens ?? 0) + (incoming.cache_read_input_tokens ?? 0)
|
|
100
|
+
cache_read_input_tokens: (existing.cache_read_input_tokens ?? 0) + (incoming.cache_read_input_tokens ?? 0),
|
|
101
|
+
speed: incoming.speed ?? existing.speed
|
|
91
102
|
};
|
|
92
103
|
}
|
|
93
104
|
function buildCompactionSummary(turns, title) {
|
|
@@ -120,10 +131,95 @@ function buildCompactionSummary(turns, title) {
|
|
|
120
131
|
}
|
|
121
132
|
return parts.join("\n");
|
|
122
133
|
}
|
|
134
|
+
function groupPlanModeBlocks(blocks) {
|
|
135
|
+
const result = [];
|
|
136
|
+
let i = 0;
|
|
137
|
+
while (i < blocks.length) {
|
|
138
|
+
const block = blocks[i];
|
|
139
|
+
if (block.kind === "tool_calls") {
|
|
140
|
+
const enterIdx = block.toolCalls.findIndex((tc) => tc.name === "EnterPlanMode");
|
|
141
|
+
if (enterIdx !== -1) {
|
|
142
|
+
const enterCall = block.toolCalls[enterIdx];
|
|
143
|
+
const plan = String(enterCall.input.plan ?? "");
|
|
144
|
+
const timestamp = block.timestamp;
|
|
145
|
+
const before = block.toolCalls.slice(0, enterIdx);
|
|
146
|
+
if (before.length > 0) {
|
|
147
|
+
result.push({ kind: "tool_calls", toolCalls: before, timestamp: block.timestamp });
|
|
148
|
+
}
|
|
149
|
+
const embedded = [];
|
|
150
|
+
const afterEnterInSameBlock = block.toolCalls.slice(enterIdx + 1);
|
|
151
|
+
const exitInSameBlock = afterEnterInSameBlock.findIndex((tc) => tc.name === "ExitPlanMode");
|
|
152
|
+
if (exitInSameBlock !== -1) {
|
|
153
|
+
embedded.push(...afterEnterInSameBlock.slice(0, exitInSameBlock));
|
|
154
|
+
const exitCall = afterEnterInSameBlock[exitInSameBlock];
|
|
155
|
+
const planFilePath = exitCall.input.path ? String(exitCall.input.path) : void 0;
|
|
156
|
+
let status = "pending";
|
|
157
|
+
if (exitCall.result !== null) {
|
|
158
|
+
status = exitCall.isError ? "rejected" : "approved";
|
|
159
|
+
}
|
|
160
|
+
result.push({ kind: "plan_mode", plan, planFilePath, status, toolCalls: embedded, timestamp });
|
|
161
|
+
const after = afterEnterInSameBlock.slice(exitInSameBlock + 1);
|
|
162
|
+
if (after.length > 0) {
|
|
163
|
+
result.push({ kind: "tool_calls", toolCalls: after, timestamp: block.timestamp });
|
|
164
|
+
}
|
|
165
|
+
i++;
|
|
166
|
+
continue;
|
|
167
|
+
} else {
|
|
168
|
+
embedded.push(...afterEnterInSameBlock);
|
|
169
|
+
let exitCall;
|
|
170
|
+
let planFilePath;
|
|
171
|
+
let status = "pending";
|
|
172
|
+
let j = i + 1;
|
|
173
|
+
const passthroughBlocks = [];
|
|
174
|
+
while (j < blocks.length) {
|
|
175
|
+
const next = blocks[j];
|
|
176
|
+
if (next.kind === "tool_calls") {
|
|
177
|
+
const exitIdx = next.toolCalls.findIndex((tc) => tc.name === "ExitPlanMode");
|
|
178
|
+
if (exitIdx !== -1) {
|
|
179
|
+
embedded.push(...next.toolCalls.slice(0, exitIdx));
|
|
180
|
+
exitCall = next.toolCalls[exitIdx];
|
|
181
|
+
planFilePath = exitCall.input.path ? String(exitCall.input.path) : void 0;
|
|
182
|
+
if (exitCall.result !== null) {
|
|
183
|
+
status = exitCall.isError ? "rejected" : "approved";
|
|
184
|
+
}
|
|
185
|
+
const tail = next.toolCalls.slice(exitIdx + 1);
|
|
186
|
+
result.push({ kind: "plan_mode", plan, planFilePath, status, toolCalls: embedded, timestamp });
|
|
187
|
+
result.push(...passthroughBlocks);
|
|
188
|
+
if (tail.length > 0) {
|
|
189
|
+
result.push({ kind: "tool_calls", toolCalls: tail, timestamp: next.timestamp });
|
|
190
|
+
}
|
|
191
|
+
i = j + 1;
|
|
192
|
+
break;
|
|
193
|
+
} else {
|
|
194
|
+
embedded.push(...next.toolCalls);
|
|
195
|
+
j++;
|
|
196
|
+
}
|
|
197
|
+
} else if (next.kind === "hook_event" || next.kind === "text" || next.kind === "queued_prompt") {
|
|
198
|
+
passthroughBlocks.push(next);
|
|
199
|
+
j++;
|
|
200
|
+
} else {
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (!exitCall) {
|
|
205
|
+
result.push({ kind: "plan_mode", plan, planFilePath: void 0, status: "pending", toolCalls: embedded, timestamp });
|
|
206
|
+
result.push(...passthroughBlocks);
|
|
207
|
+
i = j;
|
|
208
|
+
}
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
result.push(block);
|
|
214
|
+
i++;
|
|
215
|
+
}
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
123
218
|
function buildTurns(messages) {
|
|
124
219
|
const turns = [];
|
|
125
220
|
let current = null;
|
|
126
221
|
let pendingCompaction = null;
|
|
222
|
+
let pendingRecap = null;
|
|
127
223
|
const pendingToolUses = /* @__PURE__ */ new Map();
|
|
128
224
|
const seenMessageIds = /* @__PURE__ */ new Set();
|
|
129
225
|
const subAgentMap = /* @__PURE__ */ new Map();
|
|
@@ -154,6 +250,22 @@ function buildTurns(messages) {
|
|
|
154
250
|
for (const [parentId] of subAgentMap) {
|
|
155
251
|
flushSubAgentMessages(parentId);
|
|
156
252
|
}
|
|
253
|
+
for (const block of current.contentBlocks) {
|
|
254
|
+
if (block.kind !== "hook_event") continue;
|
|
255
|
+
for (const ev of block.events) {
|
|
256
|
+
const isPostToolUse = ev.eventName === "PostToolUse" || ev.eventName.startsWith("PostToolUse:");
|
|
257
|
+
if (!isPostToolUse || !ev.toolUseId) continue;
|
|
258
|
+
const toolCall = current.toolCalls.find((tc) => tc.id === ev.toolUseId);
|
|
259
|
+
if (!toolCall) continue;
|
|
260
|
+
if (ev.updatedToolOutput) {
|
|
261
|
+
toolCall.outputReplacedByHook = true;
|
|
262
|
+
}
|
|
263
|
+
if (ev.durationMs !== void 0) {
|
|
264
|
+
toolCall.hookDurationMs = (toolCall.hookDurationMs ?? 0) + ev.durationMs;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
current.contentBlocks = groupPlanModeBlocks(current.contentBlocks);
|
|
157
269
|
turns.push(current);
|
|
158
270
|
current = null;
|
|
159
271
|
agentBlockMap.clear();
|
|
@@ -175,6 +287,20 @@ function buildTurns(messages) {
|
|
|
175
287
|
);
|
|
176
288
|
continue;
|
|
177
289
|
}
|
|
290
|
+
if (isSystemMessage(msg) && msg.subtype === "away_summary" && msg.content) {
|
|
291
|
+
pendingRecap = { content: msg.content, timestamp: msg.timestamp };
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if (isQueueOperationMessage(msg)) {
|
|
295
|
+
if (current && msg.operation === "enqueue" && isVisibleQueuedPrompt(msg.content)) {
|
|
296
|
+
current.contentBlocks.push({
|
|
297
|
+
kind: "queued_prompt",
|
|
298
|
+
content: msg.content,
|
|
299
|
+
timestamp: msg.timestamp
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
178
304
|
if (isUserMessage(msg) && !msg.isMeta) {
|
|
179
305
|
const content = msg.message.content;
|
|
180
306
|
if (typeof content !== "string" && Array.isArray(content)) {
|
|
@@ -185,7 +311,7 @@ function buildTurns(messages) {
|
|
|
185
311
|
const pending = pendingToolUses.get(block.tool_use_id);
|
|
186
312
|
if (pending) {
|
|
187
313
|
pending.turn.toolCalls[pending.index].result = extractToolResultText(block.content);
|
|
188
|
-
pending.turn.toolCalls[pending.index].isError = block.is_error;
|
|
314
|
+
pending.turn.toolCalls[pending.index].isError = block.is_error === true;
|
|
189
315
|
pendingToolUses.delete(block.tool_use_id);
|
|
190
316
|
}
|
|
191
317
|
}
|
|
@@ -203,6 +329,7 @@ function buildTurns(messages) {
|
|
|
203
329
|
}
|
|
204
330
|
const agentMsg = {
|
|
205
331
|
agentId: toolUseResult.agentId,
|
|
332
|
+
parentToolUseId: toolUseId || void 0,
|
|
206
333
|
agentName: taskMeta?.name ?? null,
|
|
207
334
|
subagentType: taskMeta?.subagentType ?? null,
|
|
208
335
|
type: "assistant",
|
|
@@ -254,6 +381,10 @@ function buildTurns(messages) {
|
|
|
254
381
|
current.compactionSummary = pendingCompaction;
|
|
255
382
|
pendingCompaction = null;
|
|
256
383
|
}
|
|
384
|
+
if (pendingRecap) {
|
|
385
|
+
current.contentBlocks.push({ kind: "recap", content: pendingRecap.content, timestamp: pendingRecap.timestamp });
|
|
386
|
+
pendingRecap = null;
|
|
387
|
+
}
|
|
257
388
|
continue;
|
|
258
389
|
}
|
|
259
390
|
if (isAssistantMessage(msg)) {
|
|
@@ -303,6 +434,7 @@ function buildTurns(messages) {
|
|
|
303
434
|
if (block.type === "thinking") {
|
|
304
435
|
flushToolCalls2();
|
|
305
436
|
const tb = block;
|
|
437
|
+
if (!tb.thinking) continue;
|
|
306
438
|
current.thinking.push(tb);
|
|
307
439
|
msgThinking.push(tb);
|
|
308
440
|
} else if (block.type === "text") {
|
|
@@ -382,6 +514,7 @@ function buildTurns(messages) {
|
|
|
382
514
|
const taskMeta = taskMetaMap.get(parentId);
|
|
383
515
|
const agentMsg = {
|
|
384
516
|
agentId: data.agentId,
|
|
517
|
+
parentToolUseId: parentId || void 0,
|
|
385
518
|
agentName: taskMeta?.name ?? null,
|
|
386
519
|
subagentType: taskMeta?.subagentType ?? null,
|
|
387
520
|
type: data.message.type,
|
|
@@ -427,7 +560,7 @@ function buildTurns(messages) {
|
|
|
427
560
|
);
|
|
428
561
|
if (match) {
|
|
429
562
|
match.result = extractToolResultText(block.content);
|
|
430
|
-
match.isError = block.is_error;
|
|
563
|
+
match.isError = block.is_error === true;
|
|
431
564
|
}
|
|
432
565
|
}
|
|
433
566
|
}
|
|
@@ -446,6 +579,36 @@ function buildTurns(messages) {
|
|
|
446
579
|
}
|
|
447
580
|
continue;
|
|
448
581
|
}
|
|
582
|
+
if (isProgressMessage(msg) && msg.data.type === "hook_progress") {
|
|
583
|
+
const data = msg.data;
|
|
584
|
+
const eventName = String(data.hook_event_name ?? data.hookEvent ?? "unknown");
|
|
585
|
+
const hookSpecific = data.hookSpecificOutput;
|
|
586
|
+
const ev = {
|
|
587
|
+
eventName,
|
|
588
|
+
source: data.source !== void 0 ? String(data.source) : void 0,
|
|
589
|
+
toolName: data.tool_name !== void 0 ? String(data.tool_name) : void 0,
|
|
590
|
+
toolUseId: data.tool_use_id !== void 0 ? String(data.tool_use_id) : void 0,
|
|
591
|
+
command: data.command !== void 0 ? String(data.command) : void 0,
|
|
592
|
+
output: data.output !== void 0 ? String(data.output) : void 0,
|
|
593
|
+
stderr: data.stderr !== void 0 ? String(data.stderr) : void 0,
|
|
594
|
+
exitCode: data.exit_code !== void 0 ? Number(data.exit_code) : void 0,
|
|
595
|
+
decision: data.decision !== void 0 ? String(data.decision) : void 0,
|
|
596
|
+
durationMs: data.duration_ms !== void 0 ? Number(data.duration_ms) : void 0,
|
|
597
|
+
updatedToolOutput: hookSpecific?.updatedToolOutput !== void 0 ? String(hookSpecific.updatedToolOutput) : void 0,
|
|
598
|
+
sessionTitle: hookSpecific?.sessionTitle !== void 0 ? String(hookSpecific.sessionTitle) : void 0,
|
|
599
|
+
worktreePath: hookSpecific?.worktreePath !== void 0 ? String(hookSpecific.worktreePath) : void 0,
|
|
600
|
+
timestamp: msg.timestamp ?? ""
|
|
601
|
+
};
|
|
602
|
+
if (current) {
|
|
603
|
+
const last = current.contentBlocks[current.contentBlocks.length - 1];
|
|
604
|
+
if (last && last.kind === "hook_event") {
|
|
605
|
+
last.events.push(ev);
|
|
606
|
+
} else {
|
|
607
|
+
current.contentBlocks.push({ kind: "hook_event", events: [ev], timestamp: msg.timestamp });
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
449
612
|
if (isSystemMessage(msg) && msg.subtype === "turn_duration" && current) {
|
|
450
613
|
current.durationMs = msg.durationMs ?? null;
|
|
451
614
|
continue;
|
|
@@ -456,58 +619,84 @@ function buildTurns(messages) {
|
|
|
456
619
|
}
|
|
457
620
|
|
|
458
621
|
// src/lib/pricingTiers.ts
|
|
459
|
-
var EXTENDED_CONTEXT_THRESHOLD = 2e5;
|
|
460
622
|
var TIER_HAIKU_35 = { input: 0.8, output: 4, cacheWrite: 1, cacheRead: 0.08, webSearch: 0.01 };
|
|
461
623
|
var TIER_HAIKU_45 = { input: 1, output: 5, cacheWrite: 1.25, cacheRead: 0.1, webSearch: 0.01 };
|
|
462
|
-
var
|
|
463
|
-
var
|
|
624
|
+
var TIER_SONNET = { input: 3, output: 15, cacheWrite: 3.75, cacheRead: 0.3, webSearch: 0.01 };
|
|
625
|
+
var TIER_OPUS_MID = { input: 5, output: 25, cacheWrite: 6.25, cacheRead: 0.5, webSearch: 0.01 };
|
|
626
|
+
var TIER_FRONTIER = { input: 10, output: 50, cacheWrite: 12.5, cacheRead: 1, webSearch: 0.01 };
|
|
464
627
|
var TIER_OPUS_LEGACY = { input: 15, output: 75, cacheWrite: 18.75, cacheRead: 1.5, webSearch: 0.01 };
|
|
465
|
-
var
|
|
466
|
-
var
|
|
628
|
+
var TIER_GPT_56_SOL = { input: 5, output: 30, cacheWrite: 6.25, cacheRead: 0.5, webSearch: 0.01 };
|
|
629
|
+
var TIER_GPT_56_TERRA = { input: 2.5, output: 15, cacheWrite: 3.125, cacheRead: 0.25, webSearch: 0.01 };
|
|
630
|
+
var TIER_GPT_56_LUNA = { input: 1, output: 6, cacheWrite: 1.25, cacheRead: 0.1, webSearch: 0.01 };
|
|
631
|
+
var TIER_GPT_55 = { input: 5, output: 30, cacheWrite: 5, cacheRead: 0.5, webSearch: 0.01 };
|
|
632
|
+
var TIER_GPT_54 = { input: 2.5, output: 15, cacheWrite: 2.5, cacheRead: 0.25, webSearch: 0.01 };
|
|
633
|
+
var TIER_GPT_54_MINI = { input: 0.75, output: 4.5, cacheWrite: 0.75, cacheRead: 0.075, webSearch: 0.01 };
|
|
634
|
+
var TIER_FAST = { input: 30, output: 150, cacheWrite: 37.5, cacheRead: 3, webSearch: 0.01 };
|
|
467
635
|
var MODEL_TIERS = [
|
|
636
|
+
// OpenAI GPT models. Keep variants before their shared family prefixes.
|
|
637
|
+
{ match: "gpt-5.6-sol", tier: TIER_GPT_56_SOL },
|
|
638
|
+
{ match: "gpt-5.6-terra", tier: TIER_GPT_56_TERRA },
|
|
639
|
+
{ match: "gpt-5.6-luna", tier: TIER_GPT_56_LUNA },
|
|
640
|
+
{ match: "gpt-5.6", tier: TIER_GPT_56_SOL },
|
|
641
|
+
{ match: "gpt-5.4-mini", tier: TIER_GPT_54_MINI },
|
|
642
|
+
{ match: "gpt-5.5", tier: TIER_GPT_55 },
|
|
643
|
+
{ match: "gpt-5.4", tier: TIER_GPT_54 },
|
|
644
|
+
// Frontier (Fable 5 / Mythos 5 / Opus 4.8)
|
|
645
|
+
{ match: "fable-5", tier: TIER_FRONTIER },
|
|
646
|
+
{ match: "mythos-5", tier: TIER_FRONTIER },
|
|
647
|
+
{ match: "opus-4-8", tier: TIER_FRONTIER },
|
|
468
648
|
// Haiku
|
|
469
649
|
{ match: "haiku-4-5", tier: TIER_HAIKU_45 },
|
|
470
650
|
{ match: "haiku-4-0", tier: TIER_HAIKU_35 },
|
|
471
651
|
{ match: "3-5-haiku", tier: TIER_HAIKU_35 },
|
|
472
|
-
// Sonnet
|
|
473
|
-
{ match: "sonnet-4-
|
|
474
|
-
{ match: "sonnet-4-
|
|
475
|
-
|
|
476
|
-
{ match: "sonnet-4-0", tier:
|
|
477
|
-
{ match: "3-7-sonnet", tier:
|
|
478
|
-
{ match: "3-5-sonnet", tier:
|
|
479
|
-
// Opus
|
|
480
|
-
{ match: "opus-4-
|
|
481
|
-
{ match: "opus-4-
|
|
652
|
+
// Sonnet (3.5 through 4.6 — all one tier in current CC)
|
|
653
|
+
{ match: "sonnet-4-7", tier: TIER_SONNET },
|
|
654
|
+
{ match: "sonnet-4-6", tier: TIER_SONNET },
|
|
655
|
+
{ match: "sonnet-4-5", tier: TIER_SONNET },
|
|
656
|
+
{ match: "sonnet-4-0", tier: TIER_SONNET },
|
|
657
|
+
{ match: "3-7-sonnet", tier: TIER_SONNET },
|
|
658
|
+
{ match: "3-5-sonnet", tier: TIER_SONNET },
|
|
659
|
+
// Opus 4.5–4.7
|
|
660
|
+
{ match: "opus-4-7", tier: TIER_OPUS_MID },
|
|
661
|
+
{ match: "opus-4-6", tier: TIER_OPUS_MID },
|
|
662
|
+
{ match: "opus-4-5", tier: TIER_OPUS_MID },
|
|
482
663
|
// Opus legacy (4.0, 4.1)
|
|
483
664
|
{ match: "opus-4-1", tier: TIER_OPUS_LEGACY },
|
|
484
665
|
{ match: "opus-4-0", tier: TIER_OPUS_LEGACY }
|
|
485
666
|
];
|
|
486
|
-
var
|
|
487
|
-
var
|
|
667
|
+
var FAST_MODE_MODELS = ["opus-4-6", "opus-4-7"];
|
|
668
|
+
var DEFAULT_TIER = TIER_OPUS_MID;
|
|
488
669
|
var FAMILY_FALLBACKS = [
|
|
670
|
+
{ match: "fable", tier: TIER_FRONTIER },
|
|
671
|
+
{ match: "mythos", tier: TIER_FRONTIER },
|
|
489
672
|
{ match: "haiku", tier: TIER_HAIKU_45 },
|
|
490
|
-
{ match: "sonnet", tier:
|
|
491
|
-
{ match: "opus", tier:
|
|
673
|
+
{ match: "sonnet", tier: TIER_SONNET },
|
|
674
|
+
{ match: "opus", tier: TIER_OPUS_MID }
|
|
492
675
|
];
|
|
493
|
-
function resolveTier(model,
|
|
494
|
-
|
|
676
|
+
function resolveTier(model, speed) {
|
|
677
|
+
if (speed === "fast" && FAST_MODE_MODELS.some((m) => model.includes(m))) {
|
|
678
|
+
return TIER_FAST;
|
|
679
|
+
}
|
|
495
680
|
for (const entry of MODEL_TIERS) {
|
|
496
|
-
if (model.includes(entry.match))
|
|
497
|
-
return isExtended && entry.extendedTier ? entry.extendedTier : entry.tier;
|
|
498
|
-
}
|
|
681
|
+
if (model.includes(entry.match)) return entry.tier;
|
|
499
682
|
}
|
|
500
683
|
for (const entry of FAMILY_FALLBACKS) {
|
|
501
684
|
if (model.includes(entry.match)) return entry.tier;
|
|
502
685
|
}
|
|
503
|
-
return
|
|
686
|
+
return DEFAULT_TIER;
|
|
687
|
+
}
|
|
688
|
+
function hasKnownPricing(model) {
|
|
689
|
+
if (!model) return true;
|
|
690
|
+
const normalized = model.toLowerCase();
|
|
691
|
+
if (!normalized.startsWith("gpt-")) return true;
|
|
692
|
+
return MODEL_TIERS.some((entry) => normalized.includes(entry.match));
|
|
504
693
|
}
|
|
505
694
|
|
|
506
695
|
// src/lib/token-costs.ts
|
|
507
696
|
var CHARS_PER_TOKEN = 4;
|
|
508
697
|
function calculateCost(c) {
|
|
509
|
-
|
|
510
|
-
const p = resolveTier(c.model ?? "",
|
|
698
|
+
if (!hasKnownPricing(c.model)) return Number.NaN;
|
|
699
|
+
const p = resolveTier(c.model ?? "", c.speed);
|
|
511
700
|
return c.inputTokens / 1e6 * p.input + c.outputTokens / 1e6 * p.output + c.cacheWriteTokens / 1e6 * p.cacheWrite + c.cacheReadTokens / 1e6 * p.cacheRead + (c.webSearchRequests ?? 0) * p.webSearch;
|
|
512
701
|
}
|
|
513
702
|
function charsToTokens(chars) {
|
|
@@ -545,7 +734,8 @@ function calculateTurnCostEstimated(turn) {
|
|
|
545
734
|
inputTokens: u.input_tokens,
|
|
546
735
|
outputTokens: estimateTotalOutputTokens(turn),
|
|
547
736
|
cacheWriteTokens: u.cache_creation_input_tokens ?? 0,
|
|
548
|
-
cacheReadTokens: u.cache_read_input_tokens ?? 0
|
|
737
|
+
cacheReadTokens: u.cache_read_input_tokens ?? 0,
|
|
738
|
+
speed: u.speed
|
|
549
739
|
});
|
|
550
740
|
}
|
|
551
741
|
function calculateSubAgentCostEstimated(sa) {
|
|
@@ -556,7 +746,8 @@ function calculateSubAgentCostEstimated(sa) {
|
|
|
556
746
|
inputTokens: u.input_tokens,
|
|
557
747
|
outputTokens: estimateSubAgentOutput(sa),
|
|
558
748
|
cacheWriteTokens: u.cache_creation_input_tokens ?? 0,
|
|
559
|
-
cacheReadTokens: u.cache_read_input_tokens ?? 0
|
|
749
|
+
cacheReadTokens: u.cache_read_input_tokens ?? 0,
|
|
750
|
+
speed: u.speed
|
|
560
751
|
});
|
|
561
752
|
}
|
|
562
753
|
|
|
@@ -576,6 +767,19 @@ function addUsageToStats(stats, usage, estimatedOutput, cost) {
|
|
|
576
767
|
stats.totalCacheReadTokens += usage.cache_read_input_tokens ?? 0;
|
|
577
768
|
stats.totalCostUSD += cost;
|
|
578
769
|
}
|
|
770
|
+
function createEmptySessionStats(turnCount) {
|
|
771
|
+
return {
|
|
772
|
+
totalInputTokens: 0,
|
|
773
|
+
totalOutputTokens: 0,
|
|
774
|
+
totalCacheCreationTokens: 0,
|
|
775
|
+
totalCacheReadTokens: 0,
|
|
776
|
+
totalCostUSD: 0,
|
|
777
|
+
toolCallCounts: {},
|
|
778
|
+
errorCount: 0,
|
|
779
|
+
totalDurationMs: 0,
|
|
780
|
+
turnCount
|
|
781
|
+
};
|
|
782
|
+
}
|
|
579
783
|
function computeStats(turns) {
|
|
580
784
|
const stats = {
|
|
581
785
|
totalInputTokens: 0,
|
|
@@ -604,8 +808,271 @@ function computeStats(turns) {
|
|
|
604
808
|
return stats;
|
|
605
809
|
}
|
|
606
810
|
|
|
811
|
+
// src/lib/codex-patches.ts
|
|
812
|
+
var JS_SIMPLE_ESCAPES = {
|
|
813
|
+
"0": "\0",
|
|
814
|
+
b: "\b",
|
|
815
|
+
f: "\f",
|
|
816
|
+
n: "\n",
|
|
817
|
+
r: "\r",
|
|
818
|
+
t: " ",
|
|
819
|
+
v: "\v"
|
|
820
|
+
};
|
|
821
|
+
function readJsStringLiteral(source, startIndex) {
|
|
822
|
+
const quote = source[startIndex];
|
|
823
|
+
if (quote !== '"' && quote !== "'" && quote !== "`") return null;
|
|
824
|
+
let value = "";
|
|
825
|
+
let index = startIndex + 1;
|
|
826
|
+
while (index < source.length) {
|
|
827
|
+
const char = source[index];
|
|
828
|
+
if (char === quote) return { endIndex: index + 1, value };
|
|
829
|
+
if (char !== "\\") {
|
|
830
|
+
value += char;
|
|
831
|
+
index++;
|
|
832
|
+
continue;
|
|
833
|
+
}
|
|
834
|
+
index++;
|
|
835
|
+
if (index >= source.length) return null;
|
|
836
|
+
const escaped = source[index];
|
|
837
|
+
if (escaped in JS_SIMPLE_ESCAPES) {
|
|
838
|
+
value += JS_SIMPLE_ESCAPES[escaped];
|
|
839
|
+
index++;
|
|
840
|
+
continue;
|
|
841
|
+
}
|
|
842
|
+
if (escaped === "\n") {
|
|
843
|
+
index++;
|
|
844
|
+
continue;
|
|
845
|
+
}
|
|
846
|
+
if (escaped === "\r") {
|
|
847
|
+
index += source[index + 1] === "\n" ? 2 : 1;
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
if (escaped === "x" && /^[0-9a-fA-F]{2}$/.test(source.slice(index + 1, index + 3))) {
|
|
851
|
+
value += String.fromCharCode(Number.parseInt(source.slice(index + 1, index + 3), 16));
|
|
852
|
+
index += 3;
|
|
853
|
+
continue;
|
|
854
|
+
}
|
|
855
|
+
if (escaped === "u") {
|
|
856
|
+
const braced = source.slice(index + 1).match(/^\{([0-9a-fA-F]+)\}/);
|
|
857
|
+
if (braced) {
|
|
858
|
+
const codePoint = Number.parseInt(braced[1], 16);
|
|
859
|
+
if (Number.isFinite(codePoint) && codePoint <= 1114111) {
|
|
860
|
+
value += String.fromCodePoint(codePoint);
|
|
861
|
+
index += braced[0].length + 1;
|
|
862
|
+
continue;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
const hex = source.slice(index + 1, index + 5);
|
|
866
|
+
if (/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
867
|
+
value += String.fromCharCode(Number.parseInt(hex, 16));
|
|
868
|
+
index += 5;
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
value += escaped;
|
|
873
|
+
index++;
|
|
874
|
+
}
|
|
875
|
+
return null;
|
|
876
|
+
}
|
|
877
|
+
function isApplyPatchText(value) {
|
|
878
|
+
return value.includes("*** Begin Patch") && value.includes("*** End Patch");
|
|
879
|
+
}
|
|
880
|
+
function extractApplyPatchInputs(execSource) {
|
|
881
|
+
if (!execSource.includes("apply_patch") || !execSource.includes("*** Begin Patch")) return [];
|
|
882
|
+
const assignedPatches = /* @__PURE__ */ new Map();
|
|
883
|
+
for (let index = 0; index < execSource.length; index++) {
|
|
884
|
+
const literal = readJsStringLiteral(execSource, index);
|
|
885
|
+
if (!literal) continue;
|
|
886
|
+
if (isApplyPatchText(literal.value)) {
|
|
887
|
+
const assignment = execSource.slice(0, index).match(/(?:^|[;\n])\s*(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*$/);
|
|
888
|
+
if (assignment) assignedPatches.set(assignment[1], literal.value);
|
|
889
|
+
}
|
|
890
|
+
index = literal.endIndex - 1;
|
|
891
|
+
}
|
|
892
|
+
const patches = [];
|
|
893
|
+
const seen = /* @__PURE__ */ new Set();
|
|
894
|
+
const applyCall = /\btools\s*\.\s*apply_patch\s*\(/g;
|
|
895
|
+
let match;
|
|
896
|
+
while ((match = applyCall.exec(execSource)) !== null) {
|
|
897
|
+
let argumentIndex = match.index + match[0].length;
|
|
898
|
+
while (/\s/.test(execSource[argumentIndex] ?? "")) argumentIndex++;
|
|
899
|
+
const literal = readJsStringLiteral(execSource, argumentIndex);
|
|
900
|
+
const identifier = literal ? null : execSource.slice(argumentIndex).match(/^([A-Za-z_$][\w$]*)/);
|
|
901
|
+
const patch = literal?.value ?? (identifier ? assignedPatches.get(identifier[1]) : void 0);
|
|
902
|
+
if (!patch || !isApplyPatchText(patch) || seen.has(patch)) continue;
|
|
903
|
+
seen.add(patch);
|
|
904
|
+
patches.push(patch);
|
|
905
|
+
}
|
|
906
|
+
return patches;
|
|
907
|
+
}
|
|
908
|
+
function resolveCodexPatchPath(filePath, cwd) {
|
|
909
|
+
if (!cwd || filePath.startsWith("/") || /^[A-Za-z]:[\\/]/.test(filePath)) return filePath;
|
|
910
|
+
return `${cwd.replace(/[\\/]+$/, "")}/${filePath.replace(/^\.\//, "")}`;
|
|
911
|
+
}
|
|
912
|
+
function parseApplyPatch(patchText, callId, timestamp, cwd = "") {
|
|
913
|
+
const toolCalls = [];
|
|
914
|
+
const filePattern = /^\*\*\*\s+(Update File|Add File|Delete File):\s*(.+)$/gm;
|
|
915
|
+
const sections = [];
|
|
916
|
+
let match;
|
|
917
|
+
while ((match = filePattern.exec(patchText)) !== null) {
|
|
918
|
+
sections.push({
|
|
919
|
+
action: match[1],
|
|
920
|
+
filePath: match[2].trim(),
|
|
921
|
+
headerIdx: match.index,
|
|
922
|
+
startIdx: match.index + match[0].length
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
for (let i = 0; i < sections.length; i++) {
|
|
926
|
+
const section = sections[i];
|
|
927
|
+
const endIdx = i + 1 < sections.length ? sections[i + 1].headerIdx : patchText.length;
|
|
928
|
+
const body = patchText.slice(section.startIdx, endIdx);
|
|
929
|
+
const movedTo = body.match(/^\*\*\*\s+Move to:\s*(.+)$/m)?.[1]?.trim();
|
|
930
|
+
const sourcePath = resolveCodexPatchPath(section.filePath, cwd);
|
|
931
|
+
const filePath = resolveCodexPatchPath(movedTo || section.filePath, cwd);
|
|
932
|
+
const hunkBodies = body.split(/^@@.*$/m).filter((s) => s.trim());
|
|
933
|
+
const oldLines = [];
|
|
934
|
+
const newLines = [];
|
|
935
|
+
for (const hunk of hunkBodies) {
|
|
936
|
+
for (const line of hunk.split("\n")) {
|
|
937
|
+
if (line.startsWith("-")) {
|
|
938
|
+
oldLines.push(line.slice(1));
|
|
939
|
+
} else if (line.startsWith("+")) {
|
|
940
|
+
newLines.push(line.slice(1));
|
|
941
|
+
} else if (line.startsWith(" ")) {
|
|
942
|
+
oldLines.push(line.slice(1));
|
|
943
|
+
newLines.push(line.slice(1));
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
const fileId = sections.length > 1 ? `${callId}:file-${i}` : callId;
|
|
948
|
+
if (section.action === "Add File") {
|
|
949
|
+
toolCalls.push({
|
|
950
|
+
id: fileId,
|
|
951
|
+
name: "Write",
|
|
952
|
+
input: { file_path: filePath, content: newLines.join("\n") },
|
|
953
|
+
result: null,
|
|
954
|
+
isError: false,
|
|
955
|
+
timestamp
|
|
956
|
+
});
|
|
957
|
+
} else if (section.action === "Delete File") {
|
|
958
|
+
toolCalls.push({
|
|
959
|
+
id: fileId,
|
|
960
|
+
name: "Edit",
|
|
961
|
+
input: { file_path: filePath, old_string: oldLines.join("\n"), new_string: "" },
|
|
962
|
+
result: null,
|
|
963
|
+
isError: false,
|
|
964
|
+
timestamp
|
|
965
|
+
});
|
|
966
|
+
} else {
|
|
967
|
+
toolCalls.push({
|
|
968
|
+
id: fileId,
|
|
969
|
+
name: "Edit",
|
|
970
|
+
input: {
|
|
971
|
+
file_path: filePath,
|
|
972
|
+
...movedTo ? { old_path: sourcePath } : {},
|
|
973
|
+
old_string: oldLines.join("\n"),
|
|
974
|
+
new_string: newLines.join("\n")
|
|
975
|
+
},
|
|
976
|
+
result: null,
|
|
977
|
+
isError: false,
|
|
978
|
+
timestamp
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
return toolCalls;
|
|
983
|
+
}
|
|
984
|
+
function parseCodexToolPatches(toolName, rawInput, callId, timestamp, cwd = "") {
|
|
985
|
+
if (toolName === "apply_patch") {
|
|
986
|
+
return parseApplyPatch(rawInput, callId, timestamp, cwd);
|
|
987
|
+
}
|
|
988
|
+
return extractApplyPatchInputs(rawInput).flatMap(
|
|
989
|
+
(patch, patchIndex) => parseApplyPatch(patch, `${callId}:patch-${patchIndex}`, timestamp, cwd)
|
|
990
|
+
);
|
|
991
|
+
}
|
|
992
|
+
function findFailedNestedPatchCallIds(output, wrapperIsError, calls) {
|
|
993
|
+
if (!wrapperIsError || !/(?:apply[_ ]patch|invalid patch|invalid context|failed to (?:apply|find)|could not apply)/i.test(output)) {
|
|
994
|
+
return /* @__PURE__ */ new Set();
|
|
995
|
+
}
|
|
996
|
+
const groups = /* @__PURE__ */ new Map();
|
|
997
|
+
for (const call of calls) {
|
|
998
|
+
const patchIndex = call.id.match(/:patch-(\d+)(?::file-\d+)?$/)?.[1];
|
|
999
|
+
if (patchIndex === void 0) continue;
|
|
1000
|
+
const index = Number(patchIndex);
|
|
1001
|
+
const group = groups.get(index) ?? [];
|
|
1002
|
+
group.push(call);
|
|
1003
|
+
groups.set(index, group);
|
|
1004
|
+
}
|
|
1005
|
+
if (groups.size === 0) return new Set(calls.map((call) => call.id));
|
|
1006
|
+
const normalizedOutput = output.toLowerCase().replaceAll("\\", "/");
|
|
1007
|
+
const mentionedGroups = /* @__PURE__ */ new Set();
|
|
1008
|
+
for (const [index, group] of groups) {
|
|
1009
|
+
if (group.some((call) => {
|
|
1010
|
+
const filePath = String(call.input.file_path ?? "").toLowerCase().replaceAll("\\", "/");
|
|
1011
|
+
if (!filePath) return false;
|
|
1012
|
+
const segments = filePath.split("/").filter(Boolean);
|
|
1013
|
+
const suffix = segments.slice(-2).join("/");
|
|
1014
|
+
return normalizedOutput.includes(filePath) || suffix.includes("/") && normalizedOutput.includes(suffix);
|
|
1015
|
+
})) {
|
|
1016
|
+
mentionedGroups.add(index);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
for (const match of normalizedOutput.matchAll(/\bpatch\s*(?:#|-)?\s*(\d+)\b/g)) {
|
|
1020
|
+
const index = Number(match[1]);
|
|
1021
|
+
if (groups.has(index)) mentionedGroups.add(index);
|
|
1022
|
+
}
|
|
1023
|
+
if (mentionedGroups.size === 0) mentionedGroups.add(Math.max(...groups.keys()));
|
|
1024
|
+
const firstFailedIndex = Math.min(...mentionedGroups);
|
|
1025
|
+
for (const index of groups.keys()) {
|
|
1026
|
+
if (index >= firstFailedIndex) mentionedGroups.add(index);
|
|
1027
|
+
}
|
|
1028
|
+
return new Set(
|
|
1029
|
+
[...mentionedGroups].flatMap((index) => groups.get(index)?.map((call) => call.id) ?? [])
|
|
1030
|
+
);
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// src/lib/codex-tool-normalization.ts
|
|
1034
|
+
function isObject(value) {
|
|
1035
|
+
return typeof value === "object" && value !== null;
|
|
1036
|
+
}
|
|
1037
|
+
function inferToolError(output) {
|
|
1038
|
+
if (!output) return false;
|
|
1039
|
+
const exitMatch = output.match(/Process exited with code (\d+)/);
|
|
1040
|
+
if (exitMatch) return exitMatch[1] !== "0";
|
|
1041
|
+
const withoutSuccessSummaries = output.replace(/\b0\s+(?:fail(?:ed|ures?)?|errors?)\b/gi, "").replace(/\bno\s+(?:failures?|errors?)\b/gi, "");
|
|
1042
|
+
return /\b(error|failed|exception)\b/i.test(withoutSuccessSummaries);
|
|
1043
|
+
}
|
|
1044
|
+
function normalizePlanToTodos(input) {
|
|
1045
|
+
const plan = Array.isArray(input.plan) ? input.plan : [];
|
|
1046
|
+
const todos = plan.filter((item) => isObject(item)).map((item) => ({
|
|
1047
|
+
content: typeof item.step === "string" ? item.step : "",
|
|
1048
|
+
status: typeof item.status === "string" ? item.status : "pending",
|
|
1049
|
+
activeForm: typeof item.step === "string" ? item.step : ""
|
|
1050
|
+
}));
|
|
1051
|
+
return { todos };
|
|
1052
|
+
}
|
|
1053
|
+
function extractCustomToolOutputText(output) {
|
|
1054
|
+
if (typeof output === "string") return output;
|
|
1055
|
+
if (!Array.isArray(output)) return "";
|
|
1056
|
+
return output.filter((block) => isObject(block) && typeof block.text === "string").map((block) => block.text).join("");
|
|
1057
|
+
}
|
|
1058
|
+
function parseCustomToolOutput(output) {
|
|
1059
|
+
const rawOutput = extractCustomToolOutputText(output);
|
|
1060
|
+
if (!rawOutput) return { text: "", isError: false };
|
|
1061
|
+
try {
|
|
1062
|
+
const parsed = JSON.parse(rawOutput);
|
|
1063
|
+
const text = typeof parsed.output === "string" ? parsed.output : rawOutput;
|
|
1064
|
+
const meta = isObject(parsed.metadata) ? parsed.metadata : null;
|
|
1065
|
+
const isError = meta ? meta.exit_code !== 0 && meta.exit_code !== void 0 : inferToolError(text);
|
|
1066
|
+
return { text, isError };
|
|
1067
|
+
} catch {
|
|
1068
|
+
return { text: rawOutput, isError: inferToolError(rawOutput) };
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
|
|
607
1072
|
// src/lib/codex.ts
|
|
608
1073
|
var SKIP_PROMPT_PREFIXES = [
|
|
1074
|
+
"# AGENTS.md instructions for ",
|
|
1075
|
+
"<recommended_plugins>",
|
|
609
1076
|
"<environment_context>",
|
|
610
1077
|
"<permissions instructions>",
|
|
611
1078
|
"<collaboration_mode>",
|
|
@@ -621,17 +1088,120 @@ function safeParseLine(line) {
|
|
|
621
1088
|
return null;
|
|
622
1089
|
}
|
|
623
1090
|
}
|
|
624
|
-
function
|
|
1091
|
+
function isObject2(value) {
|
|
625
1092
|
return typeof value === "object" && value !== null;
|
|
626
1093
|
}
|
|
627
1094
|
function isCodexRecord(record) {
|
|
628
1095
|
if (!record || typeof record.type !== "string") return false;
|
|
629
|
-
return record.type === "session_meta" || record.type === "turn_context" || record.type === "event_msg" || record.type === "response_item";
|
|
1096
|
+
return record.type === "session_meta" || record.type === "turn_context" || record.type === "event_msg" || record.type === "response_item" || record.type === "inter_agent_communication_metadata" || record.type === "compacted" || record.type === "world_state";
|
|
630
1097
|
}
|
|
631
1098
|
function extractMessageText(payload, blockType) {
|
|
632
1099
|
const content = payload?.content;
|
|
633
1100
|
if (!Array.isArray(content)) return "";
|
|
634
|
-
return content.filter((block) =>
|
|
1101
|
+
return content.filter((block) => isObject2(block) && block.type === blockType && typeof block.text === "string").map((block) => block.text).join("\n").trim();
|
|
1102
|
+
}
|
|
1103
|
+
var CODEX_IMAGE_DATA_URL = /^data:(image\/(?:png|jpeg|gif|webp));base64,(.+)$/is;
|
|
1104
|
+
var CODEX_AUDIO_DATA_URL = /^data:(audio\/(?:wav|x-wav|mpeg|mp4|webm|ogg));base64,(.+)$/is;
|
|
1105
|
+
function parseImageDataUrl(value) {
|
|
1106
|
+
if (typeof value !== "string") return null;
|
|
1107
|
+
const match = CODEX_IMAGE_DATA_URL.exec(value);
|
|
1108
|
+
if (!match?.[1] || !match[2]) return null;
|
|
1109
|
+
return {
|
|
1110
|
+
type: "image",
|
|
1111
|
+
source: {
|
|
1112
|
+
type: "base64",
|
|
1113
|
+
media_type: match[1].toLowerCase(),
|
|
1114
|
+
data: match[2]
|
|
1115
|
+
}
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
function parseAudioDataUrl(value) {
|
|
1119
|
+
if (typeof value !== "string") return null;
|
|
1120
|
+
const match = CODEX_AUDIO_DATA_URL.exec(value);
|
|
1121
|
+
if (!match?.[1] || !match[2]) return null;
|
|
1122
|
+
return {
|
|
1123
|
+
type: "audio",
|
|
1124
|
+
source: {
|
|
1125
|
+
type: "base64",
|
|
1126
|
+
media_type: match[1].toLowerCase(),
|
|
1127
|
+
data: match[2]
|
|
1128
|
+
}
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
function extractEventMessageImages(payload) {
|
|
1132
|
+
if (!Array.isArray(payload.images)) return [];
|
|
1133
|
+
return payload.images.map(parseImageDataUrl).filter((image) => image !== null);
|
|
1134
|
+
}
|
|
1135
|
+
function extractEventMessageAudio(payload) {
|
|
1136
|
+
if (!Array.isArray(payload.audio)) return [];
|
|
1137
|
+
return payload.audio.map(parseAudioDataUrl).filter((audio) => audio !== null);
|
|
1138
|
+
}
|
|
1139
|
+
function extractResponseMessageImages(payload) {
|
|
1140
|
+
if (!Array.isArray(payload.content)) return [];
|
|
1141
|
+
return payload.content.filter((block) => isObject2(block) && block.type === "input_image").map((block) => parseImageDataUrl(block.image_url)).filter((image) => image !== null);
|
|
1142
|
+
}
|
|
1143
|
+
function extractResponseMessageAudio(payload) {
|
|
1144
|
+
if (!Array.isArray(payload.content)) return [];
|
|
1145
|
+
return payload.content.filter((block) => isObject2(block) && block.type === "input_audio").map((block) => parseAudioDataUrl(block.audio_url)).filter((audio) => audio !== null);
|
|
1146
|
+
}
|
|
1147
|
+
function buildCodexUserContent(message, images, audio = [], localImages = [], localAudio = []) {
|
|
1148
|
+
const attachmentSuffix = [
|
|
1149
|
+
...localImages.map((path) => `
|
|
1150
|
+
`),
|
|
1151
|
+
...localAudio.map((path) => `
|
|
1152
|
+
[audio attachment](<${path}>)`)
|
|
1153
|
+
].join("");
|
|
1154
|
+
const text = message + attachmentSuffix;
|
|
1155
|
+
if (images.length === 0 && audio.length === 0) return text;
|
|
1156
|
+
return [
|
|
1157
|
+
...images,
|
|
1158
|
+
...audio,
|
|
1159
|
+
...text ? [{ type: "text", text }] : []
|
|
1160
|
+
];
|
|
1161
|
+
}
|
|
1162
|
+
function normalizeFunctionName(rawName) {
|
|
1163
|
+
const leaf = rawName.split(/(?:__|[.:/])+/).filter(Boolean).at(-1) ?? rawName;
|
|
1164
|
+
const aliases = {
|
|
1165
|
+
spawnAgent: "spawn_agent",
|
|
1166
|
+
waitAgent: "wait_agent",
|
|
1167
|
+
sendMessage: "send_message",
|
|
1168
|
+
followupTask: "followup_task",
|
|
1169
|
+
listAgents: "list_agents",
|
|
1170
|
+
interruptAgent: "interrupt_agent"
|
|
1171
|
+
};
|
|
1172
|
+
const canonicalCollaborationNames = /* @__PURE__ */ new Set([
|
|
1173
|
+
"spawn_agent",
|
|
1174
|
+
"wait_agent",
|
|
1175
|
+
"send_message",
|
|
1176
|
+
"followup_task",
|
|
1177
|
+
"list_agents",
|
|
1178
|
+
"interrupt_agent"
|
|
1179
|
+
]);
|
|
1180
|
+
if (aliases[leaf]) return aliases[leaf];
|
|
1181
|
+
if (canonicalCollaborationNames.has(leaf)) return leaf;
|
|
1182
|
+
return rawName;
|
|
1183
|
+
}
|
|
1184
|
+
function extractInterAgentMessage(payload) {
|
|
1185
|
+
const rawText = extractMessageText(payload, "input_text");
|
|
1186
|
+
if (!rawText) return { messageType: null, text: "" };
|
|
1187
|
+
const headerEnd = rawText.match(/\r?\nPayload:\s*(?:\r?\n|$)/);
|
|
1188
|
+
if (!rawText.startsWith("Message Type:") || !headerEnd || headerEnd.index === void 0) {
|
|
1189
|
+
return { messageType: null, text: rawText.trim() };
|
|
1190
|
+
}
|
|
1191
|
+
const header = rawText.slice(0, headerEnd.index);
|
|
1192
|
+
const messageType = header.match(/^Message Type:\s*([^\r\n]+)/)?.[1]?.trim().toUpperCase() ?? null;
|
|
1193
|
+
const text = rawText.slice(headerEnd.index + headerEnd[0].length).trim();
|
|
1194
|
+
return { messageType, text };
|
|
1195
|
+
}
|
|
1196
|
+
function agentNameFromPath(agentPath) {
|
|
1197
|
+
const segments = agentPath.split("/").filter(Boolean);
|
|
1198
|
+
return segments.at(-1) ?? null;
|
|
1199
|
+
}
|
|
1200
|
+
function readableAgentPrompt(value) {
|
|
1201
|
+
if (typeof value !== "string") return "";
|
|
1202
|
+
const prompt = value.trim();
|
|
1203
|
+
if (/^gAAAAA[A-Za-z0-9_-]+$/.test(prompt)) return "";
|
|
1204
|
+
return prompt;
|
|
635
1205
|
}
|
|
636
1206
|
function normalizePromptText(text) {
|
|
637
1207
|
const trimmed = text.trim();
|
|
@@ -649,11 +1219,13 @@ function mergeTokenUsage2(existing, incoming) {
|
|
|
649
1219
|
};
|
|
650
1220
|
}
|
|
651
1221
|
function parseTokenUsage(value) {
|
|
652
|
-
if (!
|
|
653
|
-
const
|
|
1222
|
+
if (!isObject2(value)) return null;
|
|
1223
|
+
const reportedInputTokens = typeof value.input_tokens === "number" ? value.input_tokens : 0;
|
|
654
1224
|
const outputTokens = typeof value.output_tokens === "number" ? value.output_tokens : 0;
|
|
655
|
-
const cacheCreation = typeof value.cache_creation_input_tokens === "number" ? value.cache_creation_input_tokens : 0;
|
|
656
|
-
const
|
|
1225
|
+
const cacheCreation = typeof value.cache_write_input_tokens === "number" ? value.cache_write_input_tokens : typeof value.cache_creation_input_tokens === "number" ? value.cache_creation_input_tokens : 0;
|
|
1226
|
+
const nativeCachedInput = typeof value.cached_input_tokens === "number" ? Math.max(0, value.cached_input_tokens) : null;
|
|
1227
|
+
const cacheRead = nativeCachedInput ?? (typeof value.cache_read_input_tokens === "number" ? value.cache_read_input_tokens : 0);
|
|
1228
|
+
const inputTokens = nativeCachedInput === null ? reportedInputTokens : Math.max(0, reportedInputTokens - nativeCachedInput);
|
|
657
1229
|
if (inputTokens === 0 && outputTokens === 0 && cacheCreation === 0 && cacheRead === 0) return null;
|
|
658
1230
|
return {
|
|
659
1231
|
input_tokens: inputTokens,
|
|
@@ -694,7 +1266,7 @@ function appendToolCall(turn, toolCall, timestamp) {
|
|
|
694
1266
|
}
|
|
695
1267
|
function finalizeTurn(turns, current, lastTimestamp) {
|
|
696
1268
|
if (!current) return null;
|
|
697
|
-
const hasContent = current.userMessage !== null || current.assistantText.length > 0 || current.toolCalls.length > 0 || current.thinking.length > 0;
|
|
1269
|
+
const hasContent = current.userMessage !== null || current.assistantText.length > 0 || current.toolCalls.length > 0 || current.thinking.length > 0 || current.subAgentActivity.length > 0;
|
|
698
1270
|
if (!hasContent) return null;
|
|
699
1271
|
if (current.timestamp && lastTimestamp) {
|
|
700
1272
|
const start = new Date(current.timestamp).getTime();
|
|
@@ -710,17 +1282,11 @@ function parseToolInput(argumentsText) {
|
|
|
710
1282
|
if (typeof argumentsText !== "string" || !argumentsText.trim()) return {};
|
|
711
1283
|
try {
|
|
712
1284
|
const parsed = JSON.parse(argumentsText);
|
|
713
|
-
return
|
|
1285
|
+
return isObject2(parsed) ? parsed : { value: parsed };
|
|
714
1286
|
} catch {
|
|
715
1287
|
return { raw: argumentsText };
|
|
716
1288
|
}
|
|
717
1289
|
}
|
|
718
|
-
function inferToolError(output) {
|
|
719
|
-
if (!output) return false;
|
|
720
|
-
const exitMatch = output.match(/Process exited with code (\d+)/);
|
|
721
|
-
if (exitMatch) return exitMatch[1] !== "0";
|
|
722
|
-
return /\b(error|failed|exception)\b/i.test(output);
|
|
723
|
-
}
|
|
724
1290
|
function createTurn(turnId, timestamp, model) {
|
|
725
1291
|
return {
|
|
726
1292
|
id: turnId || randomTurnId("codex-turn"),
|
|
@@ -737,10 +1303,10 @@ function createTurn(turnId, timestamp, model) {
|
|
|
737
1303
|
};
|
|
738
1304
|
}
|
|
739
1305
|
function extractPromptFromRecord(record) {
|
|
740
|
-
if (record.type === "event_msg" &&
|
|
1306
|
+
if (record.type === "event_msg" && isObject2(record.payload) && record.payload.type === "user_message" && typeof record.payload.message === "string") {
|
|
741
1307
|
return normalizePromptText(record.payload.message);
|
|
742
1308
|
}
|
|
743
|
-
if (record.type === "response_item" &&
|
|
1309
|
+
if (record.type === "response_item" && isObject2(record.payload) && record.payload.type === "message" && record.payload.role === "user") {
|
|
744
1310
|
return normalizePromptText(extractMessageText(record.payload, "input_text"));
|
|
745
1311
|
}
|
|
746
1312
|
return "";
|
|
@@ -757,14 +1323,17 @@ function extractMetadataFromRecords(records) {
|
|
|
757
1323
|
let timestamp = "";
|
|
758
1324
|
let lastTimestamp = "";
|
|
759
1325
|
let turnCount = 0;
|
|
1326
|
+
let isSubagent = false;
|
|
1327
|
+
let parentSessionId = null;
|
|
1328
|
+
let agentPath = "/root";
|
|
760
1329
|
let previousPrompt = "";
|
|
761
1330
|
for (const record of records) {
|
|
762
|
-
if (!
|
|
1331
|
+
if (!isObject2(record.payload)) continue;
|
|
763
1332
|
if (record.type === "session_meta") {
|
|
764
1333
|
sessionId ||= typeof record.payload.id === "string" ? record.payload.id : "";
|
|
765
1334
|
version ||= typeof record.payload.cli_version === "string" ? record.payload.cli_version : "";
|
|
766
1335
|
cwd ||= typeof record.payload.cwd === "string" ? record.payload.cwd : "";
|
|
767
|
-
if (!branchedFrom &&
|
|
1336
|
+
if (!branchedFrom && isObject2(record.payload.branchedFrom)) {
|
|
768
1337
|
const sourceId = typeof record.payload.branchedFrom.sessionId === "string" ? record.payload.branchedFrom.sessionId : "";
|
|
769
1338
|
if (sourceId) {
|
|
770
1339
|
branchedFrom = {
|
|
@@ -773,7 +1342,18 @@ function extractMetadataFromRecords(records) {
|
|
|
773
1342
|
};
|
|
774
1343
|
}
|
|
775
1344
|
}
|
|
776
|
-
const
|
|
1345
|
+
const source = isObject2(record.payload.source) ? record.payload.source : null;
|
|
1346
|
+
if (source && isObject2(source.subagent)) {
|
|
1347
|
+
isSubagent = true;
|
|
1348
|
+
const threadSpawn = isObject2(source.subagent.thread_spawn) ? source.subagent.thread_spawn : null;
|
|
1349
|
+
if (threadSpawn && typeof threadSpawn.agent_path === "string") {
|
|
1350
|
+
agentPath = threadSpawn.agent_path;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
if (typeof record.payload.forked_from_id === "string" && record.payload.forked_from_id) {
|
|
1354
|
+
parentSessionId = record.payload.forked_from_id;
|
|
1355
|
+
}
|
|
1356
|
+
const git = isObject2(record.payload.git) ? record.payload.git : null;
|
|
777
1357
|
gitBranch ||= git && typeof git.branch === "string" ? git.branch : "";
|
|
778
1358
|
}
|
|
779
1359
|
if (record.type === "turn_context") {
|
|
@@ -798,12 +1378,16 @@ function extractMetadataFromRecords(records) {
|
|
|
798
1378
|
cwd,
|
|
799
1379
|
model,
|
|
800
1380
|
slug: "",
|
|
1381
|
+
name: "",
|
|
801
1382
|
branchedFrom,
|
|
802
1383
|
firstUserMessage,
|
|
803
1384
|
lastUserMessage,
|
|
804
1385
|
timestamp,
|
|
805
1386
|
lastTimestamp,
|
|
806
|
-
turnCount
|
|
1387
|
+
turnCount,
|
|
1388
|
+
isSubagent,
|
|
1389
|
+
parentSessionId,
|
|
1390
|
+
agentPath
|
|
807
1391
|
};
|
|
808
1392
|
}
|
|
809
1393
|
function isCodexSessionText(jsonlText) {
|
|
@@ -818,18 +1402,99 @@ function extractCodexMetadataFromLines(lines) {
|
|
|
818
1402
|
const records = lines.map(safeParseLine).filter(isCodexRecord);
|
|
819
1403
|
return extractMetadataFromRecords(records);
|
|
820
1404
|
}
|
|
821
|
-
function parseCodexSession(jsonlText) {
|
|
1405
|
+
function parseCodexSession(jsonlText, options) {
|
|
822
1406
|
const records = jsonlText.split("\n").map((line) => line.trim()).filter(Boolean).map(safeParseLine).filter(isCodexRecord);
|
|
823
1407
|
const metadata = extractMetadataFromRecords(records);
|
|
824
1408
|
const turns = [];
|
|
825
1409
|
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
1410
|
+
const webSearchCalls = /* @__PURE__ */ new Map();
|
|
1411
|
+
const patchCallIds = /* @__PURE__ */ new Map();
|
|
1412
|
+
const spawnAgentCalls = /* @__PURE__ */ new Map();
|
|
1413
|
+
const agentRegistry = /* @__PURE__ */ new Map();
|
|
1414
|
+
const agentIdByPath = /* @__PURE__ */ new Map();
|
|
1415
|
+
const agentIdBySpawnCall = /* @__PURE__ */ new Map();
|
|
1416
|
+
const agentMessages = /* @__PURE__ */ new Map();
|
|
1417
|
+
function upsertAgentMessage(agentId, targetTurn, eventTimestamp, update = {}) {
|
|
1418
|
+
const info = agentRegistry.get(agentId);
|
|
1419
|
+
let entry = agentMessages.get(agentId);
|
|
1420
|
+
if (!entry) {
|
|
1421
|
+
const agentMessage2 = {
|
|
1422
|
+
agentId,
|
|
1423
|
+
parentToolUseId: info?.parentToolCallId || void 0,
|
|
1424
|
+
agentName: info?.nickname ?? (info?.agentPath ? agentNameFromPath(info.agentPath) : null),
|
|
1425
|
+
subagentType: info?.agentType ?? null,
|
|
1426
|
+
type: "assistant",
|
|
1427
|
+
content: null,
|
|
1428
|
+
toolCalls: [],
|
|
1429
|
+
thinking: [],
|
|
1430
|
+
text: [],
|
|
1431
|
+
timestamp: info?.timestamp ?? eventTimestamp,
|
|
1432
|
+
tokenUsage: null,
|
|
1433
|
+
model: info?.model ?? null,
|
|
1434
|
+
isBackground: false,
|
|
1435
|
+
prompt: info?.message || void 0,
|
|
1436
|
+
status: update.status ?? "running"
|
|
1437
|
+
};
|
|
1438
|
+
targetTurn.subAgentActivity.push(agentMessage2);
|
|
1439
|
+
const lastBlock = targetTurn.contentBlocks[targetTurn.contentBlocks.length - 1];
|
|
1440
|
+
if (lastBlock?.kind === "sub_agent") {
|
|
1441
|
+
lastBlock.messages.push(agentMessage2);
|
|
1442
|
+
} else {
|
|
1443
|
+
targetTurn.contentBlocks.push({ kind: "sub_agent", messages: [agentMessage2], timestamp: eventTimestamp });
|
|
1444
|
+
}
|
|
1445
|
+
entry = { message: agentMessage2, turn: targetTurn };
|
|
1446
|
+
agentMessages.set(agentId, entry);
|
|
1447
|
+
}
|
|
1448
|
+
const agentMessage = entry.message;
|
|
1449
|
+
if (info) {
|
|
1450
|
+
agentMessage.parentToolUseId = info.parentToolCallId || agentMessage.parentToolUseId;
|
|
1451
|
+
agentMessage.agentName = info.nickname ?? (info.agentPath ? agentNameFromPath(info.agentPath) : agentMessage.agentName);
|
|
1452
|
+
agentMessage.subagentType = info.agentType;
|
|
1453
|
+
agentMessage.model = info.model;
|
|
1454
|
+
agentMessage.prompt = info.message || agentMessage.prompt;
|
|
1455
|
+
}
|
|
1456
|
+
if (update.text && !agentMessage.text.includes(update.text)) {
|
|
1457
|
+
agentMessage.text.push(update.text);
|
|
1458
|
+
agentMessage.content = agentMessage.text.join("\n\n");
|
|
1459
|
+
}
|
|
1460
|
+
if (update.status) {
|
|
1461
|
+
const terminalStatuses = /* @__PURE__ */ new Set(["completed", "failed", "interrupted"]);
|
|
1462
|
+
if (!(update.status === "running" && agentMessage.status && terminalStatuses.has(agentMessage.status))) {
|
|
1463
|
+
agentMessage.status = update.status;
|
|
1464
|
+
}
|
|
1465
|
+
if (terminalStatuses.has(update.status) && agentMessage.timestamp && eventTimestamp) {
|
|
1466
|
+
const startedAt = new Date(agentMessage.timestamp).getTime();
|
|
1467
|
+
const endedAt = new Date(eventTimestamp).getTime();
|
|
1468
|
+
if (Number.isFinite(startedAt) && Number.isFinite(endedAt) && endedAt >= startedAt) {
|
|
1469
|
+
agentMessage.durationMs = endedAt - startedAt;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
return agentMessage;
|
|
1474
|
+
}
|
|
826
1475
|
let current = null;
|
|
827
1476
|
let currentTurnId = null;
|
|
828
1477
|
let currentModel = metadata.model || null;
|
|
829
1478
|
let lastTurnTimestamp = "";
|
|
1479
|
+
let pendingCompaction = null;
|
|
1480
|
+
function createActiveTurn(turnId, timestamp, model) {
|
|
1481
|
+
const turn = createTurn(turnId, timestamp, model);
|
|
1482
|
+
if (pendingCompaction) {
|
|
1483
|
+
turn.compactionSummary = pendingCompaction;
|
|
1484
|
+
pendingCompaction = null;
|
|
1485
|
+
}
|
|
1486
|
+
return turn;
|
|
1487
|
+
}
|
|
830
1488
|
for (const record of records) {
|
|
831
|
-
const payload =
|
|
1489
|
+
const payload = isObject2(record.payload) ? record.payload : void 0;
|
|
832
1490
|
const timestamp = record.timestamp ?? "";
|
|
1491
|
+
if (record.type === "compacted") {
|
|
1492
|
+
current = finalizeTurn(turns, current, lastTurnTimestamp);
|
|
1493
|
+
pendingCompaction = "Conversation compacted";
|
|
1494
|
+
lastTurnTimestamp = timestamp;
|
|
1495
|
+
continue;
|
|
1496
|
+
}
|
|
1497
|
+
if (record.type === "world_state") continue;
|
|
833
1498
|
if (record.type === "turn_context") {
|
|
834
1499
|
current = finalizeTurn(turns, current, lastTurnTimestamp);
|
|
835
1500
|
currentTurnId = typeof payload?.turn_id === "string" ? payload.turn_id : null;
|
|
@@ -841,24 +1506,93 @@ function parseCodexSession(jsonlText) {
|
|
|
841
1506
|
if (current && (current.assistantText.length > 0 || current.toolCalls.length > 0 || current.thinking.length > 0)) {
|
|
842
1507
|
current = finalizeTurn(turns, current, lastTurnTimestamp);
|
|
843
1508
|
}
|
|
844
|
-
current ??=
|
|
845
|
-
|
|
1509
|
+
current ??= createActiveTurn(currentTurnId, timestamp, currentModel);
|
|
1510
|
+
const localImages = (Array.isArray(payload.local_images) ? payload.local_images : []).filter(
|
|
1511
|
+
(p) => typeof p === "string" && p.length > 0
|
|
1512
|
+
);
|
|
1513
|
+
const localAudio = (Array.isArray(payload.local_audio) ? payload.local_audio : []).filter(
|
|
1514
|
+
(p) => typeof p === "string" && p.length > 0
|
|
1515
|
+
);
|
|
1516
|
+
current.userMessage = buildCodexUserContent(
|
|
1517
|
+
payload.message,
|
|
1518
|
+
extractEventMessageImages(payload),
|
|
1519
|
+
extractEventMessageAudio(payload),
|
|
1520
|
+
localImages,
|
|
1521
|
+
localAudio
|
|
1522
|
+
);
|
|
846
1523
|
current.timestamp = current.timestamp || timestamp;
|
|
847
1524
|
lastTurnTimestamp = timestamp;
|
|
848
1525
|
continue;
|
|
849
1526
|
}
|
|
850
|
-
current ??=
|
|
1527
|
+
current ??= createActiveTurn(currentTurnId, timestamp, currentModel);
|
|
851
1528
|
if (!current.model && currentModel) current.model = currentModel;
|
|
852
1529
|
if (!current.timestamp) current.timestamp = timestamp;
|
|
853
1530
|
if (timestamp) lastTurnTimestamp = timestamp;
|
|
854
1531
|
if (record.type === "event_msg" && payload?.type === "token_count") {
|
|
855
|
-
const info =
|
|
1532
|
+
const info = isObject2(payload.info) ? payload.info : null;
|
|
856
1533
|
const lastUsage = info ? parseTokenUsage(info.last_token_usage) : null;
|
|
857
1534
|
if (lastUsage) {
|
|
858
1535
|
current.tokenUsage = mergeTokenUsage2(current.tokenUsage, lastUsage);
|
|
859
1536
|
}
|
|
860
1537
|
continue;
|
|
861
1538
|
}
|
|
1539
|
+
if (record.type === "event_msg" && payload?.type === "web_search_end" && typeof payload.call_id === "string") {
|
|
1540
|
+
const input = {};
|
|
1541
|
+
if (typeof payload.query === "string" && payload.query) input.query = payload.query;
|
|
1542
|
+
if (isObject2(payload.action)) input.action = payload.action;
|
|
1543
|
+
const existing = webSearchCalls.get(payload.call_id);
|
|
1544
|
+
if (existing) {
|
|
1545
|
+
existing.input = { ...existing.input, ...input };
|
|
1546
|
+
existing.result = "completed";
|
|
1547
|
+
existing.isError = false;
|
|
1548
|
+
} else {
|
|
1549
|
+
const toolCall = {
|
|
1550
|
+
id: payload.call_id,
|
|
1551
|
+
name: "WebSearch",
|
|
1552
|
+
input,
|
|
1553
|
+
result: "completed",
|
|
1554
|
+
isError: false,
|
|
1555
|
+
timestamp
|
|
1556
|
+
};
|
|
1557
|
+
webSearchCalls.set(toolCall.id, toolCall);
|
|
1558
|
+
appendToolCall(current, toolCall, timestamp);
|
|
1559
|
+
}
|
|
1560
|
+
continue;
|
|
1561
|
+
}
|
|
1562
|
+
if (record.type === "event_msg" && payload?.type === "sub_agent_activity" && typeof payload.agent_thread_id === "string" && typeof payload.agent_path === "string") {
|
|
1563
|
+
const agentId = payload.agent_thread_id;
|
|
1564
|
+
const eventId = typeof payload.event_id === "string" ? payload.event_id : "";
|
|
1565
|
+
const kind = typeof payload.kind === "string" ? payload.kind : "started";
|
|
1566
|
+
const spawnInfo = eventId ? spawnAgentCalls.get(eventId) : void 0;
|
|
1567
|
+
const existingId = agentIdByPath.get(payload.agent_path);
|
|
1568
|
+
const existing = agentRegistry.get(agentId) ?? (existingId ? agentRegistry.get(existingId) : void 0);
|
|
1569
|
+
if (kind !== "started" && !existing && !spawnInfo) continue;
|
|
1570
|
+
if (existingId && existingId !== agentId && !agentMessages.has(agentId)) {
|
|
1571
|
+
const provisionalEntry = agentMessages.get(existingId);
|
|
1572
|
+
if (provisionalEntry) {
|
|
1573
|
+
provisionalEntry.message.agentId = agentId;
|
|
1574
|
+
agentMessages.delete(existingId);
|
|
1575
|
+
agentMessages.set(agentId, provisionalEntry);
|
|
1576
|
+
}
|
|
1577
|
+
agentRegistry.delete(existingId);
|
|
1578
|
+
}
|
|
1579
|
+
const info = {
|
|
1580
|
+
agentId,
|
|
1581
|
+
nickname: existing?.nickname ?? agentNameFromPath(payload.agent_path) ?? spawnInfo?.taskName ?? null,
|
|
1582
|
+
agentPath: payload.agent_path,
|
|
1583
|
+
message: existing?.message ?? spawnInfo?.message ?? "",
|
|
1584
|
+
model: existing?.model ?? spawnInfo?.model ?? null,
|
|
1585
|
+
agentType: existing?.agentType ?? spawnInfo?.agentType ?? null,
|
|
1586
|
+
timestamp: existing?.timestamp ?? (timestamp || spawnInfo?.timestamp || ""),
|
|
1587
|
+
parentToolCallId: existing?.parentToolCallId ?? eventId
|
|
1588
|
+
};
|
|
1589
|
+
agentRegistry.set(agentId, info);
|
|
1590
|
+
agentIdByPath.set(payload.agent_path, agentId);
|
|
1591
|
+
if (eventId && (kind === "started" || spawnInfo)) agentIdBySpawnCall.set(eventId, agentId);
|
|
1592
|
+
const status = kind === "interrupted" ? "interrupted" : "running";
|
|
1593
|
+
upsertAgentMessage(agentId, current, timestamp, { status });
|
|
1594
|
+
continue;
|
|
1595
|
+
}
|
|
862
1596
|
if (record.type !== "response_item" || !payload) continue;
|
|
863
1597
|
if (payload.type === "reasoning") {
|
|
864
1598
|
const summary = Array.isArray(payload.summary) ? payload.summary.filter((item) => typeof item === "string").join("\n") : "";
|
|
@@ -871,14 +1605,95 @@ function parseCodexSession(jsonlText) {
|
|
|
871
1605
|
}
|
|
872
1606
|
if (payload.type === "message" && payload.role === "user" && current.userMessage === null) {
|
|
873
1607
|
const text = normalizePromptText(extractMessageText(payload, "input_text"));
|
|
874
|
-
|
|
1608
|
+
const images = extractResponseMessageImages(payload);
|
|
1609
|
+
const audio = extractResponseMessageAudio(payload);
|
|
1610
|
+
if (text || images.length > 0 || audio.length > 0) {
|
|
1611
|
+
current.userMessage = buildCodexUserContent(text, images, audio);
|
|
1612
|
+
}
|
|
1613
|
+
continue;
|
|
1614
|
+
}
|
|
1615
|
+
if (payload.type === "web_search_call" && typeof payload.id === "string") {
|
|
1616
|
+
const input = {};
|
|
1617
|
+
if (isObject2(payload.action)) input.action = payload.action;
|
|
1618
|
+
const status = typeof payload.status === "string" ? payload.status : "completed";
|
|
1619
|
+
const existing = webSearchCalls.get(payload.id);
|
|
1620
|
+
if (existing) {
|
|
1621
|
+
existing.input = { ...existing.input, ...input };
|
|
1622
|
+
existing.result = status;
|
|
1623
|
+
existing.isError = status === "failed";
|
|
1624
|
+
} else {
|
|
1625
|
+
const toolCall = {
|
|
1626
|
+
id: payload.id,
|
|
1627
|
+
name: "WebSearch",
|
|
1628
|
+
input,
|
|
1629
|
+
result: status,
|
|
1630
|
+
isError: status === "failed",
|
|
1631
|
+
timestamp
|
|
1632
|
+
};
|
|
1633
|
+
webSearchCalls.set(toolCall.id, toolCall);
|
|
1634
|
+
appendToolCall(current, toolCall, timestamp);
|
|
1635
|
+
}
|
|
1636
|
+
continue;
|
|
1637
|
+
}
|
|
1638
|
+
if (payload.type === "agent_message" && typeof payload.author === "string") {
|
|
1639
|
+
const author = payload.author;
|
|
1640
|
+
const recipient = typeof payload.recipient === "string" ? payload.recipient : null;
|
|
1641
|
+
const interAgentMessage = extractInterAgentMessage(payload);
|
|
1642
|
+
if (interAgentMessage.messageType === "NEW_TASK") continue;
|
|
1643
|
+
const knownAgentId = agentIdByPath.get(author);
|
|
1644
|
+
const isPotentialChild = author.startsWith(`${metadata.agentPath}/`) && (recipient === null || recipient === metadata.agentPath);
|
|
1645
|
+
if (!knownAgentId && !isPotentialChild) continue;
|
|
1646
|
+
const agentId = knownAgentId ?? author;
|
|
1647
|
+
if (!knownAgentId) {
|
|
1648
|
+
agentRegistry.set(agentId, {
|
|
1649
|
+
agentId,
|
|
1650
|
+
nickname: agentNameFromPath(author),
|
|
1651
|
+
agentPath: author,
|
|
1652
|
+
message: "",
|
|
1653
|
+
model: null,
|
|
1654
|
+
agentType: null,
|
|
1655
|
+
timestamp,
|
|
1656
|
+
parentToolCallId: ""
|
|
1657
|
+
});
|
|
1658
|
+
agentIdByPath.set(author, agentId);
|
|
1659
|
+
}
|
|
1660
|
+
let status;
|
|
1661
|
+
if (interAgentMessage.messageType === "FINAL_ANSWER") status = "completed";
|
|
1662
|
+
else if (interAgentMessage.messageType === "ERROR" || interAgentMessage.messageType === "FAILED") status = "failed";
|
|
1663
|
+
else if (interAgentMessage.messageType === "INTERRUPTED") status = "interrupted";
|
|
1664
|
+
else if (interAgentMessage.messageType === "MESSAGE") status = "running";
|
|
1665
|
+
upsertAgentMessage(agentId, current, timestamp, {
|
|
1666
|
+
status,
|
|
1667
|
+
text: interAgentMessage.text || void 0
|
|
1668
|
+
});
|
|
875
1669
|
continue;
|
|
876
1670
|
}
|
|
877
1671
|
if (payload.type === "function_call" && typeof payload.call_id === "string") {
|
|
1672
|
+
const rawName = typeof payload.name === "string" ? payload.name : "tool";
|
|
1673
|
+
const functionName = normalizeFunctionName(rawName);
|
|
1674
|
+
const parsedInput = parseToolInput(payload.arguments);
|
|
1675
|
+
let name = functionName;
|
|
1676
|
+
let input = parsedInput;
|
|
1677
|
+
if (functionName === "exec_command") {
|
|
1678
|
+
name = "Bash";
|
|
1679
|
+
} else if (functionName === "update_plan") {
|
|
1680
|
+
name = "TodoWrite";
|
|
1681
|
+
input = normalizePlanToTodos(parsedInput);
|
|
1682
|
+
}
|
|
1683
|
+
if (functionName === "spawn_agent") {
|
|
1684
|
+
spawnAgentCalls.set(payload.call_id, {
|
|
1685
|
+
callId: payload.call_id,
|
|
1686
|
+
message: readableAgentPrompt(parsedInput.message),
|
|
1687
|
+
taskName: typeof parsedInput.task_name === "string" ? parsedInput.task_name : null,
|
|
1688
|
+
model: typeof parsedInput.model === "string" ? parsedInput.model : null,
|
|
1689
|
+
agentType: typeof parsedInput.agent_type === "string" ? parsedInput.agent_type : null,
|
|
1690
|
+
timestamp
|
|
1691
|
+
});
|
|
1692
|
+
}
|
|
878
1693
|
const toolCall = {
|
|
879
1694
|
id: payload.call_id,
|
|
880
|
-
name
|
|
881
|
-
input
|
|
1695
|
+
name,
|
|
1696
|
+
input,
|
|
882
1697
|
result: null,
|
|
883
1698
|
isError: false,
|
|
884
1699
|
timestamp
|
|
@@ -894,6 +1709,116 @@ function parseCodexSession(jsonlText) {
|
|
|
894
1709
|
toolCall.result = output;
|
|
895
1710
|
toolCall.isError = inferToolError(output);
|
|
896
1711
|
pendingToolCalls.delete(payload.call_id);
|
|
1712
|
+
if (toolCall.name === "spawn_agent" && output) {
|
|
1713
|
+
try {
|
|
1714
|
+
const result = JSON.parse(output);
|
|
1715
|
+
const agentId = typeof result.agent_id === "string" ? result.agent_id : agentIdBySpawnCall.get(toolCall.id) ?? "";
|
|
1716
|
+
const agentPath = typeof result.task_name === "string" ? result.task_name : null;
|
|
1717
|
+
const nickname = typeof result.nickname === "string" ? result.nickname : agentPath ? agentNameFromPath(agentPath) : null;
|
|
1718
|
+
if (agentId) {
|
|
1719
|
+
const spawnInfo = spawnAgentCalls.get(toolCall.id);
|
|
1720
|
+
const existing = agentRegistry.get(agentId);
|
|
1721
|
+
const info = {
|
|
1722
|
+
agentId,
|
|
1723
|
+
nickname: nickname ?? existing?.nickname ?? spawnInfo?.taskName ?? null,
|
|
1724
|
+
agentPath: agentPath ?? existing?.agentPath ?? null,
|
|
1725
|
+
message: existing?.message ?? spawnInfo?.message ?? "",
|
|
1726
|
+
model: existing?.model ?? spawnInfo?.model ?? null,
|
|
1727
|
+
agentType: existing?.agentType ?? spawnInfo?.agentType ?? null,
|
|
1728
|
+
timestamp: existing?.timestamp ?? spawnInfo?.timestamp ?? timestamp,
|
|
1729
|
+
parentToolCallId: existing?.parentToolCallId ?? toolCall.id
|
|
1730
|
+
};
|
|
1731
|
+
agentRegistry.set(agentId, info);
|
|
1732
|
+
agentIdBySpawnCall.set(toolCall.id, agentId);
|
|
1733
|
+
if (info.agentPath) agentIdByPath.set(info.agentPath, agentId);
|
|
1734
|
+
upsertAgentMessage(agentId, current, timestamp, { status: "running" });
|
|
1735
|
+
}
|
|
1736
|
+
} catch {
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
if (toolCall.name === "wait_agent" && output && current) {
|
|
1740
|
+
try {
|
|
1741
|
+
const result = JSON.parse(output);
|
|
1742
|
+
const statusMap = isObject2(result.status) ? result.status : {};
|
|
1743
|
+
for (const [agentId, status] of Object.entries(statusMap)) {
|
|
1744
|
+
const statusValue = isObject2(status) ? status : {};
|
|
1745
|
+
const completedText = typeof statusValue.completed === "string" ? statusValue.completed : "";
|
|
1746
|
+
const failedText = typeof statusValue.failed === "string" ? statusValue.failed : typeof statusValue.error === "string" ? statusValue.error : "";
|
|
1747
|
+
const interruptedText = typeof statusValue.interrupted === "string" ? statusValue.interrupted : "";
|
|
1748
|
+
const lifecycle = completedText ? "completed" : failedText ? "failed" : interruptedText ? "interrupted" : typeof statusValue.status === "string" ? statusValue.status : "running";
|
|
1749
|
+
if (!agentRegistry.has(agentId)) {
|
|
1750
|
+
agentRegistry.set(agentId, {
|
|
1751
|
+
agentId,
|
|
1752
|
+
nickname: null,
|
|
1753
|
+
agentPath: null,
|
|
1754
|
+
message: "",
|
|
1755
|
+
model: null,
|
|
1756
|
+
agentType: null,
|
|
1757
|
+
timestamp,
|
|
1758
|
+
parentToolCallId: ""
|
|
1759
|
+
});
|
|
1760
|
+
}
|
|
1761
|
+
upsertAgentMessage(agentId, current, timestamp, {
|
|
1762
|
+
status: lifecycle,
|
|
1763
|
+
text: completedText || failedText || interruptedText || void 0
|
|
1764
|
+
});
|
|
1765
|
+
}
|
|
1766
|
+
} catch {
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
continue;
|
|
1770
|
+
}
|
|
1771
|
+
if (payload.type === "custom_tool_call" && typeof payload.call_id === "string") {
|
|
1772
|
+
const name = typeof payload.name === "string" ? payload.name : "tool";
|
|
1773
|
+
const rawInput = typeof payload.input === "string" ? payload.input : "";
|
|
1774
|
+
const callId = payload.call_id;
|
|
1775
|
+
const perFileCalls = rawInput ? parseCodexToolPatches(name, rawInput, callId, timestamp, metadata.cwd) : [];
|
|
1776
|
+
if (name !== "apply_patch") {
|
|
1777
|
+
const toolCall = {
|
|
1778
|
+
id: callId,
|
|
1779
|
+
name: name === "exec_command" ? "Bash" : name,
|
|
1780
|
+
input: parseToolInput(rawInput),
|
|
1781
|
+
result: null,
|
|
1782
|
+
isError: false,
|
|
1783
|
+
timestamp
|
|
1784
|
+
};
|
|
1785
|
+
pendingToolCalls.set(toolCall.id, toolCall);
|
|
1786
|
+
appendToolCall(current, toolCall, timestamp);
|
|
1787
|
+
}
|
|
1788
|
+
for (const tc of perFileCalls) {
|
|
1789
|
+
pendingToolCalls.set(tc.id, tc);
|
|
1790
|
+
appendToolCall(current, tc, timestamp);
|
|
1791
|
+
}
|
|
1792
|
+
if (perFileCalls.length > 0) {
|
|
1793
|
+
patchCallIds.set(callId, {
|
|
1794
|
+
calls: perFileCalls,
|
|
1795
|
+
direct: name === "apply_patch"
|
|
1796
|
+
});
|
|
1797
|
+
}
|
|
1798
|
+
continue;
|
|
1799
|
+
}
|
|
1800
|
+
if (payload.type === "custom_tool_call_output" && typeof payload.call_id === "string") {
|
|
1801
|
+
const callId = payload.call_id;
|
|
1802
|
+
const { text, isError } = parseCustomToolOutput(payload.output);
|
|
1803
|
+
const patchCalls = patchCallIds.get(callId);
|
|
1804
|
+
if (patchCalls) {
|
|
1805
|
+
const failedIds = patchCalls.direct && isError ? new Set(patchCalls.calls.map((call) => call.id)) : findFailedNestedPatchCallIds(text, isError, patchCalls.calls);
|
|
1806
|
+
for (const patchCall of patchCalls.calls) {
|
|
1807
|
+
const tc = pendingToolCalls.get(patchCall.id);
|
|
1808
|
+
if (tc) {
|
|
1809
|
+
tc.result = text;
|
|
1810
|
+
tc.isError = failedIds.has(tc.id);
|
|
1811
|
+
pendingToolCalls.delete(tc.id);
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
patchCallIds.delete(callId);
|
|
1815
|
+
}
|
|
1816
|
+
const toolCall = pendingToolCalls.get(callId);
|
|
1817
|
+
if (toolCall) {
|
|
1818
|
+
toolCall.result = text;
|
|
1819
|
+
toolCall.isError = isError;
|
|
1820
|
+
pendingToolCalls.delete(callId);
|
|
1821
|
+
}
|
|
897
1822
|
continue;
|
|
898
1823
|
}
|
|
899
1824
|
}
|
|
@@ -904,19 +1829,17 @@ function parseCodexSession(jsonlText) {
|
|
|
904
1829
|
gitBranch: metadata.gitBranch,
|
|
905
1830
|
cwd: metadata.cwd,
|
|
906
1831
|
slug: metadata.slug,
|
|
1832
|
+
name: "",
|
|
907
1833
|
model: metadata.model,
|
|
908
1834
|
turns,
|
|
909
|
-
stats: computeStats(turns),
|
|
910
|
-
rawMessages: records,
|
|
1835
|
+
stats: options?.skipStats ? createEmptySessionStats(turns.length) : computeStats(turns),
|
|
1836
|
+
rawMessages: options?.skipStats ? [] : records,
|
|
911
1837
|
branchedFrom: metadata.branchedFrom,
|
|
912
1838
|
agentKind: "codex"
|
|
913
1839
|
};
|
|
914
1840
|
}
|
|
915
1841
|
|
|
916
1842
|
// src/lib/parser.ts
|
|
917
|
-
function isAssistantMessage2(msg) {
|
|
918
|
-
return msg.type === "assistant";
|
|
919
|
-
}
|
|
920
1843
|
function extractTextFromContent2(content) {
|
|
921
1844
|
if (typeof content === "string") return content;
|
|
922
1845
|
return content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
|
|
@@ -934,49 +1857,37 @@ function parseLines(jsonlText) {
|
|
|
934
1857
|
return messages;
|
|
935
1858
|
}
|
|
936
1859
|
function extractSessionMetadata(messages) {
|
|
937
|
-
const meta = { sessionId: "", version: "", gitBranch: "", cwd: "", slug: "", model: "", branchedFrom: void 0 };
|
|
1860
|
+
const meta = { sessionId: "", version: "", gitBranch: "", cwd: "", slug: "", name: "", model: "", branchedFrom: void 0 };
|
|
938
1861
|
for (const msg of messages) {
|
|
939
1862
|
if (msg.sessionId && !meta.sessionId) meta.sessionId = msg.sessionId;
|
|
940
1863
|
if (msg.version && !meta.version) meta.version = msg.version;
|
|
941
1864
|
if (msg.gitBranch && !meta.gitBranch) meta.gitBranch = msg.gitBranch;
|
|
942
1865
|
if (msg.cwd && !meta.cwd) meta.cwd = msg.cwd;
|
|
943
1866
|
if (msg.slug && !meta.slug) meta.slug = msg.slug;
|
|
1867
|
+
if (msg.name && !meta.name) meta.name = msg.name;
|
|
944
1868
|
if (msg.branchedFrom && !meta.branchedFrom) {
|
|
945
1869
|
meta.branchedFrom = msg.branchedFrom;
|
|
946
1870
|
}
|
|
947
|
-
if (
|
|
1871
|
+
if (isAssistantMessage(msg) && msg.message.model && !meta.model) {
|
|
948
1872
|
meta.model = msg.message.model;
|
|
949
1873
|
}
|
|
950
1874
|
if (meta.sessionId && meta.version && meta.gitBranch && meta.cwd && meta.model) break;
|
|
951
1875
|
}
|
|
952
1876
|
return meta;
|
|
953
1877
|
}
|
|
954
|
-
function
|
|
955
|
-
return {
|
|
956
|
-
totalInputTokens: 0,
|
|
957
|
-
totalOutputTokens: 0,
|
|
958
|
-
totalCacheCreationTokens: 0,
|
|
959
|
-
totalCacheReadTokens: 0,
|
|
960
|
-
totalCostUSD: 0,
|
|
961
|
-
toolCallCounts: {},
|
|
962
|
-
errorCount: 0,
|
|
963
|
-
totalDurationMs: 0,
|
|
964
|
-
turnCount
|
|
965
|
-
};
|
|
966
|
-
}
|
|
967
|
-
function parseSession(jsonlText, opts) {
|
|
1878
|
+
function parseSession(jsonlText, options) {
|
|
968
1879
|
if (isCodexSessionText(jsonlText)) {
|
|
969
|
-
return parseCodexSession(jsonlText);
|
|
1880
|
+
return parseCodexSession(jsonlText, options);
|
|
970
1881
|
}
|
|
971
1882
|
const rawMessages = parseLines(jsonlText);
|
|
972
1883
|
const metadata = extractSessionMetadata(rawMessages);
|
|
973
1884
|
const turns = buildTurns(rawMessages);
|
|
974
|
-
const stats =
|
|
1885
|
+
const stats = options?.skipStats ? createEmptySessionStats(turns.length) : computeStats(turns);
|
|
975
1886
|
return {
|
|
976
1887
|
...metadata,
|
|
977
1888
|
turns,
|
|
978
1889
|
stats,
|
|
979
|
-
rawMessages:
|
|
1890
|
+
rawMessages: options?.skipStats ? [] : rawMessages,
|
|
980
1891
|
agentKind: "claude"
|
|
981
1892
|
};
|
|
982
1893
|
}
|
|
@@ -1231,17 +2142,11 @@ var SearchIndex = class {
|
|
|
1231
2142
|
buildFull(projectsDir) {
|
|
1232
2143
|
this.projectsDir = projectsDir;
|
|
1233
2144
|
this.db.close();
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
(0, import_node_fs.unlinkSync)(this.dbPath + "-wal");
|
|
1240
|
-
} catch {
|
|
1241
|
-
}
|
|
1242
|
-
try {
|
|
1243
|
-
(0, import_node_fs.unlinkSync)(this.dbPath + "-shm");
|
|
1244
|
-
} catch {
|
|
2145
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
2146
|
+
try {
|
|
2147
|
+
(0, import_node_fs.unlinkSync)(this.dbPath + suffix);
|
|
2148
|
+
} catch {
|
|
2149
|
+
}
|
|
1245
2150
|
}
|
|
1246
2151
|
this.db = new Database(this.dbPath);
|
|
1247
2152
|
this.db.exec("PRAGMA journal_mode = WAL");
|
|
@@ -1513,7 +2418,8 @@ var import_node_os = require("node:os");
|
|
|
1513
2418
|
var dirs = {
|
|
1514
2419
|
PROJECTS_DIR: (0, import_node_path2.join)((0, import_node_os.homedir)(), ".claude", "projects"),
|
|
1515
2420
|
TEAMS_DIR: (0, import_node_path2.join)((0, import_node_os.homedir)(), ".claude", "teams"),
|
|
1516
|
-
TASKS_DIR: (0, import_node_path2.join)((0, import_node_os.homedir)(), ".claude", "tasks")
|
|
2421
|
+
TASKS_DIR: (0, import_node_path2.join)((0, import_node_os.homedir)(), ".claude", "tasks"),
|
|
2422
|
+
CODEX_SESSIONS_DIR: (0, import_node_path2.join)((0, import_node_os.homedir)(), ".codex", "sessions")
|
|
1517
2423
|
};
|
|
1518
2424
|
var DEFAULT_DB_PATH = (0, import_node_path2.join)((0, import_node_os.homedir)(), ".claude", "cogpit-memory", "search-index.db");
|
|
1519
2425
|
|
|
@@ -1980,6 +2886,8 @@ function extractUserMessageText(userMessage) {
|
|
|
1980
2886
|
for (const block of userMessage) {
|
|
1981
2887
|
if (block.type === "text") parts.push(block.text);
|
|
1982
2888
|
else if (block.type === "image") parts.push("[image attached]");
|
|
2889
|
+
else if (block.type === "document") parts.push("[document attached]");
|
|
2890
|
+
else if (block.type === "audio") parts.push("[audio attached]");
|
|
1983
2891
|
}
|
|
1984
2892
|
return parts.length > 0 ? parts.join("\n") : null;
|
|
1985
2893
|
}
|
|
@@ -2078,6 +2986,10 @@ function mapTurnToDetail(session, turnIndex) {
|
|
|
2078
2986
|
durationMs: turn.durationMs
|
|
2079
2987
|
};
|
|
2080
2988
|
}
|
|
2989
|
+
function mapToolCall(tc) {
|
|
2990
|
+
const { result, resultTruncated } = truncateResult(tc.result);
|
|
2991
|
+
return { id: tc.id, name: tc.name, input: tc.input, result, resultTruncated, isError: tc.isError };
|
|
2992
|
+
}
|
|
2081
2993
|
function mapContentBlock(block) {
|
|
2082
2994
|
switch (block.kind) {
|
|
2083
2995
|
case "thinking": {
|
|
@@ -2089,10 +3001,7 @@ function mapContentBlock(block) {
|
|
|
2089
3001
|
case "tool_calls":
|
|
2090
3002
|
return {
|
|
2091
3003
|
kind: "tool_calls",
|
|
2092
|
-
toolCalls: block.toolCalls.map(
|
|
2093
|
-
const { result, resultTruncated } = truncateResult(tc.result);
|
|
2094
|
-
return { id: tc.id, name: tc.name, input: tc.input, result, resultTruncated, isError: tc.isError };
|
|
2095
|
-
}),
|
|
3004
|
+
toolCalls: block.toolCalls.map(mapToolCall),
|
|
2096
3005
|
timestamp: block.timestamp ?? null
|
|
2097
3006
|
};
|
|
2098
3007
|
case "sub_agent":
|
|
@@ -2102,6 +3011,21 @@ function mapContentBlock(block) {
|
|
|
2102
3011
|
agents: block.messages.map(mapSubAgentDetail),
|
|
2103
3012
|
timestamp: block.timestamp ?? null
|
|
2104
3013
|
};
|
|
3014
|
+
case "queued_prompt":
|
|
3015
|
+
return { kind: "queued_prompt", content: block.content, timestamp: block.timestamp ?? null };
|
|
3016
|
+
case "hook_event":
|
|
3017
|
+
return { kind: "hook_event", events: block.events, timestamp: block.timestamp ?? null };
|
|
3018
|
+
case "plan_mode":
|
|
3019
|
+
return {
|
|
3020
|
+
kind: "plan_mode",
|
|
3021
|
+
plan: block.plan,
|
|
3022
|
+
planFilePath: block.planFilePath ?? null,
|
|
3023
|
+
status: block.status,
|
|
3024
|
+
toolCalls: block.toolCalls.map(mapToolCall),
|
|
3025
|
+
timestamp: block.timestamp ?? null
|
|
3026
|
+
};
|
|
3027
|
+
case "recap":
|
|
3028
|
+
return { kind: "recap", content: block.content, timestamp: block.timestamp ?? null };
|
|
2105
3029
|
}
|
|
2106
3030
|
}
|
|
2107
3031
|
async function findSubagentFile(parentJsonlPath, agentId) {
|
|
@@ -2224,7 +3148,6 @@ async function getAgentOverview(sessionId, agentId) {
|
|
|
2224
3148
|
async function getAgentTurnDetail(sessionId, agentId, turnIndex) {
|
|
2225
3149
|
const jsonlPath = await findJsonlPath(sessionId);
|
|
2226
3150
|
if (!jsonlPath) return { error: "Session not found" };
|
|
2227
|
-
const content = await (0, import_promises3.readFile)(jsonlPath, "utf-8");
|
|
2228
3151
|
const subagentFile = await findSubagentFile(jsonlPath, agentId);
|
|
2229
3152
|
if (!subagentFile) return { error: "Agent not found" };
|
|
2230
3153
|
const subagentContent = await (0, import_promises3.readFile)(subagentFile.filePath, "utf-8");
|
|
@@ -2236,7 +3159,6 @@ async function getAgentTurnDetail(sessionId, agentId, turnIndex) {
|
|
|
2236
3159
|
// src/commands/sessions.ts
|
|
2237
3160
|
var import_promises5 = require("node:fs/promises");
|
|
2238
3161
|
var import_node_path6 = require("node:path");
|
|
2239
|
-
var import_node_os3 = require("node:os");
|
|
2240
3162
|
|
|
2241
3163
|
// src/lib/metadata.ts
|
|
2242
3164
|
var import_promises4 = require("node:fs/promises");
|
|
@@ -2253,6 +3175,19 @@ function deriveSessionStatus(rawMessages) {
|
|
|
2253
3175
|
if (toolName) info.toolName = toolName;
|
|
2254
3176
|
return info;
|
|
2255
3177
|
}
|
|
3178
|
+
for (let i = rawMessages.length - 1; i >= 0; i--) {
|
|
3179
|
+
const msg = rawMessages[i];
|
|
3180
|
+
if (msg.type === "queue-operation") continue;
|
|
3181
|
+
if (msg.type === "progress") {
|
|
3182
|
+
const data = msg.data;
|
|
3183
|
+
if (data?.type === "hook_progress") {
|
|
3184
|
+
const decision = data.decision ?? data.hookSpecificOutput?.permissionDecision;
|
|
3185
|
+
if (decision === "defer") return result("deferred");
|
|
3186
|
+
}
|
|
3187
|
+
break;
|
|
3188
|
+
}
|
|
3189
|
+
break;
|
|
3190
|
+
}
|
|
2256
3191
|
for (let i = rawMessages.length - 1; i >= 0; i--) {
|
|
2257
3192
|
const msg = rawMessages[i];
|
|
2258
3193
|
if (msg.type === "queue-operation") {
|
|
@@ -2287,6 +3222,13 @@ function deriveSessionStatus(rawMessages) {
|
|
|
2287
3222
|
if (isMeta) continue;
|
|
2288
3223
|
return result("processing");
|
|
2289
3224
|
}
|
|
3225
|
+
if (msg.type === "system" && msg.subtype === "terminal_reason") {
|
|
3226
|
+
const reason = msg.reason;
|
|
3227
|
+
if (reason) {
|
|
3228
|
+
return { status: "completed", terminalReason: reason, pendingQueue: Math.max(0, pendingEnqueues) };
|
|
3229
|
+
}
|
|
3230
|
+
continue;
|
|
3231
|
+
}
|
|
2290
3232
|
if (msg.type === "summary") continue;
|
|
2291
3233
|
if (msg.type === "system" && msg.subtype === "compact_boundary") continue;
|
|
2292
3234
|
}
|
|
@@ -2512,7 +3454,23 @@ async function getSessionStatus(filePath) {
|
|
|
2512
3454
|
}
|
|
2513
3455
|
|
|
2514
3456
|
// src/commands/sessions.ts
|
|
2515
|
-
|
|
3457
|
+
function toSessionSummary(file, meta, status) {
|
|
3458
|
+
return {
|
|
3459
|
+
sessionId: meta.sessionId,
|
|
3460
|
+
filePath: file.path,
|
|
3461
|
+
timestamp: meta.timestamp,
|
|
3462
|
+
model: meta.model,
|
|
3463
|
+
cwd: meta.cwd,
|
|
3464
|
+
gitBranch: meta.gitBranch,
|
|
3465
|
+
slug: meta.slug,
|
|
3466
|
+
firstMessage: meta.firstUserMessage,
|
|
3467
|
+
lastMessage: meta.lastUserMessage,
|
|
3468
|
+
turnCount: meta.turnCount,
|
|
3469
|
+
status,
|
|
3470
|
+
mtime: file.mtimeMs,
|
|
3471
|
+
source: file.path.startsWith(dirs.CODEX_SESSIONS_DIR + "/") ? "codex" : "claude"
|
|
3472
|
+
};
|
|
3473
|
+
}
|
|
2516
3474
|
async function listCodexSessionFiles(cutoff) {
|
|
2517
3475
|
const walk = async (dir, depth) => {
|
|
2518
3476
|
if (depth > 4) return [];
|
|
@@ -2539,7 +3497,7 @@ async function listCodexSessionFiles(cutoff) {
|
|
|
2539
3497
|
}
|
|
2540
3498
|
return results;
|
|
2541
3499
|
};
|
|
2542
|
-
return walk(CODEX_SESSIONS_DIR, 0);
|
|
3500
|
+
return walk(dirs.CODEX_SESSIONS_DIR, 0);
|
|
2543
3501
|
}
|
|
2544
3502
|
async function listSessions(opts = {}) {
|
|
2545
3503
|
const limit = Math.min(Math.max(1, opts.limit ?? 20), 100);
|
|
@@ -2552,29 +3510,31 @@ async function listSessions(opts = {}) {
|
|
|
2552
3510
|
return [];
|
|
2553
3511
|
}
|
|
2554
3512
|
const projectDirs = entries.filter((e) => e.isDirectory() && e.name !== "memory").map((e) => (0, import_node_path6.join)(dirs.PROJECTS_DIR, e.name));
|
|
2555
|
-
const nested = await Promise.all(
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
3513
|
+
const [nested, codexFiles] = await Promise.all([
|
|
3514
|
+
Promise.all(
|
|
3515
|
+
projectDirs.map(async (projectDir) => {
|
|
3516
|
+
try {
|
|
3517
|
+
const files = await (0, import_promises5.readdir)(projectDir);
|
|
3518
|
+
const jsonlFiles = files.filter((f) => f.endsWith(".jsonl"));
|
|
3519
|
+
const statResults = await Promise.all(
|
|
3520
|
+
jsonlFiles.map(async (f) => {
|
|
3521
|
+
const filePath = (0, import_node_path6.join)(projectDir, f);
|
|
3522
|
+
try {
|
|
3523
|
+
const s = await (0, import_promises5.stat)(filePath);
|
|
3524
|
+
return s.mtimeMs >= cutoff ? { path: filePath, mtimeMs: s.mtimeMs } : null;
|
|
3525
|
+
} catch {
|
|
3526
|
+
return null;
|
|
3527
|
+
}
|
|
3528
|
+
})
|
|
3529
|
+
);
|
|
3530
|
+
return statResults.filter((r) => r !== null);
|
|
3531
|
+
} catch {
|
|
3532
|
+
return [];
|
|
3533
|
+
}
|
|
3534
|
+
})
|
|
3535
|
+
),
|
|
3536
|
+
listCodexSessionFiles(cutoff)
|
|
3537
|
+
]);
|
|
2578
3538
|
const allFiles = [...nested.flat(), ...codexFiles];
|
|
2579
3539
|
allFiles.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
2580
3540
|
const results = [];
|
|
@@ -2586,21 +3546,7 @@ async function listSessions(opts = {}) {
|
|
|
2586
3546
|
getSessionStatus(file.path)
|
|
2587
3547
|
]);
|
|
2588
3548
|
if (opts.cwd && meta.cwd !== opts.cwd) continue;
|
|
2589
|
-
results.push(
|
|
2590
|
-
sessionId: meta.sessionId,
|
|
2591
|
-
filePath: file.path,
|
|
2592
|
-
timestamp: meta.timestamp,
|
|
2593
|
-
model: meta.model,
|
|
2594
|
-
cwd: meta.cwd,
|
|
2595
|
-
gitBranch: meta.gitBranch,
|
|
2596
|
-
slug: meta.slug,
|
|
2597
|
-
firstMessage: meta.firstUserMessage,
|
|
2598
|
-
lastMessage: meta.lastUserMessage,
|
|
2599
|
-
turnCount: meta.turnCount,
|
|
2600
|
-
status: statusInfo.status,
|
|
2601
|
-
mtime: file.mtimeMs,
|
|
2602
|
-
source: file.path.startsWith(CODEX_SESSIONS_DIR + "/") ? "codex" : "claude"
|
|
2603
|
-
});
|
|
3549
|
+
results.push(toSessionSummary(file, meta, statusInfo.status));
|
|
2604
3550
|
} catch {
|
|
2605
3551
|
}
|
|
2606
3552
|
}
|
|
@@ -2616,18 +3562,20 @@ async function currentSession(cwd) {
|
|
|
2616
3562
|
files = [];
|
|
2617
3563
|
}
|
|
2618
3564
|
const jsonlFiles = files.filter((f) => f.endsWith(".jsonl"));
|
|
2619
|
-
const statResults = await Promise.all(
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
3565
|
+
const [statResults, codexCandidates] = await Promise.all([
|
|
3566
|
+
Promise.all(
|
|
3567
|
+
jsonlFiles.map(async (f) => {
|
|
3568
|
+
const filePath = (0, import_node_path6.join)(projectDir, f);
|
|
3569
|
+
try {
|
|
3570
|
+
const s = await (0, import_promises5.stat)(filePath);
|
|
3571
|
+
return { path: filePath, mtimeMs: s.mtimeMs };
|
|
3572
|
+
} catch {
|
|
3573
|
+
return null;
|
|
3574
|
+
}
|
|
3575
|
+
})
|
|
3576
|
+
),
|
|
3577
|
+
listCodexSessionFiles(0)
|
|
3578
|
+
]);
|
|
2631
3579
|
const codexMatches = [];
|
|
2632
3580
|
for (const file of codexCandidates) {
|
|
2633
3581
|
try {
|
|
@@ -2649,21 +3597,7 @@ async function currentSession(cwd) {
|
|
|
2649
3597
|
getSessionMeta(latest.path),
|
|
2650
3598
|
getSessionStatus(latest.path)
|
|
2651
3599
|
]);
|
|
2652
|
-
return
|
|
2653
|
-
sessionId: meta.sessionId,
|
|
2654
|
-
filePath: latest.path,
|
|
2655
|
-
timestamp: meta.timestamp,
|
|
2656
|
-
model: meta.model,
|
|
2657
|
-
cwd: meta.cwd,
|
|
2658
|
-
gitBranch: meta.gitBranch,
|
|
2659
|
-
slug: meta.slug,
|
|
2660
|
-
firstMessage: meta.firstUserMessage,
|
|
2661
|
-
lastMessage: meta.lastUserMessage,
|
|
2662
|
-
turnCount: meta.turnCount,
|
|
2663
|
-
status: statusInfo.status,
|
|
2664
|
-
mtime: latest.mtimeMs,
|
|
2665
|
-
source: latest.path.startsWith(CODEX_SESSIONS_DIR + "/") ? "codex" : "claude"
|
|
2666
|
-
};
|
|
3600
|
+
return toSessionSummary(latest, meta, statusInfo.status);
|
|
2667
3601
|
}
|
|
2668
3602
|
|
|
2669
3603
|
// src/commands/index-cmd.ts
|
|
@@ -2820,27 +3754,29 @@ async function main() {
|
|
|
2820
3754
|
const cmd = parseArgs(argv);
|
|
2821
3755
|
let result;
|
|
2822
3756
|
switch (cmd.command) {
|
|
2823
|
-
case "search":
|
|
3757
|
+
case "search": {
|
|
2824
3758
|
if (!cmd.args.query) {
|
|
2825
3759
|
console.error(JSON.stringify({ error: "Usage: cogpit-memory search <query>" }));
|
|
2826
3760
|
process.exit(1);
|
|
2827
3761
|
}
|
|
2828
|
-
|
|
3762
|
+
const searchResult = await searchSessions(cmd.args.query, {
|
|
2829
3763
|
sessionId: cmd.args.session,
|
|
2830
3764
|
maxAge: cmd.args.maxAge,
|
|
2831
3765
|
limit: cmd.args.limit,
|
|
2832
3766
|
caseSensitive: cmd.args.caseSensitive
|
|
2833
3767
|
});
|
|
2834
|
-
if (
|
|
2835
|
-
const sl = cmd.args.sessionLimit ??
|
|
3768
|
+
if ("results" in searchResult && (cmd.args.sessionLimit || cmd.args.hitsPerSession)) {
|
|
3769
|
+
const sl = cmd.args.sessionLimit ?? searchResult.results.length;
|
|
2836
3770
|
const hpp = cmd.args.hitsPerSession ?? Infinity;
|
|
2837
|
-
|
|
3771
|
+
searchResult.results = searchResult.results.slice(0, sl).map((sr) => ({
|
|
2838
3772
|
...sr,
|
|
2839
3773
|
hits: sr.hits.slice(0, hpp)
|
|
2840
3774
|
}));
|
|
2841
|
-
|
|
3775
|
+
searchResult.returnedHits = searchResult.results.reduce((n, sr) => n + sr.hits.length, 0);
|
|
2842
3776
|
}
|
|
3777
|
+
result = searchResult;
|
|
2843
3778
|
break;
|
|
3779
|
+
}
|
|
2844
3780
|
case "context":
|
|
2845
3781
|
if (!cmd.args.sessionId) {
|
|
2846
3782
|
console.error(JSON.stringify({ error: "Usage: cogpit-memory context <sessionId>" }));
|