cogpit-memory 0.1.10 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/cli.js +630 -286
- package/dist/commands/sessions.d.ts +0 -3
- package/dist/index.js +673 -337
- package/dist/lib/agentEnvelope.d.ts +24 -0
- package/dist/lib/codex-exec.d.ts +27 -0
- package/dist/lib/codex-tool-normalization.d.ts +6 -0
- package/dist/lib/helpers.d.ts +14 -0
- package/dist/lib/interactiveState.d.ts +7 -2
- package/dist/lib/messageTypeGuards.d.ts +10 -1
- package/dist/lib/sessionStats.d.ts +4 -0
- package/dist/lib/sessionStatus.d.ts +12 -6
- package/dist/lib/token-costs.d.ts +15 -36
- package/dist/lib/turnBuilder.d.ts +33 -0
- package/dist/lib/types.d.ts +159 -7
- package/package.json +20 -2
- package/skill/SKILL.md +1 -1
- package/dist/lib/pricingTiers.d.ts +0 -30
package/dist/cli.js
CHANGED
|
@@ -67,10 +67,41 @@ function isSummaryMessage(msg) {
|
|
|
67
67
|
function isQueueOperationMessage(msg) {
|
|
68
68
|
return msg.type === "queue-operation";
|
|
69
69
|
}
|
|
70
|
+
function isAttachmentMessage(msg) {
|
|
71
|
+
return msg.type === "attachment";
|
|
72
|
+
}
|
|
73
|
+
function isCompactSummaryMessage(msg) {
|
|
74
|
+
return msg.type === "user" && msg.isCompactSummary === true;
|
|
75
|
+
}
|
|
70
76
|
function isCompactBoundary(msg) {
|
|
71
77
|
return msg.type === "system" && msg.subtype === "compact_boundary";
|
|
72
78
|
}
|
|
73
79
|
|
|
80
|
+
// src/lib/agentEnvelope.ts
|
|
81
|
+
var ENVELOPE_RE = /<(agent-message|teammate-message)([^>]*)>([\s\S]*?)<\/\1>/g;
|
|
82
|
+
var SENDER_PATTERNS = [
|
|
83
|
+
/\bteammate_id=(?:"([^"]*)"|'([^']*)')/,
|
|
84
|
+
/\bfrom=(?:"([^"]*)"|'([^']*)')/
|
|
85
|
+
];
|
|
86
|
+
function readSender(attrs) {
|
|
87
|
+
for (const pattern of SENDER_PATTERNS) {
|
|
88
|
+
const m = attrs.match(pattern);
|
|
89
|
+
const value = m?.[1] ?? m?.[2];
|
|
90
|
+
if (value) return value;
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
function parseAgentEnvelope(text) {
|
|
95
|
+
let sender = null;
|
|
96
|
+
let matched = false;
|
|
97
|
+
const unwrapped = text.replace(ENVELOPE_RE, (_full, _tag, attrs, inner) => {
|
|
98
|
+
matched = true;
|
|
99
|
+
if (sender === null) sender = readSender(attrs);
|
|
100
|
+
return inner.trim();
|
|
101
|
+
});
|
|
102
|
+
return { sender, matched, body: matched ? unwrapped.trim() : text };
|
|
103
|
+
}
|
|
104
|
+
|
|
74
105
|
// src/lib/turnBuilder.ts
|
|
75
106
|
function extractTextFromContent(content) {
|
|
76
107
|
if (typeof content === "string") return content;
|
|
@@ -87,7 +118,58 @@ function extractToolResultText(content) {
|
|
|
87
118
|
function isVisibleQueuedPrompt(content) {
|
|
88
119
|
if (!content?.trim()) return false;
|
|
89
120
|
const trimmed = content.trimStart();
|
|
90
|
-
|
|
121
|
+
if (!trimmed.startsWith("<")) return true;
|
|
122
|
+
return !trimmed.includes("<task-notification>") && !trimmed.startsWith("<system-reminder>") && !trimmed.startsWith("<local-command-");
|
|
123
|
+
}
|
|
124
|
+
var TASK_NOTIFICATION_BLOCK_RE = /<task-notification>[\s\S]*?<\/task-notification>/g;
|
|
125
|
+
var ANY_TAG_RE = /<[^>]*>/g;
|
|
126
|
+
function isTaskNotificationRecord(msg) {
|
|
127
|
+
if (msg.origin?.kind === "task-notification") return true;
|
|
128
|
+
const text = extractTextFromContent(msg.message.content);
|
|
129
|
+
if (!text.includes("<task-notification>")) return false;
|
|
130
|
+
return text.replace(TASK_NOTIFICATION_BLOCK_RE, "").replace(ANY_TAG_RE, "").trim() === "";
|
|
131
|
+
}
|
|
132
|
+
function queuedCommandPrompt(msg) {
|
|
133
|
+
if (!isAttachmentMessage(msg)) return null;
|
|
134
|
+
const attachment = msg.attachment;
|
|
135
|
+
if (!attachment || attachment.type !== "queued_command") return null;
|
|
136
|
+
if (attachment.commandMode !== "prompt") return null;
|
|
137
|
+
const prompt = attachment.prompt;
|
|
138
|
+
if (prompt == null) return null;
|
|
139
|
+
const raw = typeof prompt === "string" ? prompt : extractTextFromContent(prompt);
|
|
140
|
+
if (!isVisibleQueuedPrompt(raw)) return null;
|
|
141
|
+
const origin = attachment.origin;
|
|
142
|
+
if (origin?.kind === "peer") {
|
|
143
|
+
const sender = origin.name ?? origin.from ?? null;
|
|
144
|
+
if (sender) {
|
|
145
|
+
return {
|
|
146
|
+
raw,
|
|
147
|
+
sender,
|
|
148
|
+
body: origin.body ?? parseAgentEnvelope(raw).body
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (origin?.kind === "human") {
|
|
153
|
+
return { raw, sender: null, body: raw };
|
|
154
|
+
}
|
|
155
|
+
const parsed = parseAgentEnvelope(raw);
|
|
156
|
+
return { raw, sender: parsed.sender, body: parsed.body };
|
|
157
|
+
}
|
|
158
|
+
var ATTRIBUTION_FIELDS = [
|
|
159
|
+
["attributionAgent", "agent"],
|
|
160
|
+
["attributionSkill", "skill"],
|
|
161
|
+
["attributionPlugin", "plugin"],
|
|
162
|
+
["attributionMcpServer", "mcpServer"],
|
|
163
|
+
["attributionMcpTool", "mcpTool"]
|
|
164
|
+
];
|
|
165
|
+
function mergeAttribution(current, msg) {
|
|
166
|
+
let merged = current;
|
|
167
|
+
for (const [field, key] of ATTRIBUTION_FIELDS) {
|
|
168
|
+
const value = msg[field];
|
|
169
|
+
if (typeof value !== "string" || !value) continue;
|
|
170
|
+
merged = { ...merged, [key]: value };
|
|
171
|
+
}
|
|
172
|
+
return merged;
|
|
91
173
|
}
|
|
92
174
|
function mergeTokenUsage(existing, incoming) {
|
|
93
175
|
if (!existing) {
|
|
@@ -101,35 +183,30 @@ function mergeTokenUsage(existing, incoming) {
|
|
|
101
183
|
speed: incoming.speed ?? existing.speed
|
|
102
184
|
};
|
|
103
185
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const shown = prompts.slice(0, 6);
|
|
129
|
-
for (const p of shown) parts.push(`- ${p}`);
|
|
130
|
-
if (prompts.length > 6) parts.push(`- ...and ${prompts.length - 6} more`);
|
|
131
|
-
}
|
|
132
|
-
return parts.join("\n");
|
|
186
|
+
var RESUME_PREAMBLE_RE = /^This session is being continued from a previous conversation[\s\S]*?\n\nSummary:\s*\n/;
|
|
187
|
+
var RESUME_INSTRUCTIONS_RE = /\n+(?:If you need specific details from before compaction|(?:Please c|C)ontinue the conversation from where it left off)[\s\S]*$/;
|
|
188
|
+
function extractCompactionSummary(content) {
|
|
189
|
+
const text = extractTextFromContent(content).trim();
|
|
190
|
+
return text.replace(RESUME_PREAMBLE_RE, "").replace(RESUME_INSTRUCTIONS_RE, "").trim();
|
|
191
|
+
}
|
|
192
|
+
function normalizeCompactionMeta(meta) {
|
|
193
|
+
if (!meta) return void 0;
|
|
194
|
+
return { trigger: meta.trigger, preTokens: meta.preTokens, postTokens: meta.postTokens };
|
|
195
|
+
}
|
|
196
|
+
function createTurn(msg, userMessage) {
|
|
197
|
+
return {
|
|
198
|
+
id: msg.uuid ?? crypto.randomUUID(),
|
|
199
|
+
userMessage,
|
|
200
|
+
contentBlocks: [],
|
|
201
|
+
thinking: [],
|
|
202
|
+
assistantText: [],
|
|
203
|
+
toolCalls: [],
|
|
204
|
+
subAgentActivity: [],
|
|
205
|
+
timestamp: msg.timestamp ?? "",
|
|
206
|
+
durationMs: null,
|
|
207
|
+
tokenUsage: null,
|
|
208
|
+
model: null
|
|
209
|
+
};
|
|
133
210
|
}
|
|
134
211
|
function groupPlanModeBlocks(blocks) {
|
|
135
212
|
const result = [];
|
|
@@ -194,7 +271,7 @@ function groupPlanModeBlocks(blocks) {
|
|
|
194
271
|
embedded.push(...next.toolCalls);
|
|
195
272
|
j++;
|
|
196
273
|
}
|
|
197
|
-
} else if (next.kind === "hook_event" || next.kind === "text" || next.kind === "queued_prompt") {
|
|
274
|
+
} else if (next.kind === "hook_event" || next.kind === "text" || next.kind === "queued_prompt" || next.kind === "agent_message") {
|
|
198
275
|
passthroughBlocks.push(next);
|
|
199
276
|
j++;
|
|
200
277
|
} else {
|
|
@@ -215,10 +292,55 @@ function groupPlanModeBlocks(blocks) {
|
|
|
215
292
|
}
|
|
216
293
|
return result;
|
|
217
294
|
}
|
|
295
|
+
function clearAgentMessageReplies(turn) {
|
|
296
|
+
if (!turn.contentBlocks.some((b) => b.kind === "agent_message")) return turn;
|
|
297
|
+
return {
|
|
298
|
+
...turn,
|
|
299
|
+
contentBlocks: turn.contentBlocks.map((block) => {
|
|
300
|
+
if (block.kind !== "agent_message") return block;
|
|
301
|
+
const { reply: _paired, ...unpaired } = block;
|
|
302
|
+
return unpaired;
|
|
303
|
+
})
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
function pairAgentMessageReplies(turns) {
|
|
307
|
+
const repaired = turns.map(clearAgentMessageReplies);
|
|
308
|
+
const unanswered = /* @__PURE__ */ new Map();
|
|
309
|
+
for (const turn of repaired) {
|
|
310
|
+
for (const block of turn.contentBlocks) {
|
|
311
|
+
if (block.kind === "agent_message") {
|
|
312
|
+
const waiting = unanswered.get(block.sender);
|
|
313
|
+
if (waiting) waiting.push(block);
|
|
314
|
+
else unanswered.set(block.sender, [block]);
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
if (block.kind !== "tool_calls") continue;
|
|
318
|
+
for (const call of block.toolCalls) {
|
|
319
|
+
if (call.name !== "SendMessage") continue;
|
|
320
|
+
const to = call.input.to;
|
|
321
|
+
if (typeof to !== "string" || !to) continue;
|
|
322
|
+
const target = unanswered.get(to)?.shift();
|
|
323
|
+
if (!target) continue;
|
|
324
|
+
const summary = call.input.summary;
|
|
325
|
+
target.reply = {
|
|
326
|
+
summary: typeof summary === "string" ? summary : "",
|
|
327
|
+
timestamp: call.timestamp || block.timestamp || ""
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return repaired;
|
|
333
|
+
}
|
|
218
334
|
function buildTurns(messages) {
|
|
219
335
|
const turns = [];
|
|
220
336
|
let current = null;
|
|
221
337
|
let pendingCompaction = null;
|
|
338
|
+
function attachPendingCompaction(turn) {
|
|
339
|
+
if (!pendingCompaction) return;
|
|
340
|
+
turn.compactionSummary = pendingCompaction.summary;
|
|
341
|
+
turn.compactionMeta = pendingCompaction.meta;
|
|
342
|
+
pendingCompaction = null;
|
|
343
|
+
}
|
|
222
344
|
let pendingRecap = null;
|
|
223
345
|
const pendingToolUses = /* @__PURE__ */ new Map();
|
|
224
346
|
const seenMessageIds = /* @__PURE__ */ new Set();
|
|
@@ -226,6 +348,41 @@ function buildTurns(messages) {
|
|
|
226
348
|
const agentBlockMap = /* @__PURE__ */ new Map();
|
|
227
349
|
const backgroundAgentParentIds = /* @__PURE__ */ new Set();
|
|
228
350
|
const taskMetaMap = /* @__PURE__ */ new Map();
|
|
351
|
+
const pendingQueuedPrompts = [];
|
|
352
|
+
const enqueueSourcedPrompts = /* @__PURE__ */ new Map();
|
|
353
|
+
function noteEnqueueSourced(turn, content) {
|
|
354
|
+
let counts = enqueueSourcedPrompts.get(turn);
|
|
355
|
+
if (!counts) {
|
|
356
|
+
counts = /* @__PURE__ */ new Map();
|
|
357
|
+
enqueueSourcedPrompts.set(turn, counts);
|
|
358
|
+
}
|
|
359
|
+
counts.set(content, (counts.get(content) ?? 0) + 1);
|
|
360
|
+
}
|
|
361
|
+
function consumeEnqueueSourced(turn, content) {
|
|
362
|
+
const counts = enqueueSourcedPrompts.get(turn);
|
|
363
|
+
const remaining = counts?.get(content);
|
|
364
|
+
if (!counts || !remaining) return false;
|
|
365
|
+
if (remaining === 1) counts.delete(content);
|
|
366
|
+
else counts.set(content, remaining - 1);
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
function flushPendingQueuedPrompts() {
|
|
370
|
+
for (const prompt of pendingQueuedPrompts) {
|
|
371
|
+
prompt.turn.contentBlocks.push(
|
|
372
|
+
prompt.sender ? {
|
|
373
|
+
kind: "agent_message",
|
|
374
|
+
sender: prompt.sender,
|
|
375
|
+
body: prompt.body,
|
|
376
|
+
timestamp: prompt.timestamp
|
|
377
|
+
} : {
|
|
378
|
+
kind: "queued_prompt",
|
|
379
|
+
content: prompt.content,
|
|
380
|
+
timestamp: prompt.timestamp
|
|
381
|
+
}
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
pendingQueuedPrompts.length = 0;
|
|
385
|
+
}
|
|
229
386
|
function flushSubAgentMessages(parentId) {
|
|
230
387
|
if (!current) return;
|
|
231
388
|
const agentMsgs = subAgentMap.get(parentId);
|
|
@@ -273,18 +430,20 @@ function buildTurns(messages) {
|
|
|
273
430
|
for (const msg of messages) {
|
|
274
431
|
if (isSummaryMessage(msg)) {
|
|
275
432
|
finalizeTurn2();
|
|
276
|
-
pendingCompaction =
|
|
277
|
-
turns,
|
|
278
|
-
msg.summary ?? "Conversation compacted"
|
|
279
|
-
);
|
|
433
|
+
pendingCompaction = { summary: msg.summary };
|
|
280
434
|
continue;
|
|
281
435
|
}
|
|
282
436
|
if (isCompactBoundary(msg)) {
|
|
283
437
|
finalizeTurn2();
|
|
284
|
-
pendingCompaction =
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
438
|
+
pendingCompaction = { meta: normalizeCompactionMeta(msg.compactMetadata) };
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
if (isCompactSummaryMessage(msg)) {
|
|
442
|
+
finalizeTurn2();
|
|
443
|
+
current = createTurn(msg, null);
|
|
444
|
+
current.compactionSummary = extractCompactionSummary(msg.message.content);
|
|
445
|
+
current.compactionMeta = pendingCompaction?.meta;
|
|
446
|
+
pendingCompaction = null;
|
|
288
447
|
continue;
|
|
289
448
|
}
|
|
290
449
|
if (isSystemMessage(msg) && msg.subtype === "away_summary" && msg.content) {
|
|
@@ -293,14 +452,44 @@ function buildTurns(messages) {
|
|
|
293
452
|
}
|
|
294
453
|
if (isQueueOperationMessage(msg)) {
|
|
295
454
|
if (current && msg.operation === "enqueue" && isVisibleQueuedPrompt(msg.content)) {
|
|
296
|
-
|
|
297
|
-
|
|
455
|
+
const parsed = parseAgentEnvelope(msg.content);
|
|
456
|
+
pendingQueuedPrompts.push({
|
|
457
|
+
turn: current,
|
|
298
458
|
content: msg.content,
|
|
299
|
-
timestamp: msg.timestamp
|
|
459
|
+
timestamp: msg.timestamp,
|
|
460
|
+
sender: parsed.sender,
|
|
461
|
+
body: parsed.body
|
|
300
462
|
});
|
|
463
|
+
noteEnqueueSourced(current, msg.content);
|
|
301
464
|
}
|
|
302
465
|
continue;
|
|
303
466
|
}
|
|
467
|
+
if (isAttachmentMessage(msg)) {
|
|
468
|
+
const queued = queuedCommandPrompt(msg);
|
|
469
|
+
if (current && queued !== null && !consumeEnqueueSourced(current, queued.raw)) {
|
|
470
|
+
pendingQueuedPrompts.push({
|
|
471
|
+
turn: current,
|
|
472
|
+
content: queued.raw,
|
|
473
|
+
timestamp: msg.attachment?.timestamp ?? msg.timestamp,
|
|
474
|
+
sender: queued.sender,
|
|
475
|
+
body: queued.body
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
if (pendingQueuedPrompts.length > 0) {
|
|
481
|
+
const userContent = isUserMessage(msg) && !msg.isMeta ? msg.message.content : null;
|
|
482
|
+
if (typeof userContent === "string") {
|
|
483
|
+
const duplicateIndex = pendingQueuedPrompts.findIndex(
|
|
484
|
+
(prompt) => prompt.content === userContent
|
|
485
|
+
);
|
|
486
|
+
if (duplicateIndex >= 0) {
|
|
487
|
+
const [dropped] = pendingQueuedPrompts.splice(duplicateIndex, 1);
|
|
488
|
+
consumeEnqueueSourced(dropped.turn, dropped.content);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
flushPendingQueuedPrompts();
|
|
492
|
+
}
|
|
304
493
|
if (isUserMessage(msg) && !msg.isMeta) {
|
|
305
494
|
const content = msg.message.content;
|
|
306
495
|
if (typeof content !== "string" && Array.isArray(content)) {
|
|
@@ -363,24 +552,24 @@ function buildTurns(messages) {
|
|
|
363
552
|
continue;
|
|
364
553
|
}
|
|
365
554
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
if (pendingCompaction) {
|
|
381
|
-
current.compactionSummary = pendingCompaction;
|
|
382
|
-
pendingCompaction = null;
|
|
555
|
+
if (isTaskNotificationRecord(msg)) {
|
|
556
|
+
const notification = {
|
|
557
|
+
kind: "task_notification",
|
|
558
|
+
content: extractTextFromContent(content),
|
|
559
|
+
timestamp: msg.timestamp
|
|
560
|
+
};
|
|
561
|
+
if (current) {
|
|
562
|
+
current.contentBlocks.push(notification);
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
current = createTurn(msg, null);
|
|
566
|
+
current.contentBlocks.push(notification);
|
|
567
|
+
attachPendingCompaction(current);
|
|
568
|
+
continue;
|
|
383
569
|
}
|
|
570
|
+
finalizeTurn2();
|
|
571
|
+
current = createTurn(msg, msg.message.content);
|
|
572
|
+
attachPendingCompaction(current);
|
|
384
573
|
if (pendingRecap) {
|
|
385
574
|
current.contentBlocks.push({ kind: "recap", content: pendingRecap.content, timestamp: pendingRecap.timestamp });
|
|
386
575
|
pendingRecap = null;
|
|
@@ -406,21 +595,12 @@ function buildTurns(messages) {
|
|
|
406
595
|
};
|
|
407
596
|
var flushToolCalls = flushToolCalls2, flushThinking = flushThinking2;
|
|
408
597
|
if (!current) {
|
|
409
|
-
current =
|
|
410
|
-
|
|
411
|
-
userMessage: null,
|
|
412
|
-
contentBlocks: [],
|
|
413
|
-
thinking: [],
|
|
414
|
-
assistantText: [],
|
|
415
|
-
toolCalls: [],
|
|
416
|
-
subAgentActivity: [],
|
|
417
|
-
timestamp: msg.timestamp ?? "",
|
|
418
|
-
durationMs: null,
|
|
419
|
-
tokenUsage: null,
|
|
420
|
-
model: null
|
|
421
|
-
};
|
|
598
|
+
current = createTurn(msg, null);
|
|
599
|
+
attachPendingCompaction(current);
|
|
422
600
|
}
|
|
423
601
|
current.model = msg.message.model;
|
|
602
|
+
if (msg.effort) current.effort = msg.effort;
|
|
603
|
+
current.attribution = mergeAttribution(current.attribution, msg);
|
|
424
604
|
const msgId = msg.message.id;
|
|
425
605
|
if (!seenMessageIds.has(msgId)) {
|
|
426
606
|
seenMessageIds.add(msgId);
|
|
@@ -610,145 +790,15 @@ function buildTurns(messages) {
|
|
|
610
790
|
continue;
|
|
611
791
|
}
|
|
612
792
|
if (isSystemMessage(msg) && msg.subtype === "turn_duration" && current) {
|
|
613
|
-
|
|
793
|
+
if (msg.durationMs !== void 0 && msg.durationMs !== null) {
|
|
794
|
+
current.durationMs = (current.durationMs ?? 0) + msg.durationMs;
|
|
795
|
+
}
|
|
614
796
|
continue;
|
|
615
797
|
}
|
|
616
798
|
}
|
|
799
|
+
flushPendingQueuedPrompts();
|
|
617
800
|
finalizeTurn2();
|
|
618
|
-
return turns;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
// src/lib/pricingTiers.ts
|
|
622
|
-
var TIER_HAIKU_35 = { input: 0.8, output: 4, cacheWrite: 1, cacheRead: 0.08, webSearch: 0.01 };
|
|
623
|
-
var TIER_HAIKU_45 = { input: 1, output: 5, cacheWrite: 1.25, cacheRead: 0.1, webSearch: 0.01 };
|
|
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 };
|
|
627
|
-
var TIER_OPUS_LEGACY = { input: 15, output: 75, cacheWrite: 18.75, cacheRead: 1.5, webSearch: 0.01 };
|
|
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 };
|
|
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 },
|
|
648
|
-
// Haiku
|
|
649
|
-
{ match: "haiku-4-5", tier: TIER_HAIKU_45 },
|
|
650
|
-
{ match: "haiku-4-0", tier: TIER_HAIKU_35 },
|
|
651
|
-
{ match: "3-5-haiku", tier: TIER_HAIKU_35 },
|
|
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 },
|
|
663
|
-
// Opus legacy (4.0, 4.1)
|
|
664
|
-
{ match: "opus-4-1", tier: TIER_OPUS_LEGACY },
|
|
665
|
-
{ match: "opus-4-0", tier: TIER_OPUS_LEGACY }
|
|
666
|
-
];
|
|
667
|
-
var FAST_MODE_MODELS = ["opus-4-6", "opus-4-7"];
|
|
668
|
-
var DEFAULT_TIER = TIER_OPUS_MID;
|
|
669
|
-
var FAMILY_FALLBACKS = [
|
|
670
|
-
{ match: "fable", tier: TIER_FRONTIER },
|
|
671
|
-
{ match: "mythos", tier: TIER_FRONTIER },
|
|
672
|
-
{ match: "haiku", tier: TIER_HAIKU_45 },
|
|
673
|
-
{ match: "sonnet", tier: TIER_SONNET },
|
|
674
|
-
{ match: "opus", tier: TIER_OPUS_MID }
|
|
675
|
-
];
|
|
676
|
-
function resolveTier(model, speed) {
|
|
677
|
-
if (speed === "fast" && FAST_MODE_MODELS.some((m) => model.includes(m))) {
|
|
678
|
-
return TIER_FAST;
|
|
679
|
-
}
|
|
680
|
-
for (const entry of MODEL_TIERS) {
|
|
681
|
-
if (model.includes(entry.match)) return entry.tier;
|
|
682
|
-
}
|
|
683
|
-
for (const entry of FAMILY_FALLBACKS) {
|
|
684
|
-
if (model.includes(entry.match)) return entry.tier;
|
|
685
|
-
}
|
|
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));
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
// src/lib/token-costs.ts
|
|
696
|
-
var CHARS_PER_TOKEN = 4;
|
|
697
|
-
function calculateCost(c) {
|
|
698
|
-
if (!hasKnownPricing(c.model)) return Number.NaN;
|
|
699
|
-
const p = resolveTier(c.model ?? "", c.speed);
|
|
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;
|
|
701
|
-
}
|
|
702
|
-
function charsToTokens(chars) {
|
|
703
|
-
return Math.ceil(chars / CHARS_PER_TOKEN);
|
|
704
|
-
}
|
|
705
|
-
function totalLength(strings) {
|
|
706
|
-
let n = 0;
|
|
707
|
-
for (const s of strings) n += s.length;
|
|
708
|
-
return n;
|
|
709
|
-
}
|
|
710
|
-
function totalToolInputLength(toolCalls) {
|
|
711
|
-
let n = 0;
|
|
712
|
-
for (const tc of toolCalls) n += JSON.stringify(tc.input).length;
|
|
713
|
-
return n;
|
|
714
|
-
}
|
|
715
|
-
function estimateThinkingTokens(turn) {
|
|
716
|
-
return charsToTokens(totalLength(turn.thinking.map((b) => b.thinking)));
|
|
717
|
-
}
|
|
718
|
-
function estimateVisibleOutputTokens(turn) {
|
|
719
|
-
return charsToTokens(totalLength(turn.assistantText) + totalToolInputLength(turn.toolCalls));
|
|
720
|
-
}
|
|
721
|
-
function estimateTotalOutputTokens(turn) {
|
|
722
|
-
const estimated = estimateThinkingTokens(turn) + estimateVisibleOutputTokens(turn);
|
|
723
|
-
return Math.max(estimated, turn.tokenUsage?.output_tokens ?? 0);
|
|
724
|
-
}
|
|
725
|
-
function estimateSubAgentOutput(sa) {
|
|
726
|
-
const chars = totalLength(sa.thinking) + totalLength(sa.text) + totalToolInputLength(sa.toolCalls);
|
|
727
|
-
return Math.max(charsToTokens(chars), sa.tokenUsage?.output_tokens ?? 0);
|
|
728
|
-
}
|
|
729
|
-
function calculateTurnCostEstimated(turn) {
|
|
730
|
-
if (!turn.tokenUsage) return 0;
|
|
731
|
-
const u = turn.tokenUsage;
|
|
732
|
-
return calculateCost({
|
|
733
|
-
model: turn.model,
|
|
734
|
-
inputTokens: u.input_tokens,
|
|
735
|
-
outputTokens: estimateTotalOutputTokens(turn),
|
|
736
|
-
cacheWriteTokens: u.cache_creation_input_tokens ?? 0,
|
|
737
|
-
cacheReadTokens: u.cache_read_input_tokens ?? 0,
|
|
738
|
-
speed: u.speed
|
|
739
|
-
});
|
|
740
|
-
}
|
|
741
|
-
function calculateSubAgentCostEstimated(sa) {
|
|
742
|
-
if (!sa.tokenUsage) return 0;
|
|
743
|
-
const u = sa.tokenUsage;
|
|
744
|
-
return calculateCost({
|
|
745
|
-
model: sa.model,
|
|
746
|
-
inputTokens: u.input_tokens,
|
|
747
|
-
outputTokens: estimateSubAgentOutput(sa),
|
|
748
|
-
cacheWriteTokens: u.cache_creation_input_tokens ?? 0,
|
|
749
|
-
cacheReadTokens: u.cache_read_input_tokens ?? 0,
|
|
750
|
-
speed: u.speed
|
|
751
|
-
});
|
|
801
|
+
return pairAgentMessageReplies(turns);
|
|
752
802
|
}
|
|
753
803
|
|
|
754
804
|
// src/lib/sessionStats.ts
|
|
@@ -760,12 +810,11 @@ function countToolCalls(toolCalls, counts) {
|
|
|
760
810
|
}
|
|
761
811
|
return errors;
|
|
762
812
|
}
|
|
763
|
-
function addUsageToStats(stats, usage
|
|
813
|
+
function addUsageToStats(stats, usage) {
|
|
764
814
|
stats.totalInputTokens += usage.input_tokens;
|
|
765
|
-
stats.totalOutputTokens +=
|
|
815
|
+
stats.totalOutputTokens += usage.output_tokens;
|
|
766
816
|
stats.totalCacheCreationTokens += usage.cache_creation_input_tokens ?? 0;
|
|
767
817
|
stats.totalCacheReadTokens += usage.cache_read_input_tokens ?? 0;
|
|
768
|
-
stats.totalCostUSD += cost;
|
|
769
818
|
}
|
|
770
819
|
function createEmptySessionStats(turnCount) {
|
|
771
820
|
return {
|
|
@@ -773,7 +822,6 @@ function createEmptySessionStats(turnCount) {
|
|
|
773
822
|
totalOutputTokens: 0,
|
|
774
823
|
totalCacheCreationTokens: 0,
|
|
775
824
|
totalCacheReadTokens: 0,
|
|
776
|
-
totalCostUSD: 0,
|
|
777
825
|
toolCallCounts: {},
|
|
778
826
|
errorCount: 0,
|
|
779
827
|
totalDurationMs: 0,
|
|
@@ -781,34 +829,20 @@ function createEmptySessionStats(turnCount) {
|
|
|
781
829
|
};
|
|
782
830
|
}
|
|
783
831
|
function computeStats(turns) {
|
|
784
|
-
const stats =
|
|
785
|
-
totalInputTokens: 0,
|
|
786
|
-
totalOutputTokens: 0,
|
|
787
|
-
totalCacheCreationTokens: 0,
|
|
788
|
-
totalCacheReadTokens: 0,
|
|
789
|
-
totalCostUSD: 0,
|
|
790
|
-
toolCallCounts: {},
|
|
791
|
-
errorCount: 0,
|
|
792
|
-
totalDurationMs: 0,
|
|
793
|
-
turnCount: turns.length
|
|
794
|
-
};
|
|
832
|
+
const stats = createEmptySessionStats(turns.length);
|
|
795
833
|
for (const turn of turns) {
|
|
796
|
-
if (turn.tokenUsage)
|
|
797
|
-
addUsageToStats(stats, turn.tokenUsage, estimateTotalOutputTokens(turn), calculateTurnCostEstimated(turn));
|
|
798
|
-
}
|
|
834
|
+
if (turn.tokenUsage) addUsageToStats(stats, turn.tokenUsage);
|
|
799
835
|
if (turn.durationMs) stats.totalDurationMs += turn.durationMs;
|
|
800
836
|
stats.errorCount += countToolCalls(turn.toolCalls, stats.toolCallCounts);
|
|
801
837
|
for (const sa of turn.subAgentActivity) {
|
|
802
838
|
stats.errorCount += countToolCalls(sa.toolCalls, stats.toolCallCounts);
|
|
803
|
-
if (sa.tokenUsage)
|
|
804
|
-
addUsageToStats(stats, sa.tokenUsage, estimateSubAgentOutput(sa), calculateSubAgentCostEstimated(sa));
|
|
805
|
-
}
|
|
839
|
+
if (sa.tokenUsage) addUsageToStats(stats, sa.tokenUsage);
|
|
806
840
|
}
|
|
807
841
|
}
|
|
808
842
|
return stats;
|
|
809
843
|
}
|
|
810
844
|
|
|
811
|
-
// src/lib/codex-
|
|
845
|
+
// src/lib/codex-exec.ts
|
|
812
846
|
var JS_SIMPLE_ESCAPES = {
|
|
813
847
|
"0": "\0",
|
|
814
848
|
b: "\b",
|
|
@@ -874,6 +908,8 @@ function readJsStringLiteral(source, startIndex) {
|
|
|
874
908
|
}
|
|
875
909
|
return null;
|
|
876
910
|
}
|
|
911
|
+
|
|
912
|
+
// src/lib/codex-patches.ts
|
|
877
913
|
function isApplyPatchText(value) {
|
|
878
914
|
return value.includes("*** Begin Patch") && value.includes("*** End Patch");
|
|
879
915
|
}
|
|
@@ -907,7 +943,9 @@ function extractApplyPatchInputs(execSource) {
|
|
|
907
943
|
}
|
|
908
944
|
function resolveCodexPatchPath(filePath, cwd) {
|
|
909
945
|
if (!cwd || filePath.startsWith("/") || /^[A-Za-z]:[\\/]/.test(filePath)) return filePath;
|
|
910
|
-
|
|
946
|
+
const base = cwd.replace(/[\\/]+$/, "");
|
|
947
|
+
const relative = filePath.replace(/^\.[\\/]/, "");
|
|
948
|
+
return /^[A-Za-z]:[\\/]/.test(cwd) ? `${base}\\${relative.replace(/\//g, "\\")}` : `${base}/${relative}`;
|
|
911
949
|
}
|
|
912
950
|
function parseApplyPatch(patchText, callId, timestamp, cwd = "") {
|
|
913
951
|
const toolCalls = [];
|
|
@@ -1034,6 +1072,24 @@ function findFailedNestedPatchCallIds(output, wrapperIsError, calls) {
|
|
|
1034
1072
|
function isObject(value) {
|
|
1035
1073
|
return typeof value === "object" && value !== null;
|
|
1036
1074
|
}
|
|
1075
|
+
var COLLABORATION_TOOL_NAMES = /* @__PURE__ */ new Map([
|
|
1076
|
+
["spawn_agent", "spawn_agent"],
|
|
1077
|
+
["spawnAgent", "spawn_agent"],
|
|
1078
|
+
["wait_agent", "wait_agent"],
|
|
1079
|
+
["waitAgent", "wait_agent"],
|
|
1080
|
+
["send_message", "send_message"],
|
|
1081
|
+
["sendMessage", "send_message"],
|
|
1082
|
+
["followup_task", "followup_task"],
|
|
1083
|
+
["followupTask", "followup_task"],
|
|
1084
|
+
["list_agents", "list_agents"],
|
|
1085
|
+
["listAgents", "list_agents"],
|
|
1086
|
+
["interrupt_agent", "interrupt_agent"],
|
|
1087
|
+
["interruptAgent", "interrupt_agent"]
|
|
1088
|
+
]);
|
|
1089
|
+
function normalizeFunctionName(rawName) {
|
|
1090
|
+
const leaf = rawName.split(/(?:__|[.:/])+/).filter(Boolean).at(-1) ?? rawName;
|
|
1091
|
+
return COLLABORATION_TOOL_NAMES.get(leaf) ?? rawName;
|
|
1092
|
+
}
|
|
1037
1093
|
function inferToolError(output) {
|
|
1038
1094
|
if (!output) return false;
|
|
1039
1095
|
const exitMatch = output.match(/Process exited with code (\d+)/);
|
|
@@ -1159,28 +1215,6 @@ function buildCodexUserContent(message, images, audio = [], localImages = [], lo
|
|
|
1159
1215
|
...text ? [{ type: "text", text }] : []
|
|
1160
1216
|
];
|
|
1161
1217
|
}
|
|
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
1218
|
function extractInterAgentMessage(payload) {
|
|
1185
1219
|
const rawText = extractMessageText(payload, "input_text");
|
|
1186
1220
|
if (!rawText) return { messageType: null, text: "" };
|
|
@@ -1194,7 +1228,7 @@ function extractInterAgentMessage(payload) {
|
|
|
1194
1228
|
return { messageType, text };
|
|
1195
1229
|
}
|
|
1196
1230
|
function agentNameFromPath(agentPath) {
|
|
1197
|
-
const segments = agentPath.split(
|
|
1231
|
+
const segments = agentPath.split(/[\\/]/).filter(Boolean);
|
|
1198
1232
|
return segments.at(-1) ?? null;
|
|
1199
1233
|
}
|
|
1200
1234
|
function readableAgentPrompt(value) {
|
|
@@ -1287,9 +1321,35 @@ function parseToolInput(argumentsText) {
|
|
|
1287
1321
|
return { raw: argumentsText };
|
|
1288
1322
|
}
|
|
1289
1323
|
}
|
|
1290
|
-
function
|
|
1324
|
+
function safeStringify(value) {
|
|
1325
|
+
if (typeof value === "string") return value;
|
|
1326
|
+
try {
|
|
1327
|
+
return JSON.stringify(value);
|
|
1328
|
+
} catch {
|
|
1329
|
+
return String(value ?? "");
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
function findString(...values) {
|
|
1333
|
+
return values.find((value) => typeof value === "string");
|
|
1334
|
+
}
|
|
1335
|
+
function isCodeModeChild(callId) {
|
|
1336
|
+
return callId.startsWith("exec-");
|
|
1337
|
+
}
|
|
1338
|
+
function parseMcpEventResult(value) {
|
|
1339
|
+
if (!isObject2(value)) return { text: safeStringify(value), isError: false };
|
|
1340
|
+
if ("Err" in value) return { text: safeStringify(value.Err), isError: true };
|
|
1341
|
+
const ok = isObject2(value.Ok) ? value.Ok : null;
|
|
1342
|
+
if (!ok) return { text: safeStringify(value), isError: false };
|
|
1343
|
+
const parsedContent = parseCustomToolOutput(ok.content);
|
|
1344
|
+
const text = parsedContent.text || (ok.structuredContent !== void 0 ? safeStringify(ok.structuredContent) : safeStringify(ok));
|
|
1345
|
+
return {
|
|
1346
|
+
text,
|
|
1347
|
+
isError: ok.isError === true || parsedContent.isError
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
function createTurn2(turnId, timestamp, model) {
|
|
1291
1351
|
return {
|
|
1292
|
-
id: turnId
|
|
1352
|
+
id: turnId ? `${turnId}@${timestamp}` : randomTurnId("codex-turn"),
|
|
1293
1353
|
userMessage: null,
|
|
1294
1354
|
contentBlocks: [],
|
|
1295
1355
|
thinking: [],
|
|
@@ -1311,6 +1371,17 @@ function extractPromptFromRecord(record) {
|
|
|
1311
1371
|
}
|
|
1312
1372
|
return "";
|
|
1313
1373
|
}
|
|
1374
|
+
function recordOpensTurn(record, payload) {
|
|
1375
|
+
if (record.type === "session_meta") return false;
|
|
1376
|
+
if (record.type === "event_msg" && payload?.type === "task_started") return false;
|
|
1377
|
+
if (record.type === "response_item" && payload?.type === "message") {
|
|
1378
|
+
if (payload.role === "developer" || payload.role === "system") return false;
|
|
1379
|
+
if (payload.role === "user") {
|
|
1380
|
+
return extractPromptFromRecord(record) !== "" || extractResponseMessageImages(payload).length > 0 || extractResponseMessageAudio(payload).length > 0;
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
return true;
|
|
1384
|
+
}
|
|
1314
1385
|
function extractMetadataFromRecords(records) {
|
|
1315
1386
|
let sessionId = "";
|
|
1316
1387
|
let version = "";
|
|
@@ -1477,14 +1548,35 @@ function parseCodexSession(jsonlText, options) {
|
|
|
1477
1548
|
let currentModel = metadata.model || null;
|
|
1478
1549
|
let lastTurnTimestamp = "";
|
|
1479
1550
|
let pendingCompaction = null;
|
|
1551
|
+
let sawTurnStart = false;
|
|
1480
1552
|
function createActiveTurn(turnId, timestamp, model) {
|
|
1481
|
-
const turn =
|
|
1553
|
+
const turn = createTurn2(turnId, timestamp, model);
|
|
1554
|
+
if (!sawTurnStart) turn.isFragment = true;
|
|
1482
1555
|
if (pendingCompaction) {
|
|
1483
1556
|
turn.compactionSummary = pendingCompaction;
|
|
1484
1557
|
pendingCompaction = null;
|
|
1485
1558
|
}
|
|
1486
1559
|
return turn;
|
|
1487
1560
|
}
|
|
1561
|
+
function upsertWebSearchCall(targetTurn, id, input, status, timestamp) {
|
|
1562
|
+
const existing = webSearchCalls.get(id);
|
|
1563
|
+
if (existing) {
|
|
1564
|
+
existing.input = { ...existing.input, ...input };
|
|
1565
|
+
existing.result = status;
|
|
1566
|
+
existing.isError = status === "failed";
|
|
1567
|
+
return;
|
|
1568
|
+
}
|
|
1569
|
+
const toolCall = {
|
|
1570
|
+
id,
|
|
1571
|
+
name: "WebSearch",
|
|
1572
|
+
input,
|
|
1573
|
+
result: status,
|
|
1574
|
+
isError: status === "failed",
|
|
1575
|
+
timestamp
|
|
1576
|
+
};
|
|
1577
|
+
webSearchCalls.set(id, toolCall);
|
|
1578
|
+
appendToolCall(targetTurn, toolCall, timestamp);
|
|
1579
|
+
}
|
|
1488
1580
|
for (const record of records) {
|
|
1489
1581
|
const payload = isObject2(record.payload) ? record.payload : void 0;
|
|
1490
1582
|
const timestamp = record.timestamp ?? "";
|
|
@@ -1500,12 +1592,14 @@ function parseCodexSession(jsonlText, options) {
|
|
|
1500
1592
|
currentTurnId = typeof payload?.turn_id === "string" ? payload.turn_id : null;
|
|
1501
1593
|
currentModel = typeof payload?.model === "string" ? payload.model : currentModel;
|
|
1502
1594
|
lastTurnTimestamp = timestamp;
|
|
1595
|
+
sawTurnStart = true;
|
|
1503
1596
|
continue;
|
|
1504
1597
|
}
|
|
1505
1598
|
if (record.type === "event_msg" && payload?.type === "user_message" && typeof payload.message === "string") {
|
|
1506
1599
|
if (current && (current.assistantText.length > 0 || current.toolCalls.length > 0 || current.thinking.length > 0)) {
|
|
1507
1600
|
current = finalizeTurn(turns, current, lastTurnTimestamp);
|
|
1508
1601
|
}
|
|
1602
|
+
sawTurnStart = true;
|
|
1509
1603
|
current ??= createActiveTurn(currentTurnId, timestamp, currentModel);
|
|
1510
1604
|
const localImages = (Array.isArray(payload.local_images) ? payload.local_images : []).filter(
|
|
1511
1605
|
(p) => typeof p === "string" && p.length > 0
|
|
@@ -1520,10 +1614,12 @@ function parseCodexSession(jsonlText, options) {
|
|
|
1520
1614
|
localImages,
|
|
1521
1615
|
localAudio
|
|
1522
1616
|
);
|
|
1617
|
+
current.isFragment = false;
|
|
1523
1618
|
current.timestamp = current.timestamp || timestamp;
|
|
1524
1619
|
lastTurnTimestamp = timestamp;
|
|
1525
1620
|
continue;
|
|
1526
1621
|
}
|
|
1622
|
+
if (!current && !recordOpensTurn(record, payload)) continue;
|
|
1527
1623
|
current ??= createActiveTurn(currentTurnId, timestamp, currentModel);
|
|
1528
1624
|
if (!current.model && currentModel) current.model = currentModel;
|
|
1529
1625
|
if (!current.timestamp) current.timestamp = timestamp;
|
|
@@ -1540,22 +1636,29 @@ function parseCodexSession(jsonlText, options) {
|
|
|
1540
1636
|
const input = {};
|
|
1541
1637
|
if (typeof payload.query === "string" && payload.query) input.query = payload.query;
|
|
1542
1638
|
if (isObject2(payload.action)) input.action = payload.action;
|
|
1543
|
-
|
|
1639
|
+
upsertWebSearchCall(current, payload.call_id, input, "completed", timestamp);
|
|
1640
|
+
continue;
|
|
1641
|
+
}
|
|
1642
|
+
if (record.type === "event_msg" && payload?.type === "mcp_tool_call_end" && typeof payload.call_id === "string" && isObject2(payload.invocation)) {
|
|
1643
|
+
const invocation = payload.invocation;
|
|
1644
|
+
const server = typeof invocation.server === "string" ? invocation.server : "mcp";
|
|
1645
|
+
const tool = typeof invocation.tool === "string" ? invocation.tool : "tool";
|
|
1646
|
+
const input = isObject2(invocation.arguments) ? invocation.arguments : {};
|
|
1647
|
+
const result = parseMcpEventResult(payload.result);
|
|
1648
|
+
const existing = pendingToolCalls.get(payload.call_id);
|
|
1544
1649
|
if (existing) {
|
|
1545
|
-
existing.
|
|
1546
|
-
existing.
|
|
1547
|
-
|
|
1548
|
-
} else {
|
|
1549
|
-
|
|
1650
|
+
existing.result = result.text;
|
|
1651
|
+
existing.isError = result.isError;
|
|
1652
|
+
pendingToolCalls.delete(payload.call_id);
|
|
1653
|
+
} else if (!isCodeModeChild(payload.call_id)) {
|
|
1654
|
+
appendToolCall(current, {
|
|
1550
1655
|
id: payload.call_id,
|
|
1551
|
-
name:
|
|
1656
|
+
name: `mcp__${server}__${tool}`,
|
|
1552
1657
|
input,
|
|
1553
|
-
result:
|
|
1554
|
-
isError:
|
|
1658
|
+
result: result.text,
|
|
1659
|
+
isError: result.isError,
|
|
1555
1660
|
timestamp
|
|
1556
|
-
};
|
|
1557
|
-
webSearchCalls.set(toolCall.id, toolCall);
|
|
1558
|
-
appendToolCall(current, toolCall, timestamp);
|
|
1661
|
+
}, timestamp);
|
|
1559
1662
|
}
|
|
1560
1663
|
continue;
|
|
1561
1664
|
}
|
|
@@ -1609,6 +1712,8 @@ function parseCodexSession(jsonlText, options) {
|
|
|
1609
1712
|
const audio = extractResponseMessageAudio(payload);
|
|
1610
1713
|
if (text || images.length > 0 || audio.length > 0) {
|
|
1611
1714
|
current.userMessage = buildCodexUserContent(text, images, audio);
|
|
1715
|
+
current.isFragment = false;
|
|
1716
|
+
sawTurnStart = true;
|
|
1612
1717
|
}
|
|
1613
1718
|
continue;
|
|
1614
1719
|
}
|
|
@@ -1616,25 +1721,38 @@ function parseCodexSession(jsonlText, options) {
|
|
|
1616
1721
|
const input = {};
|
|
1617
1722
|
if (isObject2(payload.action)) input.action = payload.action;
|
|
1618
1723
|
const status = typeof payload.status === "string" ? payload.status : "completed";
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1724
|
+
upsertWebSearchCall(current, payload.id, input, status, timestamp);
|
|
1725
|
+
continue;
|
|
1726
|
+
}
|
|
1727
|
+
if (payload.type === "tool_search_call") {
|
|
1728
|
+
const callId = findString(payload.call_id, payload.id) ?? "";
|
|
1729
|
+
if (callId) {
|
|
1625
1730
|
const toolCall = {
|
|
1626
|
-
id:
|
|
1627
|
-
name: "
|
|
1628
|
-
input,
|
|
1629
|
-
result:
|
|
1630
|
-
isError:
|
|
1731
|
+
id: callId,
|
|
1732
|
+
name: "ToolSearch",
|
|
1733
|
+
input: isObject2(payload.arguments) ? payload.arguments : {},
|
|
1734
|
+
result: null,
|
|
1735
|
+
isError: false,
|
|
1631
1736
|
timestamp
|
|
1632
1737
|
};
|
|
1633
|
-
|
|
1738
|
+
pendingToolCalls.set(callId, toolCall);
|
|
1634
1739
|
appendToolCall(current, toolCall, timestamp);
|
|
1635
1740
|
}
|
|
1636
1741
|
continue;
|
|
1637
1742
|
}
|
|
1743
|
+
if (payload.type === "tool_search_output" && typeof payload.call_id === "string") {
|
|
1744
|
+
const toolCall = pendingToolCalls.get(payload.call_id);
|
|
1745
|
+
if (toolCall) {
|
|
1746
|
+
const tools = Array.isArray(payload.tools) ? payload.tools : [];
|
|
1747
|
+
const names = tools.filter(isObject2).map((tool) => findString(tool.name) ?? "").filter(Boolean);
|
|
1748
|
+
const count = names.length > 0 ? names.length : tools.length;
|
|
1749
|
+
const found = `Found ${count} tool${count === 1 ? "" : "s"}`;
|
|
1750
|
+
toolCall.result = names.length > 0 ? `${found}: ${names.join(", ")}` : found;
|
|
1751
|
+
toolCall.isError = payload.status === "failed";
|
|
1752
|
+
pendingToolCalls.delete(payload.call_id);
|
|
1753
|
+
}
|
|
1754
|
+
continue;
|
|
1755
|
+
}
|
|
1638
1756
|
if (payload.type === "agent_message" && typeof payload.author === "string") {
|
|
1639
1757
|
const author = payload.author;
|
|
1640
1758
|
const recipient = typeof payload.recipient === "string" ? payload.recipient : null;
|
|
@@ -2527,7 +2645,11 @@ async function matchSubagentToMember(leadSessionId, subagentFileName, members) {
|
|
|
2527
2645
|
}
|
|
2528
2646
|
return null;
|
|
2529
2647
|
}
|
|
2530
|
-
|
|
2648
|
+
function encodeClaudeDirName(cwd) {
|
|
2649
|
+
const normalized = cwd.replace(/[\\/]+$/, "") || cwd;
|
|
2650
|
+
return normalized.replace(/[^a-zA-Z0-9]/g, "-");
|
|
2651
|
+
}
|
|
2652
|
+
var HOME_PREFIX = encodeClaudeDirName((0, import_node_os2.homedir)()).replace(/^-/, "").toLowerCase();
|
|
2531
2653
|
|
|
2532
2654
|
// src/commands/search.ts
|
|
2533
2655
|
async function searchSessions(query, opts, searchIndex) {
|
|
@@ -2879,6 +3001,7 @@ var import_promises3 = require("node:fs/promises");
|
|
|
2879
3001
|
var import_node_path5 = require("node:path");
|
|
2880
3002
|
var RESULT_TRUNCATE_LIMIT = 1e4;
|
|
2881
3003
|
var L1_RESPONSE_LIMIT = 15e4;
|
|
3004
|
+
var COMPACTION_SUMMARY_PREVIEW = 400;
|
|
2882
3005
|
function extractUserMessageText(userMessage) {
|
|
2883
3006
|
if (userMessage === null) return null;
|
|
2884
3007
|
if (typeof userMessage === "string") return userMessage;
|
|
@@ -2970,9 +3093,13 @@ function mapTurnToSummary(turn, turnIndex) {
|
|
|
2970
3093
|
subAgents,
|
|
2971
3094
|
hasThinking: turn.thinking.length > 0,
|
|
2972
3095
|
isError: turn.toolCalls.some((tc) => tc.isError),
|
|
2973
|
-
compactionSummary: turn.compactionSummary
|
|
3096
|
+
compactionSummary: truncateCompactionSummary(turn.compactionSummary)
|
|
2974
3097
|
};
|
|
2975
3098
|
}
|
|
3099
|
+
function truncateCompactionSummary(summary) {
|
|
3100
|
+
if (!summary) return null;
|
|
3101
|
+
return summary.length > COMPACTION_SUMMARY_PREVIEW ? summary.slice(0, COMPACTION_SUMMARY_PREVIEW) + "... [truncated, use L2 for full text]" : summary;
|
|
3102
|
+
}
|
|
2976
3103
|
function mapTurnToDetail(session, turnIndex) {
|
|
2977
3104
|
const turn = session.turns[turnIndex];
|
|
2978
3105
|
const contentBlocks = turn.contentBlocks.map((block) => mapContentBlock(block));
|
|
@@ -2980,6 +3107,7 @@ function mapTurnToDetail(session, turnIndex) {
|
|
|
2980
3107
|
sessionId: session.sessionId,
|
|
2981
3108
|
turnIndex,
|
|
2982
3109
|
userMessage: extractUserMessageText(turn.userMessage),
|
|
3110
|
+
compactionSummary: turn.compactionSummary ?? null,
|
|
2983
3111
|
contentBlocks,
|
|
2984
3112
|
tokenUsage: turn.tokenUsage ? { input: turn.tokenUsage.input_tokens, output: turn.tokenUsage.output_tokens } : null,
|
|
2985
3113
|
model: turn.model,
|
|
@@ -3013,6 +3141,14 @@ function mapContentBlock(block) {
|
|
|
3013
3141
|
};
|
|
3014
3142
|
case "queued_prompt":
|
|
3015
3143
|
return { kind: "queued_prompt", content: block.content, timestamp: block.timestamp ?? null };
|
|
3144
|
+
case "agent_message":
|
|
3145
|
+
return {
|
|
3146
|
+
kind: "agent_message",
|
|
3147
|
+
sender: block.sender,
|
|
3148
|
+
body: block.body,
|
|
3149
|
+
reply: block.reply ?? null,
|
|
3150
|
+
timestamp: block.timestamp ?? null
|
|
3151
|
+
};
|
|
3016
3152
|
case "hook_event":
|
|
3017
3153
|
return { kind: "hook_event", events: block.events, timestamp: block.timestamp ?? null };
|
|
3018
3154
|
case "plan_mode":
|
|
@@ -3026,6 +3162,12 @@ function mapContentBlock(block) {
|
|
|
3026
3162
|
};
|
|
3027
3163
|
case "recap":
|
|
3028
3164
|
return { kind: "recap", content: block.content, timestamp: block.timestamp ?? null };
|
|
3165
|
+
case "task_notification":
|
|
3166
|
+
return { kind: "task_notification", content: block.content, timestamp: block.timestamp ?? null };
|
|
3167
|
+
default: {
|
|
3168
|
+
const exhaustive = block;
|
|
3169
|
+
return exhaustive;
|
|
3170
|
+
}
|
|
3029
3171
|
}
|
|
3030
3172
|
}
|
|
3031
3173
|
async function findSubagentFile(parentJsonlPath, agentId) {
|
|
@@ -3164,17 +3306,99 @@ var import_node_path6 = require("node:path");
|
|
|
3164
3306
|
var import_promises4 = require("node:fs/promises");
|
|
3165
3307
|
|
|
3166
3308
|
// src/lib/sessionStatus.ts
|
|
3309
|
+
var TASK_NOTIFICATION_MARKER = "<task-notification>";
|
|
3310
|
+
var TASK_ID_RE = /<task-id>([^<]+)<\/task-id>/g;
|
|
3311
|
+
var TOOL_USE_ID_RE = /<tool-use-id>([^<]+)<\/tool-use-id>/g;
|
|
3312
|
+
function isObject3(value) {
|
|
3313
|
+
return typeof value === "object" && value !== null;
|
|
3314
|
+
}
|
|
3315
|
+
function userMessageTexts(msg) {
|
|
3316
|
+
const message = isObject3(msg.message) ? msg.message : null;
|
|
3317
|
+
const content = message?.content;
|
|
3318
|
+
if (typeof content === "string") return [content];
|
|
3319
|
+
if (!Array.isArray(content)) return [];
|
|
3320
|
+
return content.filter((block) => isObject3(block) && typeof block.text === "string").map((block) => block.text);
|
|
3321
|
+
}
|
|
3322
|
+
function collectClaudeBackgroundWork(rawMessages) {
|
|
3323
|
+
const launches = [];
|
|
3324
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
3325
|
+
const queuedIds = /* @__PURE__ */ new Set();
|
|
3326
|
+
const deliveredIds = /* @__PURE__ */ new Set();
|
|
3327
|
+
function noteNotifications(text, sink) {
|
|
3328
|
+
if (!text.includes(TASK_NOTIFICATION_MARKER)) return;
|
|
3329
|
+
for (const match of text.matchAll(TASK_ID_RE)) {
|
|
3330
|
+
resolved.add(match[1]);
|
|
3331
|
+
sink?.add(match[1]);
|
|
3332
|
+
}
|
|
3333
|
+
for (const match of text.matchAll(TOOL_USE_ID_RE)) {
|
|
3334
|
+
resolved.add(match[1]);
|
|
3335
|
+
sink?.add(match[1]);
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
for (const msg of rawMessages) {
|
|
3339
|
+
if (msg.type === "user") {
|
|
3340
|
+
const toolUseResult = isObject3(msg.toolUseResult) ? msg.toolUseResult : null;
|
|
3341
|
+
if (toolUseResult?.status === "async_launched") {
|
|
3342
|
+
const message = isObject3(msg.message) ? msg.message : null;
|
|
3343
|
+
const blocks = Array.isArray(message?.content) ? message.content : [];
|
|
3344
|
+
const toolUseIds = blocks.filter((block) => isObject3(block) && block.type === "tool_result").map((block) => block.tool_use_id);
|
|
3345
|
+
const ids = [toolUseResult.agentId, toolUseResult.taskId, ...toolUseIds].filter((id) => typeof id === "string" && id.length > 0);
|
|
3346
|
+
const description = [toolUseResult.description, toolUseResult.workflowName, toolUseResult.summary].find((value) => typeof value === "string" && value.length > 0) ?? "";
|
|
3347
|
+
if (ids.length > 0) launches.push({ ids, description });
|
|
3348
|
+
}
|
|
3349
|
+
for (const text of userMessageTexts(msg)) noteNotifications(text, deliveredIds);
|
|
3350
|
+
continue;
|
|
3351
|
+
}
|
|
3352
|
+
if (msg.type === "assistant") {
|
|
3353
|
+
const message = isObject3(msg.message) ? msg.message : null;
|
|
3354
|
+
const blocks = Array.isArray(message?.content) ? message.content : [];
|
|
3355
|
+
for (const block of blocks) {
|
|
3356
|
+
if (!isObject3(block) || block.type !== "tool_use" || block.name !== "TaskStop") continue;
|
|
3357
|
+
const input = isObject3(block.input) ? block.input : null;
|
|
3358
|
+
const taskId = input?.task_id ?? input?.shell_id;
|
|
3359
|
+
if (typeof taskId === "string") resolved.add(taskId);
|
|
3360
|
+
}
|
|
3361
|
+
continue;
|
|
3362
|
+
}
|
|
3363
|
+
if (msg.type === "attachment") {
|
|
3364
|
+
const attachment = isObject3(msg.attachment) ? msg.attachment : null;
|
|
3365
|
+
if (typeof attachment?.prompt === "string") noteNotifications(attachment.prompt, deliveredIds);
|
|
3366
|
+
continue;
|
|
3367
|
+
}
|
|
3368
|
+
if (msg.type === "queue-operation" && typeof msg.content === "string") {
|
|
3369
|
+
const op = msg.operation;
|
|
3370
|
+
noteNotifications(msg.content, op === "enqueue" ? queuedIds : deliveredIds);
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
return {
|
|
3374
|
+
pendingDescriptions: launches.filter((launch) => !launch.ids.some((id) => resolved.has(id))).map((launch) => launch.description),
|
|
3375
|
+
undeliveredNotifications: [...queuedIds].filter((id) => !deliveredIds.has(id)).length
|
|
3376
|
+
};
|
|
3377
|
+
}
|
|
3378
|
+
function awaitingAgentsResult(pendingDescriptions, pendingQueue) {
|
|
3379
|
+
return {
|
|
3380
|
+
status: "awaiting_agents",
|
|
3381
|
+
pendingQueue,
|
|
3382
|
+
pendingAgents: pendingDescriptions.length,
|
|
3383
|
+
pendingAgentDescriptions: pendingDescriptions.filter((description) => description.length > 0)
|
|
3384
|
+
};
|
|
3385
|
+
}
|
|
3167
3386
|
function deriveSessionStatus(rawMessages) {
|
|
3168
3387
|
const firstType = rawMessages[0]?.type;
|
|
3169
3388
|
if (firstType === "session_meta" || firstType === "turn_context" || firstType === "event_msg" || firstType === "response_item") {
|
|
3170
3389
|
return deriveCodexSessionStatus(rawMessages);
|
|
3171
3390
|
}
|
|
3172
3391
|
let pendingEnqueues = 0;
|
|
3392
|
+
let backgroundWork = null;
|
|
3393
|
+
let deliveringNotification = false;
|
|
3173
3394
|
function result(status, toolName) {
|
|
3174
3395
|
const info = { status, pendingQueue: Math.max(0, pendingEnqueues) };
|
|
3175
3396
|
if (toolName) info.toolName = toolName;
|
|
3176
3397
|
return info;
|
|
3177
3398
|
}
|
|
3399
|
+
function getBackgroundWork() {
|
|
3400
|
+
return backgroundWork ??= collectClaudeBackgroundWork(rawMessages);
|
|
3401
|
+
}
|
|
3178
3402
|
for (let i = rawMessages.length - 1; i >= 0; i--) {
|
|
3179
3403
|
const msg = rawMessages[i];
|
|
3180
3404
|
if (msg.type === "queue-operation") continue;
|
|
@@ -3191,15 +3415,26 @@ function deriveSessionStatus(rawMessages) {
|
|
|
3191
3415
|
for (let i = rawMessages.length - 1; i >= 0; i--) {
|
|
3192
3416
|
const msg = rawMessages[i];
|
|
3193
3417
|
if (msg.type === "queue-operation") {
|
|
3418
|
+
if (typeof msg.content === "string" && msg.content.includes(TASK_NOTIFICATION_MARKER)) continue;
|
|
3194
3419
|
const op = msg.operation;
|
|
3195
3420
|
if (op === "enqueue") pendingEnqueues++;
|
|
3196
3421
|
else if (op === "dequeue" || op === "remove") pendingEnqueues--;
|
|
3197
3422
|
continue;
|
|
3198
3423
|
}
|
|
3424
|
+
if (msg.type === "attachment") {
|
|
3425
|
+
const attachment = isObject3(msg.attachment) ? msg.attachment : null;
|
|
3426
|
+
if (attachment?.commandMode === "task-notification") deliveringNotification = true;
|
|
3427
|
+
continue;
|
|
3428
|
+
}
|
|
3199
3429
|
if (msg.type === "assistant") {
|
|
3200
3430
|
const message = msg.message;
|
|
3201
3431
|
const stopReason = message?.stop_reason;
|
|
3202
3432
|
if (stopReason === "end_turn") {
|
|
3433
|
+
const work = getBackgroundWork();
|
|
3434
|
+
if (deliveringNotification || work.undeliveredNotifications > 0) return result("processing");
|
|
3435
|
+
if (work.pendingDescriptions.length > 0) {
|
|
3436
|
+
return awaitingAgentsResult(work.pendingDescriptions, Math.max(0, pendingEnqueues));
|
|
3437
|
+
}
|
|
3203
3438
|
let hasActivity = false;
|
|
3204
3439
|
for (let j = i - 1; j >= 0; j--) {
|
|
3205
3440
|
const m = rawMessages[j];
|
|
@@ -3234,14 +3469,124 @@ function deriveSessionStatus(rawMessages) {
|
|
|
3234
3469
|
}
|
|
3235
3470
|
return { status: "idle" };
|
|
3236
3471
|
}
|
|
3472
|
+
var TERMINAL_INTER_AGENT_TYPES = /* @__PURE__ */ new Set(["FINAL_ANSWER", "ERROR", "FAILED", "INTERRUPTED"]);
|
|
3473
|
+
function codexPayloadText(payload) {
|
|
3474
|
+
const content = payload.content;
|
|
3475
|
+
if (!Array.isArray(content)) return "";
|
|
3476
|
+
return content.filter((block) => isObject3(block) && block.type === "input_text" && typeof block.text === "string").map((block) => block.text).join("\n");
|
|
3477
|
+
}
|
|
3478
|
+
function parseJsonObject(value) {
|
|
3479
|
+
if (typeof value !== "string") return null;
|
|
3480
|
+
try {
|
|
3481
|
+
const parsed = JSON.parse(value);
|
|
3482
|
+
return isObject3(parsed) ? parsed : null;
|
|
3483
|
+
} catch {
|
|
3484
|
+
return null;
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
function collectCodexPendingAgents(rawMessages) {
|
|
3488
|
+
const alive = /* @__PURE__ */ new Map();
|
|
3489
|
+
const done = /* @__PURE__ */ new Set();
|
|
3490
|
+
const agentIdByPath = /* @__PURE__ */ new Map();
|
|
3491
|
+
const spawnCalls = /* @__PURE__ */ new Map();
|
|
3492
|
+
const waitCalls = /* @__PURE__ */ new Set();
|
|
3493
|
+
const interruptCalls = /* @__PURE__ */ new Map();
|
|
3494
|
+
function markAlive(agentId, description) {
|
|
3495
|
+
if (!done.has(agentId)) alive.set(agentId, alive.get(agentId) || description);
|
|
3496
|
+
}
|
|
3497
|
+
for (const msg of rawMessages) {
|
|
3498
|
+
const payload = isObject3(msg.payload) ? msg.payload : null;
|
|
3499
|
+
if (!payload) continue;
|
|
3500
|
+
if (msg.type === "event_msg" && payload.type === "sub_agent_activity" && typeof payload.agent_thread_id === "string" && typeof payload.agent_path === "string") {
|
|
3501
|
+
const agentId = payload.agent_thread_id;
|
|
3502
|
+
const kind = typeof payload.kind === "string" ? payload.kind : "started";
|
|
3503
|
+
const eventId = typeof payload.event_id === "string" ? payload.event_id : "";
|
|
3504
|
+
const existingId = agentIdByPath.get(payload.agent_path);
|
|
3505
|
+
const known = alive.has(agentId) || done.has(agentId) || existingId !== void 0 || spawnCalls.has(eventId);
|
|
3506
|
+
if (kind !== "started" && !known) continue;
|
|
3507
|
+
if (existingId && existingId !== agentId) {
|
|
3508
|
+
if (alive.has(existingId)) {
|
|
3509
|
+
markAlive(agentId, alive.get(existingId) ?? "");
|
|
3510
|
+
alive.delete(existingId);
|
|
3511
|
+
}
|
|
3512
|
+
if (done.has(existingId)) done.add(agentId);
|
|
3513
|
+
}
|
|
3514
|
+
agentIdByPath.set(payload.agent_path, agentId);
|
|
3515
|
+
if (kind === "interrupted") {
|
|
3516
|
+
done.add(agentId);
|
|
3517
|
+
alive.delete(agentId);
|
|
3518
|
+
} else {
|
|
3519
|
+
markAlive(agentId, spawnCalls.get(eventId) ?? "");
|
|
3520
|
+
}
|
|
3521
|
+
continue;
|
|
3522
|
+
}
|
|
3523
|
+
if (msg.type !== "response_item") continue;
|
|
3524
|
+
if (payload.type === "function_call" && typeof payload.call_id === "string") {
|
|
3525
|
+
const name = normalizeFunctionName(typeof payload.name === "string" ? payload.name : "");
|
|
3526
|
+
if (name === "spawn_agent") {
|
|
3527
|
+
const input = parseJsonObject(payload.arguments);
|
|
3528
|
+
spawnCalls.set(payload.call_id, typeof input?.task_name === "string" ? input.task_name : "");
|
|
3529
|
+
} else if (name === "wait_agent") {
|
|
3530
|
+
waitCalls.add(payload.call_id);
|
|
3531
|
+
} else if (name === "interrupt_agent") {
|
|
3532
|
+
const input = parseJsonObject(payload.arguments);
|
|
3533
|
+
const targets = [input?.agent_id, ...Array.isArray(input?.agent_ids) ? input.agent_ids : []].filter((id) => typeof id === "string");
|
|
3534
|
+
interruptCalls.set(payload.call_id, targets);
|
|
3535
|
+
}
|
|
3536
|
+
continue;
|
|
3537
|
+
}
|
|
3538
|
+
if (payload.type === "function_call_output" && typeof payload.call_id === "string") {
|
|
3539
|
+
const callId = payload.call_id;
|
|
3540
|
+
if (spawnCalls.has(callId)) {
|
|
3541
|
+
const output = parseJsonObject(payload.output);
|
|
3542
|
+
const agentId = typeof output?.agent_id === "string" ? output.agent_id : "";
|
|
3543
|
+
if (agentId) {
|
|
3544
|
+
markAlive(agentId, spawnCalls.get(callId) ?? "");
|
|
3545
|
+
const agentPath = typeof output?.task_name === "string" ? output.task_name : "";
|
|
3546
|
+
if (agentPath && !agentIdByPath.has(agentPath)) agentIdByPath.set(agentPath, agentId);
|
|
3547
|
+
}
|
|
3548
|
+
} else if (waitCalls.has(callId)) {
|
|
3549
|
+
const output = parseJsonObject(payload.output);
|
|
3550
|
+
const statusMap = isObject3(output?.status) ? output.status : {};
|
|
3551
|
+
for (const [agentId, status] of Object.entries(statusMap)) {
|
|
3552
|
+
const statusValue = isObject3(status) ? status : {};
|
|
3553
|
+
const lifecycle = typeof statusValue.status === "string" ? statusValue.status : "";
|
|
3554
|
+
const isTerminal = typeof statusValue.completed === "string" || typeof statusValue.failed === "string" || typeof statusValue.error === "string" || typeof statusValue.interrupted === "string" || lifecycle === "completed" || lifecycle === "failed" || lifecycle === "interrupted";
|
|
3555
|
+
if (isTerminal) {
|
|
3556
|
+
done.add(agentId);
|
|
3557
|
+
alive.delete(agentId);
|
|
3558
|
+
}
|
|
3559
|
+
}
|
|
3560
|
+
} else if (interruptCalls.has(callId)) {
|
|
3561
|
+
for (const agentId of interruptCalls.get(callId) ?? []) {
|
|
3562
|
+
done.add(agentId);
|
|
3563
|
+
alive.delete(agentId);
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
continue;
|
|
3567
|
+
}
|
|
3568
|
+
if (payload.type === "agent_message" && typeof payload.author === "string") {
|
|
3569
|
+
const text = codexPayloadText(payload);
|
|
3570
|
+
const messageType = text.match(/^Message Type:\s*([^\r\n]+)/)?.[1]?.trim().toUpperCase() ?? "";
|
|
3571
|
+
if (!TERMINAL_INTER_AGENT_TYPES.has(messageType)) continue;
|
|
3572
|
+
const agentId = agentIdByPath.get(payload.author) ?? payload.author;
|
|
3573
|
+
done.add(agentId);
|
|
3574
|
+
alive.delete(agentId);
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
return [...alive.entries()].filter(([agentId]) => !done.has(agentId)).map(([, description]) => description);
|
|
3578
|
+
}
|
|
3237
3579
|
function deriveCodexSessionStatus(rawMessages) {
|
|
3238
3580
|
for (let i = rawMessages.length - 1; i >= 0; i--) {
|
|
3239
3581
|
const msg = rawMessages[i];
|
|
3240
3582
|
if (msg.type === "event_msg") {
|
|
3241
3583
|
const payload = msg.payload;
|
|
3242
3584
|
switch (payload?.type) {
|
|
3243
|
-
case "task_complete":
|
|
3585
|
+
case "task_complete": {
|
|
3586
|
+
const pending = collectCodexPendingAgents(rawMessages);
|
|
3587
|
+
if (pending.length > 0) return awaitingAgentsResult(pending, 0);
|
|
3244
3588
|
return { status: "completed" };
|
|
3589
|
+
}
|
|
3245
3590
|
case "task_started":
|
|
3246
3591
|
return { status: "processing" };
|
|
3247
3592
|
case "agent_message":
|
|
@@ -3254,7 +3599,7 @@ function deriveCodexSessionStatus(rawMessages) {
|
|
|
3254
3599
|
const payload = msg.payload;
|
|
3255
3600
|
if (!payload) continue;
|
|
3256
3601
|
if (payload.type === "function_call") {
|
|
3257
|
-
return { status: "tool_use", toolName: payload.name };
|
|
3602
|
+
return { status: "tool_use", toolName: payload.name ? normalizeFunctionName(payload.name) : payload.name };
|
|
3258
3603
|
}
|
|
3259
3604
|
if (payload.type === "message") {
|
|
3260
3605
|
if (payload.role === "assistant") return { status: "thinking" };
|
|
@@ -3553,8 +3898,7 @@ async function listSessions(opts = {}) {
|
|
|
3553
3898
|
return results;
|
|
3554
3899
|
}
|
|
3555
3900
|
async function currentSession(cwd) {
|
|
3556
|
-
const
|
|
3557
|
-
const projectDir = (0, import_node_path6.join)(dirs.PROJECTS_DIR, projectDirName);
|
|
3901
|
+
const projectDir = (0, import_node_path6.join)(dirs.PROJECTS_DIR, encodeClaudeDirName(cwd));
|
|
3558
3902
|
let files;
|
|
3559
3903
|
try {
|
|
3560
3904
|
files = await (0, import_promises5.readdir)(projectDir);
|