codesesh 0.18.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/dist/{chunk-HIYT2ZJL.js → chunk-BEF6LLRG.js} +2982 -473
- package/dist/chunk-BEF6LLRG.js.map +1 -0
- package/dist/{chunk-DHNP33L4.js → chunk-TIHF4T3K.js} +2 -2
- package/dist/{dist-DMGLFBW6.js → dist-3F467V44.js} +16 -2
- package/dist/index.js +218 -52
- package/dist/index.js.map +1 -1
- package/dist/scan-refresh-worker.js +243 -24
- package/dist/scan-refresh-worker.js.map +1 -1
- package/dist/search-index-worker.js +9 -11
- package/dist/search-index-worker.js.map +1 -1
- package/dist/smart-tag-worker.js +2 -2
- package/dist/web/assets/ErrorBoundary-BY-N1QzJ.js +1 -0
- package/dist/web/assets/InteractiveReceipt-DYGyHeZd.js +1 -0
- package/dist/web/assets/OverviewScreen-_ZaVjhxN.js +1 -0
- package/dist/web/assets/PrismHighlighter-BZkkX0AU.js +6 -0
- package/dist/web/assets/Projects-q6cEm2dc.js +1 -0
- package/dist/web/assets/SearchFilterBar-CrVBpo40.js +1 -0
- package/dist/web/assets/SearchResultsPanel-Cg5V8Ia6.js +1 -0
- package/dist/web/assets/SessionDetail-BSSuddsZ.js +54 -0
- package/dist/web/assets/{session-title-CNhapdPJ.js → SmartTagChips-ux6idUGJ.js} +1 -1
- package/dist/web/assets/chunk-62JRHF6Z-BWRsEBnL.js +4 -0
- package/dist/web/assets/code-metrics-UYFsCC7E.js +1 -0
- package/dist/web/assets/ibm-plex-sans-cyrillic-ext-wght-normal-d45eAU9y.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-cyrillic-wght-normal-BAAhND-U.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-greek-wght-normal-CmyJS8uq.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-latin-ext-wght-normal-CIII54If.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-latin-wght-normal-IvpUvPa2.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-vietnamese-wght-normal-Dg1JeJN0.woff2 +0 -0
- package/dist/web/assets/index-C3KgxGwE.js +1410 -0
- package/dist/web/assets/index-DQX42cFQ.css +1 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/dist/web/assets/jsx-runtime-B-hcVAMW.js +1 -0
- package/dist/web/assets/panel-B7kuE60V.js +1 -0
- package/dist/web/assets/session-indexes-DmnjZ_JL.js +1 -0
- package/dist/web/assets/session-title-DdtlxtlR.js +1 -0
- package/dist/web/assets/space-grotesk-latin-ext-wght-normal-D9tNdqV9.woff2 +0 -0
- package/dist/web/assets/space-grotesk-latin-wght-normal-BhU9QXUp.woff2 +0 -0
- package/dist/web/assets/space-grotesk-vietnamese-wght-normal-D0rl6rjA.woff2 +0 -0
- package/dist/web/assets/utils-t2u9IbNT.js +1 -0
- package/dist/web/icon/agent/grok.svg +1 -0
- package/dist/web/index.html +43 -31
- package/package.json +8 -8
- package/dist/chunk-HIYT2ZJL.js.map +0 -1
- package/dist/web/assets/AgentIcon-DcUJM198.js +0 -1
- package/dist/web/assets/BookmarkButton-CJjTjyae.js +0 -1
- package/dist/web/assets/Dashboard-wjTLT17d.js +0 -39
- package/dist/web/assets/DialogTitle-Bz0eXTGu.js +0 -1
- package/dist/web/assets/InteractiveReceipt-xnRqJvfd.js +0 -1
- package/dist/web/assets/PrismHighlighter-C7OfXHq2.js +0 -6
- package/dist/web/assets/Projects-DBxubQuM.js +0 -1
- package/dist/web/assets/SearchFilterBar-BG9ah_Po.js +0 -1
- package/dist/web/assets/SearchResultsPanel-Clmf2xKw.js +0 -1
- package/dist/web/assets/SessionDetail-CgR7ipmi.js +0 -54
- package/dist/web/assets/index-DR07vs5V.js +0 -1890
- package/dist/web/assets/index-DuB6tGoe.css +0 -2
- package/dist/web/assets/index.dom-CCLJ9_FR.js +0 -1
- package/dist/web/assets/jsx-runtime-n5LQ9ujS.js +0 -1
- package/dist/web/assets/preload-helper-CmKXJxR3.js +0 -1
- package/dist/web/assets/session-indexes-EHJdYE4j.js +0 -4
- package/dist/web/assets/useTheme-CwDcVdGz.js +0 -1
- package/dist/web/assets/utils-B6KiDbIe.js +0 -1
- /package/dist/{chunk-DHNP33L4.js.map → chunk-TIHF4T3K.js.map} +0 -0
- /package/dist/{dist-DMGLFBW6.js.map → dist-3F467V44.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// ../core/dist/chunk-
|
|
3
|
+
// ../core/dist/chunk-TXYCGCSU.mjs
|
|
4
4
|
function toRecord(value) {
|
|
5
5
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
6
6
|
}
|
|
@@ -123,14 +123,14 @@ function normalizeMessageParts(value) {
|
|
|
123
123
|
function mergeSessionsUpdatedEvents(previous, next) {
|
|
124
124
|
const changedSessionHeads = /* @__PURE__ */ new Map();
|
|
125
125
|
const removedSessionRefs = /* @__PURE__ */ new Map();
|
|
126
|
-
const
|
|
126
|
+
const sessionKey2 = (agentName, sessionId) => `${agentName}\0${sessionId}`;
|
|
127
127
|
const addChanged = (item) => {
|
|
128
|
-
const key =
|
|
128
|
+
const key = sessionKey2(item.reference.agentName, item.reference.sessionId);
|
|
129
129
|
removedSessionRefs.delete(key);
|
|
130
130
|
changedSessionHeads.set(key, item);
|
|
131
131
|
};
|
|
132
132
|
const addRemoved = (item) => {
|
|
133
|
-
const key =
|
|
133
|
+
const key = sessionKey2(item.agentName, item.sessionId);
|
|
134
134
|
changedSessionHeads.delete(key);
|
|
135
135
|
removedSessionRefs.set(key, item);
|
|
136
136
|
};
|
|
@@ -253,6 +253,159 @@ function mergeSortedSessions(shards) {
|
|
|
253
253
|
}
|
|
254
254
|
return merged;
|
|
255
255
|
}
|
|
256
|
+
function getSessionRouteKey(agentName, sessionId) {
|
|
257
|
+
return formatSessionReference({ agentName, sessionId });
|
|
258
|
+
}
|
|
259
|
+
function sessionKey(session) {
|
|
260
|
+
return getSessionRouteKey(getSessionAgentKey(session), session.id);
|
|
261
|
+
}
|
|
262
|
+
function parentKey(session) {
|
|
263
|
+
return session.parent_reference ? formatSessionReference(session.parent_reference) : null;
|
|
264
|
+
}
|
|
265
|
+
function activityTime(session) {
|
|
266
|
+
return session.time_updated ?? session.time_created;
|
|
267
|
+
}
|
|
268
|
+
function hasActivityInWindow(session, from, to) {
|
|
269
|
+
const activity = activityTime(session);
|
|
270
|
+
return (from == null || activity >= from) && (to == null || activity <= to);
|
|
271
|
+
}
|
|
272
|
+
function isChildSession(session) {
|
|
273
|
+
return session.parent_reference != null;
|
|
274
|
+
}
|
|
275
|
+
function getRootSessions(sessions) {
|
|
276
|
+
return sessions.filter((session) => !session.parent_reference);
|
|
277
|
+
}
|
|
278
|
+
function ownStats(session) {
|
|
279
|
+
const stats = session.stats;
|
|
280
|
+
const cost = stats.total_cost ?? 0;
|
|
281
|
+
return {
|
|
282
|
+
messageCount: stats.message_count,
|
|
283
|
+
inputTokens: stats.total_input_tokens,
|
|
284
|
+
outputTokens: stats.total_output_tokens,
|
|
285
|
+
cacheReadTokens: stats.total_cache_read_tokens ?? 0,
|
|
286
|
+
cacheCreateTokens: stats.total_cache_create_tokens ?? 0,
|
|
287
|
+
totalTokens: stats.total_tokens ?? stats.total_input_tokens + stats.total_output_tokens,
|
|
288
|
+
cost,
|
|
289
|
+
costSource: cost > 0 ? stats.cost_source ?? "recorded" : void 0
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function addStats(target, source) {
|
|
293
|
+
target.messageCount += source.messageCount;
|
|
294
|
+
target.inputTokens += source.inputTokens;
|
|
295
|
+
target.outputTokens += source.outputTokens;
|
|
296
|
+
target.cacheReadTokens += source.cacheReadTokens;
|
|
297
|
+
target.cacheCreateTokens += source.cacheCreateTokens;
|
|
298
|
+
target.totalTokens += source.totalTokens;
|
|
299
|
+
target.cost += source.cost;
|
|
300
|
+
if (source.costSource === "estimated") target.costSource = "estimated";
|
|
301
|
+
}
|
|
302
|
+
function rollUpSubtree(entry) {
|
|
303
|
+
const order = [];
|
|
304
|
+
const pending2 = [entry];
|
|
305
|
+
while (pending2.length > 0) {
|
|
306
|
+
const node = pending2.pop();
|
|
307
|
+
order.push(node);
|
|
308
|
+
for (const child of node.children) pending2.push(child);
|
|
309
|
+
}
|
|
310
|
+
for (let index = order.length - 1; index >= 0; index -= 1) {
|
|
311
|
+
const node = order[index];
|
|
312
|
+
const stats = node.inclusiveStats;
|
|
313
|
+
let descendants = 0;
|
|
314
|
+
for (const child of node.children) {
|
|
315
|
+
descendants += 1 + child.descendantCount;
|
|
316
|
+
addStats(stats, child.inclusiveStats);
|
|
317
|
+
}
|
|
318
|
+
if (stats.cost > 0 && stats.costSource == null) stats.costSource = "recorded";
|
|
319
|
+
node.descendantCount = descendants;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
function buildSessionTree(sessions) {
|
|
323
|
+
const byRouteKey = /* @__PURE__ */ new Map();
|
|
324
|
+
const nodes = sessions.map((session) => ({
|
|
325
|
+
session,
|
|
326
|
+
children: [],
|
|
327
|
+
descendantCount: 0,
|
|
328
|
+
inclusiveStats: ownStats(session)
|
|
329
|
+
}));
|
|
330
|
+
for (const node of nodes) {
|
|
331
|
+
const key = sessionKey(node.session);
|
|
332
|
+
if (!byRouteKey.has(key)) byRouteKey.set(key, node);
|
|
333
|
+
}
|
|
334
|
+
const chainTerminates = /* @__PURE__ */ new Map();
|
|
335
|
+
function reachesRoot(startKey) {
|
|
336
|
+
const path2 = [];
|
|
337
|
+
const onPath = /* @__PURE__ */ new Set();
|
|
338
|
+
let key = startKey;
|
|
339
|
+
let terminates = true;
|
|
340
|
+
while (key != null) {
|
|
341
|
+
const memo = chainTerminates.get(key);
|
|
342
|
+
if (memo != null) {
|
|
343
|
+
terminates = memo;
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
if (onPath.has(key)) {
|
|
347
|
+
terminates = false;
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
onPath.add(key);
|
|
351
|
+
path2.push(key);
|
|
352
|
+
const parent = parentKey(byRouteKey.get(key).session);
|
|
353
|
+
key = parent != null && byRouteKey.has(parent) ? parent : null;
|
|
354
|
+
}
|
|
355
|
+
for (const visited of path2) chainTerminates.set(visited, terminates);
|
|
356
|
+
return terminates;
|
|
357
|
+
}
|
|
358
|
+
const mountStates = /* @__PURE__ */ new Map();
|
|
359
|
+
const roots = [];
|
|
360
|
+
const orphans = [];
|
|
361
|
+
const entries = [];
|
|
362
|
+
for (const node of nodes) {
|
|
363
|
+
const key = sessionKey(node.session);
|
|
364
|
+
const parent = parentKey(node.session);
|
|
365
|
+
const state = parent == null ? "root" : byRouteKey.has(parent) && reachesRoot(key) ? "mounted-child" : "orphan";
|
|
366
|
+
mountStates.set(key, state);
|
|
367
|
+
if (state === "mounted-child") {
|
|
368
|
+
byRouteKey.get(parent).children.push(node);
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
(state === "root" ? roots : orphans).push(node);
|
|
372
|
+
entries.push(node);
|
|
373
|
+
}
|
|
374
|
+
for (const entry of entries) rollUpSubtree(entry);
|
|
375
|
+
return {
|
|
376
|
+
roots,
|
|
377
|
+
orphans,
|
|
378
|
+
entries,
|
|
379
|
+
byRouteKey,
|
|
380
|
+
mountStateOf(session) {
|
|
381
|
+
return mountStates.get(sessionKey(session)) ?? (session.parent_reference ? "orphan" : "root");
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
function filterSessionTreeByActivityWindow(sessions, from, to) {
|
|
386
|
+
if (from == null && to == null) return sessions;
|
|
387
|
+
const available = new Set(sessions.map(sessionKey));
|
|
388
|
+
const childrenByParent = /* @__PURE__ */ new Map();
|
|
389
|
+
const pending2 = [];
|
|
390
|
+
for (const session of sessions) {
|
|
391
|
+
const parent = parentKey(session);
|
|
392
|
+
if (parent != null && available.has(parent)) {
|
|
393
|
+
const children = childrenByParent.get(parent);
|
|
394
|
+
if (children) children.push(sessionKey(session));
|
|
395
|
+
else childrenByParent.set(parent, [sessionKey(session)]);
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
if (hasActivityInWindow(session, from, to)) pending2.push(sessionKey(session));
|
|
399
|
+
}
|
|
400
|
+
const visible = /* @__PURE__ */ new Set();
|
|
401
|
+
while (pending2.length > 0) {
|
|
402
|
+
const key = pending2.pop();
|
|
403
|
+
if (visible.has(key)) continue;
|
|
404
|
+
visible.add(key);
|
|
405
|
+
for (const childKey of childrenByParent.get(key) ?? []) pending2.push(childKey);
|
|
406
|
+
}
|
|
407
|
+
return sessions.filter((session) => visible.has(sessionKey(session)));
|
|
408
|
+
}
|
|
256
409
|
var SAMPLE_SESSION_HEAD = {
|
|
257
410
|
id: "session-1",
|
|
258
411
|
slug: "claudecode/session-1",
|
|
@@ -301,9 +454,15 @@ var SAMPLE_DASHBOARD_DATA = {
|
|
|
301
454
|
messages: 12,
|
|
302
455
|
tokens: 6e3,
|
|
303
456
|
cost: 0.042,
|
|
457
|
+
costRecorded: 0.042,
|
|
458
|
+
costEstimated: 0,
|
|
459
|
+
cacheReadTokens: 3e3,
|
|
304
460
|
cost_source: "recorded",
|
|
305
|
-
latestActivity: 17000036e5
|
|
461
|
+
latestActivity: 17000036e5,
|
|
462
|
+
latestActivityProject: "example/project",
|
|
463
|
+
latestActivityAgent: "claudecode"
|
|
306
464
|
},
|
|
465
|
+
scopeCounts: { projects: 1, agents: 1 },
|
|
307
466
|
perAgent: [
|
|
308
467
|
{
|
|
309
468
|
name: "claudecode",
|
|
@@ -311,14 +470,38 @@ var SAMPLE_DASHBOARD_DATA = {
|
|
|
311
470
|
icon: "claude",
|
|
312
471
|
sessions: 1,
|
|
313
472
|
messages: 12,
|
|
314
|
-
tokens: 6e3
|
|
473
|
+
tokens: 6e3,
|
|
474
|
+
cost: 0.042
|
|
315
475
|
}
|
|
316
476
|
],
|
|
317
|
-
dailyActivity: [
|
|
318
|
-
|
|
319
|
-
|
|
477
|
+
dailyActivity: [
|
|
478
|
+
{
|
|
479
|
+
date: "2023-11-14",
|
|
480
|
+
sessions: 1,
|
|
481
|
+
messages: 12,
|
|
482
|
+
cost: 0.042,
|
|
483
|
+
input: 700,
|
|
484
|
+
output: 1800,
|
|
485
|
+
cache_read: 3e3,
|
|
486
|
+
cache_create: 500
|
|
487
|
+
}
|
|
320
488
|
],
|
|
321
489
|
modelDistribution: [{ model: "claude-5-sonnet", tokens: 6e3, sessions: 1 }],
|
|
490
|
+
perProject: [
|
|
491
|
+
{
|
|
492
|
+
identityKind: "git_remote",
|
|
493
|
+
identityKey: "github.com/example/project",
|
|
494
|
+
displayName: "example/project",
|
|
495
|
+
sessions: 1,
|
|
496
|
+
messages: 12,
|
|
497
|
+
tokens: 6e3,
|
|
498
|
+
cost: 0.042,
|
|
499
|
+
cost_source: "recorded",
|
|
500
|
+
agents: ["claudecode"],
|
|
501
|
+
sparkline: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.042]
|
|
502
|
+
}
|
|
503
|
+
],
|
|
504
|
+
projectRollup: { projects: 0, sessions: 0, tokens: 0, cost: 0 },
|
|
322
505
|
recentSessions: [
|
|
323
506
|
{
|
|
324
507
|
reference: { agentName: "claudecode", sessionId: SAMPLE_SESSION_HEAD.id },
|
|
@@ -326,6 +509,7 @@ var SAMPLE_DASHBOARD_DATA = {
|
|
|
326
509
|
}
|
|
327
510
|
],
|
|
328
511
|
recentFileActivities: [],
|
|
512
|
+
modelCost: null,
|
|
329
513
|
window: { from: 16999e8, to: 17000036e5, days: 1 }
|
|
330
514
|
};
|
|
331
515
|
|
|
@@ -352,28 +536,40 @@ import { createRequire } from "module";
|
|
|
352
536
|
import { createHash } from "crypto";
|
|
353
537
|
import { existsSync as existsSync7, readFileSync as readFileSync3, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
|
|
354
538
|
import { join as join8, basename as basename5, dirname as dirname4 } from "path";
|
|
355
|
-
import {
|
|
356
|
-
import {
|
|
357
|
-
import {
|
|
539
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4, readdirSync as readdirSync5, statSync as statSync5 } from "fs";
|
|
540
|
+
import { basename as basename6, dirname as dirname5, join as join9, resolve } from "path";
|
|
541
|
+
import {
|
|
542
|
+
closeSync as closeSync2,
|
|
543
|
+
existsSync as existsSync9,
|
|
544
|
+
openSync as openSync2,
|
|
545
|
+
readdirSync as readdirSync6,
|
|
546
|
+
readFileSync as readFileSync5,
|
|
547
|
+
readSync as readSync2,
|
|
548
|
+
statSync as statSync6
|
|
549
|
+
} from "fs";
|
|
550
|
+
import { join as join10, basename as basename7 } from "path";
|
|
551
|
+
import { existsSync as existsSync10, readdirSync as readdirSync7, readFileSync as readFileSync6, statSync as statSync7 } from "fs";
|
|
358
552
|
import { homedir as homedir3, platform as platform2 } from "os";
|
|
359
|
-
import { join as
|
|
360
|
-
import { existsSync as
|
|
361
|
-
import { basename as
|
|
553
|
+
import { join as join11, normalize } from "path";
|
|
554
|
+
import { existsSync as existsSync11 } from "fs";
|
|
555
|
+
import { basename as basename8, join as join12 } from "path";
|
|
362
556
|
import { homedir as homedir4, platform as platform3 } from "os";
|
|
363
|
-
import { join as
|
|
557
|
+
import { join as join13 } from "path";
|
|
558
|
+
import { existsSync as existsSync12, readFileSync as readFileSync7, statSync as statSync8 } from "fs";
|
|
559
|
+
import { dirname as dirname6, join as join14 } from "path";
|
|
364
560
|
import { availableParallelism } from "os";
|
|
365
561
|
import { Worker } from "worker_threads";
|
|
366
|
-
import { existsSync as
|
|
562
|
+
import { existsSync as existsSync13, readFileSync as readFileSync8 } from "fs";
|
|
367
563
|
import { spawnSync } from "child_process";
|
|
368
564
|
import * as os from "os";
|
|
369
565
|
import * as path from "path";
|
|
370
|
-
import { resolve, sep } from "path";
|
|
371
|
-
import { existsSync as
|
|
372
|
-
import { existsSync as
|
|
373
|
-
import { join as
|
|
566
|
+
import { resolve as resolve2, sep } from "path";
|
|
567
|
+
import { existsSync as existsSync15, rmSync as rmSync2, unlinkSync } from "fs";
|
|
568
|
+
import { existsSync as existsSync14 } from "fs";
|
|
569
|
+
import { join as join15 } from "path";
|
|
374
570
|
import { homedir as homedir6 } from "os";
|
|
375
571
|
import { homedir as homedir7, platform as platform4 } from "os";
|
|
376
|
-
import { join as
|
|
572
|
+
import { join as join16 } from "path";
|
|
377
573
|
var registrations = [];
|
|
378
574
|
function registerAgent(reg) {
|
|
379
575
|
registrations.push(reg);
|
|
@@ -432,9 +628,9 @@ function filteredSession(reason) {
|
|
|
432
628
|
function getParsedSession(result) {
|
|
433
629
|
return result.status === "parsed" ? result.data : null;
|
|
434
630
|
}
|
|
435
|
-
function matchesScanWindow(
|
|
436
|
-
if (options?.from != null &&
|
|
437
|
-
if (options?.to != null &&
|
|
631
|
+
function matchesScanWindow(activityTime2, options) {
|
|
632
|
+
if (options?.from != null && activityTime2 < options.from) return false;
|
|
633
|
+
if (options?.to != null && activityTime2 > options.to) return false;
|
|
438
634
|
return true;
|
|
439
635
|
}
|
|
440
636
|
function readCachedMeta(meta, sessionId) {
|
|
@@ -468,6 +664,9 @@ function diffSessionSources(refs, cachedSessions, cachedMeta, options) {
|
|
|
468
664
|
return { changedIds, removedIds };
|
|
469
665
|
}
|
|
470
666
|
var BaseAgent = class {
|
|
667
|
+
filterCachedSessions(sessions) {
|
|
668
|
+
return sessions;
|
|
669
|
+
}
|
|
471
670
|
getUri(sessionId) {
|
|
472
671
|
return `${this.name}://${sessionId}`;
|
|
473
672
|
}
|
|
@@ -475,6 +674,14 @@ var BaseAgent = class {
|
|
|
475
674
|
var FileSystemSessionSource = class extends BaseAgent {
|
|
476
675
|
sessionMetaMap = /* @__PURE__ */ new Map();
|
|
477
676
|
sourceFileStats = /* @__PURE__ */ new Map();
|
|
677
|
+
/**
|
|
678
|
+
* 变更集合扩展:当某些会话变更会影响其他会话的派生数据时
|
|
679
|
+
* (如 subagent 文件变更需要父会话重新聚合 token 统计),
|
|
680
|
+
* 子类返回需要一并重解析的会话 ID 集合。默认无关联,原样返回。
|
|
681
|
+
*/
|
|
682
|
+
expandChangedSessionIds(changedIds, _refs) {
|
|
683
|
+
return changedIds;
|
|
684
|
+
}
|
|
478
685
|
scan(options) {
|
|
479
686
|
const sources = this.listSessionSources(options);
|
|
480
687
|
const sessions = [];
|
|
@@ -572,10 +779,11 @@ var FileSystemSessionSource = class extends BaseAgent {
|
|
|
572
779
|
* refs 未传时回退为自行枚举,供独立调用方(如测试)沿用旧行为。
|
|
573
780
|
*/
|
|
574
781
|
incrementalScan(cachedSessions, changedIds, refs) {
|
|
782
|
+
const sources = refs ?? this.listSessionSources();
|
|
575
783
|
const sessionMap = new Map(cachedSessions.map((session) => [session.id, session]));
|
|
576
|
-
const changedSet = new Set(changedIds);
|
|
784
|
+
const changedSet = new Set(this.expandChangedSessionIds(changedIds, sources));
|
|
577
785
|
const currentIds = /* @__PURE__ */ new Set();
|
|
578
|
-
for (const ref of
|
|
786
|
+
for (const ref of sources) {
|
|
579
787
|
currentIds.add(ref.sessionId);
|
|
580
788
|
if (!changedSet.has(ref.sessionId)) continue;
|
|
581
789
|
const head = this.scanSessionSource(ref.sourcePath);
|
|
@@ -1527,7 +1735,7 @@ var TranscriptBuilder = class {
|
|
|
1527
1735
|
};
|
|
1528
1736
|
}
|
|
1529
1737
|
};
|
|
1530
|
-
var HEAD_INDEX_VERSION = "claudecode-head-
|
|
1738
|
+
var HEAD_INDEX_VERSION = "claudecode-head-v4";
|
|
1531
1739
|
function resolveClaudeCodeDataRoot() {
|
|
1532
1740
|
return resolveHomePath("CLAUDE_CONFIG_DIR", ".claude");
|
|
1533
1741
|
}
|
|
@@ -1583,6 +1791,10 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1583
1791
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1584
1792
|
sessionsIndexCache = {};
|
|
1585
1793
|
sessionsIndexMtime = {};
|
|
1794
|
+
childContextsBySource = /* @__PURE__ */ new Map();
|
|
1795
|
+
childContextCache = /* @__PURE__ */ new Map();
|
|
1796
|
+
childSessionIdByToolUseId = /* @__PURE__ */ new Map();
|
|
1797
|
+
childIndexReady = false;
|
|
1586
1798
|
findBasePath() {
|
|
1587
1799
|
return firstExisting(join5(resolveClaudeCodeDataRoot(), "projects"), "data/claudecode");
|
|
1588
1800
|
}
|
|
@@ -1615,18 +1827,97 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1615
1827
|
const indexPath = this.getSessionsIndexPath(projectDir);
|
|
1616
1828
|
indexMtimes.set(projectDir, this.readFileMtimeMs(indexPath));
|
|
1617
1829
|
}
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
})
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1830
|
+
const projectDirSet = new Set(projectDirs);
|
|
1831
|
+
const allSources = this.walkFiles(projectDirs, (entry) => entry.name.endsWith(".jsonl"), {
|
|
1832
|
+
recursive: true
|
|
1833
|
+
});
|
|
1834
|
+
this.indexChildContexts(allSources, projectDirs);
|
|
1835
|
+
const indexedSources = allSources.flatMap((source) => {
|
|
1836
|
+
const child = this.childContextsBySource.get(source.file);
|
|
1837
|
+
if (!child && !projectDirSet.has(dirname2(source.file))) return [];
|
|
1838
|
+
return [
|
|
1839
|
+
{
|
|
1840
|
+
source,
|
|
1841
|
+
sessionId: child?.sessionId ?? basename3(source.file, ".jsonl"),
|
|
1842
|
+
child
|
|
1843
|
+
}
|
|
1844
|
+
];
|
|
1845
|
+
});
|
|
1846
|
+
let selectedSources = indexedSources.filter(
|
|
1847
|
+
({ source }) => matchesScanWindow(source.stat.mtimeMs, options)
|
|
1848
|
+
);
|
|
1849
|
+
if (options?.from != null || options?.to != null) {
|
|
1850
|
+
const childrenByParent = /* @__PURE__ */ new Map();
|
|
1851
|
+
for (const indexed of indexedSources) {
|
|
1852
|
+
const parentId = indexed.child?.parentSessionId;
|
|
1853
|
+
if (!parentId) continue;
|
|
1854
|
+
const children = childrenByParent.get(parentId);
|
|
1855
|
+
if (children) children.push(indexed);
|
|
1856
|
+
else childrenByParent.set(parentId, [indexed]);
|
|
1857
|
+
}
|
|
1858
|
+
const windowSelectedById = new Map(
|
|
1859
|
+
selectedSources.map((indexed) => [indexed.sessionId, indexed])
|
|
1860
|
+
);
|
|
1861
|
+
const connectedSelected = /* @__PURE__ */ new Map();
|
|
1862
|
+
const acceptedIds = /* @__PURE__ */ new Set();
|
|
1863
|
+
const visitingIds = /* @__PURE__ */ new Set();
|
|
1864
|
+
const hasSelectedParent = (sessionId) => {
|
|
1865
|
+
if (acceptedIds.has(sessionId)) return true;
|
|
1866
|
+
if (visitingIds.has(sessionId)) return false;
|
|
1867
|
+
const indexed = windowSelectedById.get(sessionId);
|
|
1868
|
+
if (!indexed) return false;
|
|
1869
|
+
const parentId = indexed.child?.parentSessionId;
|
|
1870
|
+
if (!parentId) {
|
|
1871
|
+
acceptedIds.add(sessionId);
|
|
1872
|
+
return true;
|
|
1873
|
+
}
|
|
1874
|
+
visitingIds.add(sessionId);
|
|
1875
|
+
const connected = hasSelectedParent(parentId);
|
|
1876
|
+
visitingIds.delete(sessionId);
|
|
1877
|
+
if (connected) acceptedIds.add(sessionId);
|
|
1878
|
+
return connected;
|
|
1879
|
+
};
|
|
1880
|
+
for (const indexed of selectedSources) {
|
|
1881
|
+
if (hasSelectedParent(indexed.sessionId)) {
|
|
1882
|
+
connectedSelected.set(indexed.sessionId, indexed);
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
if (options?.includeRelatedSessions !== false) {
|
|
1886
|
+
const pending2 = [...connectedSelected.values()].filter(({ child }) => !child?.parentSessionId).map(({ sessionId }) => sessionId);
|
|
1887
|
+
while (pending2.length > 0) {
|
|
1888
|
+
const parentId = pending2.pop();
|
|
1889
|
+
for (const child of childrenByParent.get(parentId) ?? []) {
|
|
1890
|
+
if (connectedSelected.has(child.sessionId)) continue;
|
|
1891
|
+
connectedSelected.set(child.sessionId, child);
|
|
1892
|
+
pending2.push(child.sessionId);
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
selectedSources = [...connectedSelected.values()];
|
|
1897
|
+
}
|
|
1898
|
+
return selectedSources.map(({ source: { file, stat }, sessionId, child }) => {
|
|
1899
|
+
const projectDir = child?.projectDir ?? dirname2(file);
|
|
1900
|
+
return {
|
|
1901
|
+
sessionId,
|
|
1902
|
+
sourcePath: file,
|
|
1903
|
+
fingerprint: this.sourceFingerprint(
|
|
1904
|
+
stat,
|
|
1905
|
+
indexMtimes.get(projectDir) ?? null,
|
|
1906
|
+
child?.metaMtimeMs
|
|
1907
|
+
)
|
|
1908
|
+
};
|
|
1909
|
+
});
|
|
1910
|
+
}
|
|
1911
|
+
setSessionMetaMap(meta) {
|
|
1912
|
+
super.setSessionMetaMap(meta);
|
|
1913
|
+
this.childContextsBySource.clear();
|
|
1914
|
+
this.childSessionIdByToolUseId.clear();
|
|
1915
|
+
this.childIndexReady = false;
|
|
1626
1916
|
}
|
|
1627
1917
|
parseFileSessionHead(sourcePath) {
|
|
1628
|
-
const
|
|
1629
|
-
|
|
1918
|
+
const child = this.getChildContext(sourcePath);
|
|
1919
|
+
const projectDir = child?.projectDir ?? dirname2(sourcePath);
|
|
1920
|
+
return getParsedSession(this.parseSessionHeadResult(sourcePath, projectDir, child));
|
|
1630
1921
|
}
|
|
1631
1922
|
getSessionData(sessionId) {
|
|
1632
1923
|
const meta = this.sessionMetaMap.get(sessionId);
|
|
@@ -1636,12 +1927,19 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1636
1927
|
if (!existsSync5(meta.sourcePath)) {
|
|
1637
1928
|
throw new Error(`Session file missing: ${meta.sourcePath}`);
|
|
1638
1929
|
}
|
|
1930
|
+
this.ensureChildIndex();
|
|
1639
1931
|
const builder = new TranscriptBuilder();
|
|
1640
1932
|
const assistantUuidToToolCalls = /* @__PURE__ */ new Map();
|
|
1641
1933
|
const countedUsageKeys = /* @__PURE__ */ new Set();
|
|
1642
1934
|
for (const record of readJsonlFile(meta.sourcePath)) {
|
|
1643
1935
|
try {
|
|
1644
|
-
this.convertRecord(
|
|
1936
|
+
this.convertRecord(
|
|
1937
|
+
record,
|
|
1938
|
+
builder,
|
|
1939
|
+
assistantUuidToToolCalls,
|
|
1940
|
+
countedUsageKeys,
|
|
1941
|
+
this.childSessionIdByToolUseId
|
|
1942
|
+
);
|
|
1645
1943
|
} catch {
|
|
1646
1944
|
}
|
|
1647
1945
|
}
|
|
@@ -1652,6 +1950,7 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1652
1950
|
title: meta.title,
|
|
1653
1951
|
slug: `claudecode/${meta.id}`,
|
|
1654
1952
|
directory: meta.directory,
|
|
1953
|
+
parent_reference: meta.parentSessionId == null ? void 0 : { agentName: this.name, sessionId: meta.parentSessionId },
|
|
1655
1954
|
version: void 0,
|
|
1656
1955
|
time_created: meta.createdAt,
|
|
1657
1956
|
time_updated: meta.updatedAt,
|
|
@@ -1668,25 +1967,125 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1668
1967
|
return [];
|
|
1669
1968
|
}
|
|
1670
1969
|
}
|
|
1970
|
+
ensureChildIndex() {
|
|
1971
|
+
if (this.childIndexReady) return;
|
|
1972
|
+
this.basePath ??= this.findBasePath();
|
|
1973
|
+
if (!this.basePath) {
|
|
1974
|
+
this.childIndexReady = true;
|
|
1975
|
+
return;
|
|
1976
|
+
}
|
|
1977
|
+
const projectDirs = this.listProjectDirs();
|
|
1978
|
+
this.indexChildContexts(
|
|
1979
|
+
this.walkFiles(projectDirs, (entry) => entry.name.endsWith(".jsonl"), {
|
|
1980
|
+
recursive: true
|
|
1981
|
+
}),
|
|
1982
|
+
projectDirs
|
|
1983
|
+
);
|
|
1984
|
+
}
|
|
1985
|
+
indexChildContexts(sources, projectDirs) {
|
|
1986
|
+
this.childContextsBySource.clear();
|
|
1987
|
+
this.childSessionIdByToolUseId.clear();
|
|
1988
|
+
const projectDirSet = new Set(projectDirs);
|
|
1989
|
+
const contexts = /* @__PURE__ */ new Map();
|
|
1990
|
+
const knownSessionIds = /* @__PURE__ */ new Set();
|
|
1991
|
+
for (const source of sources) {
|
|
1992
|
+
const child = this.readChildContext(source.file);
|
|
1993
|
+
if (!child) {
|
|
1994
|
+
if (projectDirSet.has(dirname2(source.file))) {
|
|
1995
|
+
knownSessionIds.add(basename3(source.file, ".jsonl"));
|
|
1996
|
+
}
|
|
1997
|
+
continue;
|
|
1998
|
+
}
|
|
1999
|
+
contexts.set(source.file, child);
|
|
2000
|
+
knownSessionIds.add(child.sessionId);
|
|
2001
|
+
}
|
|
2002
|
+
for (const [sourcePath, child] of contexts) {
|
|
2003
|
+
const parentSessionId = child.parentSessionId && knownSessionIds.has(child.parentSessionId) ? child.parentSessionId : null;
|
|
2004
|
+
const normalized = parentSessionId === child.parentSessionId ? child : { ...child, parentSessionId };
|
|
2005
|
+
this.childContextsBySource.set(sourcePath, normalized);
|
|
2006
|
+
if (child.toolUseId) {
|
|
2007
|
+
this.childSessionIdByToolUseId.set(child.toolUseId, normalized.sessionId);
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
this.childIndexReady = true;
|
|
2011
|
+
}
|
|
2012
|
+
getChildContext(sourcePath) {
|
|
2013
|
+
const cached = this.childContextsBySource.get(sourcePath);
|
|
2014
|
+
if (cached) return cached;
|
|
2015
|
+
const child = this.readChildContext(sourcePath);
|
|
2016
|
+
if (!child) return null;
|
|
2017
|
+
this.childContextsBySource.set(sourcePath, child);
|
|
2018
|
+
if (child.toolUseId) {
|
|
2019
|
+
this.childSessionIdByToolUseId.set(child.toolUseId, child.sessionId);
|
|
2020
|
+
}
|
|
2021
|
+
return child;
|
|
2022
|
+
}
|
|
2023
|
+
readChildContext(sourcePath) {
|
|
2024
|
+
const subagentsDir = dirname2(sourcePath);
|
|
2025
|
+
if (basename3(subagentsDir) !== "subagents") return null;
|
|
2026
|
+
const parentDir = dirname2(subagentsDir);
|
|
2027
|
+
const projectDir = dirname2(parentDir);
|
|
2028
|
+
const fileStem = basename3(sourcePath, ".jsonl");
|
|
2029
|
+
const metaPath = join5(subagentsDir, fileStem + ".meta.json");
|
|
2030
|
+
const metaMtimeMs = this.readFileMtimeMs(metaPath);
|
|
2031
|
+
const cached = this.childContextCache.get(sourcePath);
|
|
2032
|
+
if (cached?.metaMtimeMs === metaMtimeMs) return cached.context;
|
|
2033
|
+
let metadata = null;
|
|
2034
|
+
if (metaMtimeMs !== null) {
|
|
2035
|
+
try {
|
|
2036
|
+
metadata = asRecord(JSON.parse(readFileSync2(metaPath, "utf-8"))) ?? null;
|
|
2037
|
+
} catch {
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
const sessionId = asString(metadata?.["agentId"])?.trim() || fileStem.replace(/^agent-/, "");
|
|
2041
|
+
if (!sessionId) return null;
|
|
2042
|
+
const parentAgentId = asString(metadata?.["parentAgentId"])?.trim();
|
|
2043
|
+
const candidateParentId = parentAgentId || basename3(parentDir) || null;
|
|
2044
|
+
const parentSessionId = candidateParentId && this.hasChildParent(sourcePath, projectDir, candidateParentId) ? candidateParentId : null;
|
|
2045
|
+
const name = asString(metadata?.["name"])?.trim();
|
|
2046
|
+
const description = asString(metadata?.["description"])?.trim();
|
|
2047
|
+
const context = {
|
|
2048
|
+
sessionId,
|
|
2049
|
+
projectDir,
|
|
2050
|
+
parentSessionId,
|
|
2051
|
+
explicitTitle: name || description || null,
|
|
2052
|
+
metaMtimeMs,
|
|
2053
|
+
toolUseId: asString(metadata?.["toolUseId"])?.trim() || null
|
|
2054
|
+
};
|
|
2055
|
+
this.childContextCache.set(sourcePath, { metaMtimeMs, context });
|
|
2056
|
+
return context;
|
|
2057
|
+
}
|
|
2058
|
+
hasChildParent(sourcePath, projectDir, parentSessionId) {
|
|
2059
|
+
const subagentsDir = dirname2(sourcePath);
|
|
2060
|
+
return [
|
|
2061
|
+
join5(projectDir, parentSessionId + ".jsonl"),
|
|
2062
|
+
join5(subagentsDir, "agent-" + parentSessionId + ".jsonl"),
|
|
2063
|
+
join5(subagentsDir, parentSessionId + ".jsonl")
|
|
2064
|
+
].some((path2) => existsSync5(path2));
|
|
2065
|
+
}
|
|
1671
2066
|
createFileSessionMeta(head, source) {
|
|
1672
|
-
const
|
|
2067
|
+
const child = this.getChildContext(source.file);
|
|
2068
|
+
const projectDir = child?.projectDir ?? dirname2(source.file);
|
|
1673
2069
|
const indexPath = this.getSessionsIndexPath(projectDir);
|
|
1674
2070
|
const indexMtime = this.readFileMtimeMs(indexPath);
|
|
1675
2071
|
return this.buildFileSessionMeta({
|
|
1676
2072
|
head,
|
|
1677
2073
|
source,
|
|
1678
|
-
fingerprint: this.sourceFingerprint(source.stat, indexMtime),
|
|
2074
|
+
fingerprint: this.sourceFingerprint(source.stat, indexMtime, child?.metaMtimeMs),
|
|
1679
2075
|
extras: {
|
|
1680
2076
|
indexPath: indexMtime === null ? null : indexPath,
|
|
1681
2077
|
indexMtimeMs: indexMtime,
|
|
1682
2078
|
headIndexVersion: HEAD_INDEX_VERSION,
|
|
1683
|
-
model: head.stats.total_tokens ? "unknown" : void 0
|
|
2079
|
+
model: head.stats.total_tokens ? "unknown" : void 0,
|
|
2080
|
+
parentSessionId: head.parent_reference?.sessionId ?? null
|
|
1684
2081
|
}
|
|
1685
2082
|
});
|
|
1686
2083
|
}
|
|
1687
2084
|
/** Fingerprint depends on an already-fetched stat to avoid re-statting the same file. */
|
|
1688
|
-
sourceFingerprint(stat, indexMtime) {
|
|
1689
|
-
|
|
2085
|
+
sourceFingerprint(stat, indexMtime, metaMtime) {
|
|
2086
|
+
const fingerprint = [HEAD_INDEX_VERSION, stat.mtimeMs, stat.size, indexMtime];
|
|
2087
|
+
if (metaMtime !== void 0) fingerprint.push(metaMtime);
|
|
2088
|
+
return JSON.stringify(fingerprint);
|
|
1690
2089
|
}
|
|
1691
2090
|
getSessionsIndexPath(projectDir) {
|
|
1692
2091
|
return join5(projectDir, "sessions-index.json");
|
|
@@ -1716,11 +2115,11 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1716
2115
|
this.sessionsIndexMtime[cacheKey] = mtime;
|
|
1717
2116
|
return map;
|
|
1718
2117
|
}
|
|
1719
|
-
parseSessionHeadResult(filePath, projectDir) {
|
|
1720
|
-
const sessionId = basename3(filePath, ".jsonl");
|
|
2118
|
+
parseSessionHeadResult(filePath, projectDir, child) {
|
|
2119
|
+
const sessionId = child?.sessionId ?? basename3(filePath, ".jsonl");
|
|
1721
2120
|
const index = this.loadSessionsIndex(projectDir);
|
|
1722
2121
|
const indexEntry = index.get(sessionId);
|
|
1723
|
-
const explicitTitle = indexEntry?.summary ? String(indexEntry.summary) : null;
|
|
2122
|
+
const explicitTitle = child?.explicitTitle ?? (indexEntry?.summary ? String(indexEntry.summary) : null);
|
|
1724
2123
|
let createdAt = 0;
|
|
1725
2124
|
let updatedAt = 0;
|
|
1726
2125
|
let lineIndex = 0;
|
|
@@ -1819,6 +2218,7 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1819
2218
|
slug: `claudecode/${sessionId}`,
|
|
1820
2219
|
title,
|
|
1821
2220
|
directory,
|
|
2221
|
+
parent_reference: child?.parentSessionId == null ? void 0 : { agentName: this.name, sessionId: child.parentSessionId },
|
|
1822
2222
|
time_created: createdAt,
|
|
1823
2223
|
time_updated: updatedAt,
|
|
1824
2224
|
stats: {
|
|
@@ -1851,19 +2251,25 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1851
2251
|
return null;
|
|
1852
2252
|
}
|
|
1853
2253
|
// --- Record conversion ---
|
|
1854
|
-
convertRecord(data, builder, assistantUuidToToolCalls, countedUsageKeys) {
|
|
2254
|
+
convertRecord(data, builder, assistantUuidToToolCalls, countedUsageKeys, childSessionIdByToolUseId) {
|
|
1855
2255
|
if (data["isMeta"] === true) return;
|
|
1856
2256
|
const msgType = String(data["type"] ?? "");
|
|
1857
2257
|
if (isInternalEventType(msgType)) return;
|
|
1858
2258
|
if (msgType === "assistant") {
|
|
1859
|
-
this.convertAssistantRecord(
|
|
2259
|
+
this.convertAssistantRecord(
|
|
2260
|
+
data,
|
|
2261
|
+
builder,
|
|
2262
|
+
assistantUuidToToolCalls,
|
|
2263
|
+
countedUsageKeys,
|
|
2264
|
+
childSessionIdByToolUseId
|
|
2265
|
+
);
|
|
1860
2266
|
} else if (msgType === "user") {
|
|
1861
2267
|
this.convertUserRecord(data, builder, assistantUuidToToolCalls);
|
|
1862
2268
|
} else if (msgType === "tool_result") {
|
|
1863
2269
|
this.convertToolResultRecord(data, builder);
|
|
1864
2270
|
}
|
|
1865
2271
|
}
|
|
1866
|
-
convertAssistantRecord(data, builder, assistantUuidToToolCalls, countedUsageKeys) {
|
|
2272
|
+
convertAssistantRecord(data, builder, assistantUuidToToolCalls, countedUsageKeys, childSessionIdByToolUseId) {
|
|
1867
2273
|
const msg = asRecord(data["message"]) ?? {};
|
|
1868
2274
|
const timestampMs = parseTimestampMs(data);
|
|
1869
2275
|
const rawContent = asArray(msg["content"]) ?? [];
|
|
@@ -1903,12 +2309,14 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1903
2309
|
}
|
|
1904
2310
|
if (partType !== "tool_use") continue;
|
|
1905
2311
|
const toolCallId = String(part["id"] ?? "").trim();
|
|
1906
|
-
const
|
|
2312
|
+
const subagentId = childSessionIdByToolUseId.get(toolCallId);
|
|
2313
|
+
const toolPart2 = this.buildToolPart(part, timestampMs);
|
|
1907
2314
|
const message = builder.appendToolCall(
|
|
1908
|
-
|
|
1909
|
-
{ id: uuid, timestampMs, agent: "claude" },
|
|
2315
|
+
toolPart2,
|
|
2316
|
+
{ id: uuid, timestampMs, agent: "claude", subagentId },
|
|
1910
2317
|
{ modeOnCreate: "tool" }
|
|
1911
2318
|
);
|
|
2319
|
+
if (subagentId) message.subagent_id = subagentId;
|
|
1912
2320
|
this.applyAssistantMetadata(message, data, msg, countedUsageKeys);
|
|
1913
2321
|
if (toolCallId) {
|
|
1914
2322
|
toolCallIds.push(toolCallId);
|
|
@@ -2231,7 +2639,7 @@ function runSchemaMigrations(db, options) {
|
|
|
2231
2639
|
migration.migrate(db);
|
|
2232
2640
|
setUserVersion(db, migration.version);
|
|
2233
2641
|
});
|
|
2234
|
-
apply();
|
|
2642
|
+
apply.immediate();
|
|
2235
2643
|
currentVersion = migration.version;
|
|
2236
2644
|
getCoreDiagnostics()?.info?.("sqlite.migration.completed", {
|
|
2237
2645
|
label: options.backupLabel,
|
|
@@ -2285,10 +2693,10 @@ function openDb(dbPath) {
|
|
|
2285
2693
|
const db = DatabaseConstructor(dbPath);
|
|
2286
2694
|
restrictPrivateDatabase(dbPath);
|
|
2287
2695
|
try {
|
|
2696
|
+
db.pragma("busy_timeout = 5000");
|
|
2288
2697
|
db.pragma("journal_mode = WAL");
|
|
2289
2698
|
db.pragma("synchronous = NORMAL");
|
|
2290
2699
|
db.pragma("foreign_keys = ON");
|
|
2291
|
-
db.pragma("busy_timeout = 5000");
|
|
2292
2700
|
} catch {
|
|
2293
2701
|
}
|
|
2294
2702
|
return db;
|
|
@@ -2305,6 +2713,24 @@ function isSqliteAvailable() {
|
|
|
2305
2713
|
return DatabaseConstructor !== null;
|
|
2306
2714
|
}
|
|
2307
2715
|
var MESSAGE_ROLES = /* @__PURE__ */ new Set(["user", "assistant", "tool"]);
|
|
2716
|
+
var SESSION_ID_QUERY_CHUNK_SIZE = 500;
|
|
2717
|
+
function compareSessionRowsByActivityDesc(left, right) {
|
|
2718
|
+
const leftUpdated = Number(left.time_updated ?? left.time_created ?? 0);
|
|
2719
|
+
const rightUpdated = Number(right.time_updated ?? right.time_created ?? 0);
|
|
2720
|
+
return rightUpdated - leftUpdated || Number(right.time_created ?? 0) - Number(left.time_created ?? 0) || String(right.id ?? "").localeCompare(String(left.id ?? ""));
|
|
2721
|
+
}
|
|
2722
|
+
function accumulateTokenStats(stats, msgData, agentName) {
|
|
2723
|
+
const cost = Number(msgData.cost ?? 0);
|
|
2724
|
+
const tokens = parseTokens(msgData.tokens, agentName);
|
|
2725
|
+
const inputTokens = Number(tokens?.input ?? 0);
|
|
2726
|
+
const outputTokens = Number(tokens?.output ?? 0);
|
|
2727
|
+
const model = parseModel(msgData.modelID, agentName);
|
|
2728
|
+
const estimatedCost = cost > 0 ? null : estimateTokenCost(model, { input: inputTokens, output: outputTokens });
|
|
2729
|
+
if (estimatedCost !== null) stats.cost_source = "estimated";
|
|
2730
|
+
stats.total_cost += cost || estimatedCost || 0;
|
|
2731
|
+
stats.total_input_tokens += inputTokens;
|
|
2732
|
+
stats.total_output_tokens += outputTokens;
|
|
2733
|
+
}
|
|
2308
2734
|
function parseJsonRecord(raw, agentName, field) {
|
|
2309
2735
|
const parsed = asRecord(JSON.parse(String(raw ?? "{}")));
|
|
2310
2736
|
if (parsed) return parsed;
|
|
@@ -2360,28 +2786,48 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2360
2786
|
const hasMessageTable = Boolean(
|
|
2361
2787
|
db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'message'").get()
|
|
2362
2788
|
);
|
|
2789
|
+
const hasTaskType = columnExists(db, "session", "task_type");
|
|
2790
|
+
const hasParentId = columnExists(db, "session", "parent_id");
|
|
2791
|
+
const childPredicate = hasParentId ? "AND s.parent_id IS NULL" : hasTaskType ? "AND (s.task_type IS NULL OR s.task_type != 'subagent_child')" : "";
|
|
2363
2792
|
let rows;
|
|
2793
|
+
const parentIdSelect = hasParentId ? ", s.parent_id" : "";
|
|
2364
2794
|
if (hasMessageTable) {
|
|
2365
2795
|
rows = db.prepare(`
|
|
2366
2796
|
SELECT
|
|
2367
2797
|
s.id, s.title, s.time_created, s.time_updated, s.slug, s.directory,
|
|
2368
|
-
s.version, s.summary_files
|
|
2798
|
+
s.version, s.summary_files${parentIdSelect}
|
|
2369
2799
|
FROM session s
|
|
2370
2800
|
WHERE COALESCE(s.time_updated, s.time_created) >= ?
|
|
2371
|
-
|
|
2801
|
+
${childPredicate}
|
|
2802
|
+
ORDER BY COALESCE(s.time_updated, s.time_created) DESC, s.time_created DESC, s.id DESC
|
|
2372
2803
|
`).all(cutoffTime);
|
|
2373
2804
|
} else {
|
|
2374
2805
|
rows = db.prepare(`
|
|
2375
2806
|
SELECT s.id, s.title, s.time_created, s.time_updated, s.slug, s.directory,
|
|
2376
|
-
s.version, s.summary_files, 0 AS message_count, NULL AS model_message_data
|
|
2807
|
+
s.version, s.summary_files, 0 AS message_count, NULL AS model_message_data${parentIdSelect}
|
|
2377
2808
|
FROM session s
|
|
2378
2809
|
WHERE COALESCE(s.time_updated, s.time_created) >= ?
|
|
2379
|
-
|
|
2810
|
+
${childPredicate}
|
|
2811
|
+
ORDER BY COALESCE(s.time_updated, s.time_created) DESC, s.time_created DESC, s.id DESC
|
|
2380
2812
|
`).all(cutoffTime);
|
|
2381
2813
|
}
|
|
2814
|
+
if (hasParentId && options?.includeRelatedSessions !== false) {
|
|
2815
|
+
const rootIds = rows.map((row) => String(row.id ?? "")).filter(Boolean);
|
|
2816
|
+
const relatedRows = this.readRelatedSessionRows(db, rootIds);
|
|
2817
|
+
const knownIds = new Set(rootIds);
|
|
2818
|
+
rows.push(...relatedRows.filter((row) => !knownIds.has(String(row.id ?? ""))));
|
|
2819
|
+
}
|
|
2820
|
+
rows.sort(compareSessionRowsByActivityDesc);
|
|
2821
|
+
const sessionIds = new Set(rows.map((row) => String(row.id ?? "")).filter(Boolean));
|
|
2382
2822
|
const headContexts = hasMessageTable ? this.buildHeadContexts(
|
|
2383
|
-
this.readHeadMessageRows(
|
|
2384
|
-
|
|
2823
|
+
this.readHeadMessageRows(
|
|
2824
|
+
db,
|
|
2825
|
+
cutoffTime,
|
|
2826
|
+
hasParentId,
|
|
2827
|
+
sessionIds.size > 0 ? sessionIds : void 0
|
|
2828
|
+
),
|
|
2829
|
+
this.readHeadPartRows(db, cutoffTime, sessionIds.size > 0 ? sessionIds : void 0),
|
|
2830
|
+
hasParentId
|
|
2385
2831
|
) : /* @__PURE__ */ new Map();
|
|
2386
2832
|
const heads = [];
|
|
2387
2833
|
options?.onProgress?.({ total: rows.length, processed: 0, sessions: 0 });
|
|
@@ -2423,6 +2869,7 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2423
2869
|
slug: `${this.name}/${id}`,
|
|
2424
2870
|
title: resolveSessionTitle(String(row.title ?? ""), messageTitle, null),
|
|
2425
2871
|
directory: String(row.directory ?? ""),
|
|
2872
|
+
parent_reference: row.parent_id == null || String(row.parent_id) === "" ? void 0 : { agentName: this.name, sessionId: String(row.parent_id) },
|
|
2426
2873
|
time_created: timeCreated,
|
|
2427
2874
|
time_updated: timeUpdated,
|
|
2428
2875
|
stats: {
|
|
@@ -2434,28 +2881,103 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2434
2881
|
}
|
|
2435
2882
|
});
|
|
2436
2883
|
}
|
|
2437
|
-
readHeadMessageRows(db, cutoffTime) {
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2884
|
+
readHeadMessageRows(db, cutoffTime, withParentId, sessionIds) {
|
|
2885
|
+
const parentIdSelect = withParentId ? ", s.parent_id" : "";
|
|
2886
|
+
const ids = sessionIds ? [...sessionIds] : [];
|
|
2887
|
+
if (ids.length === 0) {
|
|
2888
|
+
return db.prepare(
|
|
2889
|
+
`
|
|
2890
|
+
SELECT m.id, m.session_id, m.data, m.time_created${parentIdSelect}
|
|
2891
|
+
FROM message m
|
|
2892
|
+
JOIN session s ON s.id = m.session_id
|
|
2893
|
+
WHERE COALESCE(s.time_updated, s.time_created) >= ?
|
|
2894
|
+
ORDER BY m.session_id, m.time_created ASC
|
|
2895
|
+
`
|
|
2896
|
+
).all(cutoffTime);
|
|
2897
|
+
}
|
|
2898
|
+
const rows = [];
|
|
2899
|
+
for (let offset = 0; offset < ids.length; offset += SESSION_ID_QUERY_CHUNK_SIZE) {
|
|
2900
|
+
const chunk = ids.slice(offset, offset + SESSION_ID_QUERY_CHUNK_SIZE);
|
|
2901
|
+
rows.push(
|
|
2902
|
+
...db.prepare(
|
|
2903
|
+
`
|
|
2904
|
+
SELECT m.id, m.session_id, m.data, m.time_created${parentIdSelect}
|
|
2905
|
+
FROM message m
|
|
2906
|
+
JOIN session s ON s.id = m.session_id
|
|
2907
|
+
WHERE s.id IN (${chunk.map(() => "?").join(",")})
|
|
2908
|
+
ORDER BY m.session_id, m.time_created ASC
|
|
2909
|
+
`
|
|
2910
|
+
).all(...chunk)
|
|
2911
|
+
);
|
|
2912
|
+
}
|
|
2913
|
+
return rows;
|
|
2914
|
+
}
|
|
2915
|
+
readHeadPartRows(db, cutoffTime, sessionIds) {
|
|
2916
|
+
const ids = sessionIds ? [...sessionIds] : [];
|
|
2917
|
+
if (ids.length === 0) {
|
|
2918
|
+
return db.prepare(
|
|
2445
2919
|
`
|
|
2446
|
-
|
|
2920
|
+
SELECT p.message_id, p.data, p.time_created
|
|
2921
|
+
FROM part p
|
|
2922
|
+
JOIN message m ON m.id = p.message_id
|
|
2923
|
+
JOIN session s ON s.id = m.session_id
|
|
2924
|
+
WHERE COALESCE(s.time_updated, s.time_created) >= ?
|
|
2925
|
+
ORDER BY p.message_id, p.time_created ASC, p.id ASC
|
|
2926
|
+
`
|
|
2927
|
+
).all(cutoffTime);
|
|
2928
|
+
}
|
|
2929
|
+
const rows = [];
|
|
2930
|
+
for (let offset = 0; offset < ids.length; offset += SESSION_ID_QUERY_CHUNK_SIZE) {
|
|
2931
|
+
const chunk = ids.slice(offset, offset + SESSION_ID_QUERY_CHUNK_SIZE);
|
|
2932
|
+
rows.push(
|
|
2933
|
+
...db.prepare(
|
|
2934
|
+
`
|
|
2935
|
+
SELECT p.message_id, p.data, p.time_created
|
|
2936
|
+
FROM part p
|
|
2937
|
+
JOIN message m ON m.id = p.message_id
|
|
2938
|
+
JOIN session s ON s.id = m.session_id
|
|
2939
|
+
WHERE s.id IN (${chunk.map(() => "?").join(",")})
|
|
2940
|
+
ORDER BY p.message_id, p.time_created ASC, p.id ASC
|
|
2941
|
+
`
|
|
2942
|
+
).all(...chunk)
|
|
2943
|
+
);
|
|
2944
|
+
}
|
|
2945
|
+
return rows;
|
|
2447
2946
|
}
|
|
2448
|
-
|
|
2449
|
-
return
|
|
2947
|
+
readRelatedSessionRows(db, rootIds) {
|
|
2948
|
+
if (rootIds.length === 0) return [];
|
|
2949
|
+
const rows = db.prepare(
|
|
2450
2950
|
`
|
|
2451
|
-
SELECT
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
WHERE
|
|
2456
|
-
ORDER BY
|
|
2951
|
+
SELECT
|
|
2952
|
+
s.id, s.title, s.time_created, s.time_updated, s.slug, s.directory,
|
|
2953
|
+
s.version, s.summary_files, s.parent_id
|
|
2954
|
+
FROM session s
|
|
2955
|
+
WHERE s.parent_id IS NOT NULL
|
|
2956
|
+
ORDER BY s.time_created ASC
|
|
2457
2957
|
`
|
|
2458
|
-
).all(
|
|
2958
|
+
).all();
|
|
2959
|
+
const childrenByParent = /* @__PURE__ */ new Map();
|
|
2960
|
+
for (const row of rows) {
|
|
2961
|
+
const parentId = String(row.parent_id ?? "");
|
|
2962
|
+
if (!parentId) continue;
|
|
2963
|
+
const children = childrenByParent.get(parentId);
|
|
2964
|
+
if (children) children.push(row);
|
|
2965
|
+
else childrenByParent.set(parentId, [row]);
|
|
2966
|
+
}
|
|
2967
|
+
const result = [];
|
|
2968
|
+
const pending2 = [...rootIds];
|
|
2969
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2970
|
+
while (pending2.length > 0) {
|
|
2971
|
+
const parentId = pending2.pop();
|
|
2972
|
+
for (const row of childrenByParent.get(parentId) ?? []) {
|
|
2973
|
+
const id = String(row.id ?? "");
|
|
2974
|
+
if (!id || seen.has(id)) continue;
|
|
2975
|
+
seen.add(id);
|
|
2976
|
+
result.push(row);
|
|
2977
|
+
pending2.push(id);
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
return result;
|
|
2459
2981
|
}
|
|
2460
2982
|
parsePartRow(partRow) {
|
|
2461
2983
|
const partData = parseJsonRecord(partRow.data, this.name, "part.data");
|
|
@@ -2498,16 +3020,10 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2498
3020
|
}
|
|
2499
3021
|
return partsByMessage;
|
|
2500
3022
|
}
|
|
2501
|
-
buildHeadContexts(messageRows, partRows) {
|
|
3023
|
+
buildHeadContexts(messageRows, partRows, withParentId) {
|
|
2502
3024
|
const partsByMessage = this.buildPartsByMessage(partRows);
|
|
2503
3025
|
const contexts = /* @__PURE__ */ new Map();
|
|
2504
|
-
|
|
2505
|
-
const sessionId = String(row.session_id ?? "");
|
|
2506
|
-
if (!sessionId) continue;
|
|
2507
|
-
const msgData = parseJsonRecord(row.data, this.name, "message.data");
|
|
2508
|
-
if (isInternalEventType(msgData.type)) continue;
|
|
2509
|
-
const parts = partsByMessage.get(String(row.id ?? "")) ?? [];
|
|
2510
|
-
if (parts.length === 0) continue;
|
|
3026
|
+
const ensureContext = (sessionId) => {
|
|
2511
3027
|
let context = contexts.get(sessionId);
|
|
2512
3028
|
if (!context) {
|
|
2513
3029
|
context = {
|
|
@@ -2521,16 +3037,41 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2521
3037
|
};
|
|
2522
3038
|
contexts.set(sessionId, context);
|
|
2523
3039
|
}
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
const
|
|
2528
|
-
|
|
2529
|
-
const
|
|
2530
|
-
if (
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
3040
|
+
return context;
|
|
3041
|
+
};
|
|
3042
|
+
for (const row of messageRows) {
|
|
3043
|
+
const sessionId = String(row.session_id ?? "");
|
|
3044
|
+
if (!sessionId) continue;
|
|
3045
|
+
const msgData = parseJsonRecord(row.data, this.name, "message.data");
|
|
3046
|
+
if (isInternalEventType(msgData.type)) continue;
|
|
3047
|
+
const parentId = withParentId ? String(row.parent_id ?? "") : "";
|
|
3048
|
+
const isChild = parentId !== "";
|
|
3049
|
+
if (isChild) {
|
|
3050
|
+
const childContext = ensureContext(sessionId);
|
|
3051
|
+
const parts2 = partsByMessage.get(String(row.id ?? "")) ?? [];
|
|
3052
|
+
if (parts2.length > 0) {
|
|
3053
|
+
accumulateTokenStats(childContext.stats, msgData, this.name);
|
|
3054
|
+
childContext.stats.message_count += 1;
|
|
3055
|
+
if (!childContext.messageTitle && String(msgData.role ?? "") === "user") {
|
|
3056
|
+
childContext.messageTitle = firstUserMessageTitle([
|
|
3057
|
+
{
|
|
3058
|
+
id: String(row.id ?? ""),
|
|
3059
|
+
role: "user",
|
|
3060
|
+
agent: null,
|
|
3061
|
+
time_created: Number(row.time_created ?? 0),
|
|
3062
|
+
parts: parts2
|
|
3063
|
+
}
|
|
3064
|
+
]);
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
const parentContext = ensureContext(parentId);
|
|
3068
|
+
accumulateTokenStats(parentContext.stats, msgData, this.name);
|
|
3069
|
+
continue;
|
|
3070
|
+
}
|
|
3071
|
+
const parts = partsByMessage.get(String(row.id ?? "")) ?? [];
|
|
3072
|
+
if (parts.length === 0) continue;
|
|
3073
|
+
const context = ensureContext(sessionId);
|
|
3074
|
+
accumulateTokenStats(context.stats, msgData, this.name);
|
|
2534
3075
|
context.stats.message_count += 1;
|
|
2535
3076
|
if (!context.messageTitle && String(msgData.role ?? "") === "user") {
|
|
2536
3077
|
context.messageTitle = firstUserMessageTitle([
|
|
@@ -2551,6 +3092,29 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2551
3092
|
}
|
|
2552
3093
|
return contexts;
|
|
2553
3094
|
}
|
|
3095
|
+
sumChildTokenStats(db, parentSessionId) {
|
|
3096
|
+
if (!columnExists(db, "session", "parent_id")) return [];
|
|
3097
|
+
const childRows = db.prepare("SELECT id FROM session WHERE parent_id = ?").all(parentSessionId);
|
|
3098
|
+
const results = [];
|
|
3099
|
+
for (const child of childRows) {
|
|
3100
|
+
const childId = String(child.id ?? "");
|
|
3101
|
+
if (!childId) continue;
|
|
3102
|
+
const msgRows = db.prepare("SELECT data FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(childId);
|
|
3103
|
+
const stats = {
|
|
3104
|
+
message_count: 0,
|
|
3105
|
+
total_input_tokens: 0,
|
|
3106
|
+
total_output_tokens: 0,
|
|
3107
|
+
total_cost: 0
|
|
3108
|
+
};
|
|
3109
|
+
for (const row of msgRows) {
|
|
3110
|
+
const msgData = parseJsonRecord(row.data, this.name, "message.data");
|
|
3111
|
+
if (isInternalEventType(msgData.type)) continue;
|
|
3112
|
+
accumulateTokenStats(stats, msgData, this.name);
|
|
3113
|
+
}
|
|
3114
|
+
results.push(stats);
|
|
3115
|
+
}
|
|
3116
|
+
return results;
|
|
3117
|
+
}
|
|
2554
3118
|
getSessionData(sessionId) {
|
|
2555
3119
|
if (!this.dbPath) {
|
|
2556
3120
|
this.dbPath = this.findDbPath();
|
|
@@ -2573,9 +3137,6 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2573
3137
|
const timeCreated = Number(sessionRow.time_created ?? 0);
|
|
2574
3138
|
const timeUpdated = Number(sessionRow.time_updated ?? timeCreated);
|
|
2575
3139
|
const messages = [];
|
|
2576
|
-
let totalCost = 0;
|
|
2577
|
-
let totalInputTokens = 0;
|
|
2578
|
-
let totalOutputTokens = 0;
|
|
2579
3140
|
let hasEstimatedCost = false;
|
|
2580
3141
|
const msgRows = db.prepare("SELECT * FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId);
|
|
2581
3142
|
const partsByMessage = this.buildPartsByMessage(this.readSessionPartRows(db, sessionId));
|
|
@@ -2611,28 +3172,41 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2611
3172
|
firstUserMessageTitle(cleanedMessages),
|
|
2612
3173
|
null
|
|
2613
3174
|
);
|
|
3175
|
+
const stats = {
|
|
3176
|
+
message_count: cleanedMessages.length,
|
|
3177
|
+
total_input_tokens: 0,
|
|
3178
|
+
total_output_tokens: 0,
|
|
3179
|
+
total_cost: 0
|
|
3180
|
+
};
|
|
2614
3181
|
for (const message of cleanedMessages) {
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
3182
|
+
stats.total_cost += message.cost ?? 0;
|
|
3183
|
+
stats.total_input_tokens += message.tokens?.input ?? 0;
|
|
3184
|
+
stats.total_output_tokens += message.tokens?.output ?? 0;
|
|
2618
3185
|
if (message.cost_source === "estimated") hasEstimatedCost = true;
|
|
2619
3186
|
}
|
|
3187
|
+
for (const childStats of this.sumChildTokenStats(db, sessionId)) {
|
|
3188
|
+
stats.total_cost += childStats.total_cost;
|
|
3189
|
+
stats.total_input_tokens += childStats.total_input_tokens;
|
|
3190
|
+
stats.total_output_tokens += childStats.total_output_tokens;
|
|
3191
|
+
if (childStats.cost_source === "estimated") hasEstimatedCost = true;
|
|
3192
|
+
}
|
|
2620
3193
|
return {
|
|
2621
3194
|
reference: { agentName: this.name, sessionId: id },
|
|
2622
3195
|
id,
|
|
2623
3196
|
title,
|
|
2624
3197
|
slug,
|
|
2625
3198
|
directory,
|
|
3199
|
+
parent_reference: sessionRow.parent_id == null || String(sessionRow.parent_id) === "" ? void 0 : { agentName: this.name, sessionId: String(sessionRow.parent_id) },
|
|
2626
3200
|
version: asString(sessionRow.version) ?? void 0,
|
|
2627
3201
|
time_created: timeCreated,
|
|
2628
3202
|
time_updated: timeUpdated,
|
|
2629
3203
|
summary_files: sessionRow.summary_files ?? void 0,
|
|
2630
3204
|
stats: {
|
|
2631
|
-
message_count:
|
|
2632
|
-
total_input_tokens:
|
|
2633
|
-
total_output_tokens:
|
|
2634
|
-
total_cost:
|
|
2635
|
-
cost_source:
|
|
3205
|
+
message_count: stats.message_count,
|
|
3206
|
+
total_input_tokens: stats.total_input_tokens,
|
|
3207
|
+
total_output_tokens: stats.total_output_tokens,
|
|
3208
|
+
total_cost: stats.total_cost,
|
|
3209
|
+
cost_source: stats.total_cost > 0 ? hasEstimatedCost ? "estimated" : "recorded" : void 0
|
|
2636
3210
|
},
|
|
2637
3211
|
messages: cleanedMessages
|
|
2638
3212
|
};
|
|
@@ -3076,7 +3650,7 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
3076
3650
|
const rawArgs = function_.arguments;
|
|
3077
3651
|
const normalizedArgs = normalizeToolArguments(rawArgs);
|
|
3078
3652
|
const buffer = typeof rawArgs === "string" && typeof normalizedArgs !== "string" ? rawArgs : null;
|
|
3079
|
-
const
|
|
3653
|
+
const toolPart2 = {
|
|
3080
3654
|
type: "tool",
|
|
3081
3655
|
tool: toolName,
|
|
3082
3656
|
callID: callId,
|
|
@@ -3085,7 +3659,7 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
3085
3659
|
time_created: timestampMs
|
|
3086
3660
|
};
|
|
3087
3661
|
builder.appendToolCall(
|
|
3088
|
-
|
|
3662
|
+
toolPart2,
|
|
3089
3663
|
{ id: `wire-${seq}`, timestampMs: 0, agent: "kimi" },
|
|
3090
3664
|
{ markModeAsTool: true, target: "current" }
|
|
3091
3665
|
);
|
|
@@ -3282,66 +3856,636 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
3282
3856
|
};
|
|
3283
3857
|
}
|
|
3284
3858
|
};
|
|
3285
|
-
var
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3859
|
+
var KIMI_CODE_TOOL_TITLE_MAP = {
|
|
3860
|
+
Read: "read",
|
|
3861
|
+
Write: "write",
|
|
3862
|
+
Edit: "edit",
|
|
3863
|
+
Bash: "bash",
|
|
3864
|
+
TodoList: "todo",
|
|
3865
|
+
AskUserQuestion: "ask",
|
|
3866
|
+
EnterPlanMode: "plan mode",
|
|
3867
|
+
ExitPlanMode: "plan approved",
|
|
3868
|
+
ReadFile: "read",
|
|
3869
|
+
Glob: "glob",
|
|
3870
|
+
StrReplaceFile: "edit",
|
|
3871
|
+
Grep: "grep",
|
|
3872
|
+
WriteFile: "write",
|
|
3873
|
+
Shell: "bash"
|
|
3874
|
+
};
|
|
3875
|
+
var KIMI_CODE_IGNORED_TOOLS = /* @__PURE__ */ new Set(["SetTodoList"]);
|
|
3876
|
+
function resolveKimiCodeDataRoot() {
|
|
3877
|
+
return resolveHomePath("KIMI_CODE_HOME", ".kimi-code");
|
|
3289
3878
|
}
|
|
3290
|
-
function
|
|
3291
|
-
|
|
3292
|
-
const separator = name.lastIndexOf("__");
|
|
3293
|
-
if (separator > 0 && separator + 2 < name.length) {
|
|
3294
|
-
return { name: name.slice(separator + 2), namespace: name.slice(0, separator + 2) };
|
|
3295
|
-
}
|
|
3296
|
-
}
|
|
3297
|
-
return { name };
|
|
3879
|
+
function mapToolTitle2(toolName) {
|
|
3880
|
+
return KIMI_CODE_TOOL_TITLE_MAP[toolName] ?? toolName;
|
|
3298
3881
|
}
|
|
3299
|
-
function
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3882
|
+
function normalizeToolArguments2(raw) {
|
|
3883
|
+
if (typeof raw !== "string") return raw;
|
|
3884
|
+
try {
|
|
3885
|
+
return JSON.parse(raw);
|
|
3886
|
+
} catch {
|
|
3887
|
+
return raw;
|
|
3303
3888
|
}
|
|
3304
|
-
return calls.length - 1;
|
|
3305
3889
|
}
|
|
3306
|
-
function
|
|
3307
|
-
if (typeof
|
|
3308
|
-
if (
|
|
3309
|
-
|
|
3310
|
-
|
|
3890
|
+
function parseTimestamp(raw) {
|
|
3891
|
+
if (typeof raw === "number") return Number.isFinite(raw) ? raw : null;
|
|
3892
|
+
if (typeof raw !== "string" || raw.trim() === "") return null;
|
|
3893
|
+
const numeric = Number(raw);
|
|
3894
|
+
if (Number.isFinite(numeric)) return numeric;
|
|
3895
|
+
const parsed = Date.parse(raw);
|
|
3896
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
3897
|
+
}
|
|
3898
|
+
function timestampFromRecord(record) {
|
|
3899
|
+
return parseTimestamp(record.time) ?? 0;
|
|
3900
|
+
}
|
|
3901
|
+
function contentText(content) {
|
|
3902
|
+
if (typeof content === "string") return content;
|
|
3903
|
+
if (!Array.isArray(content)) {
|
|
3904
|
+
const record = asRecord(content);
|
|
3905
|
+
return record ? String(record.text ?? "") : "";
|
|
3311
3906
|
}
|
|
3312
|
-
return
|
|
3907
|
+
return content.map((item) => {
|
|
3908
|
+
if (typeof item === "string") return item;
|
|
3909
|
+
const record = asRecord(item);
|
|
3910
|
+
return record ? String(record.text ?? "") : "";
|
|
3911
|
+
}).join(" ");
|
|
3313
3912
|
}
|
|
3314
|
-
function
|
|
3315
|
-
|
|
3316
|
-
const
|
|
3317
|
-
const
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3913
|
+
function contentParts(content, timestampMs) {
|
|
3914
|
+
const values = Array.isArray(content) ? content : [content];
|
|
3915
|
+
const parts = [];
|
|
3916
|
+
for (const value of values) {
|
|
3917
|
+
if (typeof value === "string") {
|
|
3918
|
+
const text = cleanInternalText(value);
|
|
3919
|
+
if (text) parts.push({ type: "text", text, time_created: timestampMs });
|
|
3920
|
+
continue;
|
|
3921
|
+
}
|
|
3922
|
+
const record = asRecord(value);
|
|
3923
|
+
if (!record) continue;
|
|
3924
|
+
const type = asString(record.type) ?? "";
|
|
3925
|
+
if (type === "text") {
|
|
3926
|
+
const text = cleanInternalText(asString(record.text) ?? "");
|
|
3927
|
+
if (text) parts.push({ type: "text", text, time_created: timestampMs });
|
|
3928
|
+
continue;
|
|
3929
|
+
}
|
|
3930
|
+
if (type === "think") {
|
|
3931
|
+
const text = cleanInternalText(asString(record.think) ?? "");
|
|
3932
|
+
if (text) parts.push({ type: "reasoning", text, time_created: timestampMs });
|
|
3933
|
+
continue;
|
|
3934
|
+
}
|
|
3935
|
+
if (type === "plan") {
|
|
3936
|
+
const text = cleanInternalText(asString(record.text) ?? "");
|
|
3937
|
+
if (text) {
|
|
3938
|
+
parts.push({
|
|
3939
|
+
type: "plan",
|
|
3940
|
+
text,
|
|
3941
|
+
approval_status: record.approved === false ? "fail" : "success",
|
|
3942
|
+
time_created: timestampMs
|
|
3943
|
+
});
|
|
3944
|
+
}
|
|
3945
|
+
continue;
|
|
3946
|
+
}
|
|
3947
|
+
if (type === "image") {
|
|
3948
|
+
const source = asRecord(record.source);
|
|
3949
|
+
const imageUrl = asRecord(record.imageUrl);
|
|
3950
|
+
const url = asString(record.url) ?? asString(imageUrl?.url) ?? (source?.kind === "url" ? asString(source.url) : void 0);
|
|
3951
|
+
const data = asString(record.data) ?? (source?.kind === "base64" ? asString(source.data) : void 0);
|
|
3952
|
+
const mimeType = asString(record.mime_type) ?? asString(record.media_type) ?? (source?.kind === "base64" ? asString(source.media_type) : void 0) ?? "application/octet-stream";
|
|
3953
|
+
if (url) parts.push({ type: "image", url, mime_type: mimeType, time_created: timestampMs });
|
|
3954
|
+
else if (data) {
|
|
3955
|
+
parts.push({
|
|
3956
|
+
type: "image",
|
|
3957
|
+
data,
|
|
3958
|
+
mime_type: mimeType,
|
|
3959
|
+
time_created: timestampMs
|
|
3960
|
+
});
|
|
3961
|
+
}
|
|
3962
|
+
continue;
|
|
3963
|
+
}
|
|
3964
|
+
if (type === "image_url") {
|
|
3965
|
+
const imageUrl = asRecord(record.imageUrl);
|
|
3966
|
+
const url = asString(imageUrl?.url) ?? asString(record.url);
|
|
3967
|
+
if (url) parts.push({ type: "image", url, time_created: timestampMs });
|
|
3326
3968
|
}
|
|
3327
3969
|
}
|
|
3328
|
-
return
|
|
3970
|
+
return parts;
|
|
3329
3971
|
}
|
|
3330
|
-
function
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
while ((match = assignRe.exec(input)) !== null) {
|
|
3335
|
-
const reader = new JsValueReader(input, assignRe.lastIndex, scope);
|
|
3336
|
-
const value = reader.parseValue();
|
|
3337
|
-
if (value !== PARSE_FAIL) {
|
|
3338
|
-
if (typeof value === "string") scope.set(match[1], value);
|
|
3339
|
-
assignRe.lastIndex = reader.pos;
|
|
3340
|
-
}
|
|
3972
|
+
function toolOutputParts(output, timestampMs) {
|
|
3973
|
+
if (typeof output === "string") {
|
|
3974
|
+
const text2 = cleanInternalText(output);
|
|
3975
|
+
return text2 ? [{ type: "text", text: text2, time_created: timestampMs }] : [];
|
|
3341
3976
|
}
|
|
3342
|
-
return
|
|
3977
|
+
if (Array.isArray(output)) return contentParts(output, timestampMs);
|
|
3978
|
+
if (output == null) return [];
|
|
3979
|
+
const record = asRecord(output);
|
|
3980
|
+
if (record?.type === "text") return contentParts([record], timestampMs);
|
|
3981
|
+
const text = cleanInternalText(JSON.stringify(output, null, 2));
|
|
3982
|
+
return text ? [{ type: "text", text, time_created: timestampMs }] : [];
|
|
3343
3983
|
}
|
|
3344
|
-
|
|
3984
|
+
function toolPart(toolName, callId, input, timestampMs) {
|
|
3985
|
+
return {
|
|
3986
|
+
type: "tool",
|
|
3987
|
+
tool: toolName,
|
|
3988
|
+
callID: callId,
|
|
3989
|
+
title: mapToolTitle2(toolName),
|
|
3990
|
+
state: {
|
|
3991
|
+
status: "running",
|
|
3992
|
+
...input === void 0 ? {} : { input },
|
|
3993
|
+
output: null
|
|
3994
|
+
},
|
|
3995
|
+
time_created: timestampMs
|
|
3996
|
+
};
|
|
3997
|
+
}
|
|
3998
|
+
function toolCallParts(message, timestampMs, ignoredToolCallIds) {
|
|
3999
|
+
const calls = asArray(message.toolCalls) ?? [];
|
|
4000
|
+
const parts = [];
|
|
4001
|
+
for (const call of calls) {
|
|
4002
|
+
const callRecord = asRecord(call);
|
|
4003
|
+
const functionRecord = asRecord(callRecord?.function) ?? callRecord;
|
|
4004
|
+
const toolName = asString(functionRecord?.name)?.trim() ?? "";
|
|
4005
|
+
const callId = asString(callRecord?.id)?.trim() ?? "";
|
|
4006
|
+
if (!toolName || !callId) continue;
|
|
4007
|
+
if (KIMI_CODE_IGNORED_TOOLS.has(toolName)) {
|
|
4008
|
+
ignoredToolCallIds.add(callId);
|
|
4009
|
+
continue;
|
|
4010
|
+
}
|
|
4011
|
+
const rawArguments = functionRecord?.arguments ?? callRecord?.arguments;
|
|
4012
|
+
parts.push(toolPart(toolName, callId, normalizeToolArguments2(rawArguments), timestampMs));
|
|
4013
|
+
}
|
|
4014
|
+
return parts;
|
|
4015
|
+
}
|
|
4016
|
+
function addToolResolution(builder, callId, output, timestampMs, isError = false, note) {
|
|
4017
|
+
const outputParts = toolOutputParts(output, timestampMs);
|
|
4018
|
+
return builder.resolveToolCall(callId, {
|
|
4019
|
+
output: outputParts,
|
|
4020
|
+
status: isError ? "error" : "completed",
|
|
4021
|
+
...note ? { metadata: { note: cleanInternalText(note) } } : {}
|
|
4022
|
+
});
|
|
4023
|
+
}
|
|
4024
|
+
function usageNumber(usage, field) {
|
|
4025
|
+
return asNumber(usage[field]) ?? 0;
|
|
4026
|
+
}
|
|
4027
|
+
function emptyStats() {
|
|
4028
|
+
return {
|
|
4029
|
+
message_count: 0,
|
|
4030
|
+
total_input_tokens: 0,
|
|
4031
|
+
total_output_tokens: 0,
|
|
4032
|
+
total_cost: 0
|
|
4033
|
+
};
|
|
4034
|
+
}
|
|
4035
|
+
function buildUsageTotals() {
|
|
4036
|
+
return {
|
|
4037
|
+
totalCost: 0,
|
|
4038
|
+
totalInputTokens: 0,
|
|
4039
|
+
totalOutputTokens: 0,
|
|
4040
|
+
totalCacheReadTokens: 0,
|
|
4041
|
+
totalCacheCreateTokens: 0,
|
|
4042
|
+
modelUsage: {}
|
|
4043
|
+
};
|
|
4044
|
+
}
|
|
4045
|
+
function applyUsage(builder, record, activeModel, totals) {
|
|
4046
|
+
const usage = asRecord(record.usage);
|
|
4047
|
+
if (!usage) return;
|
|
4048
|
+
const cacheRead = usageNumber(usage, "inputCacheRead");
|
|
4049
|
+
const cacheCreate = usageNumber(usage, "inputCacheCreation");
|
|
4050
|
+
const inputOther = usageNumber(usage, "inputOther");
|
|
4051
|
+
const output = usageNumber(usage, "output");
|
|
4052
|
+
const input = inputOther + cacheRead + cacheCreate;
|
|
4053
|
+
const model = asString(record.model) ?? activeModel;
|
|
4054
|
+
const tokens = {
|
|
4055
|
+
input,
|
|
4056
|
+
output,
|
|
4057
|
+
cache_read: cacheRead,
|
|
4058
|
+
cache_create: cacheCreate
|
|
4059
|
+
};
|
|
4060
|
+
const cost = estimateTokenCost(model, tokens);
|
|
4061
|
+
totals.totalInputTokens += input;
|
|
4062
|
+
totals.totalOutputTokens += output;
|
|
4063
|
+
totals.totalCacheReadTokens += cacheRead;
|
|
4064
|
+
totals.totalCacheCreateTokens += cacheCreate;
|
|
4065
|
+
if (model) totals.modelUsage[model] = (totals.modelUsage[model] ?? 0) + input + output;
|
|
4066
|
+
if (cost !== null) totals.totalCost += cost;
|
|
4067
|
+
builder.attachUsageToLatestAssistant(tokens, {
|
|
4068
|
+
model,
|
|
4069
|
+
cost: cost ?? void 0,
|
|
4070
|
+
costSource: cost === null ? void 0 : "estimated"
|
|
4071
|
+
});
|
|
4072
|
+
}
|
|
4073
|
+
function readState(stateFile) {
|
|
4074
|
+
try {
|
|
4075
|
+
return asRecord(JSON.parse(readFileSync4(stateFile, "utf-8"))) ?? null;
|
|
4076
|
+
} catch {
|
|
4077
|
+
return null;
|
|
4078
|
+
}
|
|
4079
|
+
}
|
|
4080
|
+
var KimiCodeAgent = class extends FileSystemSessionSource {
|
|
4081
|
+
name = "kimi-code";
|
|
4082
|
+
displayName = "Kimi-Code";
|
|
4083
|
+
basePath = null;
|
|
4084
|
+
workDirBySessionPath = /* @__PURE__ */ new Map();
|
|
4085
|
+
findBasePath() {
|
|
4086
|
+
const sessionsPath = join9(resolveKimiCodeDataRoot(), "sessions");
|
|
4087
|
+
return existsSync8(sessionsPath) ? sessionsPath : null;
|
|
4088
|
+
}
|
|
4089
|
+
getSessionWatchPlan() {
|
|
4090
|
+
const dataRoot = resolveKimiCodeDataRoot();
|
|
4091
|
+
return {
|
|
4092
|
+
status: "supported",
|
|
4093
|
+
targets: [
|
|
4094
|
+
{ root: dataRoot, path: join9(dataRoot, "sessions") },
|
|
4095
|
+
{ root: dataRoot, path: join9(dataRoot, "session_index.jsonl") }
|
|
4096
|
+
]
|
|
4097
|
+
};
|
|
4098
|
+
}
|
|
4099
|
+
isAvailable() {
|
|
4100
|
+
this.basePath = this.findBasePath();
|
|
4101
|
+
if (!this.basePath) return false;
|
|
4102
|
+
try {
|
|
4103
|
+
return this.listSessionDirs().length > 0;
|
|
4104
|
+
} catch {
|
|
4105
|
+
return false;
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4108
|
+
loadSessionIndex() {
|
|
4109
|
+
this.workDirBySessionPath.clear();
|
|
4110
|
+
if (!this.basePath) return;
|
|
4111
|
+
const indexPath = join9(dirname5(this.basePath), "session_index.jsonl");
|
|
4112
|
+
if (!existsSync8(indexPath)) return;
|
|
4113
|
+
for (const record of readJsonlFile(indexPath)) {
|
|
4114
|
+
const sessionPath = asString(record.sessionDir);
|
|
4115
|
+
const workDir = asString(record.workDir);
|
|
4116
|
+
if (!sessionPath || !workDir) continue;
|
|
4117
|
+
this.workDirBySessionPath.set(resolve(sessionPath), workDir);
|
|
4118
|
+
}
|
|
4119
|
+
}
|
|
4120
|
+
listSessionDirs() {
|
|
4121
|
+
if (!this.basePath) return [];
|
|
4122
|
+
const dirs = [];
|
|
4123
|
+
try {
|
|
4124
|
+
for (const bucket of readdirSync5(this.basePath, { withFileTypes: true })) {
|
|
4125
|
+
if (!bucket.isDirectory()) continue;
|
|
4126
|
+
const bucketPath = join9(this.basePath, bucket.name);
|
|
4127
|
+
for (const session of readdirSync5(bucketPath, { withFileTypes: true })) {
|
|
4128
|
+
if (!session.isDirectory()) continue;
|
|
4129
|
+
const sessionPath = join9(bucketPath, session.name);
|
|
4130
|
+
if (existsSync8(join9(sessionPath, "state.json")) && existsSync8(join9(sessionPath, "agents", "main", "wire.jsonl"))) {
|
|
4131
|
+
dirs.push(sessionPath);
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
}
|
|
4135
|
+
} catch {
|
|
4136
|
+
return dirs;
|
|
4137
|
+
}
|
|
4138
|
+
return dirs;
|
|
4139
|
+
}
|
|
4140
|
+
resolveSessionSourceResult(sessionDir) {
|
|
4141
|
+
try {
|
|
4142
|
+
const stateFile = join9(sessionDir, "state.json");
|
|
4143
|
+
const wireFile = join9(sessionDir, "agents", "main", "wire.jsonl");
|
|
4144
|
+
if (!existsSync8(stateFile) || !existsSync8(wireFile)) return skippedSession("missing wire");
|
|
4145
|
+
const state = readState(stateFile);
|
|
4146
|
+
if (!state) return skippedSession("malformed state");
|
|
4147
|
+
const stateMtime = statSync5(stateFile).mtimeMs;
|
|
4148
|
+
const wireMtime = statSync5(wireFile).mtimeMs;
|
|
4149
|
+
const createdAt = parseTimestamp(state.createdAt) ?? parseTimestamp(state.created_at) ?? stateMtime;
|
|
4150
|
+
const updatedAt = Math.max(
|
|
4151
|
+
parseTimestamp(state.updatedAt) ?? parseTimestamp(state.updated_at) ?? createdAt,
|
|
4152
|
+
wireMtime
|
|
4153
|
+
);
|
|
4154
|
+
const custom = asRecord(state.custom);
|
|
4155
|
+
const workDir = asString(state.workDir) ?? asString(custom?.cwd) ?? this.workDirBySessionPath.get(resolve(sessionDir)) ?? "";
|
|
4156
|
+
const explicitTitle = asString(state.title) ?? asString(state.customTitle) ?? "";
|
|
4157
|
+
return parsedSession({
|
|
4158
|
+
id: basename6(sessionDir),
|
|
4159
|
+
sourcePath: sessionDir,
|
|
4160
|
+
stateFile,
|
|
4161
|
+
wireFile,
|
|
4162
|
+
workDir,
|
|
4163
|
+
createdAt,
|
|
4164
|
+
updatedAt,
|
|
4165
|
+
explicitTitle
|
|
4166
|
+
});
|
|
4167
|
+
} catch {
|
|
4168
|
+
return skippedSession("malformed session");
|
|
4169
|
+
}
|
|
4170
|
+
}
|
|
4171
|
+
sourceFingerprint(source) {
|
|
4172
|
+
return JSON.stringify([
|
|
4173
|
+
this.readFileMtimeMs(source.stateFile),
|
|
4174
|
+
this.readFileMtimeMs(source.wireFile),
|
|
4175
|
+
source.workDir
|
|
4176
|
+
]);
|
|
4177
|
+
}
|
|
4178
|
+
listSessionSources(options) {
|
|
4179
|
+
if (!this.basePath) return [];
|
|
4180
|
+
this.loadSessionIndex();
|
|
4181
|
+
const refs = [];
|
|
4182
|
+
for (const sessionDir of this.listSessionDirs()) {
|
|
4183
|
+
const source = getParsedSession(this.resolveSessionSourceResult(sessionDir));
|
|
4184
|
+
if (!source || !matchesScanWindow(source.createdAt, options)) continue;
|
|
4185
|
+
refs.push({
|
|
4186
|
+
sessionId: source.id,
|
|
4187
|
+
sourcePath: source.sourcePath,
|
|
4188
|
+
fingerprint: this.sourceFingerprint(source)
|
|
4189
|
+
});
|
|
4190
|
+
}
|
|
4191
|
+
return refs;
|
|
4192
|
+
}
|
|
4193
|
+
checkForChanges(sinceTimestamp, cachedSessions) {
|
|
4194
|
+
const result = super.checkForChanges(sinceTimestamp, cachedSessions);
|
|
4195
|
+
const emptySessionIds = cachedSessions.filter((session) => !this.hasMessages(session)).map((session) => session.id);
|
|
4196
|
+
if (emptySessionIds.length === 0) return result;
|
|
4197
|
+
return {
|
|
4198
|
+
...result,
|
|
4199
|
+
hasChanges: true,
|
|
4200
|
+
changedIds: [.../* @__PURE__ */ new Set([...result.changedIds ?? [], ...emptySessionIds])]
|
|
4201
|
+
};
|
|
4202
|
+
}
|
|
4203
|
+
filterCachedSessions(sessions) {
|
|
4204
|
+
return this.removeEmptyCachedSessions(sessions);
|
|
4205
|
+
}
|
|
4206
|
+
incrementalScan(cachedSessions, changedIds, refs) {
|
|
4207
|
+
const visibleSessions = this.removeEmptyCachedSessions(cachedSessions);
|
|
4208
|
+
return super.incrementalScan(visibleSessions, changedIds, refs);
|
|
4209
|
+
}
|
|
4210
|
+
hasMessages(session) {
|
|
4211
|
+
return session.stats.message_count > 0;
|
|
4212
|
+
}
|
|
4213
|
+
removeEmptyCachedSessions(sessions) {
|
|
4214
|
+
return sessions.filter((session) => {
|
|
4215
|
+
if (this.hasMessages(session)) return true;
|
|
4216
|
+
this.sessionMetaMap.delete(session.id);
|
|
4217
|
+
return false;
|
|
4218
|
+
});
|
|
4219
|
+
}
|
|
4220
|
+
scanSessionSource(sourcePath) {
|
|
4221
|
+
this.loadSessionIndex();
|
|
4222
|
+
const source = getParsedSession(this.resolveSessionSourceResult(sourcePath));
|
|
4223
|
+
if (!source) return null;
|
|
4224
|
+
const parsed = this.parseWire(source);
|
|
4225
|
+
const transcript = parsed.builder.finish(parsed.stats);
|
|
4226
|
+
if (transcript.messages.length === 0) {
|
|
4227
|
+
this.sessionMetaMap.delete(source.id);
|
|
4228
|
+
return null;
|
|
4229
|
+
}
|
|
4230
|
+
const title = resolveSessionTitle(source.explicitTitle, parsed.firstUserTitle, null);
|
|
4231
|
+
const meta = {
|
|
4232
|
+
...source,
|
|
4233
|
+
title,
|
|
4234
|
+
sourceMtimeMs: source.createdAt,
|
|
4235
|
+
sourceFingerprint: this.sourceFingerprint(source)
|
|
4236
|
+
};
|
|
4237
|
+
this.sessionMetaMap.set(meta.id, meta);
|
|
4238
|
+
return {
|
|
4239
|
+
id: meta.id,
|
|
4240
|
+
slug: `${this.name}/${meta.id}`,
|
|
4241
|
+
title: meta.title,
|
|
4242
|
+
directory: meta.workDir,
|
|
4243
|
+
time_created: meta.createdAt,
|
|
4244
|
+
time_updated: meta.updatedAt,
|
|
4245
|
+
stats: transcript.stats,
|
|
4246
|
+
...Object.keys(parsed.modelUsage).length > 0 ? { model_usage: parsed.modelUsage } : {}
|
|
4247
|
+
};
|
|
4248
|
+
}
|
|
4249
|
+
getSessionData(sessionId) {
|
|
4250
|
+
const meta = this.sessionMetaMap.get(sessionId);
|
|
4251
|
+
if (!meta) throw new Error(`Session not found: ${sessionId}`);
|
|
4252
|
+
const parsed = this.parseWire(meta);
|
|
4253
|
+
const transcript = parsed.builder.finish(parsed.stats);
|
|
4254
|
+
return {
|
|
4255
|
+
reference: { agentName: this.name, sessionId: meta.id },
|
|
4256
|
+
id: meta.id,
|
|
4257
|
+
title: meta.title,
|
|
4258
|
+
slug: `${this.name}/${meta.id}`,
|
|
4259
|
+
directory: meta.workDir,
|
|
4260
|
+
time_created: meta.createdAt,
|
|
4261
|
+
time_updated: meta.updatedAt,
|
|
4262
|
+
stats: transcript.stats,
|
|
4263
|
+
messages: transcript.messages
|
|
4264
|
+
};
|
|
4265
|
+
}
|
|
4266
|
+
parseWire(source) {
|
|
4267
|
+
const builder = new TranscriptBuilder();
|
|
4268
|
+
const totals = buildUsageTotals();
|
|
4269
|
+
const ignoredToolCallIds = /* @__PURE__ */ new Set();
|
|
4270
|
+
let activeModel = null;
|
|
4271
|
+
let activeProvider = null;
|
|
4272
|
+
let firstUserTitle = null;
|
|
4273
|
+
let sequence = 0;
|
|
4274
|
+
for (const record of readJsonlFile(source.wireFile)) {
|
|
4275
|
+
sequence += 1;
|
|
4276
|
+
try {
|
|
4277
|
+
const timestampMs = timestampFromRecord(record);
|
|
4278
|
+
const recordType = asString(record.type) ?? "";
|
|
4279
|
+
if (recordType === "llm.request") {
|
|
4280
|
+
activeModel = asString(record.model) ?? activeModel;
|
|
4281
|
+
activeProvider = asString(record.provider) ?? activeProvider;
|
|
4282
|
+
continue;
|
|
4283
|
+
}
|
|
4284
|
+
if (recordType === "config.update") {
|
|
4285
|
+
activeModel = asString(record.modelAlias) ?? activeModel;
|
|
4286
|
+
continue;
|
|
4287
|
+
}
|
|
4288
|
+
if (recordType === "usage.record") {
|
|
4289
|
+
applyUsage(builder, record, activeModel, totals);
|
|
4290
|
+
continue;
|
|
4291
|
+
}
|
|
4292
|
+
if (recordType === "context.append_message") {
|
|
4293
|
+
const message = asRecord(record.message);
|
|
4294
|
+
if (!message) continue;
|
|
4295
|
+
const role = asString(message.role) ?? "";
|
|
4296
|
+
const messageTimestamp = timestampMs;
|
|
4297
|
+
if (role === "user") {
|
|
4298
|
+
const text = normalizeTitleText(contentText(message.content));
|
|
4299
|
+
if (!firstUserTitle && text) firstUserTitle = text;
|
|
4300
|
+
}
|
|
4301
|
+
if (role === "tool") {
|
|
4302
|
+
const callId = asString(message.toolCallId)?.trim() ?? "";
|
|
4303
|
+
const output = message.content;
|
|
4304
|
+
if (callId && ignoredToolCallIds.has(callId)) continue;
|
|
4305
|
+
if (callId && addToolResolution(builder, callId, output, messageTimestamp)) continue;
|
|
4306
|
+
const outputParts = toolOutputParts(output, messageTimestamp);
|
|
4307
|
+
if (outputParts.length > 0) {
|
|
4308
|
+
builder.appendMessage({
|
|
4309
|
+
id: `wire-${sequence}`,
|
|
4310
|
+
role: "tool",
|
|
4311
|
+
timestampMs: messageTimestamp,
|
|
4312
|
+
parts: outputParts
|
|
4313
|
+
});
|
|
4314
|
+
}
|
|
4315
|
+
continue;
|
|
4316
|
+
}
|
|
4317
|
+
if (role !== "user" && role !== "assistant") continue;
|
|
4318
|
+
const parts = [
|
|
4319
|
+
...contentParts(message.content, messageTimestamp),
|
|
4320
|
+
...role === "assistant" ? toolCallParts(message, messageTimestamp, ignoredToolCallIds) : []
|
|
4321
|
+
];
|
|
4322
|
+
if (parts.length === 0) continue;
|
|
4323
|
+
const allTools = parts.every((part) => part.type === "tool");
|
|
4324
|
+
const input = {
|
|
4325
|
+
id: `wire-${sequence}`,
|
|
4326
|
+
role,
|
|
4327
|
+
timestampMs: messageTimestamp,
|
|
4328
|
+
parts,
|
|
4329
|
+
...role === "assistant" ? {
|
|
4330
|
+
agent: this.name,
|
|
4331
|
+
mode: allTools ? "tool" : void 0,
|
|
4332
|
+
model: activeModel,
|
|
4333
|
+
provider: activeProvider
|
|
4334
|
+
} : {}
|
|
4335
|
+
};
|
|
4336
|
+
builder.appendMessage(input);
|
|
4337
|
+
continue;
|
|
4338
|
+
}
|
|
4339
|
+
if (recordType === "context.append_loop_event") {
|
|
4340
|
+
const event = asRecord(record.event);
|
|
4341
|
+
if (!event) continue;
|
|
4342
|
+
const eventType = asString(event.type) ?? "";
|
|
4343
|
+
const metadata = {
|
|
4344
|
+
id: `wire-${sequence}`,
|
|
4345
|
+
timestampMs,
|
|
4346
|
+
agent: this.name,
|
|
4347
|
+
model: activeModel,
|
|
4348
|
+
provider: activeProvider
|
|
4349
|
+
};
|
|
4350
|
+
if (eventType === "step.begin") {
|
|
4351
|
+
builder.beginTurn();
|
|
4352
|
+
continue;
|
|
4353
|
+
}
|
|
4354
|
+
if (eventType === "content.part") {
|
|
4355
|
+
const part = asRecord(event.part);
|
|
4356
|
+
if (!part) continue;
|
|
4357
|
+
const parts = contentParts([part], timestampMs);
|
|
4358
|
+
for (const contentPart of parts) {
|
|
4359
|
+
if (contentPart.type === "tool") continue;
|
|
4360
|
+
if (contentPart.type === "image" && builder.appendToCurrentAssistant(contentPart)) {
|
|
4361
|
+
continue;
|
|
4362
|
+
}
|
|
4363
|
+
builder.appendAssistantPart(contentPart, metadata, { grouping: "current" });
|
|
4364
|
+
}
|
|
4365
|
+
continue;
|
|
4366
|
+
}
|
|
4367
|
+
if (eventType === "tool.call") {
|
|
4368
|
+
const toolName = asString(event.name)?.trim() ?? "";
|
|
4369
|
+
const callId = asString(event.toolCallId)?.trim() ?? "";
|
|
4370
|
+
if (!toolName || !callId) continue;
|
|
4371
|
+
if (KIMI_CODE_IGNORED_TOOLS.has(toolName)) {
|
|
4372
|
+
ignoredToolCallIds.add(callId);
|
|
4373
|
+
continue;
|
|
4374
|
+
}
|
|
4375
|
+
builder.appendToolCall(toolPart(toolName, callId, event.args, timestampMs), metadata, {
|
|
4376
|
+
markModeAsTool: true,
|
|
4377
|
+
target: "current"
|
|
4378
|
+
});
|
|
4379
|
+
continue;
|
|
4380
|
+
}
|
|
4381
|
+
if (eventType === "tool.result") {
|
|
4382
|
+
const callId = asString(event.toolCallId)?.trim() ?? "";
|
|
4383
|
+
const result = asRecord(event.result);
|
|
4384
|
+
if (!callId || ignoredToolCallIds.has(callId)) continue;
|
|
4385
|
+
const output = result?.output;
|
|
4386
|
+
const isError = result?.isError === true;
|
|
4387
|
+
const note = asString(result?.note);
|
|
4388
|
+
if (addToolResolution(builder, callId, output, timestampMs, isError, note)) continue;
|
|
4389
|
+
const outputParts = toolOutputParts(output, timestampMs);
|
|
4390
|
+
if (outputParts.length > 0) {
|
|
4391
|
+
builder.appendMessage({
|
|
4392
|
+
id: `wire-${sequence}`,
|
|
4393
|
+
role: "tool",
|
|
4394
|
+
timestampMs,
|
|
4395
|
+
parts: outputParts
|
|
4396
|
+
});
|
|
4397
|
+
}
|
|
4398
|
+
}
|
|
4399
|
+
continue;
|
|
4400
|
+
}
|
|
4401
|
+
if (recordType === "context.apply_compaction") {
|
|
4402
|
+
const summary = cleanInternalText(asString(record.summary) ?? "");
|
|
4403
|
+
if (summary) {
|
|
4404
|
+
builder.appendMessage({
|
|
4405
|
+
id: `wire-${sequence}`,
|
|
4406
|
+
role: "user",
|
|
4407
|
+
timestampMs,
|
|
4408
|
+
parts: [{ type: "text", text: summary, time_created: timestampMs }]
|
|
4409
|
+
});
|
|
4410
|
+
}
|
|
4411
|
+
}
|
|
4412
|
+
} catch {
|
|
4413
|
+
continue;
|
|
4414
|
+
}
|
|
4415
|
+
}
|
|
4416
|
+
const stats = {
|
|
4417
|
+
...emptyStats(),
|
|
4418
|
+
total_input_tokens: totals.totalInputTokens,
|
|
4419
|
+
total_output_tokens: totals.totalOutputTokens,
|
|
4420
|
+
total_cost: Number(totals.totalCost.toFixed(8)),
|
|
4421
|
+
total_tokens: totals.totalInputTokens + totals.totalOutputTokens,
|
|
4422
|
+
...totals.totalCacheReadTokens > 0 ? { total_cache_read_tokens: totals.totalCacheReadTokens } : {},
|
|
4423
|
+
...totals.totalCacheCreateTokens > 0 ? { total_cache_create_tokens: totals.totalCacheCreateTokens } : {},
|
|
4424
|
+
...totals.totalCost > 0 ? { cost_source: "estimated" } : {}
|
|
4425
|
+
};
|
|
4426
|
+
return { builder, stats, firstUserTitle, modelUsage: totals.modelUsage };
|
|
4427
|
+
}
|
|
4428
|
+
};
|
|
4429
|
+
var PARSE_FAIL = /* @__PURE__ */ Symbol("parse-fail");
|
|
4430
|
+
var EXEC_OUTPUT_ENVELOPE_RE = /^Script completed\nWall time [^\n]*\nOutput:\n?/;
|
|
4431
|
+
function stripExecOutputEnvelope(text) {
|
|
4432
|
+
return text.replace(EXEC_OUTPUT_ENVELOPE_RE, "");
|
|
4433
|
+
}
|
|
4434
|
+
function splitExecToolName(name) {
|
|
4435
|
+
if (name.startsWith("mcp__")) {
|
|
4436
|
+
const separator = name.lastIndexOf("__");
|
|
4437
|
+
if (separator > 0 && separator + 2 < name.length) {
|
|
4438
|
+
return { name: name.slice(separator + 2), namespace: name.slice(0, separator + 2) };
|
|
4439
|
+
}
|
|
4440
|
+
}
|
|
4441
|
+
return { name };
|
|
4442
|
+
}
|
|
4443
|
+
function pickExecOutputTarget(calls) {
|
|
4444
|
+
for (let index = calls.length - 1; index >= 0; index -= 1) {
|
|
4445
|
+
const { name } = splitExecToolName(calls[index].name);
|
|
4446
|
+
if (name !== "apply_patch" && name !== "update_plan") return index;
|
|
4447
|
+
}
|
|
4448
|
+
return calls.length - 1;
|
|
4449
|
+
}
|
|
4450
|
+
function getExecPatchText(args) {
|
|
4451
|
+
if (typeof args === "string") return args;
|
|
4452
|
+
if (args && typeof args === "object") {
|
|
4453
|
+
const patch = args["patch"];
|
|
4454
|
+
if (typeof patch === "string") return patch;
|
|
4455
|
+
}
|
|
4456
|
+
return "";
|
|
4457
|
+
}
|
|
4458
|
+
function decodeExecCalls(input) {
|
|
4459
|
+
if (typeof input !== "string" || !input.includes("tools.")) return [];
|
|
4460
|
+
const scope = collectStringVars(input);
|
|
4461
|
+
const calls = [];
|
|
4462
|
+
const callRe = /tools\.([A-Za-z_$][\w$]*)\s*\(/g;
|
|
4463
|
+
let match;
|
|
4464
|
+
while ((match = callRe.exec(input)) !== null) {
|
|
4465
|
+
const reader = new JsValueReader(input, callRe.lastIndex, scope);
|
|
4466
|
+
const args = reader.parseValue();
|
|
4467
|
+
if (args !== PARSE_FAIL) {
|
|
4468
|
+
calls.push({ name: match[1], args });
|
|
4469
|
+
callRe.lastIndex = reader.pos;
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4472
|
+
return calls;
|
|
4473
|
+
}
|
|
4474
|
+
function collectStringVars(input) {
|
|
4475
|
+
const scope = /* @__PURE__ */ new Map();
|
|
4476
|
+
const assignRe = /(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*/g;
|
|
4477
|
+
let match;
|
|
4478
|
+
while ((match = assignRe.exec(input)) !== null) {
|
|
4479
|
+
const reader = new JsValueReader(input, assignRe.lastIndex, scope);
|
|
4480
|
+
const value = reader.parseValue();
|
|
4481
|
+
if (value !== PARSE_FAIL) {
|
|
4482
|
+
if (typeof value === "string") scope.set(match[1], value);
|
|
4483
|
+
assignRe.lastIndex = reader.pos;
|
|
4484
|
+
}
|
|
4485
|
+
}
|
|
4486
|
+
return scope;
|
|
4487
|
+
}
|
|
4488
|
+
var IDENT_START_RE = /[A-Za-z_$]/;
|
|
3345
4489
|
var IDENT_PART_RE = /[\w$]/;
|
|
3346
4490
|
var JsValueReader = class {
|
|
3347
4491
|
pos;
|
|
@@ -3543,7 +4687,7 @@ var PROPOSED_PLAN_PATTERN = /<proposed_plan>\s*([\s\S]*?)\s*<\/proposed_plan>/;
|
|
|
3543
4687
|
var PLAN_APPROVAL_PREFIX = "PLEASE IMPLEMENT THIS PLAN";
|
|
3544
4688
|
var SUBAGENT_NOTIFICATION_PATTERN = /<subagent_notification>\s*([\s\S]*?)\s*<\/subagent_notification>/;
|
|
3545
4689
|
var HEAD_INDEX_VERSION2 = "codex-head-v1";
|
|
3546
|
-
var PARSER_VERSION = "codex-parser-
|
|
4690
|
+
var PARSER_VERSION = "codex-parser-v6";
|
|
3547
4691
|
function resolveCodexDataRoot() {
|
|
3548
4692
|
return resolveHomePath("CODEX_HOME", ".codex");
|
|
3549
4693
|
}
|
|
@@ -3566,7 +4710,7 @@ var CODEX_TOOL_TITLE_MAP = {
|
|
|
3566
4710
|
subagent: "subagent"
|
|
3567
4711
|
};
|
|
3568
4712
|
function extractSessionId(filename) {
|
|
3569
|
-
const stem =
|
|
4713
|
+
const stem = basename7(filename, ".jsonl");
|
|
3570
4714
|
const parts = stem.split("-");
|
|
3571
4715
|
if (parts.length >= 5) {
|
|
3572
4716
|
return parts.slice(-5).join("-");
|
|
@@ -3595,6 +4739,14 @@ function narrowRecordField(value, field) {
|
|
|
3595
4739
|
function extractPayload(data) {
|
|
3596
4740
|
return narrowRecordField(data["payload"], "payload") ?? {};
|
|
3597
4741
|
}
|
|
4742
|
+
function extractThreadMeta(firstRecord) {
|
|
4743
|
+
if (firstRecord["type"] !== "session_meta") return null;
|
|
4744
|
+
const payload = extractPayload(firstRecord);
|
|
4745
|
+
const threadSource = asString(payload["thread_source"]) ?? "";
|
|
4746
|
+
const parentThreadId = asString(payload["parent_thread_id"]) ?? null;
|
|
4747
|
+
const agentNickname = asString(payload["agent_nickname"]) ?? null;
|
|
4748
|
+
return { threadSource, parentThreadId, agentNickname };
|
|
4749
|
+
}
|
|
3598
4750
|
function extractTokenUsage(payload) {
|
|
3599
4751
|
const info = narrowRecordField(payload["info"], "token_count.info");
|
|
3600
4752
|
return {
|
|
@@ -3620,7 +4772,7 @@ function resolveToolIdentity(name, namespace) {
|
|
|
3620
4772
|
metadata: { name, namespace: namespaceText }
|
|
3621
4773
|
};
|
|
3622
4774
|
}
|
|
3623
|
-
function
|
|
4775
|
+
function normalizeToolArguments3(raw) {
|
|
3624
4776
|
if (typeof raw === "string") {
|
|
3625
4777
|
try {
|
|
3626
4778
|
return JSON.parse(raw);
|
|
@@ -3647,6 +4799,16 @@ function flattenOutputText(output) {
|
|
|
3647
4799
|
}
|
|
3648
4800
|
return "";
|
|
3649
4801
|
}
|
|
4802
|
+
function extractAssistantOutputText(payload) {
|
|
4803
|
+
const content = payload["content"];
|
|
4804
|
+
if (Array.isArray(content)) {
|
|
4805
|
+
return content.map((item) => {
|
|
4806
|
+
const record = asRecord(item);
|
|
4807
|
+
return record && String(record["type"] ?? "") === "output_text" ? String(record["text"] ?? "") : "";
|
|
4808
|
+
}).filter(Boolean).join("\n");
|
|
4809
|
+
}
|
|
4810
|
+
return "";
|
|
4811
|
+
}
|
|
3650
4812
|
var PATCH_BEGIN_RE = /\*\*\* Begin Patch/;
|
|
3651
4813
|
var PATCH_END_RE = /\*\*\* End Patch/;
|
|
3652
4814
|
var PATCH_HEADER_RE = /\*\*\*\s+(Add|Delete|Update|Move)\s+File:\s*(.+)/;
|
|
@@ -3729,6 +4891,18 @@ function extractPatchContent(lines, startIndex) {
|
|
|
3729
4891
|
}
|
|
3730
4892
|
return { text: contentLines.join("\n"), nextLineIndex: i };
|
|
3731
4893
|
}
|
|
4894
|
+
function compareSourceActivityDesc(left, right) {
|
|
4895
|
+
const leftTimestamp = sourceTimestamp(left.file, left.stat.mtimeMs);
|
|
4896
|
+
const rightTimestamp = sourceTimestamp(right.file, right.stat.mtimeMs);
|
|
4897
|
+
return rightTimestamp - leftTimestamp || left.file.localeCompare(right.file);
|
|
4898
|
+
}
|
|
4899
|
+
function sourceTimestamp(filePath, fallback) {
|
|
4900
|
+
const match = basename7(filePath).match(/^rollout-(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2})-/);
|
|
4901
|
+
if (!match) return fallback;
|
|
4902
|
+
const timestamp = match[1].replace(/-(\d{2})-(\d{2})$/, ":$1:$2");
|
|
4903
|
+
const parsed = Date.parse(timestamp);
|
|
4904
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
4905
|
+
}
|
|
3732
4906
|
var CodexAgent = class extends SingleFileSessionSource {
|
|
3733
4907
|
name = "codex";
|
|
3734
4908
|
displayName = "Codex";
|
|
@@ -3736,38 +4910,143 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3736
4910
|
sessionIndexCache = /* @__PURE__ */ new Map();
|
|
3737
4911
|
sessionIndexMtime;
|
|
3738
4912
|
sessionIndexPath;
|
|
4913
|
+
subagentIndex = null;
|
|
4914
|
+
subagentStatsByParent = /* @__PURE__ */ new Map();
|
|
3739
4915
|
// ---- BaseAgent implementation ----
|
|
3740
4916
|
findBasePath() {
|
|
3741
|
-
return firstExisting(
|
|
4917
|
+
return firstExisting(join10(resolveCodexDataRoot(), "sessions"));
|
|
3742
4918
|
}
|
|
3743
4919
|
getSessionWatchPlan() {
|
|
3744
4920
|
const dataRoot = resolveCodexDataRoot();
|
|
3745
4921
|
return {
|
|
3746
4922
|
status: "supported",
|
|
3747
4923
|
targets: [
|
|
3748
|
-
{ path:
|
|
3749
|
-
{ path:
|
|
4924
|
+
{ path: join10(dataRoot, "sessions") },
|
|
4925
|
+
{ path: join10(dataRoot, "session_index.jsonl") }
|
|
3750
4926
|
]
|
|
3751
4927
|
};
|
|
3752
4928
|
}
|
|
3753
|
-
isAvailable() {
|
|
3754
|
-
this.basePath = this.findBasePath();
|
|
3755
|
-
if (!this.basePath) return false;
|
|
3756
|
-
return this.listRolloutFiles().length > 0;
|
|
3757
|
-
}
|
|
3758
|
-
listSessionSources(options) {
|
|
3759
|
-
if (!this.basePath) return [];
|
|
3760
|
-
this.loadSessionIndex();
|
|
3761
|
-
return this.
|
|
3762
|
-
sessionId: extractSessionId(file),
|
|
3763
|
-
sourcePath: file,
|
|
3764
|
-
fingerprint: this.sourceFingerprint(file, stat)
|
|
3765
|
-
}));
|
|
3766
|
-
}
|
|
4929
|
+
isAvailable() {
|
|
4930
|
+
this.basePath = this.findBasePath();
|
|
4931
|
+
if (!this.basePath) return false;
|
|
4932
|
+
return this.listRolloutFiles().length > 0;
|
|
4933
|
+
}
|
|
4934
|
+
listSessionSources(options) {
|
|
4935
|
+
if (!this.basePath) return [];
|
|
4936
|
+
this.loadSessionIndex();
|
|
4937
|
+
return this.listScanSources(options).map(({ file, stat }) => ({
|
|
4938
|
+
sessionId: extractSessionId(file),
|
|
4939
|
+
sourcePath: file,
|
|
4940
|
+
fingerprint: this.sourceFingerprint(file, stat)
|
|
4941
|
+
}));
|
|
4942
|
+
}
|
|
4943
|
+
setSessionMetaMap(meta) {
|
|
4944
|
+
super.setSessionMetaMap(meta);
|
|
4945
|
+
this.subagentIndex = null;
|
|
4946
|
+
this.subagentStatsByParent.clear();
|
|
4947
|
+
}
|
|
4948
|
+
/**
|
|
4949
|
+
* A changed subagent file leaves its parent's aggregated token stats stale,
|
|
4950
|
+
* so the parent must re-parse alongside the child.
|
|
4951
|
+
*/
|
|
4952
|
+
expandChangedSessionIds(changedIds, refs) {
|
|
4953
|
+
if (changedIds.length === 0) return changedIds;
|
|
4954
|
+
const pathById = new Map((refs ?? []).map((ref) => [ref.sessionId, ref.sourcePath]));
|
|
4955
|
+
const expanded = new Set(changedIds);
|
|
4956
|
+
for (const id of changedIds) {
|
|
4957
|
+
const sourcePath = pathById.get(id) ?? this.sessionMetaMap.get(id)?.sourcePath;
|
|
4958
|
+
const threadMeta = sourcePath ? this.readThreadMeta(sourcePath) : null;
|
|
4959
|
+
const parentId = threadMeta ? threadMeta.threadSource === "subagent" ? threadMeta.parentThreadId : null : this.sessionMetaMap.get(id)?.parentThreadId ?? null;
|
|
4960
|
+
if (!parentId) continue;
|
|
4961
|
+
expanded.add(parentId);
|
|
4962
|
+
this.subagentIndex = null;
|
|
4963
|
+
this.subagentStatsByParent.delete(parentId);
|
|
4964
|
+
}
|
|
4965
|
+
return [...expanded];
|
|
4966
|
+
}
|
|
4967
|
+
readThreadMeta(filePath) {
|
|
4968
|
+
try {
|
|
4969
|
+
const firstLine = this.readFilePrefix(filePath).split("\n").filter((l) => l.trim())[0];
|
|
4970
|
+
if (!firstLine) return null;
|
|
4971
|
+
return extractThreadMeta(JSON.parse(firstLine));
|
|
4972
|
+
} catch {
|
|
4973
|
+
return null;
|
|
4974
|
+
}
|
|
4975
|
+
}
|
|
4976
|
+
parseTokenStats(filePath) {
|
|
4977
|
+
let totalInputTokens = 0;
|
|
4978
|
+
let totalOutputTokens = 0;
|
|
4979
|
+
let totalCacheReadTokens = 0;
|
|
4980
|
+
let totalCost = 0;
|
|
4981
|
+
let activeModel = null;
|
|
4982
|
+
let prevCumulativeTotal = 0;
|
|
4983
|
+
let prevInput = 0;
|
|
4984
|
+
let prevOutput = 0;
|
|
4985
|
+
let prevReasoning = 0;
|
|
4986
|
+
let prevCachedInput = 0;
|
|
4987
|
+
for (const line of readJsonlFileLines(filePath)) {
|
|
4988
|
+
try {
|
|
4989
|
+
const data = JSON.parse(line);
|
|
4990
|
+
const recordType = String(data["type"] ?? "");
|
|
4991
|
+
const payload = extractPayload(data);
|
|
4992
|
+
if (recordType === "session_meta" || recordType === "turn_context") {
|
|
4993
|
+
const nextModel = extractModelName(payload["model"]);
|
|
4994
|
+
if (nextModel) activeModel = nextModel;
|
|
4995
|
+
continue;
|
|
4996
|
+
}
|
|
4997
|
+
if (recordType === "event_msg" && String(payload["type"] ?? "") === "token_count") {
|
|
4998
|
+
const { totalUsage, lastUsage } = extractTokenUsage(payload);
|
|
4999
|
+
const cumulativeTotal = Number(totalUsage?.["total_tokens"] ?? 0);
|
|
5000
|
+
if (cumulativeTotal <= 0 || cumulativeTotal === prevCumulativeTotal) continue;
|
|
5001
|
+
prevCumulativeTotal = cumulativeTotal;
|
|
5002
|
+
let inputTokens = 0;
|
|
5003
|
+
let outputTokens = 0;
|
|
5004
|
+
let reasoningTokens = 0;
|
|
5005
|
+
let cacheReadTokens = 0;
|
|
5006
|
+
if (lastUsage) {
|
|
5007
|
+
inputTokens = Number(lastUsage["input_tokens"] ?? 0);
|
|
5008
|
+
outputTokens = Number(lastUsage["output_tokens"] ?? 0);
|
|
5009
|
+
reasoningTokens = Number(lastUsage["reasoning_output_tokens"] ?? 0);
|
|
5010
|
+
cacheReadTokens = extractCachedInputTokens(lastUsage);
|
|
5011
|
+
} else if (totalUsage) {
|
|
5012
|
+
inputTokens = Number(totalUsage["input_tokens"] ?? 0) - prevInput;
|
|
5013
|
+
outputTokens = Number(totalUsage["output_tokens"] ?? 0) - prevOutput;
|
|
5014
|
+
reasoningTokens = Number(totalUsage["reasoning_output_tokens"] ?? 0) - prevReasoning;
|
|
5015
|
+
cacheReadTokens = extractCachedInputTokens(totalUsage) - prevCachedInput;
|
|
5016
|
+
prevInput = Number(totalUsage["input_tokens"] ?? 0);
|
|
5017
|
+
prevOutput = Number(totalUsage["output_tokens"] ?? 0);
|
|
5018
|
+
prevReasoning = Number(totalUsage["reasoning_output_tokens"] ?? 0);
|
|
5019
|
+
prevCachedInput = extractCachedInputTokens(totalUsage);
|
|
5020
|
+
}
|
|
5021
|
+
const totalInput = Math.max(0, inputTokens);
|
|
5022
|
+
const totalCacheRead = Math.max(0, cacheReadTokens);
|
|
5023
|
+
totalInputTokens += totalInput;
|
|
5024
|
+
totalOutputTokens += outputTokens + reasoningTokens;
|
|
5025
|
+
totalCacheReadTokens += totalCacheRead;
|
|
5026
|
+
totalCost += estimateTokenCost(activeModel, {
|
|
5027
|
+
input: totalInput,
|
|
5028
|
+
output: outputTokens,
|
|
5029
|
+
reasoning: reasoningTokens || void 0,
|
|
5030
|
+
cache_read: totalCacheRead || void 0
|
|
5031
|
+
}) ?? 0;
|
|
5032
|
+
}
|
|
5033
|
+
} catch {
|
|
5034
|
+
}
|
|
5035
|
+
}
|
|
5036
|
+
return {
|
|
5037
|
+
message_count: 0,
|
|
5038
|
+
total_input_tokens: totalInputTokens,
|
|
5039
|
+
total_output_tokens: totalOutputTokens,
|
|
5040
|
+
total_cache_read_tokens: totalCacheReadTokens || void 0,
|
|
5041
|
+
total_cost: totalCost,
|
|
5042
|
+
cost_source: totalCost > 0 ? "estimated" : void 0
|
|
5043
|
+
};
|
|
5044
|
+
}
|
|
3767
5045
|
getSessionData(sessionId) {
|
|
3768
5046
|
const meta = this.sessionMetaMap.get(sessionId);
|
|
3769
5047
|
if (!meta) throw new Error(`Session not found: ${sessionId}`);
|
|
3770
|
-
if (!
|
|
5048
|
+
if (!existsSync9(meta.sourcePath)) throw new Error(`Session file missing: ${meta.sourcePath}`);
|
|
5049
|
+
this.basePath ??= this.findBasePath();
|
|
3771
5050
|
const transcript = new TranscriptBuilder();
|
|
3772
5051
|
let totalInputTokens = 0;
|
|
3773
5052
|
let totalOutputTokens = 0;
|
|
@@ -3850,18 +5129,119 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3850
5129
|
total_cost: totalCost,
|
|
3851
5130
|
cost_source: totalCost > 0 ? "estimated" : void 0
|
|
3852
5131
|
});
|
|
5132
|
+
this.applyChildStats(result.stats, meta.id);
|
|
5133
|
+
const childMessages = this.collectChildMessages(meta.id);
|
|
5134
|
+
for (const message of childMessages) {
|
|
5135
|
+
const messageText = message.parts.find((part) => part.type === "text")?.text;
|
|
5136
|
+
const alreadyVisible = result.messages.some(
|
|
5137
|
+
(existing) => message.subagent_id !== void 0 && existing.subagent_id === message.subagent_id || existing.subagent_id === void 0 && message.nickname !== void 0 && messageText !== void 0 && existing.nickname === message.nickname && existing.parts.some((part) => part.type === "text" && part.text === messageText)
|
|
5138
|
+
);
|
|
5139
|
+
if (!alreadyVisible) result.messages.push(message);
|
|
5140
|
+
}
|
|
5141
|
+
result.stats.message_count = result.messages.length;
|
|
3853
5142
|
return {
|
|
3854
5143
|
reference: { agentName: this.name, sessionId: meta.id },
|
|
3855
5144
|
id: meta.id,
|
|
3856
5145
|
title: meta.title,
|
|
3857
5146
|
slug: `codex/${meta.id}`,
|
|
3858
5147
|
directory: meta.directory,
|
|
5148
|
+
parent_reference: meta.parentThreadId == null ? void 0 : { agentName: this.name, sessionId: meta.parentThreadId },
|
|
3859
5149
|
time_created: meta.createdAt,
|
|
3860
5150
|
time_updated: meta.updatedAt,
|
|
3861
5151
|
stats: result.stats,
|
|
3862
5152
|
messages: result.messages
|
|
3863
5153
|
};
|
|
3864
5154
|
}
|
|
5155
|
+
/**
|
|
5156
|
+
* Builds the complete parent→children map in one prefix sweep. A cache miss
|
|
5157
|
+
* afterwards means "no children", so per-session directory rescans (the old
|
|
5158
|
+
* O(N²) finalization hotspot) never happen.
|
|
5159
|
+
*/
|
|
5160
|
+
ensureSubagentIndex() {
|
|
5161
|
+
if (this.subagentIndex) return this.subagentIndex;
|
|
5162
|
+
this.basePath ??= this.findBasePath();
|
|
5163
|
+
const index = { childFilesByParent: /* @__PURE__ */ new Map(), subagentFiles: /* @__PURE__ */ new Set() };
|
|
5164
|
+
for (const file of this.listRolloutFilePaths()) {
|
|
5165
|
+
const threadMeta = this.readThreadMeta(file);
|
|
5166
|
+
if (threadMeta?.threadSource !== "subagent") continue;
|
|
5167
|
+
index.subagentFiles.add(file);
|
|
5168
|
+
if (!threadMeta.parentThreadId) continue;
|
|
5169
|
+
const files = index.childFilesByParent.get(threadMeta.parentThreadId);
|
|
5170
|
+
if (files) files.push(file);
|
|
5171
|
+
else index.childFilesByParent.set(threadMeta.parentThreadId, [file]);
|
|
5172
|
+
}
|
|
5173
|
+
this.subagentIndex = index;
|
|
5174
|
+
return index;
|
|
5175
|
+
}
|
|
5176
|
+
applyChildStats(target, sessionId) {
|
|
5177
|
+
for (const stats of this.collectChildStats(sessionId)) {
|
|
5178
|
+
target.total_input_tokens += stats.total_input_tokens ?? 0;
|
|
5179
|
+
target.total_output_tokens += stats.total_output_tokens ?? 0;
|
|
5180
|
+
target.total_cost += stats.total_cost ?? 0;
|
|
5181
|
+
if (stats.total_cache_read_tokens) {
|
|
5182
|
+
target.total_cache_read_tokens = (target.total_cache_read_tokens ?? 0) + stats.total_cache_read_tokens;
|
|
5183
|
+
}
|
|
5184
|
+
}
|
|
5185
|
+
}
|
|
5186
|
+
collectChildStats(parentSessionId) {
|
|
5187
|
+
const files = this.collectChildFiles(parentSessionId);
|
|
5188
|
+
if (files.length === 0) return [];
|
|
5189
|
+
const cached = this.subagentStatsByParent.get(parentSessionId);
|
|
5190
|
+
if (cached) return cached;
|
|
5191
|
+
const stats = files.map((file) => this.parseTokenStats(file));
|
|
5192
|
+
this.subagentStatsByParent.set(parentSessionId, stats);
|
|
5193
|
+
return stats;
|
|
5194
|
+
}
|
|
5195
|
+
collectChildFiles(parentSessionId) {
|
|
5196
|
+
return this.ensureSubagentIndex().childFilesByParent.get(parentSessionId) ?? [];
|
|
5197
|
+
}
|
|
5198
|
+
collectChildMessages(parentSessionId) {
|
|
5199
|
+
return this.collectChildFiles(parentSessionId).flatMap((file) => {
|
|
5200
|
+
const message = this.parseChildFinalMessage(file);
|
|
5201
|
+
return message ? [message] : [];
|
|
5202
|
+
}).sort((left, right) => left.time_created - right.time_created);
|
|
5203
|
+
}
|
|
5204
|
+
parseChildFinalMessage(filePath) {
|
|
5205
|
+
const sessionId = extractSessionId(filePath);
|
|
5206
|
+
const threadMeta = this.readThreadMeta(filePath);
|
|
5207
|
+
let latestOutput = null;
|
|
5208
|
+
let finalOutput = null;
|
|
5209
|
+
for (const record of readJsonlFile(filePath)) {
|
|
5210
|
+
try {
|
|
5211
|
+
const recordType = String(record["type"] ?? "");
|
|
5212
|
+
if (recordType !== "response_item") continue;
|
|
5213
|
+
const payload = extractPayload(record);
|
|
5214
|
+
if (String(payload["type"] ?? "") !== "message") continue;
|
|
5215
|
+
if (String(payload["role"] ?? "") !== "assistant") continue;
|
|
5216
|
+
const text = cleanInternalText(extractAssistantOutputText(payload));
|
|
5217
|
+
if (!text) continue;
|
|
5218
|
+
const candidate = {
|
|
5219
|
+
id: asString(payload["id"]) ?? `codex-subagent-${sessionId}`,
|
|
5220
|
+
text,
|
|
5221
|
+
timestampMs: parseTimestampMs2(record) || parseTimestampMs2(payload) || statSync6(filePath).mtimeMs,
|
|
5222
|
+
isFinal: String(record["phase"] ?? "") === "final_answer" || String(payload["phase"] ?? "") === "final_answer"
|
|
5223
|
+
};
|
|
5224
|
+
latestOutput = candidate;
|
|
5225
|
+
if (candidate.isFinal) finalOutput = candidate;
|
|
5226
|
+
} catch {
|
|
5227
|
+
}
|
|
5228
|
+
}
|
|
5229
|
+
const selected = finalOutput ?? latestOutput;
|
|
5230
|
+
if (!selected) return null;
|
|
5231
|
+
return {
|
|
5232
|
+
id: selected.id,
|
|
5233
|
+
role: "assistant",
|
|
5234
|
+
agent: "codex",
|
|
5235
|
+
time_created: selected.timestampMs,
|
|
5236
|
+
mode: null,
|
|
5237
|
+
model: null,
|
|
5238
|
+
provider: null,
|
|
5239
|
+
cost: 0,
|
|
5240
|
+
subagent_id: sessionId,
|
|
5241
|
+
nickname: threadMeta?.agentNickname ?? void 0,
|
|
5242
|
+
parts: [{ type: "text", text: selected.text, time_created: selected.timestampMs }]
|
|
5243
|
+
};
|
|
5244
|
+
}
|
|
3865
5245
|
// ---- File listing ----
|
|
3866
5246
|
listRolloutFiles(options) {
|
|
3867
5247
|
if (!this.basePath) return [];
|
|
@@ -3871,6 +5251,54 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3871
5251
|
{ scanWindow: options }
|
|
3872
5252
|
);
|
|
3873
5253
|
}
|
|
5254
|
+
/** Path-only listing for the subagent index: no per-file stat needed. */
|
|
5255
|
+
listRolloutFilePaths() {
|
|
5256
|
+
if (!this.basePath) return [];
|
|
5257
|
+
const paths = [];
|
|
5258
|
+
const walk = (directory) => {
|
|
5259
|
+
let entries;
|
|
5260
|
+
try {
|
|
5261
|
+
entries = readdirSync6(directory, { withFileTypes: true });
|
|
5262
|
+
} catch {
|
|
5263
|
+
return;
|
|
5264
|
+
}
|
|
5265
|
+
for (const entry of entries) {
|
|
5266
|
+
const filePath = join10(directory, entry.name);
|
|
5267
|
+
if (entry.isDirectory()) walk(filePath);
|
|
5268
|
+
else if (entry.name.startsWith("rollout-") && entry.name.endsWith(".jsonl")) {
|
|
5269
|
+
paths.push(filePath);
|
|
5270
|
+
}
|
|
5271
|
+
}
|
|
5272
|
+
};
|
|
5273
|
+
walk(this.basePath);
|
|
5274
|
+
return paths;
|
|
5275
|
+
}
|
|
5276
|
+
listScanSources(options) {
|
|
5277
|
+
const windowed = this.listRolloutFiles(options).sort(compareSourceActivityDesc);
|
|
5278
|
+
if (options?.from == null && options?.to == null) return windowed;
|
|
5279
|
+
const { childFilesByParent, subagentFiles } = this.ensureSubagentIndex();
|
|
5280
|
+
const rootFiles = windowed.filter((source) => !subagentFiles.has(source.file));
|
|
5281
|
+
const rootIds = new Set(rootFiles.map(({ file }) => extractSessionId(file)));
|
|
5282
|
+
if (rootIds.size === 0) return rootFiles;
|
|
5283
|
+
const selected = new Map(rootFiles.map((source) => [source.file, source]));
|
|
5284
|
+
const pending2 = [...rootIds];
|
|
5285
|
+
const seenParents = /* @__PURE__ */ new Set();
|
|
5286
|
+
while (pending2.length > 0) {
|
|
5287
|
+
const parentId = pending2.pop();
|
|
5288
|
+
if (seenParents.has(parentId)) continue;
|
|
5289
|
+
seenParents.add(parentId);
|
|
5290
|
+
for (const file of childFilesByParent.get(parentId) ?? []) {
|
|
5291
|
+
if (selected.has(file)) continue;
|
|
5292
|
+
try {
|
|
5293
|
+
selected.set(file, this.sessionSourceFile(file));
|
|
5294
|
+
} catch {
|
|
5295
|
+
continue;
|
|
5296
|
+
}
|
|
5297
|
+
pending2.push(extractSessionId(file));
|
|
5298
|
+
}
|
|
5299
|
+
}
|
|
5300
|
+
return [...selected.values()].sort(compareSourceActivityDesc);
|
|
5301
|
+
}
|
|
3874
5302
|
createFileSessionMeta(head, source) {
|
|
3875
5303
|
const indexPath = this.getSessionIndexPath();
|
|
3876
5304
|
const indexMtime = this.sessionIndexMtime ?? null;
|
|
@@ -3883,7 +5311,8 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3883
5311
|
indexMtimeMs: indexMtime,
|
|
3884
5312
|
headIndexVersion: HEAD_INDEX_VERSION2,
|
|
3885
5313
|
parserVersion: PARSER_VERSION,
|
|
3886
|
-
model: null
|
|
5314
|
+
model: null,
|
|
5315
|
+
parentThreadId: head.parent_reference?.sessionId ?? null
|
|
3887
5316
|
}
|
|
3888
5317
|
});
|
|
3889
5318
|
}
|
|
@@ -3899,7 +5328,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3899
5328
|
]);
|
|
3900
5329
|
}
|
|
3901
5330
|
getSessionIndexPath() {
|
|
3902
|
-
this.sessionIndexPath ??=
|
|
5331
|
+
this.sessionIndexPath ??= join10(resolveCodexDataRoot(), "session_index.jsonl");
|
|
3903
5332
|
return this.sessionIndexPath;
|
|
3904
5333
|
}
|
|
3905
5334
|
// ---- Session index ----
|
|
@@ -3913,7 +5342,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3913
5342
|
return;
|
|
3914
5343
|
}
|
|
3915
5344
|
try {
|
|
3916
|
-
const content =
|
|
5345
|
+
const content = readFileSync5(indexPath, "utf-8");
|
|
3917
5346
|
const cache = /* @__PURE__ */ new Map();
|
|
3918
5347
|
for (const record of parseJsonlLines(content)) {
|
|
3919
5348
|
const sid = String(record["id"] ?? "").trim();
|
|
@@ -3945,7 +5374,9 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3945
5374
|
}
|
|
3946
5375
|
parseFileSessionHead(filePath, options) {
|
|
3947
5376
|
this.loadSessionIndex();
|
|
3948
|
-
|
|
5377
|
+
const head = this.parseSessionHead(filePath, options);
|
|
5378
|
+
if (head) this.applyChildStats(head.stats, head.id);
|
|
5379
|
+
return head;
|
|
3949
5380
|
}
|
|
3950
5381
|
parseSessionHead(filePath, options) {
|
|
3951
5382
|
return getParsedSession(this.parseSessionHeadResult(filePath, options));
|
|
@@ -3956,6 +5387,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3956
5387
|
}
|
|
3957
5388
|
const sessionId = extractSessionId(filePath);
|
|
3958
5389
|
let firstPayload = {};
|
|
5390
|
+
let parentThreadId = null;
|
|
3959
5391
|
let createdAt = 0;
|
|
3960
5392
|
let lineCount = 0;
|
|
3961
5393
|
let messageTitle = null;
|
|
@@ -3985,7 +5417,9 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3985
5417
|
return skippedSession("malformed first record");
|
|
3986
5418
|
}
|
|
3987
5419
|
firstPayload = extractPayload(firstRecord);
|
|
3988
|
-
|
|
5420
|
+
const threadMeta = extractThreadMeta(firstRecord);
|
|
5421
|
+
parentThreadId = threadMeta?.threadSource === "subagent" ? threadMeta.parentThreadId : null;
|
|
5422
|
+
createdAt = parseTimestampMs2(firstRecord) || parseTimestampMs2(firstPayload) || statSync6(filePath).mtimeMs;
|
|
3989
5423
|
updatedAt = createdAt;
|
|
3990
5424
|
}
|
|
3991
5425
|
try {
|
|
@@ -4079,6 +5513,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4079
5513
|
slug: `codex/${sessionId}`,
|
|
4080
5514
|
title,
|
|
4081
5515
|
directory,
|
|
5516
|
+
parent_reference: parentThreadId == null ? void 0 : { agentName: this.name, sessionId: parentThreadId },
|
|
4082
5517
|
time_created: createdAt,
|
|
4083
5518
|
time_updated: updatedAt,
|
|
4084
5519
|
stats: {
|
|
@@ -4092,9 +5527,6 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4092
5527
|
model_usage: Object.keys(modelUsageMap).length > 0 ? modelUsageMap : void 0
|
|
4093
5528
|
});
|
|
4094
5529
|
}
|
|
4095
|
-
parseFastSessionHead(filePath) {
|
|
4096
|
-
return getParsedSession(this.parseFastSessionHeadResult(filePath));
|
|
4097
|
-
}
|
|
4098
5530
|
parseFastSessionHeadResult(filePath) {
|
|
4099
5531
|
const prefix = this.readFilePrefix(filePath);
|
|
4100
5532
|
const lines = prefix.split("\n").filter((l) => l.trim());
|
|
@@ -4106,8 +5538,10 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4106
5538
|
} catch {
|
|
4107
5539
|
return skippedSession("malformed first record");
|
|
4108
5540
|
}
|
|
5541
|
+
const threadMeta = extractThreadMeta(firstRecord);
|
|
5542
|
+
const parentThreadId = threadMeta?.threadSource === "subagent" ? threadMeta.parentThreadId : null;
|
|
4109
5543
|
const payload = extractPayload(firstRecord);
|
|
4110
|
-
const stat =
|
|
5544
|
+
const stat = statSync6(filePath);
|
|
4111
5545
|
const createdAt = parseTimestampMs2(firstRecord) || parseTimestampMs2(payload) || stat.mtimeMs;
|
|
4112
5546
|
const indexTitle = this.getTitleForSession(sessionId);
|
|
4113
5547
|
const messageTitle = this.extractTitleFromLines(lines);
|
|
@@ -4119,6 +5553,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4119
5553
|
slug: `codex/${sessionId}`,
|
|
4120
5554
|
title,
|
|
4121
5555
|
directory,
|
|
5556
|
+
parent_reference: parentThreadId == null ? void 0 : { agentName: this.name, sessionId: parentThreadId },
|
|
4122
5557
|
time_created: createdAt,
|
|
4123
5558
|
time_updated: stat.mtimeMs,
|
|
4124
5559
|
stats: {
|
|
@@ -4214,29 +5649,18 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4214
5649
|
}
|
|
4215
5650
|
// ---- Assistant message ----
|
|
4216
5651
|
convertAssistantMessage(payload, transcript, timestampMs, pendingPlan, activeModel) {
|
|
4217
|
-
const
|
|
4218
|
-
if (!
|
|
4219
|
-
const textParts = [];
|
|
4220
|
-
for (const item of content) {
|
|
4221
|
-
const ci = asRecord(item);
|
|
4222
|
-
if (!ci) continue;
|
|
4223
|
-
if (String(ci["type"] ?? "") === "output_text") {
|
|
4224
|
-
const text = String(ci["text"] ?? "");
|
|
4225
|
-
if (text.trim()) textParts.push(text);
|
|
4226
|
-
}
|
|
4227
|
-
}
|
|
4228
|
-
if (textParts.length === 0) return pendingPlan;
|
|
4229
|
-
const fullText = textParts.join("\n");
|
|
5652
|
+
const fullText = extractAssistantOutputText(payload);
|
|
5653
|
+
if (!fullText) return pendingPlan;
|
|
4230
5654
|
const planMatch = fullText.match(PROPOSED_PLAN_PATTERN);
|
|
4231
5655
|
if (planMatch) {
|
|
4232
5656
|
const planText2 = planMatch[1].trim();
|
|
4233
|
-
const
|
|
5657
|
+
const planPart2 = {
|
|
4234
5658
|
type: "plan",
|
|
4235
5659
|
text: planText2,
|
|
4236
5660
|
approval_status: "success",
|
|
4237
5661
|
time_created: timestampMs
|
|
4238
5662
|
};
|
|
4239
|
-
pendingPlan =
|
|
5663
|
+
pendingPlan = planPart2;
|
|
4240
5664
|
}
|
|
4241
5665
|
const displayText = cleanInternalText(fullText.replace(PROPOSED_PLAN_PATTERN, ""));
|
|
4242
5666
|
if (!displayText) return pendingPlan;
|
|
@@ -4341,8 +5765,8 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4341
5765
|
const name = String(payload["name"] ?? "").trim();
|
|
4342
5766
|
if (!name) return;
|
|
4343
5767
|
const toolIdentity = resolveToolIdentity(name, payload["namespace"]);
|
|
4344
|
-
const arguments_ =
|
|
4345
|
-
const
|
|
5768
|
+
const arguments_ = normalizeToolArguments3(payload["arguments"]);
|
|
5769
|
+
const toolPart2 = {
|
|
4346
5770
|
type: "tool",
|
|
4347
5771
|
tool: toolIdentity.tool,
|
|
4348
5772
|
callID: callId,
|
|
@@ -4356,7 +5780,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4356
5780
|
time_created: timestampMs
|
|
4357
5781
|
};
|
|
4358
5782
|
transcript.appendToolCall(
|
|
4359
|
-
|
|
5783
|
+
toolPart2,
|
|
4360
5784
|
{ id: "", timestampMs, agent: "codex", model: activeModel },
|
|
4361
5785
|
{ markModeAsTool: true }
|
|
4362
5786
|
);
|
|
@@ -4388,7 +5812,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4388
5812
|
const toolIdentity = resolveToolIdentity(name, payload["namespace"]);
|
|
4389
5813
|
const rawInput = payload["input"];
|
|
4390
5814
|
const normalizedInput = normalizeCustomToolArguments(name, rawInput);
|
|
4391
|
-
const
|
|
5815
|
+
const toolPart2 = {
|
|
4392
5816
|
type: "tool",
|
|
4393
5817
|
tool: toolIdentity.tool,
|
|
4394
5818
|
callID: callId,
|
|
@@ -4402,7 +5826,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4402
5826
|
time_created: timestampMs
|
|
4403
5827
|
};
|
|
4404
5828
|
transcript.appendToolCall(
|
|
4405
|
-
|
|
5829
|
+
toolPart2,
|
|
4406
5830
|
{ id: "", timestampMs, agent: "codex", model: activeModel },
|
|
4407
5831
|
{ markModeAsTool: true }
|
|
4408
5832
|
);
|
|
@@ -4419,7 +5843,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4419
5843
|
const { name, namespace } = splitExecToolName(call.name);
|
|
4420
5844
|
const toolIdentity = resolveToolIdentity(name, namespace);
|
|
4421
5845
|
const arguments_ = name === "apply_patch" ? parseApplyPatchInput(getExecPatchText(call.args)) : call.args;
|
|
4422
|
-
const
|
|
5846
|
+
const toolPart2 = {
|
|
4423
5847
|
type: "tool",
|
|
4424
5848
|
tool: toolIdentity.tool,
|
|
4425
5849
|
callID: callId,
|
|
@@ -4433,7 +5857,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4433
5857
|
time_created: timestampMs
|
|
4434
5858
|
};
|
|
4435
5859
|
transcript.appendToolCall(
|
|
4436
|
-
|
|
5860
|
+
toolPart2,
|
|
4437
5861
|
{ id: "", timestampMs, agent: "codex", model: activeModel },
|
|
4438
5862
|
{ markModeAsTool: true }
|
|
4439
5863
|
);
|
|
@@ -4518,15 +5942,15 @@ function resolveCursorDataRoot() {
|
|
|
4518
5942
|
if (override) return override;
|
|
4519
5943
|
const currentPlatform = platform2();
|
|
4520
5944
|
if (currentPlatform === "darwin") {
|
|
4521
|
-
return firstExisting(
|
|
5945
|
+
return firstExisting(join11(homedir3(), "Library", "Application Support", "Cursor", "User"));
|
|
4522
5946
|
}
|
|
4523
5947
|
if (currentPlatform === "linux") {
|
|
4524
|
-
const configRoot = readEnvPath("XDG_CONFIG_HOME") ??
|
|
4525
|
-
return firstExisting(
|
|
5948
|
+
const configRoot = readEnvPath("XDG_CONFIG_HOME") ?? join11(homedir3(), ".config");
|
|
5949
|
+
return firstExisting(join11(configRoot, "Cursor", "User"));
|
|
4526
5950
|
}
|
|
4527
5951
|
if (currentPlatform === "win32") {
|
|
4528
|
-
const appData = readEnvPath("APPDATA") ??
|
|
4529
|
-
return firstExisting(
|
|
5952
|
+
const appData = readEnvPath("APPDATA") ?? join11(homedir3(), "AppData", "Roaming");
|
|
5953
|
+
return firstExisting(join11(appData, "Cursor", "User"));
|
|
4530
5954
|
}
|
|
4531
5955
|
return null;
|
|
4532
5956
|
}
|
|
@@ -4655,7 +6079,7 @@ var CURSOR_TOOL_TITLE_MAP = {
|
|
|
4655
6079
|
ripgrep_raw_search: "grep",
|
|
4656
6080
|
glob_file_search: "glob"
|
|
4657
6081
|
};
|
|
4658
|
-
function
|
|
6082
|
+
function mapToolTitle3(toolName) {
|
|
4659
6083
|
return CURSOR_TOOL_TITLE_MAP[toolName] ?? toolName;
|
|
4660
6084
|
}
|
|
4661
6085
|
function normalizeToolOutputParts2(output, timestampMs) {
|
|
@@ -4716,9 +6140,9 @@ function buildToolPart(action, timestampMs) {
|
|
|
4716
6140
|
const toolName = action.tool ?? "unknown";
|
|
4717
6141
|
return {
|
|
4718
6142
|
type: "tool",
|
|
4719
|
-
tool:
|
|
6143
|
+
tool: mapToolTitle3(toolName),
|
|
4720
6144
|
callID: action.type ? `${action.type}:${String(action.input?.id ?? "")}` : "",
|
|
4721
|
-
title: `Tool: ${
|
|
6145
|
+
title: `Tool: ${mapToolTitle3(toolName)}`,
|
|
4722
6146
|
state: buildToolState(action),
|
|
4723
6147
|
time_created: timestampMs
|
|
4724
6148
|
};
|
|
@@ -4764,7 +6188,7 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4764
6188
|
if (!isSqliteAvailable()) return null;
|
|
4765
6189
|
const dataPath = resolveCursorDataRoot();
|
|
4766
6190
|
if (!dataPath) return null;
|
|
4767
|
-
return
|
|
6191
|
+
return join11(dataPath, "globalStorage", "state.vscdb");
|
|
4768
6192
|
}
|
|
4769
6193
|
getSessionWatchPlan() {
|
|
4770
6194
|
const dataPath = resolveCursorDataRoot();
|
|
@@ -4773,9 +6197,9 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4773
6197
|
targets: dataPath ? [
|
|
4774
6198
|
{
|
|
4775
6199
|
root: dataPath,
|
|
4776
|
-
path:
|
|
6200
|
+
path: join11(dataPath, "globalStorage", "state.vscdb")
|
|
4777
6201
|
},
|
|
4778
|
-
{ root: dataPath, path:
|
|
6202
|
+
{ root: dataPath, path: join11(dataPath, "workspaceStorage") }
|
|
4779
6203
|
] : []
|
|
4780
6204
|
};
|
|
4781
6205
|
}
|
|
@@ -4787,34 +6211,34 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4787
6211
|
const map = /* @__PURE__ */ new Map();
|
|
4788
6212
|
const dataPath = resolveCursorDataRoot();
|
|
4789
6213
|
if (!dataPath) return map;
|
|
4790
|
-
const wsStoragePath =
|
|
4791
|
-
if (!
|
|
6214
|
+
const wsStoragePath = join11(dataPath, "workspaceStorage");
|
|
6215
|
+
if (!existsSync10(wsStoragePath)) return map;
|
|
4792
6216
|
let entryNames;
|
|
4793
6217
|
try {
|
|
4794
|
-
entryNames =
|
|
6218
|
+
entryNames = readdirSync7(wsStoragePath);
|
|
4795
6219
|
} catch {
|
|
4796
6220
|
return map;
|
|
4797
6221
|
}
|
|
4798
6222
|
for (const name of entryNames) {
|
|
4799
|
-
const wsDir =
|
|
6223
|
+
const wsDir = join11(wsStoragePath, name);
|
|
4800
6224
|
try {
|
|
4801
|
-
if (!
|
|
6225
|
+
if (!statSync7(wsDir).isDirectory()) continue;
|
|
4802
6226
|
} catch {
|
|
4803
6227
|
continue;
|
|
4804
6228
|
}
|
|
4805
|
-
const wsJsonPath =
|
|
4806
|
-
if (!
|
|
6229
|
+
const wsJsonPath = join11(wsDir, "workspace.json");
|
|
6230
|
+
if (!existsSync10(wsJsonPath)) continue;
|
|
4807
6231
|
let workspacePath;
|
|
4808
6232
|
try {
|
|
4809
|
-
const data = asRecord(JSON.parse(
|
|
6233
|
+
const data = asRecord(JSON.parse(readFileSync6(wsJsonPath, "utf-8")));
|
|
4810
6234
|
const uri = narrowString("workspaceJson.folder", data?.folder) ?? narrowString("workspaceJson.workspace", data?.workspace) ?? "";
|
|
4811
6235
|
if (!uri) continue;
|
|
4812
6236
|
workspacePath = normalize(decodeURIComponent(uri.replace(/^file:\/\//, "")));
|
|
4813
6237
|
} catch {
|
|
4814
6238
|
continue;
|
|
4815
6239
|
}
|
|
4816
|
-
const wsDbPath =
|
|
4817
|
-
if (!
|
|
6240
|
+
const wsDbPath = join11(wsDir, "state.vscdb");
|
|
6241
|
+
if (!existsSync10(wsDbPath)) continue;
|
|
4818
6242
|
const wsDb = openDbReadOnly(wsDbPath);
|
|
4819
6243
|
if (!wsDb) continue;
|
|
4820
6244
|
try {
|
|
@@ -4837,7 +6261,7 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4837
6261
|
}
|
|
4838
6262
|
isAvailable() {
|
|
4839
6263
|
this.dbPath = this.findDbPath();
|
|
4840
|
-
return this.dbPath !== null &&
|
|
6264
|
+
return this.dbPath !== null && existsSync10(this.dbPath);
|
|
4841
6265
|
}
|
|
4842
6266
|
scan(options) {
|
|
4843
6267
|
if (!this.dbPath) return [];
|
|
@@ -5192,9 +6616,9 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
5192
6616
|
parts.push({ type: "text", text, time_created: timestampMs });
|
|
5193
6617
|
}
|
|
5194
6618
|
if (bubble.toolFormerData) {
|
|
5195
|
-
const
|
|
5196
|
-
if (
|
|
5197
|
-
parts.push(
|
|
6619
|
+
const toolPart2 = this.convertToolFormerData(bubble.toolFormerData, timestampMs);
|
|
6620
|
+
if (toolPart2) {
|
|
6621
|
+
parts.push(toolPart2);
|
|
5198
6622
|
}
|
|
5199
6623
|
}
|
|
5200
6624
|
if (parts.length === 0) continue;
|
|
@@ -5225,7 +6649,7 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
5225
6649
|
convertToolFormerData(toolData, timestampMs) {
|
|
5226
6650
|
if (!toolData || !toolData.name) return null;
|
|
5227
6651
|
const toolName = toolData.name;
|
|
5228
|
-
const normalizedName = toolName === "create_plan" ? "plan" :
|
|
6652
|
+
const normalizedName = toolName === "create_plan" ? "plan" : mapToolTitle3(toolName);
|
|
5229
6653
|
const state = {
|
|
5230
6654
|
status: toolData.status === "completed" ? "completed" : "running"
|
|
5231
6655
|
};
|
|
@@ -5353,7 +6777,7 @@ function narrowTimestampMs(field, value) {
|
|
|
5353
6777
|
return shaped === void 0 ? 0 : parseTimestampMs3(shaped);
|
|
5354
6778
|
}
|
|
5355
6779
|
function extractSessionIdFromFilename(filePath) {
|
|
5356
|
-
const stem =
|
|
6780
|
+
const stem = basename8(filePath, ".jsonl");
|
|
5357
6781
|
const underscore = stem.indexOf("_");
|
|
5358
6782
|
return underscore >= 0 ? stem.slice(underscore + 1) || stem : stem;
|
|
5359
6783
|
}
|
|
@@ -5409,14 +6833,14 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
5409
6833
|
displayName = "Pi";
|
|
5410
6834
|
basePath = null;
|
|
5411
6835
|
findBasePath() {
|
|
5412
|
-
return firstExisting(
|
|
6836
|
+
return firstExisting(join12(resolvePiDataRoot(), "agent", "sessions"), "data/pi");
|
|
5413
6837
|
}
|
|
5414
6838
|
getSessionWatchPlan() {
|
|
5415
6839
|
const dataRoot = resolvePiDataRoot();
|
|
5416
6840
|
return {
|
|
5417
6841
|
status: "supported",
|
|
5418
6842
|
targets: [
|
|
5419
|
-
{ root: dataRoot, path:
|
|
6843
|
+
{ root: dataRoot, path: join12(dataRoot, "agent", "sessions") },
|
|
5420
6844
|
{ root: "data/pi", path: "data/pi" }
|
|
5421
6845
|
]
|
|
5422
6846
|
};
|
|
@@ -5437,7 +6861,7 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
5437
6861
|
getSessionData(sessionId) {
|
|
5438
6862
|
const meta = this.sessionMetaMap.get(sessionId);
|
|
5439
6863
|
if (!meta) throw new Error(`Session not found: ${sessionId}`);
|
|
5440
|
-
if (!
|
|
6864
|
+
if (!existsSync11(meta.sourcePath)) throw new Error(`Session file missing: ${meta.sourcePath}`);
|
|
5441
6865
|
const parsed = this.parsePiFile(meta.sourcePath);
|
|
5442
6866
|
const state = this.convertEntries(parsed.pathEntries);
|
|
5443
6867
|
return {
|
|
@@ -5530,7 +6954,7 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
5530
6954
|
const sessionId = String(header["id"] ?? extractSessionIdFromFilename(filePath)).trim();
|
|
5531
6955
|
if (!sessionId) throw new Error("missing session id");
|
|
5532
6956
|
const stat = this.sessionSourceFile(filePath).stat;
|
|
5533
|
-
const directory = String(header["cwd"] ?? "").trim() ||
|
|
6957
|
+
const directory = String(header["cwd"] ?? "").trim() || basename8(filePath, ".jsonl");
|
|
5534
6958
|
const createdAt = narrowTimestampMs("session.timestamp", header["timestamp"]) || stat.mtimeMs;
|
|
5535
6959
|
const updatedAt = pathEntries.reduce(
|
|
5536
6960
|
(max, entry) => Math.max(max, getEntryTimestamp(entry)),
|
|
@@ -5673,7 +7097,7 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
5673
7097
|
if (type === "toolCall") {
|
|
5674
7098
|
const callId = String(item["id"] ?? "").trim();
|
|
5675
7099
|
const toolName = String(item["name"] ?? "").trim() || "tool";
|
|
5676
|
-
const
|
|
7100
|
+
const toolPart2 = {
|
|
5677
7101
|
type: "tool",
|
|
5678
7102
|
tool: toolName,
|
|
5679
7103
|
title: `Tool: ${toolName}`,
|
|
@@ -5684,7 +7108,7 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
5684
7108
|
input: item["arguments"] ?? {}
|
|
5685
7109
|
}
|
|
5686
7110
|
};
|
|
5687
|
-
parts.push(
|
|
7111
|
+
parts.push(toolPart2);
|
|
5688
7112
|
}
|
|
5689
7113
|
}
|
|
5690
7114
|
return parts;
|
|
@@ -5739,70 +7163,790 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
5739
7163
|
const text = cleanInternalText(rawText);
|
|
5740
7164
|
if (!text) return null;
|
|
5741
7165
|
return {
|
|
5742
|
-
id: narrowPiField("summary.id", entry["id"], asString) ?? "",
|
|
5743
|
-
role: type === "custom_message" ? "user" : "assistant",
|
|
5744
|
-
agent: type === "custom_message" ? void 0 : "pi",
|
|
5745
|
-
timestampMs,
|
|
5746
|
-
parts: [{ type: "text", text, time_created: timestampMs }]
|
|
7166
|
+
id: narrowPiField("summary.id", entry["id"], asString) ?? "",
|
|
7167
|
+
role: type === "custom_message" ? "user" : "assistant",
|
|
7168
|
+
agent: type === "custom_message" ? void 0 : "pi",
|
|
7169
|
+
timestampMs,
|
|
7170
|
+
parts: [{ type: "text", text, time_created: timestampMs }]
|
|
7171
|
+
};
|
|
7172
|
+
}
|
|
7173
|
+
normalizeUsage(raw) {
|
|
7174
|
+
const usage = narrowPiField("message.usage", raw, asRecord) ?? {};
|
|
7175
|
+
const inputTokens = narrowPiField("message.usage.input", usage["input"], asNumber) ?? 0;
|
|
7176
|
+
const outputTokens = narrowPiField("message.usage.output", usage["output"], asNumber) ?? 0;
|
|
7177
|
+
const cacheReadTokens = narrowPiField("message.usage.cacheRead", usage["cacheRead"], asNumber) ?? 0;
|
|
7178
|
+
const cacheCreateTokens = narrowPiField("message.usage.cacheWrite", usage["cacheWrite"], asNumber) ?? 0;
|
|
7179
|
+
const totalTokens = narrowPiField("message.usage.totalTokens", usage["totalTokens"], asNumber) ?? inputTokens + outputTokens + cacheReadTokens + cacheCreateTokens;
|
|
7180
|
+
const cost = isObject(usage["cost"]) ? Number(usage["cost"]["total"] ?? 0) : null;
|
|
7181
|
+
return {
|
|
7182
|
+
inputTokens: inputTokens + cacheReadTokens + cacheCreateTokens,
|
|
7183
|
+
outputTokens,
|
|
7184
|
+
cacheReadTokens,
|
|
7185
|
+
cacheCreateTokens,
|
|
7186
|
+
totalTokens,
|
|
7187
|
+
cost: cost && Number.isFinite(cost) ? cost : null,
|
|
7188
|
+
tokens: {
|
|
7189
|
+
input: inputTokens + cacheReadTokens + cacheCreateTokens,
|
|
7190
|
+
output: outputTokens,
|
|
7191
|
+
cache_read: cacheReadTokens || void 0,
|
|
7192
|
+
cache_create: cacheCreateTokens || void 0
|
|
7193
|
+
}
|
|
7194
|
+
};
|
|
7195
|
+
}
|
|
7196
|
+
emptyUsageResult(message) {
|
|
7197
|
+
return {
|
|
7198
|
+
message,
|
|
7199
|
+
totalTokens: 0,
|
|
7200
|
+
model: null
|
|
7201
|
+
};
|
|
7202
|
+
}
|
|
7203
|
+
};
|
|
7204
|
+
function resolveZCodeDataRoot() {
|
|
7205
|
+
const currentPlatform = platform3();
|
|
7206
|
+
if (currentPlatform !== "darwin" && currentPlatform !== "win32") return null;
|
|
7207
|
+
return join13(homedir4(), ".zcode");
|
|
7208
|
+
}
|
|
7209
|
+
function findZCodeDbPath() {
|
|
7210
|
+
if (!isSqliteAvailable()) return null;
|
|
7211
|
+
const dataRoot = resolveZCodeDataRoot();
|
|
7212
|
+
if (!dataRoot) return null;
|
|
7213
|
+
return firstExisting(join13(dataRoot, "cli", "db", "db.sqlite"));
|
|
7214
|
+
}
|
|
7215
|
+
function getZCodeSessionWatchPlan() {
|
|
7216
|
+
const dataRoot = resolveZCodeDataRoot();
|
|
7217
|
+
return {
|
|
7218
|
+
status: "supported",
|
|
7219
|
+
targets: dataRoot ? [{ root: dataRoot, path: join13(dataRoot, "cli", "db", "db.sqlite") }] : []
|
|
7220
|
+
};
|
|
7221
|
+
}
|
|
7222
|
+
var ZCodeAgent = class extends OpenCodeSqliteAgent {
|
|
7223
|
+
constructor() {
|
|
7224
|
+
super({
|
|
7225
|
+
name: "zcode",
|
|
7226
|
+
displayName: "ZCode",
|
|
7227
|
+
findDbPath: findZCodeDbPath,
|
|
7228
|
+
getSessionWatchPlan: getZCodeSessionWatchPlan
|
|
7229
|
+
});
|
|
7230
|
+
}
|
|
7231
|
+
};
|
|
7232
|
+
var HEAD_INDEX_VERSION4 = "grok-head-v1";
|
|
7233
|
+
var PARSER_VERSION3 = "grok-parser-v1";
|
|
7234
|
+
var SUMMARY_FILE = "summary.json";
|
|
7235
|
+
var UPDATES_FILE = "updates.jsonl";
|
|
7236
|
+
var ACP_UPDATE_METHOD = "session/update";
|
|
7237
|
+
var XAI_UPDATE_METHOD = "_x.ai/session/update";
|
|
7238
|
+
var USD_TICKS_PER_USD = 1e10;
|
|
7239
|
+
var GROK_TOOL_TITLE_MAP = {
|
|
7240
|
+
get_command_or_subagent_output: "task",
|
|
7241
|
+
grep: "grep",
|
|
7242
|
+
list_dir: "list",
|
|
7243
|
+
read_file: "read",
|
|
7244
|
+
run_terminal_command: "bash",
|
|
7245
|
+
search_replace: "edit",
|
|
7246
|
+
todo_write: "todo",
|
|
7247
|
+
web_fetch: "web fetch"
|
|
7248
|
+
};
|
|
7249
|
+
function resolveGrokDataRoot() {
|
|
7250
|
+
return resolveHomePath("GROK_HOME", ".grok");
|
|
7251
|
+
}
|
|
7252
|
+
function safeCount(value) {
|
|
7253
|
+
const number = asNumber(value);
|
|
7254
|
+
return number !== void 0 && Number.isSafeInteger(number) && number >= 0 ? number : 0;
|
|
7255
|
+
}
|
|
7256
|
+
function addCount(total, value) {
|
|
7257
|
+
return Math.min(Number.MAX_SAFE_INTEGER, total + value);
|
|
7258
|
+
}
|
|
7259
|
+
function timestampFromIso(value) {
|
|
7260
|
+
const text = asString(value);
|
|
7261
|
+
if (!text) return null;
|
|
7262
|
+
const timestamp = Date.parse(text);
|
|
7263
|
+
return Number.isFinite(timestamp) ? timestamp : null;
|
|
7264
|
+
}
|
|
7265
|
+
function readJsonRecord(filePath) {
|
|
7266
|
+
try {
|
|
7267
|
+
return asRecord(JSON.parse(readFileSync7(filePath, "utf8"))) ?? null;
|
|
7268
|
+
} catch {
|
|
7269
|
+
return null;
|
|
7270
|
+
}
|
|
7271
|
+
}
|
|
7272
|
+
function parseSummary(source) {
|
|
7273
|
+
const summary = readJsonRecord(source.file);
|
|
7274
|
+
const info = asRecord(summary?.info);
|
|
7275
|
+
const id = asString(info?.id)?.trim();
|
|
7276
|
+
const cwd = asString(info?.cwd)?.trim();
|
|
7277
|
+
if (!id || !cwd) return null;
|
|
7278
|
+
const createdAt = timestampFromIso(summary?.created_at) ?? source.stat.mtimeMs;
|
|
7279
|
+
const updatedAt = timestampFromIso(summary?.last_active_at) ?? timestampFromIso(summary?.updated_at) ?? createdAt;
|
|
7280
|
+
const generatedTitle = asString(summary?.generated_title)?.trim();
|
|
7281
|
+
const sessionSummary = asString(summary?.session_summary)?.trim();
|
|
7282
|
+
const parentSessionId = asString(summary?.parent_session_id)?.trim() || null;
|
|
7283
|
+
return {
|
|
7284
|
+
id,
|
|
7285
|
+
cwd,
|
|
7286
|
+
title: generatedTitle || sessionSummary || null,
|
|
7287
|
+
createdAt,
|
|
7288
|
+
updatedAt,
|
|
7289
|
+
messageCount: safeCount(summary?.num_chat_messages),
|
|
7290
|
+
currentModel: asString(summary?.current_model_id)?.trim() || null,
|
|
7291
|
+
parentSessionId: parentSessionId === id ? null : parentSessionId
|
|
7292
|
+
};
|
|
7293
|
+
}
|
|
7294
|
+
function optionalFileFingerprint(filePath) {
|
|
7295
|
+
try {
|
|
7296
|
+
const stat = statSync8(filePath);
|
|
7297
|
+
return [stat.mtimeMs, stat.size];
|
|
7298
|
+
} catch {
|
|
7299
|
+
return null;
|
|
7300
|
+
}
|
|
7301
|
+
}
|
|
7302
|
+
function unpackUpdate(record, fallbackTimestampMs) {
|
|
7303
|
+
const method = asString(record.method) ?? ACP_UPDATE_METHOD;
|
|
7304
|
+
const params = asRecord(record.params) ?? record;
|
|
7305
|
+
const update = asRecord(params.update);
|
|
7306
|
+
if (!update) return null;
|
|
7307
|
+
const paramsMeta = asRecord(params._meta);
|
|
7308
|
+
const agentTimestampMs = asNumber(paramsMeta?.agentTimestampMs);
|
|
7309
|
+
const envelopeTimestamp = asNumber(record.timestamp);
|
|
7310
|
+
const timestampMs = agentTimestampMs !== void 0 && Number.isSafeInteger(agentTimestampMs) && agentTimestampMs >= 0 ? agentTimestampMs : envelopeTimestamp !== void 0 && Number.isSafeInteger(envelopeTimestamp) && envelopeTimestamp >= 0 && envelopeTimestamp <= Number.MAX_SAFE_INTEGER / 1e3 ? envelopeTimestamp * 1e3 : fallbackTimestampMs;
|
|
7311
|
+
return { method, params, update, timestampMs };
|
|
7312
|
+
}
|
|
7313
|
+
function isHostTurn(update) {
|
|
7314
|
+
return asRecord(update._meta)?.hostTurn === true;
|
|
7315
|
+
}
|
|
7316
|
+
function updateType(update) {
|
|
7317
|
+
return asString(update.update.sessionUpdate) ?? "";
|
|
7318
|
+
}
|
|
7319
|
+
function parseUsage(update) {
|
|
7320
|
+
const usage = asRecord(update.usage);
|
|
7321
|
+
if (!usage) return null;
|
|
7322
|
+
const input = safeCount(usage.inputTokens);
|
|
7323
|
+
const output = safeCount(usage.outputTokens);
|
|
7324
|
+
const reasoning = safeCount(usage.reasoningTokens);
|
|
7325
|
+
const cacheRead = safeCount(usage.cachedReadTokens);
|
|
7326
|
+
const cacheCreate = safeCount(usage.cacheCreationTokens);
|
|
7327
|
+
const totalTokens = safeCount(usage.totalTokens) || addCount(input, output);
|
|
7328
|
+
const isIncomplete = usage.usageIsIncomplete === true || usage.costIsPartial === true;
|
|
7329
|
+
const costTicks = safeCount(usage.costUsdTicks);
|
|
7330
|
+
const cost = !isIncomplete && costTicks > 0 ? costTicks / USD_TICKS_PER_USD : null;
|
|
7331
|
+
const modelUsage = {};
|
|
7332
|
+
const modelUsageRecord = asRecord(usage.modelUsage);
|
|
7333
|
+
if (modelUsageRecord) {
|
|
7334
|
+
for (const [model, rawUsage] of Object.entries(modelUsageRecord)) {
|
|
7335
|
+
const modelRecord = asRecord(rawUsage);
|
|
7336
|
+
if (!modelRecord) continue;
|
|
7337
|
+
const modelTotal = safeCount(modelRecord.totalTokens) || addCount(safeCount(modelRecord.inputTokens), safeCount(modelRecord.outputTokens));
|
|
7338
|
+
if (modelTotal > 0) modelUsage[model] = modelTotal;
|
|
7339
|
+
}
|
|
7340
|
+
}
|
|
7341
|
+
const models = Object.keys(modelUsage);
|
|
7342
|
+
return {
|
|
7343
|
+
tokens: {
|
|
7344
|
+
input,
|
|
7345
|
+
output,
|
|
7346
|
+
reasoning: reasoning || void 0,
|
|
7347
|
+
cache_read: cacheRead || void 0,
|
|
7348
|
+
cache_create: cacheCreate || void 0
|
|
7349
|
+
},
|
|
7350
|
+
totalTokens,
|
|
7351
|
+
cost,
|
|
7352
|
+
modelUsage,
|
|
7353
|
+
primaryModel: models.length === 1 ? models[0] : null
|
|
7354
|
+
};
|
|
7355
|
+
}
|
|
7356
|
+
function textFromContentBlock(value) {
|
|
7357
|
+
const content = asRecord(value);
|
|
7358
|
+
if (!content) return "";
|
|
7359
|
+
if (content.type === "text") return asString(content.text) ?? "";
|
|
7360
|
+
const resource = asRecord(content.resource);
|
|
7361
|
+
return asString(resource?.text) ?? "";
|
|
7362
|
+
}
|
|
7363
|
+
function messagePartFromContentBlock(value, timestampMs) {
|
|
7364
|
+
const content = asRecord(value);
|
|
7365
|
+
if (!content) return null;
|
|
7366
|
+
if (content.type === "text") {
|
|
7367
|
+
const text2 = asString(content.text) ?? "";
|
|
7368
|
+
return text2 ? { type: "text", text: text2, time_created: timestampMs } : null;
|
|
7369
|
+
}
|
|
7370
|
+
if (content.type === "image") {
|
|
7371
|
+
const data = asString(content.data);
|
|
7372
|
+
const url = asString(content.uri) ?? asString(content.url);
|
|
7373
|
+
const mimeType = asString(content.mimeType) ?? asString(content.mime_type);
|
|
7374
|
+
if (data && mimeType) {
|
|
7375
|
+
return { type: "image", data, mime_type: mimeType, url, time_created: timestampMs };
|
|
7376
|
+
}
|
|
7377
|
+
if (url) return { type: "image", url, mime_type: mimeType, time_created: timestampMs };
|
|
7378
|
+
}
|
|
7379
|
+
const text = textFromContentBlock(content);
|
|
7380
|
+
return text ? { type: "text", text, time_created: timestampMs } : null;
|
|
7381
|
+
}
|
|
7382
|
+
var GrokMessageCounter = class {
|
|
7383
|
+
firstUserText = null;
|
|
7384
|
+
messageCount = 0;
|
|
7385
|
+
activeAssistant = false;
|
|
7386
|
+
activeAssistantPromptId = null;
|
|
7387
|
+
activeUser = false;
|
|
7388
|
+
activeUserPromptIndex = null;
|
|
7389
|
+
process(envelope) {
|
|
7390
|
+
if (envelope.method === XAI_UPDATE_METHOD) return;
|
|
7391
|
+
switch (updateType(envelope)) {
|
|
7392
|
+
case "user_message_chunk":
|
|
7393
|
+
this.appendUser(envelope);
|
|
7394
|
+
break;
|
|
7395
|
+
case "agent_thought_chunk":
|
|
7396
|
+
case "agent_message_chunk":
|
|
7397
|
+
if (cleanInternalText(textFromContentBlock(envelope.update.content))) {
|
|
7398
|
+
this.appendAssistant(envelope);
|
|
7399
|
+
}
|
|
7400
|
+
break;
|
|
7401
|
+
case "tool_call":
|
|
7402
|
+
if (asString(envelope.update.toolCallId)?.trim()) this.appendAssistant(envelope);
|
|
7403
|
+
break;
|
|
7404
|
+
case "plan":
|
|
7405
|
+
if (planPart(envelope.update, envelope.timestampMs)) this.appendAssistant(envelope);
|
|
7406
|
+
break;
|
|
7407
|
+
}
|
|
7408
|
+
}
|
|
7409
|
+
appendUser(envelope) {
|
|
7410
|
+
if (isHostTurn(envelope.update)) return;
|
|
7411
|
+
const part = messagePartFromContentBlock(envelope.update.content, envelope.timestampMs);
|
|
7412
|
+
if (!part) return;
|
|
7413
|
+
if (part.type === "text" && !cleanInternalText(part.text)) return;
|
|
7414
|
+
const promptIndexValue = asNumber(asRecord(envelope.update._meta)?.promptIndex);
|
|
7415
|
+
const promptIndex = promptIndexValue !== void 0 && Number.isSafeInteger(promptIndexValue) ? promptIndexValue : null;
|
|
7416
|
+
const canReuse = this.activeUser && (promptIndex === null || this.activeUserPromptIndex === null || promptIndex === this.activeUserPromptIndex);
|
|
7417
|
+
if (!canReuse) this.messageCount += 1;
|
|
7418
|
+
if (this.firstUserText === null && part.type === "text") {
|
|
7419
|
+
this.firstUserText = cleanInternalText(part.text) || null;
|
|
7420
|
+
}
|
|
7421
|
+
this.activeUser = true;
|
|
7422
|
+
this.activeUserPromptIndex = promptIndex;
|
|
7423
|
+
this.activeAssistant = false;
|
|
7424
|
+
this.activeAssistantPromptId = null;
|
|
7425
|
+
}
|
|
7426
|
+
appendAssistant(envelope) {
|
|
7427
|
+
const promptId = asString(asRecord(envelope.params._meta)?.promptId)?.trim() || null;
|
|
7428
|
+
if (promptId && this.activeAssistantPromptId && promptId !== this.activeAssistantPromptId) {
|
|
7429
|
+
this.activeAssistant = false;
|
|
7430
|
+
}
|
|
7431
|
+
if (promptId) this.activeAssistantPromptId = promptId;
|
|
7432
|
+
this.activeUser = false;
|
|
7433
|
+
this.activeUserPromptIndex = null;
|
|
7434
|
+
if (!this.activeAssistant) this.messageCount += 1;
|
|
7435
|
+
this.activeAssistant = true;
|
|
7436
|
+
}
|
|
7437
|
+
};
|
|
7438
|
+
function scanCanonicalMessages(updatesPath, fallbackTimestampMs) {
|
|
7439
|
+
const rewindIndex = new RewindIndex();
|
|
7440
|
+
let recordIndex = 0;
|
|
7441
|
+
for (const record of readJsonlFile(updatesPath)) {
|
|
7442
|
+
rewindIndex.process(record, recordIndex, fallbackTimestampMs);
|
|
7443
|
+
recordIndex += 1;
|
|
7444
|
+
}
|
|
7445
|
+
const survivingIndexes = new Set(rewindIndex.survivingRecordIndexes);
|
|
7446
|
+
const counter = new GrokMessageCounter();
|
|
7447
|
+
recordIndex = 0;
|
|
7448
|
+
for (const record of readJsonlFile(updatesPath)) {
|
|
7449
|
+
if (survivingIndexes.has(recordIndex)) {
|
|
7450
|
+
const envelope = unpackUpdate(record, fallbackTimestampMs);
|
|
7451
|
+
if (envelope) counter.process(envelope);
|
|
7452
|
+
}
|
|
7453
|
+
recordIndex += 1;
|
|
7454
|
+
}
|
|
7455
|
+
return counter;
|
|
7456
|
+
}
|
|
7457
|
+
function scanHeadData(updatesPath, fallbackTimestampMs) {
|
|
7458
|
+
let totalInputTokens = 0;
|
|
7459
|
+
let totalOutputTokens = 0;
|
|
7460
|
+
let totalCacheReadTokens = 0;
|
|
7461
|
+
let totalCacheCreateTokens = 0;
|
|
7462
|
+
let totalTokens = 0;
|
|
7463
|
+
let totalCost = 0;
|
|
7464
|
+
let hasUnknownCost = false;
|
|
7465
|
+
let hasRewinds = false;
|
|
7466
|
+
const modelUsage = {};
|
|
7467
|
+
let messageCounter = new GrokMessageCounter();
|
|
7468
|
+
if (updatesPath && existsSync12(updatesPath)) {
|
|
7469
|
+
for (const line of readJsonlFileLines(updatesPath)) {
|
|
7470
|
+
const affectsMessages = line.includes('"user_message_chunk"') || line.includes('"agent_thought_chunk"') || line.includes('"agent_message_chunk"') || line.includes('"tool_call"') || line.includes('"plan"');
|
|
7471
|
+
const isTurnCompleted = line.includes('"turn_completed"');
|
|
7472
|
+
const isRewind = line.includes('"rewind_marker"');
|
|
7473
|
+
if (!affectsMessages && !isTurnCompleted && !isRewind) continue;
|
|
7474
|
+
let record;
|
|
7475
|
+
try {
|
|
7476
|
+
record = asRecord(JSON.parse(line)) ?? {};
|
|
7477
|
+
} catch {
|
|
7478
|
+
continue;
|
|
7479
|
+
}
|
|
7480
|
+
const envelope = unpackUpdate(record, fallbackTimestampMs);
|
|
7481
|
+
if (!envelope) continue;
|
|
7482
|
+
const type = updateType(envelope);
|
|
7483
|
+
if (affectsMessages) messageCounter.process(envelope);
|
|
7484
|
+
if (envelope.method === XAI_UPDATE_METHOD && type === "rewind_marker") {
|
|
7485
|
+
hasRewinds = true;
|
|
7486
|
+
}
|
|
7487
|
+
if (envelope.method !== XAI_UPDATE_METHOD || type !== "turn_completed") continue;
|
|
7488
|
+
const usage = parseUsage(envelope.update);
|
|
7489
|
+
if (!usage) continue;
|
|
7490
|
+
totalInputTokens = addCount(totalInputTokens, usage.tokens.input ?? 0);
|
|
7491
|
+
totalOutputTokens = addCount(totalOutputTokens, usage.tokens.output ?? 0);
|
|
7492
|
+
totalCacheReadTokens = addCount(totalCacheReadTokens, usage.tokens.cache_read ?? 0);
|
|
7493
|
+
totalCacheCreateTokens = addCount(totalCacheCreateTokens, usage.tokens.cache_create ?? 0);
|
|
7494
|
+
totalTokens = addCount(totalTokens, usage.totalTokens);
|
|
7495
|
+
if (usage.cost === null && usage.totalTokens > 0) hasUnknownCost = true;
|
|
7496
|
+
else totalCost += usage.cost ?? 0;
|
|
7497
|
+
for (const [model, tokens] of Object.entries(usage.modelUsage)) {
|
|
7498
|
+
modelUsage[model] = addCount(modelUsage[model] ?? 0, tokens);
|
|
7499
|
+
}
|
|
7500
|
+
}
|
|
7501
|
+
if (hasRewinds) messageCounter = scanCanonicalMessages(updatesPath, fallbackTimestampMs);
|
|
7502
|
+
}
|
|
7503
|
+
return {
|
|
7504
|
+
firstUserText: messageCounter.firstUserText,
|
|
7505
|
+
visibleMessageCount: messageCounter.messageCount,
|
|
7506
|
+
stats: {
|
|
7507
|
+
total_input_tokens: totalInputTokens,
|
|
7508
|
+
total_output_tokens: totalOutputTokens,
|
|
7509
|
+
total_cache_read_tokens: totalCacheReadTokens || void 0,
|
|
7510
|
+
total_cache_create_tokens: totalCacheCreateTokens || void 0,
|
|
7511
|
+
total_tokens: totalTokens || addCount(totalInputTokens, totalOutputTokens),
|
|
7512
|
+
total_cost: totalCost,
|
|
7513
|
+
cost_source: totalCost > 0 && !hasUnknownCost ? "recorded" : void 0
|
|
7514
|
+
},
|
|
7515
|
+
modelUsage
|
|
7516
|
+
};
|
|
7517
|
+
}
|
|
7518
|
+
function toolTitle(toolName) {
|
|
7519
|
+
return GROK_TOOL_TITLE_MAP[toolName] ?? toolName;
|
|
7520
|
+
}
|
|
7521
|
+
function toolStatus(value) {
|
|
7522
|
+
switch (asString(value)?.toLowerCase()) {
|
|
7523
|
+
case "completed":
|
|
7524
|
+
return "completed";
|
|
7525
|
+
case "failed":
|
|
7526
|
+
return "error";
|
|
7527
|
+
case "in_progress":
|
|
7528
|
+
case "pending":
|
|
7529
|
+
return "running";
|
|
7530
|
+
default:
|
|
7531
|
+
return null;
|
|
7532
|
+
}
|
|
7533
|
+
}
|
|
7534
|
+
function toolOutput(update) {
|
|
7535
|
+
if (update.rawOutput !== void 0 && update.rawOutput !== null) return update.rawOutput;
|
|
7536
|
+
const content = asArray(update.content);
|
|
7537
|
+
if (!content) return void 0;
|
|
7538
|
+
const texts = content.flatMap((item) => {
|
|
7539
|
+
const wrapper = asRecord(item);
|
|
7540
|
+
const block = wrapper?.type === "content" ? wrapper.content : item;
|
|
7541
|
+
const text = textFromContentBlock(block);
|
|
7542
|
+
return text ? [text] : [];
|
|
7543
|
+
});
|
|
7544
|
+
if (texts.length === 0) return content;
|
|
7545
|
+
return texts.join("\n");
|
|
7546
|
+
}
|
|
7547
|
+
function planPart(update, timestampMs) {
|
|
7548
|
+
const entries = asArray(update.entries)?.flatMap((value) => {
|
|
7549
|
+
const entry = asRecord(value);
|
|
7550
|
+
const content = cleanInternalText(asString(entry?.content) ?? "");
|
|
7551
|
+
if (!entry || !content) return [];
|
|
7552
|
+
return [{ content, status: asString(entry.status) ?? "pending" }];
|
|
7553
|
+
});
|
|
7554
|
+
if (!entries || entries.length === 0) return null;
|
|
7555
|
+
const text = entries.map(({ content, status }) => `- [${status === "completed" ? "x" : " "}] ${content}`).join("\n");
|
|
7556
|
+
return {
|
|
7557
|
+
type: "plan",
|
|
7558
|
+
text,
|
|
7559
|
+
approval_status: entries.every(({ status }) => status === "completed") ? "success" : "fail",
|
|
7560
|
+
time_created: timestampMs
|
|
7561
|
+
};
|
|
7562
|
+
}
|
|
7563
|
+
var GrokTranscriptReducer = class {
|
|
7564
|
+
constructor(sessionId, currentModel, fallbackTimestampMs) {
|
|
7565
|
+
this.sessionId = sessionId;
|
|
7566
|
+
this.fallbackTimestampMs = fallbackTimestampMs;
|
|
7567
|
+
this.currentModel = currentModel;
|
|
7568
|
+
}
|
|
7569
|
+
sessionId;
|
|
7570
|
+
fallbackTimestampMs;
|
|
7571
|
+
builder = new TranscriptBuilder();
|
|
7572
|
+
activeAssistant = null;
|
|
7573
|
+
activeAssistantPromptId = null;
|
|
7574
|
+
activeUser = null;
|
|
7575
|
+
activeUserPromptIndex = null;
|
|
7576
|
+
latestPlan = null;
|
|
7577
|
+
currentMode = null;
|
|
7578
|
+
currentModel;
|
|
7579
|
+
process(record) {
|
|
7580
|
+
const envelope = unpackUpdate(record, this.fallbackTimestampMs);
|
|
7581
|
+
if (!envelope) return;
|
|
7582
|
+
if (envelope.method === XAI_UPDATE_METHOD) {
|
|
7583
|
+
this.processXaiUpdate(envelope);
|
|
7584
|
+
return;
|
|
7585
|
+
}
|
|
7586
|
+
this.processAcpUpdate(envelope);
|
|
7587
|
+
}
|
|
7588
|
+
finish(stats) {
|
|
7589
|
+
return this.builder.finish(stats);
|
|
7590
|
+
}
|
|
7591
|
+
processAcpUpdate(envelope) {
|
|
7592
|
+
switch (updateType(envelope)) {
|
|
7593
|
+
case "user_message_chunk":
|
|
7594
|
+
this.appendUserChunk(envelope);
|
|
7595
|
+
break;
|
|
7596
|
+
case "agent_thought_chunk":
|
|
7597
|
+
this.appendAssistantText(envelope, "reasoning");
|
|
7598
|
+
break;
|
|
7599
|
+
case "agent_message_chunk":
|
|
7600
|
+
this.appendAssistantText(envelope, "text");
|
|
7601
|
+
break;
|
|
7602
|
+
case "tool_call":
|
|
7603
|
+
this.appendToolCall(envelope);
|
|
7604
|
+
break;
|
|
7605
|
+
case "tool_call_update":
|
|
7606
|
+
this.updateToolCall(envelope);
|
|
7607
|
+
break;
|
|
7608
|
+
case "plan":
|
|
7609
|
+
this.updatePlan(envelope);
|
|
7610
|
+
break;
|
|
7611
|
+
case "current_mode_update":
|
|
7612
|
+
this.currentMode = asString(envelope.update.currentModeId)?.trim() || null;
|
|
7613
|
+
break;
|
|
7614
|
+
}
|
|
7615
|
+
}
|
|
7616
|
+
processXaiUpdate(envelope) {
|
|
7617
|
+
switch (updateType(envelope)) {
|
|
7618
|
+
case "turn_completed": {
|
|
7619
|
+
const usage = parseUsage(envelope.update);
|
|
7620
|
+
if (!usage) return;
|
|
7621
|
+
this.builder.attachUsageToLatestAssistant(usage.tokens, {
|
|
7622
|
+
model: usage.primaryModel ?? this.currentModel,
|
|
7623
|
+
cost: usage.cost ?? void 0,
|
|
7624
|
+
costSource: usage.cost === null ? void 0 : "recorded"
|
|
7625
|
+
});
|
|
7626
|
+
return;
|
|
7627
|
+
}
|
|
7628
|
+
case "model_changed":
|
|
7629
|
+
this.currentModel = asString(envelope.update.model_id)?.trim() || this.currentModel;
|
|
7630
|
+
return;
|
|
7631
|
+
case "model_auto_switched":
|
|
7632
|
+
this.currentModel = asString(envelope.update.new_model_id)?.trim() || this.currentModel;
|
|
7633
|
+
return;
|
|
7634
|
+
case "subagent_spawned": {
|
|
7635
|
+
const childSessionId = asString(envelope.update.child_session_id)?.trim();
|
|
7636
|
+
if (childSessionId && this.activeAssistant && !this.activeAssistant.subagent_id) {
|
|
7637
|
+
this.activeAssistant.subagent_id = childSessionId;
|
|
7638
|
+
}
|
|
7639
|
+
}
|
|
7640
|
+
}
|
|
7641
|
+
}
|
|
7642
|
+
appendUserChunk(envelope) {
|
|
7643
|
+
if (isHostTurn(envelope.update)) return;
|
|
7644
|
+
const part = messagePartFromContentBlock(envelope.update.content, envelope.timestampMs);
|
|
7645
|
+
if (!part) return;
|
|
7646
|
+
const promptIndexValue = asNumber(asRecord(envelope.update._meta)?.promptIndex);
|
|
7647
|
+
const promptIndex = promptIndexValue !== void 0 && Number.isSafeInteger(promptIndexValue) ? promptIndexValue : null;
|
|
7648
|
+
const canReuse = this.activeUser !== null && (promptIndex === null || this.activeUserPromptIndex === null || promptIndex === this.activeUserPromptIndex);
|
|
7649
|
+
if (canReuse) {
|
|
7650
|
+
const tail = this.activeUser.parts.at(-1);
|
|
7651
|
+
if (tail?.type === "text" && part.type === "text") tail.text += part.text;
|
|
7652
|
+
else this.activeUser.parts.push(part);
|
|
7653
|
+
} else {
|
|
7654
|
+
const eventId = asString(asRecord(envelope.params._meta)?.eventId);
|
|
7655
|
+
this.activeUser = this.builder.appendMessage({
|
|
7656
|
+
id: eventId || `${this.sessionId}-user-${envelope.timestampMs}`,
|
|
7657
|
+
role: "user",
|
|
7658
|
+
timestampMs: envelope.timestampMs,
|
|
7659
|
+
parts: [part]
|
|
7660
|
+
});
|
|
7661
|
+
}
|
|
7662
|
+
this.activeUserPromptIndex = promptIndex;
|
|
7663
|
+
this.activeAssistant = null;
|
|
7664
|
+
this.activeAssistantPromptId = null;
|
|
7665
|
+
this.latestPlan = null;
|
|
7666
|
+
}
|
|
7667
|
+
prepareAssistant(envelope) {
|
|
7668
|
+
const promptId = asString(asRecord(envelope.params._meta)?.promptId)?.trim() || null;
|
|
7669
|
+
if (promptId && this.activeAssistantPromptId && promptId !== this.activeAssistantPromptId) {
|
|
7670
|
+
this.builder.beginTurn();
|
|
7671
|
+
this.activeAssistant = null;
|
|
7672
|
+
this.latestPlan = null;
|
|
7673
|
+
}
|
|
7674
|
+
if (promptId) this.activeAssistantPromptId = promptId;
|
|
7675
|
+
this.activeUser = null;
|
|
7676
|
+
this.activeUserPromptIndex = null;
|
|
7677
|
+
}
|
|
7678
|
+
assistantInput(envelope) {
|
|
7679
|
+
const paramsMeta = asRecord(envelope.params._meta);
|
|
7680
|
+
return {
|
|
7681
|
+
id: asString(paramsMeta?.promptId) || asString(paramsMeta?.eventId) || `${this.sessionId}-assistant-${envelope.timestampMs}`,
|
|
7682
|
+
timestampMs: envelope.timestampMs,
|
|
7683
|
+
agent: "grok",
|
|
7684
|
+
mode: this.currentMode,
|
|
7685
|
+
model: this.currentModel,
|
|
7686
|
+
provider: "xai"
|
|
7687
|
+
};
|
|
7688
|
+
}
|
|
7689
|
+
appendAssistantText(envelope, type) {
|
|
7690
|
+
const text = textFromContentBlock(envelope.update.content);
|
|
7691
|
+
if (!text) return;
|
|
7692
|
+
this.prepareAssistant(envelope);
|
|
7693
|
+
const tail = this.activeAssistant?.parts.at(-1);
|
|
7694
|
+
if (tail?.type === type) {
|
|
7695
|
+
tail.text += text;
|
|
7696
|
+
return;
|
|
7697
|
+
}
|
|
7698
|
+
this.activeAssistant = this.builder.appendAssistantPart(
|
|
7699
|
+
{ type, text, time_created: envelope.timestampMs },
|
|
7700
|
+
this.assistantInput(envelope),
|
|
7701
|
+
{ grouping: "current" }
|
|
7702
|
+
);
|
|
7703
|
+
}
|
|
7704
|
+
appendToolCall(envelope) {
|
|
7705
|
+
const callId = asString(envelope.update.toolCallId)?.trim();
|
|
7706
|
+
const updateMeta = asRecord(envelope.update._meta);
|
|
7707
|
+
const nativeTool = asRecord(updateMeta?.["x.ai/tool"]);
|
|
7708
|
+
const name = asString(nativeTool?.name)?.trim() || asString(envelope.update.title)?.trim() || "tool";
|
|
7709
|
+
if (!callId) return;
|
|
7710
|
+
this.prepareAssistant(envelope);
|
|
7711
|
+
const metadata = {
|
|
7712
|
+
kind: asString(nativeTool?.kind) ?? asString(envelope.update.kind),
|
|
7713
|
+
locations: envelope.update.locations
|
|
7714
|
+
};
|
|
7715
|
+
const part = {
|
|
7716
|
+
type: "tool",
|
|
7717
|
+
tool: name,
|
|
7718
|
+
title: toolTitle(name),
|
|
7719
|
+
callID: callId,
|
|
7720
|
+
state: {
|
|
7721
|
+
status: toolStatus(envelope.update.status) ?? "running",
|
|
7722
|
+
input: envelope.update.rawInput,
|
|
7723
|
+
output: null,
|
|
7724
|
+
metadata
|
|
7725
|
+
},
|
|
7726
|
+
time_created: envelope.timestampMs
|
|
7727
|
+
};
|
|
7728
|
+
this.activeAssistant = this.builder.appendToolCall(part, this.assistantInput(envelope), {
|
|
7729
|
+
markModeAsTool: true,
|
|
7730
|
+
target: "current"
|
|
7731
|
+
});
|
|
7732
|
+
}
|
|
7733
|
+
updateToolCall(envelope) {
|
|
7734
|
+
const callId = asString(envelope.update.toolCallId)?.trim();
|
|
7735
|
+
if (!callId) return;
|
|
7736
|
+
this.builder.updateToolCall(callId, (part) => {
|
|
7737
|
+
const status = toolStatus(envelope.update.status);
|
|
7738
|
+
if (status) part.state.status = status;
|
|
7739
|
+
if (envelope.update.rawInput !== void 0) part.state.input = envelope.update.rawInput;
|
|
7740
|
+
const output = toolOutput(envelope.update);
|
|
7741
|
+
if (output !== void 0) {
|
|
7742
|
+
if (status === "error") part.state.error = output;
|
|
7743
|
+
else part.state.output = output;
|
|
7744
|
+
}
|
|
7745
|
+
const metadata = asRecord(part.state.metadata) ?? {};
|
|
7746
|
+
if (envelope.update.kind !== void 0) metadata.kind = envelope.update.kind;
|
|
7747
|
+
if (envelope.update.locations !== void 0) metadata.locations = envelope.update.locations;
|
|
7748
|
+
part.state.metadata = metadata;
|
|
7749
|
+
});
|
|
7750
|
+
}
|
|
7751
|
+
updatePlan(envelope) {
|
|
7752
|
+
const part = planPart(envelope.update, envelope.timestampMs);
|
|
7753
|
+
if (!part) return;
|
|
7754
|
+
this.prepareAssistant(envelope);
|
|
7755
|
+
if (this.latestPlan) {
|
|
7756
|
+
this.latestPlan.text = part.text;
|
|
7757
|
+
this.latestPlan.approval_status = part.approval_status;
|
|
7758
|
+
this.latestPlan.time_created = part.time_created;
|
|
7759
|
+
return;
|
|
7760
|
+
}
|
|
7761
|
+
this.latestPlan = part;
|
|
7762
|
+
this.activeAssistant = this.builder.appendAssistantPart(part, this.assistantInput(envelope), {
|
|
7763
|
+
grouping: "current"
|
|
7764
|
+
});
|
|
7765
|
+
}
|
|
7766
|
+
};
|
|
7767
|
+
var UserRunTracker = class {
|
|
7768
|
+
hasSeenPromptIndex = false;
|
|
7769
|
+
isInUserRun = false;
|
|
7770
|
+
currentPromptIndex = null;
|
|
7771
|
+
onUser(promptIndex) {
|
|
7772
|
+
if (promptIndex !== null) this.hasSeenPromptIndex = true;
|
|
7773
|
+
const isCounted = this.hasSeenPromptIndex ? promptIndex !== null : true;
|
|
7774
|
+
const isNewRun = !this.isInUserRun || (this.hasSeenPromptIndex || promptIndex !== null) && promptIndex !== this.currentPromptIndex;
|
|
7775
|
+
this.isInUserRun = true;
|
|
7776
|
+
if (!isNewRun) return false;
|
|
7777
|
+
this.currentPromptIndex = promptIndex;
|
|
7778
|
+
return isCounted;
|
|
7779
|
+
}
|
|
7780
|
+
onNonUser() {
|
|
7781
|
+
this.isInUserRun = false;
|
|
7782
|
+
this.currentPromptIndex = null;
|
|
7783
|
+
}
|
|
7784
|
+
};
|
|
7785
|
+
var RewindIndex = class {
|
|
7786
|
+
survivingRecordIndexes = [];
|
|
7787
|
+
hasRewinds = false;
|
|
7788
|
+
promptStarts = [];
|
|
7789
|
+
userRuns = new UserRunTracker();
|
|
7790
|
+
process(record, recordIndex, fallbackTimestampMs) {
|
|
7791
|
+
const envelope = unpackUpdate(record, fallbackTimestampMs);
|
|
7792
|
+
if (!envelope) {
|
|
7793
|
+
this.userRuns.onNonUser();
|
|
7794
|
+
this.survivingRecordIndexes.push(recordIndex);
|
|
7795
|
+
return;
|
|
7796
|
+
}
|
|
7797
|
+
const type = updateType(envelope);
|
|
7798
|
+
if (envelope.method === XAI_UPDATE_METHOD && type === "rewind_marker") {
|
|
7799
|
+
const target = asNumber(envelope.update.target_prompt_index);
|
|
7800
|
+
if (target === void 0 || !Number.isSafeInteger(target) || target < 0) {
|
|
7801
|
+
this.userRuns.onNonUser();
|
|
7802
|
+
this.survivingRecordIndexes.push(recordIndex);
|
|
7803
|
+
return;
|
|
7804
|
+
}
|
|
7805
|
+
const truncateAt = this.promptStarts[target] ?? this.survivingRecordIndexes.length;
|
|
7806
|
+
this.survivingRecordIndexes.length = truncateAt;
|
|
7807
|
+
this.promptStarts.length = Math.min(this.promptStarts.length, target);
|
|
7808
|
+
this.userRuns.onNonUser();
|
|
7809
|
+
this.hasRewinds = true;
|
|
7810
|
+
return;
|
|
7811
|
+
}
|
|
7812
|
+
if (envelope.method !== XAI_UPDATE_METHOD && type === "user_message_chunk" && !isHostTurn(envelope.update)) {
|
|
7813
|
+
const rawPromptIndex = asNumber(asRecord(envelope.update._meta)?.promptIndex);
|
|
7814
|
+
const promptIndex = rawPromptIndex !== void 0 && Number.isSafeInteger(rawPromptIndex) && rawPromptIndex >= 0 ? rawPromptIndex : null;
|
|
7815
|
+
if (this.userRuns.onUser(promptIndex)) {
|
|
7816
|
+
this.promptStarts.push(this.survivingRecordIndexes.length);
|
|
7817
|
+
}
|
|
7818
|
+
} else {
|
|
7819
|
+
this.userRuns.onNonUser();
|
|
7820
|
+
}
|
|
7821
|
+
this.survivingRecordIndexes.push(recordIndex);
|
|
7822
|
+
}
|
|
7823
|
+
};
|
|
7824
|
+
function parseTranscript(meta) {
|
|
7825
|
+
const reducer = new GrokTranscriptReducer(meta.id, meta.currentModel, meta.createdAt);
|
|
7826
|
+
if (!meta.updatesPath || !existsSync12(meta.updatesPath)) return reducer.finish(meta.stats);
|
|
7827
|
+
const rewindIndex = new RewindIndex();
|
|
7828
|
+
let recordIndex = 0;
|
|
7829
|
+
for (const record of readJsonlFile(meta.updatesPath)) {
|
|
7830
|
+
rewindIndex.process(record, recordIndex, meta.createdAt);
|
|
7831
|
+
reducer.process(record);
|
|
7832
|
+
recordIndex += 1;
|
|
7833
|
+
}
|
|
7834
|
+
if (!rewindIndex.hasRewinds) return reducer.finish(meta.stats);
|
|
7835
|
+
const survivingIndexes = new Set(rewindIndex.survivingRecordIndexes);
|
|
7836
|
+
const replay = new GrokTranscriptReducer(meta.id, meta.currentModel, meta.createdAt);
|
|
7837
|
+
recordIndex = 0;
|
|
7838
|
+
for (const record of readJsonlFile(meta.updatesPath)) {
|
|
7839
|
+
if (survivingIndexes.has(recordIndex)) replay.process(record);
|
|
7840
|
+
recordIndex += 1;
|
|
7841
|
+
}
|
|
7842
|
+
return replay.finish(meta.stats);
|
|
7843
|
+
}
|
|
7844
|
+
var GrokAgent = class extends FileSystemSessionSource {
|
|
7845
|
+
name = "grok";
|
|
7846
|
+
displayName = "Grok";
|
|
7847
|
+
basePath = null;
|
|
7848
|
+
findBasePath() {
|
|
7849
|
+
return firstExisting(join14(resolveGrokDataRoot(), "sessions"), "data/grok");
|
|
7850
|
+
}
|
|
7851
|
+
isAvailable() {
|
|
7852
|
+
this.basePath = this.findBasePath();
|
|
7853
|
+
return this.basePath !== null && this.listSessionSources().length > 0;
|
|
7854
|
+
}
|
|
7855
|
+
getSessionWatchPlan() {
|
|
7856
|
+
const dataRoot = resolveGrokDataRoot();
|
|
7857
|
+
return {
|
|
7858
|
+
status: "supported",
|
|
7859
|
+
targets: [{ root: dataRoot, path: join14(dataRoot, "sessions") }, { path: "data/grok" }]
|
|
5747
7860
|
};
|
|
5748
7861
|
}
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
const
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
7862
|
+
listSessionSources(options) {
|
|
7863
|
+
this.basePath ??= this.findBasePath();
|
|
7864
|
+
if (!this.basePath) return [];
|
|
7865
|
+
const sources = this.walkFiles(this.basePath, (entry) => entry.name === SUMMARY_FILE);
|
|
7866
|
+
return sources.flatMap((source) => {
|
|
7867
|
+
const summary = parseSummary(source);
|
|
7868
|
+
if (!summary || !matchesScanWindow(summary.updatedAt, options)) return [];
|
|
7869
|
+
return [
|
|
7870
|
+
{
|
|
7871
|
+
sessionId: summary.id,
|
|
7872
|
+
sourcePath: source.file,
|
|
7873
|
+
fingerprint: this.sourceFingerprint(source)
|
|
7874
|
+
}
|
|
7875
|
+
];
|
|
7876
|
+
});
|
|
7877
|
+
}
|
|
7878
|
+
scanSessionSource(sourcePath) {
|
|
7879
|
+
const source = this.sessionSourceFile(sourcePath);
|
|
7880
|
+
const summary = parseSummary(source);
|
|
7881
|
+
if (!summary) return null;
|
|
7882
|
+
const sessionDir = dirname6(source.file);
|
|
7883
|
+
const updatesPath = join14(sessionDir, UPDATES_FILE);
|
|
7884
|
+
const existingUpdatesPath = existsSync12(updatesPath) ? updatesPath : null;
|
|
7885
|
+
const headData = scanHeadData(existingUpdatesPath, summary.createdAt);
|
|
7886
|
+
const messageCount = headData.visibleMessageCount || summary.messageCount;
|
|
7887
|
+
if (messageCount === 0) return null;
|
|
7888
|
+
const title = resolveSessionTitle(
|
|
7889
|
+
summary.title,
|
|
7890
|
+
headData.firstUserText,
|
|
7891
|
+
basenameTitle(summary.cwd)
|
|
7892
|
+
);
|
|
7893
|
+
const stats = { message_count: messageCount, ...headData.stats };
|
|
7894
|
+
const fingerprint = this.sourceFingerprint(source);
|
|
7895
|
+
const head = {
|
|
7896
|
+
id: summary.id,
|
|
7897
|
+
slug: `grok/${summary.id}`,
|
|
7898
|
+
title,
|
|
7899
|
+
directory: summary.cwd,
|
|
7900
|
+
parent_reference: summary.parentSessionId ? { agentName: this.name, sessionId: summary.parentSessionId } : void 0,
|
|
7901
|
+
time_created: summary.createdAt,
|
|
7902
|
+
time_updated: summary.updatedAt,
|
|
7903
|
+
stats,
|
|
7904
|
+
model_usage: Object.keys(headData.modelUsage).length > 0 ? headData.modelUsage : void 0
|
|
5770
7905
|
};
|
|
7906
|
+
this.sessionMetaMap.set(summary.id, {
|
|
7907
|
+
id: summary.id,
|
|
7908
|
+
title,
|
|
7909
|
+
sourcePath: source.file,
|
|
7910
|
+
sourceFingerprint: fingerprint,
|
|
7911
|
+
sourceMtimeMs: summary.updatedAt,
|
|
7912
|
+
directory: summary.cwd,
|
|
7913
|
+
messageCount,
|
|
7914
|
+
createdAt: summary.createdAt,
|
|
7915
|
+
updatedAt: summary.updatedAt,
|
|
7916
|
+
currentModel: summary.currentModel,
|
|
7917
|
+
parentSessionId: summary.parentSessionId,
|
|
7918
|
+
updatesPath: existingUpdatesPath,
|
|
7919
|
+
stats
|
|
7920
|
+
});
|
|
7921
|
+
return head;
|
|
5771
7922
|
}
|
|
5772
|
-
|
|
7923
|
+
getSessionData(sessionId) {
|
|
7924
|
+
const meta = this.sessionMetaMap.get(sessionId);
|
|
7925
|
+
if (!meta) throw new Error(`Session not found: ${sessionId}`);
|
|
7926
|
+
const transcript = parseTranscript(meta);
|
|
5773
7927
|
return {
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
7928
|
+
reference: { agentName: this.name, sessionId: meta.id },
|
|
7929
|
+
id: meta.id,
|
|
7930
|
+
title: meta.title,
|
|
7931
|
+
slug: `grok/${meta.id}`,
|
|
7932
|
+
directory: meta.directory,
|
|
7933
|
+
parent_reference: meta.parentSessionId ? { agentName: this.name, sessionId: meta.parentSessionId } : void 0,
|
|
7934
|
+
version: void 0,
|
|
7935
|
+
time_created: meta.createdAt,
|
|
7936
|
+
time_updated: meta.updatedAt,
|
|
7937
|
+
stats: transcript.stats,
|
|
7938
|
+
messages: transcript.messages
|
|
5777
7939
|
};
|
|
5778
7940
|
}
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
if (!dataRoot) return null;
|
|
5789
|
-
return firstExisting(join12(dataRoot, "cli", "db", "db.sqlite"));
|
|
5790
|
-
}
|
|
5791
|
-
function getZCodeSessionWatchPlan() {
|
|
5792
|
-
const dataRoot = resolveZCodeDataRoot();
|
|
5793
|
-
return {
|
|
5794
|
-
status: "supported",
|
|
5795
|
-
targets: dataRoot ? [{ root: dataRoot, path: join12(dataRoot, "cli", "db", "db.sqlite") }] : []
|
|
5796
|
-
};
|
|
5797
|
-
}
|
|
5798
|
-
var ZCodeAgent = class extends OpenCodeSqliteAgent {
|
|
5799
|
-
constructor() {
|
|
5800
|
-
super({
|
|
5801
|
-
name: "zcode",
|
|
5802
|
-
displayName: "ZCode",
|
|
5803
|
-
findDbPath: findZCodeDbPath,
|
|
5804
|
-
getSessionWatchPlan: getZCodeSessionWatchPlan
|
|
5805
|
-
});
|
|
7941
|
+
sourceFingerprint(source) {
|
|
7942
|
+
const sessionDir = dirname6(source.file);
|
|
7943
|
+
return JSON.stringify([
|
|
7944
|
+
HEAD_INDEX_VERSION4,
|
|
7945
|
+
PARSER_VERSION3,
|
|
7946
|
+
source.stat.mtimeMs,
|
|
7947
|
+
source.stat.size,
|
|
7948
|
+
optionalFileFingerprint(join14(sessionDir, UPDATES_FILE))
|
|
7949
|
+
]);
|
|
5806
7950
|
}
|
|
5807
7951
|
};
|
|
5808
7952
|
registerAgent({
|
|
@@ -5834,6 +7978,13 @@ registerAgent({
|
|
|
5834
7978
|
toolStrategy: "custom",
|
|
5835
7979
|
create: () => new KimiAgent()
|
|
5836
7980
|
});
|
|
7981
|
+
registerAgent({
|
|
7982
|
+
icon: "/icon/agent/kimi.svg",
|
|
7983
|
+
resolveDataRoot: resolveKimiCodeDataRoot,
|
|
7984
|
+
resumeCommandPrefix: "kimi -r",
|
|
7985
|
+
toolStrategy: "custom",
|
|
7986
|
+
create: () => new KimiCodeAgent()
|
|
7987
|
+
});
|
|
5837
7988
|
registerAgent({
|
|
5838
7989
|
icon: "/icon/agent/codex.svg",
|
|
5839
7990
|
resolveDataRoot: resolveCodexDataRoot,
|
|
@@ -5841,6 +7992,13 @@ registerAgent({
|
|
|
5841
7992
|
toolStrategy: "custom",
|
|
5842
7993
|
create: () => new CodexAgent()
|
|
5843
7994
|
});
|
|
7995
|
+
registerAgent({
|
|
7996
|
+
icon: "/icon/agent/grok.svg",
|
|
7997
|
+
resolveDataRoot: resolveGrokDataRoot,
|
|
7998
|
+
resumeCommandPrefix: "grok --resume",
|
|
7999
|
+
toolStrategy: "custom",
|
|
8000
|
+
create: () => new GrokAgent()
|
|
8001
|
+
});
|
|
5844
8002
|
registerAgent({
|
|
5845
8003
|
icon: "/icon/agent/pi.svg",
|
|
5846
8004
|
resolveDataRoot: resolvePiDataRoot,
|
|
@@ -5863,11 +8021,11 @@ function fallbackDisplayName(input) {
|
|
|
5863
8021
|
}
|
|
5864
8022
|
var realFs = {
|
|
5865
8023
|
exists(path2) {
|
|
5866
|
-
return
|
|
8024
|
+
return existsSync13(path2);
|
|
5867
8025
|
},
|
|
5868
8026
|
readText(path2) {
|
|
5869
8027
|
try {
|
|
5870
|
-
return
|
|
8028
|
+
return readFileSync8(path2, "utf8");
|
|
5871
8029
|
} catch {
|
|
5872
8030
|
return null;
|
|
5873
8031
|
}
|
|
@@ -6037,7 +8195,9 @@ function parseManifestName(file, text) {
|
|
|
6037
8195
|
}
|
|
6038
8196
|
function buildProjectGroups(sessions) {
|
|
6039
8197
|
const groups = /* @__PURE__ */ new Map();
|
|
8198
|
+
const tree = buildSessionTree(sessions);
|
|
6040
8199
|
for (const session of sessions) {
|
|
8200
|
+
if (tree.mountStateOf(session) === "mounted-child") continue;
|
|
6041
8201
|
const identity = session.project_identity;
|
|
6042
8202
|
if (!identity) continue;
|
|
6043
8203
|
const activity = session.time_updated ?? session.time_created;
|
|
@@ -6090,7 +8250,7 @@ function isPathScopeMatch(queryPath, sessionPath) {
|
|
|
6090
8250
|
return session === queryPath || session.startsWith(queryPath + "/") || queryPath.startsWith(session + "/");
|
|
6091
8251
|
}
|
|
6092
8252
|
function normalizeScopePath(path2) {
|
|
6093
|
-
return
|
|
8253
|
+
return resolve2(path2).replaceAll(sep, "/");
|
|
6094
8254
|
}
|
|
6095
8255
|
var TAG_ORDER = [
|
|
6096
8256
|
"bugfix",
|
|
@@ -6365,16 +8525,16 @@ function setSchemaEnsuredPath(path2) {
|
|
|
6365
8525
|
schemaEnsuredPath = path2;
|
|
6366
8526
|
}
|
|
6367
8527
|
function getCacheDir2() {
|
|
6368
|
-
return
|
|
8528
|
+
return join15(homedir6(), ".cache", "codesesh");
|
|
6369
8529
|
}
|
|
6370
8530
|
function getCachePath2() {
|
|
6371
|
-
return
|
|
8531
|
+
return join15(getCacheDir2(), CACHE_FILENAME);
|
|
6372
8532
|
}
|
|
6373
8533
|
function getLegacyCachePath() {
|
|
6374
|
-
return
|
|
8534
|
+
return join15(getCacheDir2(), LEGACY_CACHE_FILENAME);
|
|
6375
8535
|
}
|
|
6376
8536
|
function hasCacheStorage() {
|
|
6377
|
-
return
|
|
8537
|
+
return existsSync14(getCachePath2());
|
|
6378
8538
|
}
|
|
6379
8539
|
function likePattern(value) {
|
|
6380
8540
|
return `%${value.trim().toLowerCase().replace(/[\\%_]/g, "\\$&")}%`;
|
|
@@ -6415,6 +8575,8 @@ function prepareUpsertSession(db) {
|
|
|
6415
8575
|
title,
|
|
6416
8576
|
source_path,
|
|
6417
8577
|
directory,
|
|
8578
|
+
parent_agent_name,
|
|
8579
|
+
parent_session_id,
|
|
6418
8580
|
project_identity_kind,
|
|
6419
8581
|
project_identity_key,
|
|
6420
8582
|
project_display_name,
|
|
@@ -6433,13 +8595,15 @@ function prepareUpsertSession(db) {
|
|
|
6433
8595
|
smart_tags_json,
|
|
6434
8596
|
smart_tags_source_updated_at,
|
|
6435
8597
|
meta_json
|
|
6436
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
8598
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
6437
8599
|
ON CONFLICT(agent_name, session_id) DO UPDATE SET
|
|
6438
8600
|
sort_index = excluded.sort_index,
|
|
6439
8601
|
slug = excluded.slug,
|
|
6440
8602
|
title = excluded.title,
|
|
6441
8603
|
source_path = excluded.source_path,
|
|
6442
8604
|
directory = excluded.directory,
|
|
8605
|
+
parent_agent_name = excluded.parent_agent_name,
|
|
8606
|
+
parent_session_id = excluded.parent_session_id,
|
|
6443
8607
|
project_identity_kind = excluded.project_identity_kind,
|
|
6444
8608
|
project_identity_key = excluded.project_identity_key,
|
|
6445
8609
|
project_display_name = excluded.project_display_name,
|
|
@@ -6470,6 +8634,8 @@ function prepareUpsertIndexedSession(db) {
|
|
|
6470
8634
|
title,
|
|
6471
8635
|
source_path,
|
|
6472
8636
|
directory,
|
|
8637
|
+
parent_agent_name,
|
|
8638
|
+
parent_session_id,
|
|
6473
8639
|
project_identity_kind,
|
|
6474
8640
|
project_identity_key,
|
|
6475
8641
|
project_display_name,
|
|
@@ -6488,11 +8654,13 @@ function prepareUpsertIndexedSession(db) {
|
|
|
6488
8654
|
smart_tags_json,
|
|
6489
8655
|
smart_tags_source_updated_at,
|
|
6490
8656
|
meta_json
|
|
6491
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
8657
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
6492
8658
|
ON CONFLICT(agent_name, session_id) DO UPDATE SET
|
|
6493
8659
|
slug = excluded.slug,
|
|
6494
8660
|
title = excluded.title,
|
|
6495
8661
|
directory = excluded.directory,
|
|
8662
|
+
parent_agent_name = excluded.parent_agent_name,
|
|
8663
|
+
parent_session_id = excluded.parent_session_id,
|
|
6496
8664
|
project_identity_kind = excluded.project_identity_kind,
|
|
6497
8665
|
project_identity_key = excluded.project_identity_key,
|
|
6498
8666
|
project_display_name = excluded.project_display_name,
|
|
@@ -6514,7 +8682,7 @@ function prepareUpsertIndexedSession(db) {
|
|
|
6514
8682
|
}
|
|
6515
8683
|
function upsertSessionRow(statement, agentName, session, metaJson, sortIndex, sourcePath) {
|
|
6516
8684
|
const identity = session.project_identity ?? computeIdentity(session.directory, realFs);
|
|
6517
|
-
const
|
|
8685
|
+
const activityTime2 = session.time_updated ?? session.time_created;
|
|
6518
8686
|
statement.run(
|
|
6519
8687
|
agentName,
|
|
6520
8688
|
session.id,
|
|
@@ -6523,12 +8691,14 @@ function upsertSessionRow(statement, agentName, session, metaJson, sortIndex, so
|
|
|
6523
8691
|
session.title,
|
|
6524
8692
|
sourcePath,
|
|
6525
8693
|
session.directory,
|
|
8694
|
+
session.parent_reference?.agentName ?? null,
|
|
8695
|
+
session.parent_reference?.sessionId ?? null,
|
|
6526
8696
|
identity.kind,
|
|
6527
8697
|
identity.key,
|
|
6528
8698
|
identity.displayName,
|
|
6529
8699
|
session.time_created,
|
|
6530
8700
|
session.time_updated ?? null,
|
|
6531
|
-
|
|
8701
|
+
activityTime2,
|
|
6532
8702
|
session.stats.message_count,
|
|
6533
8703
|
session.stats.total_input_tokens,
|
|
6534
8704
|
session.stats.total_output_tokens,
|
|
@@ -6600,6 +8770,12 @@ function sessionFromRow(row) {
|
|
|
6600
8770
|
displayName: String(row.project_display_name ?? "")
|
|
6601
8771
|
};
|
|
6602
8772
|
}
|
|
8773
|
+
if (row.parent_agent_name && row.parent_session_id) {
|
|
8774
|
+
session.parent_reference = {
|
|
8775
|
+
agentName: String(row.parent_agent_name),
|
|
8776
|
+
sessionId: String(row.parent_session_id)
|
|
8777
|
+
};
|
|
8778
|
+
}
|
|
6603
8779
|
if (row.time_updated != null) {
|
|
6604
8780
|
session.time_updated = Number(row.time_updated);
|
|
6605
8781
|
}
|
|
@@ -6817,7 +8993,7 @@ function buildSessionContentFromMessages(title, messages) {
|
|
|
6817
8993
|
}
|
|
6818
8994
|
return chunks.join("\n");
|
|
6819
8995
|
}
|
|
6820
|
-
var CACHE_SCHEMA_VERSION =
|
|
8996
|
+
var CACHE_SCHEMA_VERSION = 18;
|
|
6821
8997
|
function withCacheDb(fn) {
|
|
6822
8998
|
const cachePath = getCachePath2();
|
|
6823
8999
|
const db = openDb(cachePath);
|
|
@@ -6830,7 +9006,9 @@ function withCacheDb(fn) {
|
|
|
6830
9006
|
return fn(db);
|
|
6831
9007
|
} catch (error) {
|
|
6832
9008
|
getCoreDiagnostics()?.warn("cache.write_failed", {
|
|
6833
|
-
message: error instanceof Error ? error.message : String(error)
|
|
9009
|
+
message: error instanceof Error ? error.message : String(error),
|
|
9010
|
+
code: error?.code,
|
|
9011
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
6834
9012
|
});
|
|
6835
9013
|
return null;
|
|
6836
9014
|
} finally {
|
|
@@ -6871,7 +9049,7 @@ function runSearchIndexWrite(db, rebuild, write) {
|
|
|
6871
9049
|
createMessageSearchTriggers(db);
|
|
6872
9050
|
}
|
|
6873
9051
|
return { value, rebuildDurationMs };
|
|
6874
|
-
})();
|
|
9052
|
+
}).immediate();
|
|
6875
9053
|
}
|
|
6876
9054
|
function createCacheTables(db) {
|
|
6877
9055
|
db.exec(`
|
|
@@ -6917,6 +9095,8 @@ function createSessionTables(db) {
|
|
|
6917
9095
|
title TEXT NOT NULL,
|
|
6918
9096
|
source_path TEXT,
|
|
6919
9097
|
directory TEXT NOT NULL,
|
|
9098
|
+
parent_agent_name TEXT,
|
|
9099
|
+
parent_session_id TEXT,
|
|
6920
9100
|
project_identity_kind TEXT NOT NULL,
|
|
6921
9101
|
project_identity_key TEXT NOT NULL,
|
|
6922
9102
|
project_display_name TEXT NOT NULL,
|
|
@@ -6944,6 +9124,9 @@ function createSessionTables(db) {
|
|
|
6944
9124
|
CREATE INDEX IF NOT EXISTS idx_sessions_project
|
|
6945
9125
|
ON sessions(project_identity_kind, project_identity_key, activity_time);
|
|
6946
9126
|
|
|
9127
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_parent
|
|
9128
|
+
ON sessions(parent_agent_name, parent_session_id);
|
|
9129
|
+
|
|
6947
9130
|
CREATE TABLE IF NOT EXISTS messages (
|
|
6948
9131
|
agent_name TEXT NOT NULL,
|
|
6949
9132
|
session_id TEXT NOT NULL,
|
|
@@ -7233,6 +9416,7 @@ function createProjectGroupsView(db) {
|
|
|
7233
9416
|
`);
|
|
7234
9417
|
return;
|
|
7235
9418
|
}
|
|
9419
|
+
const hasParentReference = columnExists(db, "sessions", "parent_agent_name") && columnExists(db, "sessions", "parent_session_id");
|
|
7236
9420
|
db.exec(`
|
|
7237
9421
|
CREATE VIEW IF NOT EXISTS project_groups_v AS
|
|
7238
9422
|
SELECT
|
|
@@ -7243,6 +9427,7 @@ function createProjectGroupsView(db) {
|
|
|
7243
9427
|
COUNT(*) AS session_count,
|
|
7244
9428
|
MAX(activity_time) AS last_activity
|
|
7245
9429
|
FROM sessions
|
|
9430
|
+
${hasParentReference ? "WHERE parent_agent_name IS NULL OR parent_session_id IS NULL" : ""}
|
|
7246
9431
|
GROUP BY project_identity_kind, project_identity_key;
|
|
7247
9432
|
`);
|
|
7248
9433
|
}
|
|
@@ -7640,6 +9825,19 @@ function addMessagePartsFormatVersion(db) {
|
|
|
7640
9825
|
}
|
|
7641
9826
|
db.exec("ALTER TABLE messages ADD COLUMN parts_format_version INTEGER NOT NULL DEFAULT 0");
|
|
7642
9827
|
}
|
|
9828
|
+
function addSessionParentReference(db) {
|
|
9829
|
+
if (!tableExists(db, "sessions")) return;
|
|
9830
|
+
if (!columnExists(db, "sessions", "parent_agent_name")) {
|
|
9831
|
+
db.exec("ALTER TABLE sessions ADD COLUMN parent_agent_name TEXT");
|
|
9832
|
+
}
|
|
9833
|
+
if (!columnExists(db, "sessions", "parent_session_id")) {
|
|
9834
|
+
db.exec("ALTER TABLE sessions ADD COLUMN parent_session_id TEXT");
|
|
9835
|
+
}
|
|
9836
|
+
db.exec(
|
|
9837
|
+
"CREATE INDEX IF NOT EXISTS idx_sessions_parent ON sessions(parent_agent_name, parent_session_id)"
|
|
9838
|
+
);
|
|
9839
|
+
recreateProjectGroupsView(db);
|
|
9840
|
+
}
|
|
7643
9841
|
var CODEX_EXEC_DECODE_MIGRATION_KEY = "codex_exec_decode_migrated_v3";
|
|
7644
9842
|
function migrateCodexExecDecode(db) {
|
|
7645
9843
|
if (!tableExists(db, "cache_meta")) return;
|
|
@@ -7654,6 +9852,30 @@ function migrateCodexExecDecode(db) {
|
|
|
7654
9852
|
"INSERT INTO cache_meta(key, value) VALUES (?, '1') ON CONFLICT(key) DO UPDATE SET value = '1'"
|
|
7655
9853
|
).run(CODEX_EXEC_DECODE_MIGRATION_KEY);
|
|
7656
9854
|
}
|
|
9855
|
+
var OPENCODE_SUBAGENT_FOLD_KEY = "opencode_subagent_fold_v1";
|
|
9856
|
+
var SUBAGENT_TREE_KEY = "subagent_tree_v1";
|
|
9857
|
+
function migrateOpenCodeSubagentFold(db) {
|
|
9858
|
+
if (!tableExists(db, "cache_meta")) return;
|
|
9859
|
+
const done = db.prepare("SELECT value FROM cache_meta WHERE key = ?").get(OPENCODE_SUBAGENT_FOLD_KEY);
|
|
9860
|
+
if (done) return;
|
|
9861
|
+
if (tableExists(db, "agent_cache")) {
|
|
9862
|
+
db.prepare("DELETE FROM agent_cache WHERE agent_name IN ('zcode', 'opencode')").run();
|
|
9863
|
+
}
|
|
9864
|
+
db.prepare(
|
|
9865
|
+
"INSERT INTO cache_meta(key, value) VALUES (?, '1') ON CONFLICT(key) DO UPDATE SET value = '1'"
|
|
9866
|
+
).run(OPENCODE_SUBAGENT_FOLD_KEY);
|
|
9867
|
+
}
|
|
9868
|
+
function migrateSubagentTree(db) {
|
|
9869
|
+
if (!tableExists(db, "cache_meta")) return;
|
|
9870
|
+
const done = db.prepare("SELECT value FROM cache_meta WHERE key = ?").get(SUBAGENT_TREE_KEY);
|
|
9871
|
+
if (done) return;
|
|
9872
|
+
if (tableExists(db, "agent_cache")) {
|
|
9873
|
+
db.prepare("DELETE FROM agent_cache WHERE agent_name IN ('codex', 'zcode', 'opencode')").run();
|
|
9874
|
+
}
|
|
9875
|
+
db.prepare(
|
|
9876
|
+
"INSERT INTO cache_meta(key, value) VALUES (?, '1') ON CONFLICT(key) DO UPDATE SET value = '1'"
|
|
9877
|
+
).run(SUBAGENT_TREE_KEY);
|
|
9878
|
+
}
|
|
7657
9879
|
function rebuildSearchIndex(db) {
|
|
7658
9880
|
if (!tableExists(db, "session_documents_fts")) {
|
|
7659
9881
|
return;
|
|
@@ -7763,6 +9985,8 @@ function ensureSchema(db, dbPath) {
|
|
|
7763
9985
|
createLatestCacheSchema(db);
|
|
7764
9986
|
setCacheSchemaVersion(db);
|
|
7765
9987
|
migrateCodexExecDecode(db);
|
|
9988
|
+
migrateOpenCodeSubagentFold(db);
|
|
9989
|
+
migrateSubagentTree(db);
|
|
7766
9990
|
return;
|
|
7767
9991
|
}
|
|
7768
9992
|
runSchemaMigrations(db, {
|
|
@@ -7794,7 +10018,13 @@ function ensureSchema(db, dbPath) {
|
|
|
7794
10018
|
invalidateSearchContentHashes(db2);
|
|
7795
10019
|
}
|
|
7796
10020
|
},
|
|
7797
|
-
{
|
|
10021
|
+
{
|
|
10022
|
+
version: 7,
|
|
10023
|
+
migrate(db2) {
|
|
10024
|
+
addSessionParentReference(db2);
|
|
10025
|
+
backfillStructuredSessions(db2);
|
|
10026
|
+
}
|
|
10027
|
+
},
|
|
7798
10028
|
{ version: 8, migrate: backfillFileActivity },
|
|
7799
10029
|
{
|
|
7800
10030
|
version: 9,
|
|
@@ -7825,14 +10055,17 @@ function ensureSchema(db, dbPath) {
|
|
|
7825
10055
|
{ version: 13, migrate: createCacheTables },
|
|
7826
10056
|
{ version: 14, migrate: addIndexedMessageCount },
|
|
7827
10057
|
{ version: 15, destructive: true, migrate: compactSessionDocuments },
|
|
7828
|
-
{ version: 17, migrate: addMessagePartsFormatVersion }
|
|
10058
|
+
{ version: 17, migrate: addMessagePartsFormatVersion },
|
|
10059
|
+
{ version: 18, migrate: addSessionParentReference }
|
|
7829
10060
|
]
|
|
7830
10061
|
});
|
|
7831
10062
|
createLatestCacheSchema(db);
|
|
7832
|
-
if (getUserVersion(db)
|
|
10063
|
+
if (getUserVersion(db) < CACHE_SCHEMA_VERSION) {
|
|
7833
10064
|
setCacheSchemaVersion(db);
|
|
7834
10065
|
}
|
|
7835
10066
|
migrateCodexExecDecode(db);
|
|
10067
|
+
migrateOpenCodeSubagentFold(db);
|
|
10068
|
+
migrateSubagentTree(db);
|
|
7836
10069
|
}
|
|
7837
10070
|
function escapeFtsTerm(value) {
|
|
7838
10071
|
return value.replaceAll('"', '""');
|
|
@@ -7963,6 +10196,7 @@ function readPendingReindexIds(db, agentName) {
|
|
|
7963
10196
|
return new Set(rows.map((row) => String(row.session_id)));
|
|
7964
10197
|
}
|
|
7965
10198
|
var SEARCH_INDEX_STATE_BATCH_SIZE = 900;
|
|
10199
|
+
var SEARCH_INDEX_COMMIT_CHUNK_SIZE = 64;
|
|
7966
10200
|
function shouldBulkSyncSearchIndex(options, changedCount) {
|
|
7967
10201
|
if (options.isBulk != null) {
|
|
7968
10202
|
return options.isBulk;
|
|
@@ -8222,6 +10456,32 @@ function syncSessionSearchIndex(agentName, sessions, loadSessionData, options =
|
|
|
8222
10456
|
sortIndex: sessionSortIndexMap.get(session.id) ?? 0
|
|
8223
10457
|
}));
|
|
8224
10458
|
let indexed = 0;
|
|
10459
|
+
if (changes.length > SEARCH_INDEX_COMMIT_CHUNK_SIZE) {
|
|
10460
|
+
runSearchIndexWrite(db, false, () => {
|
|
10461
|
+
indexed += writeSearchIndexRows(db, agentName, toDelete, []);
|
|
10462
|
+
});
|
|
10463
|
+
for (let offset = 0; offset < changes.length; offset += SEARCH_INDEX_COMMIT_CHUNK_SIZE) {
|
|
10464
|
+
const chunk = changes.slice(offset, offset + SEARCH_INDEX_COMMIT_CHUNK_SIZE);
|
|
10465
|
+
runSearchIndexWrite(db, false, () => {
|
|
10466
|
+
indexed += writeSearchIndexRows(
|
|
10467
|
+
db,
|
|
10468
|
+
agentName,
|
|
10469
|
+
[],
|
|
10470
|
+
loadSearchIndexEntries(agentName, chunk, loadSessionData)
|
|
10471
|
+
);
|
|
10472
|
+
});
|
|
10473
|
+
}
|
|
10474
|
+
return {
|
|
10475
|
+
agentName,
|
|
10476
|
+
mode: "incremental",
|
|
10477
|
+
sessions: sessions.length,
|
|
10478
|
+
changed: toUpsert.length,
|
|
10479
|
+
deleted: toDelete.length,
|
|
10480
|
+
indexed,
|
|
10481
|
+
skipped: toUpsert.length - indexed,
|
|
10482
|
+
durationMs: performance.now() - startedAt
|
|
10483
|
+
};
|
|
10484
|
+
}
|
|
8225
10485
|
const writeRows = () => {
|
|
8226
10486
|
indexed = writeSearchIndexRows(
|
|
8227
10487
|
db,
|
|
@@ -8847,6 +11107,7 @@ function searchFileActivitySessions(query, options = {}) {
|
|
|
8847
11107
|
}));
|
|
8848
11108
|
}
|
|
8849
11109
|
var CACHE_INITIALIZATION_VERSION = "session-cache-v2";
|
|
11110
|
+
var FULL_SYNC_CURSOR_PREFIX = "full_sync_cursor:";
|
|
8850
11111
|
function parseCachedSessionMeta(value) {
|
|
8851
11112
|
if (!value) return null;
|
|
8852
11113
|
try {
|
|
@@ -8857,7 +11118,7 @@ function parseCachedSessionMeta(value) {
|
|
|
8857
11118
|
}
|
|
8858
11119
|
function deleteLegacyCacheFile() {
|
|
8859
11120
|
const legacyPath = getLegacyCachePath();
|
|
8860
|
-
if (!
|
|
11121
|
+
if (!existsSync15(legacyPath)) {
|
|
8861
11122
|
return;
|
|
8862
11123
|
}
|
|
8863
11124
|
try {
|
|
@@ -8884,6 +11145,8 @@ function loadCachedSessions(agentName) {
|
|
|
8884
11145
|
title,
|
|
8885
11146
|
source_path,
|
|
8886
11147
|
directory,
|
|
11148
|
+
parent_agent_name,
|
|
11149
|
+
parent_session_id,
|
|
8887
11150
|
project_identity_kind,
|
|
8888
11151
|
project_identity_key,
|
|
8889
11152
|
project_display_name,
|
|
@@ -8946,6 +11209,43 @@ function markAgentCacheInitialized(agentName, indexVersion = CACHE_INITIALIZATIO
|
|
|
8946
11209
|
).run(agentName, Date.now(), indexVersion);
|
|
8947
11210
|
});
|
|
8948
11211
|
}
|
|
11212
|
+
function markAgentFullSyncStarted(agentName) {
|
|
11213
|
+
withCacheDb((db) => {
|
|
11214
|
+
db.prepare(
|
|
11215
|
+
`
|
|
11216
|
+
UPDATE cache_initialization
|
|
11217
|
+
SET last_sync_at = 0
|
|
11218
|
+
WHERE agent_name = ?
|
|
11219
|
+
`
|
|
11220
|
+
).run(agentName);
|
|
11221
|
+
});
|
|
11222
|
+
}
|
|
11223
|
+
function getAgentFullSyncCursor(agentName) {
|
|
11224
|
+
if (!hasCacheStorage()) return null;
|
|
11225
|
+
return withCacheDbReadOnly((db) => {
|
|
11226
|
+
const row = db.prepare("SELECT value FROM cache_meta WHERE key = ?").get(`${FULL_SYNC_CURSOR_PREFIX}${agentName}`);
|
|
11227
|
+
return row?.value || null;
|
|
11228
|
+
}) ?? null;
|
|
11229
|
+
}
|
|
11230
|
+
function markAgentFullSyncProgress(agentName, cursor) {
|
|
11231
|
+
if (!cursor) return;
|
|
11232
|
+
withCacheDb((db) => {
|
|
11233
|
+
db.prepare(
|
|
11234
|
+
`
|
|
11235
|
+
INSERT INTO cache_meta(key, value)
|
|
11236
|
+
VALUES (?, ?)
|
|
11237
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value
|
|
11238
|
+
`
|
|
11239
|
+
).run(`${FULL_SYNC_CURSOR_PREFIX}${agentName}`, cursor);
|
|
11240
|
+
});
|
|
11241
|
+
}
|
|
11242
|
+
function clearAgentFullSyncCursor(agentName) {
|
|
11243
|
+
withCacheDb((db) => {
|
|
11244
|
+
db.prepare("DELETE FROM cache_meta WHERE key = ?").run(
|
|
11245
|
+
`${FULL_SYNC_CURSOR_PREFIX}${agentName}`
|
|
11246
|
+
);
|
|
11247
|
+
});
|
|
11248
|
+
}
|
|
8949
11249
|
function getAgentLastFullSyncAt(agentName) {
|
|
8950
11250
|
if (!hasCacheStorage()) {
|
|
8951
11251
|
return null;
|
|
@@ -8972,6 +11272,7 @@ function markAgentFullSyncCompleted(agentName) {
|
|
|
8972
11272
|
`
|
|
8973
11273
|
).run(Date.now(), agentName);
|
|
8974
11274
|
});
|
|
11275
|
+
clearAgentFullSyncCursor(agentName);
|
|
8975
11276
|
}
|
|
8976
11277
|
function loadCachedSessionRawEntry(agentName, sessionId) {
|
|
8977
11278
|
if (!hasCacheStorage()) {
|
|
@@ -8987,6 +11288,8 @@ function loadCachedSessionRawEntry(agentName, sessionId) {
|
|
|
8987
11288
|
title,
|
|
8988
11289
|
source_path,
|
|
8989
11290
|
directory,
|
|
11291
|
+
parent_agent_name,
|
|
11292
|
+
parent_session_id,
|
|
8990
11293
|
project_identity_kind,
|
|
8991
11294
|
project_identity_key,
|
|
8992
11295
|
project_display_name,
|
|
@@ -9107,16 +11410,13 @@ function saveCachedSessions(agentName, sessions, meta = {}) {
|
|
|
9107
11410
|
);
|
|
9108
11411
|
});
|
|
9109
11412
|
});
|
|
9110
|
-
write();
|
|
11413
|
+
write.immediate();
|
|
9111
11414
|
deleteLegacyCacheFile();
|
|
9112
11415
|
return true;
|
|
9113
11416
|
});
|
|
9114
11417
|
return persisted ?? false;
|
|
9115
11418
|
}
|
|
9116
11419
|
function saveCachedSessionChanges(agentName, changes, removedSessionIds, meta = {}) {
|
|
9117
|
-
if (changes.length === 0 && removedSessionIds.length === 0) {
|
|
9118
|
-
return true;
|
|
9119
|
-
}
|
|
9120
11420
|
const persisted = withCacheDb((db) => {
|
|
9121
11421
|
const deleteSession = db.prepare(
|
|
9122
11422
|
"DELETE FROM sessions WHERE agent_name = ? AND session_id = ?"
|
|
@@ -9161,7 +11461,7 @@ function saveCachedSessionChanges(agentName, changes, removedSessionIds, meta =
|
|
|
9161
11461
|
);
|
|
9162
11462
|
}
|
|
9163
11463
|
});
|
|
9164
|
-
write();
|
|
11464
|
+
write.immediate();
|
|
9165
11465
|
deleteLegacyCacheFile();
|
|
9166
11466
|
return true;
|
|
9167
11467
|
});
|
|
@@ -9191,7 +11491,7 @@ function clearCache() {
|
|
|
9191
11491
|
const walPath = `${cachePath}-wal`;
|
|
9192
11492
|
const shmPath = `${cachePath}-shm`;
|
|
9193
11493
|
for (const filePath of [walPath, shmPath]) {
|
|
9194
|
-
if (!
|
|
11494
|
+
if (!existsSync15(filePath)) {
|
|
9195
11495
|
continue;
|
|
9196
11496
|
}
|
|
9197
11497
|
try {
|
|
@@ -9220,6 +11520,8 @@ function sessionSignature(session) {
|
|
|
9220
11520
|
return JSON.stringify([
|
|
9221
11521
|
session.title,
|
|
9222
11522
|
session.directory,
|
|
11523
|
+
session.parent_reference?.agentName ?? null,
|
|
11524
|
+
session.parent_reference?.sessionId ?? null,
|
|
9223
11525
|
session.time_created,
|
|
9224
11526
|
session.time_updated ?? session.time_created,
|
|
9225
11527
|
session.stats.message_count,
|
|
@@ -9273,13 +11575,7 @@ function filterSessions(sessions, options) {
|
|
|
9273
11575
|
if (options.cwd) {
|
|
9274
11576
|
result = filterSessionsByProjectScope(result, options.cwd);
|
|
9275
11577
|
}
|
|
9276
|
-
|
|
9277
|
-
result = result.filter((s) => (s.time_updated ?? s.time_created) >= options.from);
|
|
9278
|
-
}
|
|
9279
|
-
if (options.to != null) {
|
|
9280
|
-
result = result.filter((s) => (s.time_updated ?? s.time_created) <= options.to);
|
|
9281
|
-
}
|
|
9282
|
-
return result;
|
|
11578
|
+
return filterSessionTreeByActivityWindow(result, options.from, options.to);
|
|
9283
11579
|
}
|
|
9284
11580
|
function saveCachedSessionDiff(agent, cachedSessions, updatedSessions, changedIds = []) {
|
|
9285
11581
|
const diff = computeSessionDiff(cachedSessions, updatedSessions, changedIds, sessionSignature);
|
|
@@ -9301,17 +11597,47 @@ function chunkSessions(items, chunkCount) {
|
|
|
9301
11597
|
});
|
|
9302
11598
|
return chunks.filter((chunk) => chunk.length > 0);
|
|
9303
11599
|
}
|
|
9304
|
-
function ensureSessionTagsSync(agent, sessions) {
|
|
11600
|
+
function ensureSessionTagsSync(agent, sessions, onProgress) {
|
|
9305
11601
|
let changed = false;
|
|
11602
|
+
let processed = 0;
|
|
11603
|
+
const total = sessions.length;
|
|
11604
|
+
const timing = {
|
|
11605
|
+
sessions: total,
|
|
11606
|
+
cacheHits: 0,
|
|
11607
|
+
staleSessions: 0,
|
|
11608
|
+
failedSessions: 0,
|
|
11609
|
+
getSessionDataCalls: 0,
|
|
11610
|
+
getSessionDataMs: 0,
|
|
11611
|
+
classifySessionTagsCalls: 0,
|
|
11612
|
+
classifySessionTagsMs: 0
|
|
11613
|
+
};
|
|
9306
11614
|
const tagged = sessions.map((session) => {
|
|
9307
11615
|
const sourceUpdatedAt = session.time_updated ?? session.time_created;
|
|
9308
11616
|
const currentTags = Array.isArray(session.smart_tags) ? session.smart_tags : null;
|
|
9309
11617
|
if (currentTags && session.smart_tags_source_updated_at === sourceUpdatedAt) {
|
|
11618
|
+
timing.cacheHits += 1;
|
|
11619
|
+
processed += 1;
|
|
11620
|
+
onProgress?.(processed, total);
|
|
9310
11621
|
return session;
|
|
9311
11622
|
}
|
|
11623
|
+
timing.staleSessions += 1;
|
|
9312
11624
|
try {
|
|
9313
|
-
|
|
9314
|
-
const
|
|
11625
|
+
timing.getSessionDataCalls += 1;
|
|
11626
|
+
const getSessionDataStartedAt = performance.now();
|
|
11627
|
+
let data;
|
|
11628
|
+
try {
|
|
11629
|
+
data = agent.getSessionData(session.id);
|
|
11630
|
+
} finally {
|
|
11631
|
+
timing.getSessionDataMs += performance.now() - getSessionDataStartedAt;
|
|
11632
|
+
}
|
|
11633
|
+
timing.classifySessionTagsCalls += 1;
|
|
11634
|
+
const classifySessionTagsStartedAt = performance.now();
|
|
11635
|
+
let tags;
|
|
11636
|
+
try {
|
|
11637
|
+
tags = classifySessionTags(data);
|
|
11638
|
+
} finally {
|
|
11639
|
+
timing.classifySessionTagsMs += performance.now() - classifySessionTagsStartedAt;
|
|
11640
|
+
}
|
|
9315
11641
|
changed = true;
|
|
9316
11642
|
return {
|
|
9317
11643
|
...session,
|
|
@@ -9319,10 +11645,14 @@ function ensureSessionTagsSync(agent, sessions) {
|
|
|
9319
11645
|
smart_tags_source_updated_at: getSmartTagSourceTimestamp(data)
|
|
9320
11646
|
};
|
|
9321
11647
|
} catch {
|
|
11648
|
+
timing.failedSessions += 1;
|
|
9322
11649
|
return session;
|
|
11650
|
+
} finally {
|
|
11651
|
+
processed += 1;
|
|
11652
|
+
onProgress?.(processed, total);
|
|
9323
11653
|
}
|
|
9324
11654
|
});
|
|
9325
|
-
return { sessions: tagged, changed };
|
|
11655
|
+
return { sessions: tagged, changed, timing };
|
|
9326
11656
|
}
|
|
9327
11657
|
async function classifySessionTagsInWorker(workerUrl, agentName, sessionIds, meta) {
|
|
9328
11658
|
return new Promise((resolveWorker, rejectWorker) => {
|
|
@@ -9434,12 +11764,13 @@ async function scanAgentSmart(agent, options, onProgress) {
|
|
|
9434
11764
|
}
|
|
9435
11765
|
agent.setSessionMetaMap(metaMap);
|
|
9436
11766
|
if (options.cacheOnly) {
|
|
11767
|
+
const visibleSessions = agent.filterCachedSessions(cached.sessions);
|
|
9437
11768
|
onProgress?.({
|
|
9438
11769
|
agent: agent.name,
|
|
9439
11770
|
phase: "cache",
|
|
9440
|
-
cachedCount:
|
|
11771
|
+
cachedCount: visibleSessions.length
|
|
9441
11772
|
});
|
|
9442
|
-
return finalizeAgentScan(agent,
|
|
11773
|
+
return finalizeAgentScan(agent, visibleSessions, {
|
|
9443
11774
|
finalization: { kind: "cache-only", cached },
|
|
9444
11775
|
options,
|
|
9445
11776
|
timing,
|
|
@@ -9515,6 +11846,7 @@ async function scanAgentFull(agent, options, onProgress, timing = { total: 0 },
|
|
|
9515
11846
|
from: options.from,
|
|
9516
11847
|
to: options.to,
|
|
9517
11848
|
fast: options.fast,
|
|
11849
|
+
includeRelatedSessions: true,
|
|
9518
11850
|
onProgress: (progress) => {
|
|
9519
11851
|
onProgress?.({
|
|
9520
11852
|
agent: agent.name,
|
|
@@ -9730,6 +12062,66 @@ function listCachedProjectGroups(sessions) {
|
|
|
9730
12062
|
lastActivity: row.last_activity == null ? null : Number(row.last_activity)
|
|
9731
12063
|
}));
|
|
9732
12064
|
}
|
|
12065
|
+
var DEFAULT_MODEL_COST_LIMIT = 20;
|
|
12066
|
+
function buildModelCostWhere(options) {
|
|
12067
|
+
const clauses = ["m.model IS NOT NULL", "m.model <> ''"];
|
|
12068
|
+
const params = [];
|
|
12069
|
+
if (options.agent != null) {
|
|
12070
|
+
clauses.push("s.agent_name = ?");
|
|
12071
|
+
params.push(options.agent);
|
|
12072
|
+
}
|
|
12073
|
+
if (options.projectKind != null || options.projectKey != null) {
|
|
12074
|
+
if (options.projectKind != null && options.projectKey != null) {
|
|
12075
|
+
clauses.push("s.project_identity_kind = ? AND s.project_identity_key = ?");
|
|
12076
|
+
params.push(options.projectKind, options.projectKey);
|
|
12077
|
+
} else {
|
|
12078
|
+
clauses.push("0");
|
|
12079
|
+
}
|
|
12080
|
+
}
|
|
12081
|
+
if (options.from != null) {
|
|
12082
|
+
clauses.push("s.activity_time >= ?");
|
|
12083
|
+
params.push(options.from);
|
|
12084
|
+
}
|
|
12085
|
+
if (options.to != null) {
|
|
12086
|
+
clauses.push("s.activity_time <= ?");
|
|
12087
|
+
params.push(options.to);
|
|
12088
|
+
}
|
|
12089
|
+
return { where: `WHERE ${clauses.join(" AND ")}`, params };
|
|
12090
|
+
}
|
|
12091
|
+
var MODEL_COST_SQL = `
|
|
12092
|
+
SELECT
|
|
12093
|
+
m.model AS model,
|
|
12094
|
+
SUM(COALESCE(m.cost, 0)) AS cost,
|
|
12095
|
+
SUM(CASE WHEN m.cost_source = 'recorded' THEN COALESCE(m.cost, 0) ELSE 0 END) AS cost_recorded,
|
|
12096
|
+
SUM(CASE WHEN m.cost_source = 'recorded' THEN 0 ELSE COALESCE(m.cost, 0) END) AS cost_estimated
|
|
12097
|
+
FROM messages m
|
|
12098
|
+
JOIN sessions s ON s.agent_name = m.agent_name AND s.session_id = m.session_id
|
|
12099
|
+
`;
|
|
12100
|
+
function listModelCostDistribution(options = {}) {
|
|
12101
|
+
if (!hasCacheStorage()) {
|
|
12102
|
+
return null;
|
|
12103
|
+
}
|
|
12104
|
+
const { where, params } = buildModelCostWhere(options);
|
|
12105
|
+
const sql = `
|
|
12106
|
+
${MODEL_COST_SQL}
|
|
12107
|
+
${where}
|
|
12108
|
+
GROUP BY m.model
|
|
12109
|
+
ORDER BY cost DESC, m.model
|
|
12110
|
+
LIMIT ?
|
|
12111
|
+
`;
|
|
12112
|
+
const rows = withCacheDbReadOnly(
|
|
12113
|
+
(db) => db.prepare(sql).all(...params, options.limit ?? DEFAULT_MODEL_COST_LIMIT)
|
|
12114
|
+
);
|
|
12115
|
+
if (rows == null) {
|
|
12116
|
+
return null;
|
|
12117
|
+
}
|
|
12118
|
+
return rows.map((row) => ({
|
|
12119
|
+
model: String(row.model ?? ""),
|
|
12120
|
+
cost: Number(row.cost ?? 0),
|
|
12121
|
+
costRecorded: Number(row.cost_recorded ?? 0),
|
|
12122
|
+
costEstimated: Number(row.cost_estimated ?? 0)
|
|
12123
|
+
}));
|
|
12124
|
+
}
|
|
9733
12125
|
var STATE_DB_FILENAME = "state.db";
|
|
9734
12126
|
var STATE_SCHEMA_VERSION = 2;
|
|
9735
12127
|
var MEMORY_STATE_STORE = "memory";
|
|
@@ -9750,16 +12142,16 @@ function getStateDir() {
|
|
|
9750
12142
|
if (process.env.CODESESH_STATE_DIR) return process.env.CODESESH_STATE_DIR;
|
|
9751
12143
|
const currentPlatform = platform4();
|
|
9752
12144
|
if (currentPlatform === "darwin") {
|
|
9753
|
-
return
|
|
12145
|
+
return join16(homedir7(), "Library", "Application Support", "codesesh");
|
|
9754
12146
|
}
|
|
9755
12147
|
if (currentPlatform === "win32") {
|
|
9756
12148
|
const appData = process.env.APPDATA ?? process.env.LOCALAPPDATA;
|
|
9757
|
-
return
|
|
12149
|
+
return join16(appData ?? join16(homedir7(), "AppData", "Roaming"), "codesesh");
|
|
9758
12150
|
}
|
|
9759
|
-
return
|
|
12151
|
+
return join16(process.env.XDG_DATA_HOME ?? join16(homedir7(), ".local", "share"), "codesesh");
|
|
9760
12152
|
}
|
|
9761
12153
|
function getStateDbPath() {
|
|
9762
|
-
return
|
|
12154
|
+
return join16(getStateDir(), STATE_DB_FILENAME);
|
|
9763
12155
|
}
|
|
9764
12156
|
function useMemoryStateStore() {
|
|
9765
12157
|
return process.env.CODESESH_STATE_STORE === MEMORY_STATE_STORE;
|
|
@@ -10049,7 +12441,7 @@ function importBookmarks(bookmarks) {
|
|
|
10049
12441
|
);
|
|
10050
12442
|
}
|
|
10051
12443
|
});
|
|
10052
|
-
write();
|
|
12444
|
+
write.immediate();
|
|
10053
12445
|
const rows = db.prepare(
|
|
10054
12446
|
`
|
|
10055
12447
|
SELECT
|
|
@@ -10167,159 +12559,267 @@ function deleteSessionAlias(reference) {
|
|
|
10167
12559
|
});
|
|
10168
12560
|
}
|
|
10169
12561
|
var DASHBOARD_RECENT_LIMIT = 10;
|
|
10170
|
-
|
|
10171
|
-
|
|
10172
|
-
}
|
|
12562
|
+
var DASHBOARD_PROJECT_LIMIT = 12;
|
|
12563
|
+
var PROJECT_SPARKLINE_DAYS = 14;
|
|
10173
12564
|
function getSessionAgentName(session) {
|
|
10174
12565
|
return getSessionAgentKey(session);
|
|
10175
12566
|
}
|
|
10176
12567
|
function getSessionActivityTime(session) {
|
|
10177
12568
|
return session.time_updated ?? session.time_created;
|
|
10178
12569
|
}
|
|
10179
|
-
function
|
|
10180
|
-
|
|
10181
|
-
|
|
10182
|
-
const
|
|
12570
|
+
function matchesScope(session, scope) {
|
|
12571
|
+
if (scope.agent && getSessionAgentName(session) !== scope.agent) return false;
|
|
12572
|
+
if (scope.projectKind == null && scope.projectKey == null) return true;
|
|
12573
|
+
const identity = session.project_identity;
|
|
12574
|
+
return identity != null && identity.kind === scope.projectKind && identity.key === scope.projectKey;
|
|
12575
|
+
}
|
|
12576
|
+
function scopedEntries(sessions, scope, from, to) {
|
|
12577
|
+
const tree = buildSessionTree(filterSessionTreeByActivityWindow(sessions, from, to));
|
|
12578
|
+
return tree.entries.filter((node) => matchesScope(node.session, scope));
|
|
12579
|
+
}
|
|
12580
|
+
function emptyDailyBucket(date) {
|
|
12581
|
+
return {
|
|
12582
|
+
date,
|
|
12583
|
+
sessions: 0,
|
|
12584
|
+
messages: 0,
|
|
12585
|
+
cost: 0,
|
|
12586
|
+
input: 0,
|
|
12587
|
+
output: 0,
|
|
12588
|
+
cache_read: 0,
|
|
12589
|
+
cache_create: 0
|
|
12590
|
+
};
|
|
12591
|
+
}
|
|
12592
|
+
function createAccumulator(byAgentNames, scope, to) {
|
|
12593
|
+
const agents = /* @__PURE__ */ new Map();
|
|
10183
12594
|
for (const name of byAgentNames) {
|
|
10184
|
-
|
|
10185
|
-
|
|
10186
|
-
|
|
12595
|
+
const key = name.toLowerCase();
|
|
12596
|
+
if (scope.agent && key !== scope.agent) continue;
|
|
12597
|
+
agents.set(key, { name, sessions: 0, messages: 0, tokens: 0, cost: 0 });
|
|
10187
12598
|
}
|
|
10188
|
-
|
|
10189
|
-
let
|
|
10190
|
-
|
|
10191
|
-
|
|
10192
|
-
|
|
10193
|
-
|
|
10194
|
-
|
|
10195
|
-
|
|
10196
|
-
|
|
10197
|
-
|
|
12599
|
+
const sparklineSlots = /* @__PURE__ */ new Map();
|
|
12600
|
+
for (let slot = 0; slot < PROJECT_SPARKLINE_DAYS; slot += 1) {
|
|
12601
|
+
const day = addCalendarDays(to, slot - (PROJECT_SPARKLINE_DAYS - 1));
|
|
12602
|
+
sparklineSlots.set(toCalendarDayKey(day), slot);
|
|
12603
|
+
}
|
|
12604
|
+
return {
|
|
12605
|
+
sessions: 0,
|
|
12606
|
+
messages: 0,
|
|
12607
|
+
tokens: 0,
|
|
12608
|
+
cost: 0,
|
|
12609
|
+
costRecorded: 0,
|
|
12610
|
+
costEstimated: 0,
|
|
12611
|
+
cacheReadTokens: 0,
|
|
12612
|
+
hasEstimatedCost: false,
|
|
12613
|
+
agents,
|
|
12614
|
+
agentKeys: /* @__PURE__ */ new Set(),
|
|
12615
|
+
daily: /* @__PURE__ */ new Map(),
|
|
12616
|
+
models: /* @__PURE__ */ new Map(),
|
|
12617
|
+
projects: /* @__PURE__ */ new Map(),
|
|
12618
|
+
sparklineSlots,
|
|
12619
|
+
recent: []
|
|
12620
|
+
};
|
|
12621
|
+
}
|
|
12622
|
+
function foldModelUsage(node, into) {
|
|
12623
|
+
if (node.session.model_usage) {
|
|
12624
|
+
for (const [model, tokens] of Object.entries(node.session.model_usage)) {
|
|
12625
|
+
into.set(model, (into.get(model) ?? 0) + tokens);
|
|
12626
|
+
}
|
|
12627
|
+
}
|
|
12628
|
+
for (const child of node.children) foldModelUsage(child, into);
|
|
12629
|
+
}
|
|
12630
|
+
function trackProject(node, acc, dayKey, agentKey) {
|
|
12631
|
+
const identity = node.session.project_identity;
|
|
12632
|
+
if (!identity) return;
|
|
12633
|
+
const key = getProjectIdentityKey(identity);
|
|
12634
|
+
let project = acc.projects.get(key);
|
|
12635
|
+
if (!project) {
|
|
12636
|
+
project = {
|
|
12637
|
+
identityKind: identity.kind,
|
|
12638
|
+
identityKey: identity.key,
|
|
12639
|
+
displayName: identity.displayName,
|
|
12640
|
+
sessions: 0,
|
|
12641
|
+
messages: 0,
|
|
12642
|
+
tokens: 0,
|
|
12643
|
+
cost: 0,
|
|
12644
|
+
hasEstimatedCost: false,
|
|
12645
|
+
agentSessions: /* @__PURE__ */ new Map(),
|
|
12646
|
+
sparkline: Array.from({ length: PROJECT_SPARKLINE_DAYS }, () => 0)
|
|
12647
|
+
};
|
|
12648
|
+
acc.projects.set(key, project);
|
|
12649
|
+
}
|
|
12650
|
+
const stats = node.inclusiveStats;
|
|
12651
|
+
project.sessions += 1;
|
|
12652
|
+
project.messages += stats.messageCount;
|
|
12653
|
+
project.tokens += stats.totalTokens;
|
|
12654
|
+
project.cost += stats.cost;
|
|
12655
|
+
if (stats.costSource === "estimated") project.hasEstimatedCost = true;
|
|
12656
|
+
project.agentSessions.set(agentKey, (project.agentSessions.get(agentKey) ?? 0) + 1);
|
|
12657
|
+
const slot = acc.sparklineSlots.get(dayKey);
|
|
12658
|
+
if (slot != null) project.sparkline[slot] += stats.cost;
|
|
12659
|
+
}
|
|
12660
|
+
function accumulate(node, acc) {
|
|
12661
|
+
const session = node.session;
|
|
12662
|
+
const stats = node.inclusiveStats;
|
|
12663
|
+
const activity = getSessionActivityTime(session);
|
|
12664
|
+
const agentKey = getSessionAgentName(session);
|
|
12665
|
+
acc.sessions += 1;
|
|
12666
|
+
acc.messages += stats.messageCount;
|
|
12667
|
+
acc.tokens += stats.totalTokens;
|
|
12668
|
+
acc.cost += stats.cost;
|
|
12669
|
+
acc.cacheReadTokens += stats.cacheReadTokens;
|
|
12670
|
+
acc.agentKeys.add(agentKey);
|
|
12671
|
+
if (stats.costSource === "recorded") acc.costRecorded += stats.cost;
|
|
12672
|
+
else acc.costEstimated += stats.cost;
|
|
12673
|
+
if (stats.costSource === "estimated") acc.hasEstimatedCost = true;
|
|
12674
|
+
const metric = acc.agents.get(agentKey);
|
|
12675
|
+
if (metric) {
|
|
12676
|
+
metric.sessions += 1;
|
|
12677
|
+
metric.messages += stats.messageCount;
|
|
12678
|
+
metric.tokens += stats.totalTokens;
|
|
12679
|
+
metric.cost += stats.cost;
|
|
12680
|
+
}
|
|
12681
|
+
const dayKey = toCalendarDayKey(activity);
|
|
12682
|
+
let bucket = acc.daily.get(dayKey);
|
|
12683
|
+
if (!bucket) {
|
|
12684
|
+
bucket = emptyDailyBucket(dayKey);
|
|
12685
|
+
acc.daily.set(dayKey, bucket);
|
|
12686
|
+
}
|
|
12687
|
+
bucket.sessions += 1;
|
|
12688
|
+
bucket.messages += stats.messageCount;
|
|
12689
|
+
bucket.cost += stats.cost;
|
|
12690
|
+
bucket.input += Math.max(0, stats.inputTokens - stats.cacheReadTokens - stats.cacheCreateTokens);
|
|
12691
|
+
bucket.output += stats.outputTokens;
|
|
12692
|
+
bucket.cache_read += stats.cacheReadTokens;
|
|
12693
|
+
bucket.cache_create += stats.cacheCreateTokens;
|
|
12694
|
+
const usage = /* @__PURE__ */ new Map();
|
|
12695
|
+
foldModelUsage(node, usage);
|
|
12696
|
+
for (const [model, tokens] of usage) {
|
|
12697
|
+
const entry = acc.models.get(model);
|
|
12698
|
+
if (entry) {
|
|
12699
|
+
entry.tokens += tokens;
|
|
12700
|
+
entry.sessions += 1;
|
|
12701
|
+
} else {
|
|
12702
|
+
acc.models.set(model, { tokens, sessions: 1 });
|
|
12703
|
+
}
|
|
12704
|
+
}
|
|
12705
|
+
trackProject(node, acc, dayKey, agentKey);
|
|
12706
|
+
let recentIndex = acc.recent.length;
|
|
12707
|
+
for (let i = 0; i < acc.recent.length; i += 1) {
|
|
12708
|
+
if (activity > acc.recent[i].activity) {
|
|
12709
|
+
recentIndex = i;
|
|
12710
|
+
break;
|
|
12711
|
+
}
|
|
12712
|
+
}
|
|
12713
|
+
if (recentIndex < DASHBOARD_RECENT_LIMIT) {
|
|
12714
|
+
acc.recent.splice(recentIndex, 0, { session, activity });
|
|
12715
|
+
if (acc.recent.length > DASHBOARD_RECENT_LIMIT) acc.recent.pop();
|
|
12716
|
+
}
|
|
12717
|
+
}
|
|
12718
|
+
function toPreviousTotals(acc) {
|
|
12719
|
+
return { sessions: acc.sessions, messages: acc.messages, tokens: acc.tokens, cost: acc.cost };
|
|
12720
|
+
}
|
|
12721
|
+
function toProjectStat(project) {
|
|
12722
|
+
return {
|
|
12723
|
+
identityKind: project.identityKind,
|
|
12724
|
+
identityKey: project.identityKey,
|
|
12725
|
+
displayName: project.displayName,
|
|
12726
|
+
sessions: project.sessions,
|
|
12727
|
+
messages: project.messages,
|
|
12728
|
+
tokens: project.tokens,
|
|
12729
|
+
cost: project.cost,
|
|
12730
|
+
cost_source: project.cost > 0 ? project.hasEstimatedCost ? "estimated" : "recorded" : void 0,
|
|
12731
|
+
agents: [...project.agentSessions.entries()].sort((a, b) => b[1] - a[1]).map(([agentKey]) => agentKey),
|
|
12732
|
+
sparkline: project.sparkline
|
|
12733
|
+
};
|
|
12734
|
+
}
|
|
12735
|
+
function buildDashboard(sessions, options) {
|
|
12736
|
+
const { byAgentNames, scope, from, to, agentInfoMap, compare } = options;
|
|
12737
|
+
const acc = createAccumulator(byAgentNames, scope, to);
|
|
10198
12738
|
if (from != null) {
|
|
10199
12739
|
const bucketDays = countCalendarDays(from, to);
|
|
10200
12740
|
for (let i = 0; i < bucketDays; i += 1) {
|
|
10201
12741
|
const key = toCalendarDayKey(addCalendarDays(from, i));
|
|
10202
|
-
|
|
10203
|
-
dailyTokenMap.set(key, { date: key, input: 0, output: 0, cache_read: 0, cache_create: 0 });
|
|
12742
|
+
acc.daily.set(key, emptyDailyBucket(key));
|
|
10204
12743
|
}
|
|
10205
12744
|
}
|
|
10206
|
-
for (const
|
|
10207
|
-
|
|
10208
|
-
|
|
10209
|
-
|
|
10210
|
-
|
|
10211
|
-
|
|
10212
|
-
continue;
|
|
10213
|
-
}
|
|
10214
|
-
}
|
|
10215
|
-
const activity = getSessionActivityTime(session);
|
|
10216
|
-
if (from != null && activity < from) continue;
|
|
10217
|
-
if (activity > to) continue;
|
|
10218
|
-
const messageCount = session.stats.message_count;
|
|
10219
|
-
const sessionTokens = getTotalTokens(session.stats);
|
|
10220
|
-
totalSessions += 1;
|
|
10221
|
-
totalMessages += messageCount;
|
|
10222
|
-
totalTokens += sessionTokens;
|
|
10223
|
-
totalCost += session.stats.total_cost ?? 0;
|
|
10224
|
-
if (session.stats.cost_source === "estimated") hasEstimatedCost = true;
|
|
10225
|
-
if (activity > latestActivity) latestActivity = activity;
|
|
10226
|
-
const metricKey = agentMetricKeyByName.get(agentName);
|
|
10227
|
-
if (metricKey) {
|
|
10228
|
-
const metric = agentMetrics.get(metricKey);
|
|
10229
|
-
metric.sessions += 1;
|
|
10230
|
-
metric.messages += messageCount;
|
|
10231
|
-
metric.tokens += sessionTokens;
|
|
10232
|
-
}
|
|
10233
|
-
const key = toCalendarDayKey(activity);
|
|
10234
|
-
let bucket = dailyMap.get(key);
|
|
10235
|
-
if (!bucket) {
|
|
10236
|
-
bucket = { date: key, sessions: 0, messages: 0 };
|
|
10237
|
-
dailyMap.set(key, bucket);
|
|
10238
|
-
}
|
|
10239
|
-
bucket.sessions += 1;
|
|
10240
|
-
bucket.messages += messageCount;
|
|
10241
|
-
let tokenBucket = dailyTokenMap.get(key);
|
|
10242
|
-
if (!tokenBucket) {
|
|
10243
|
-
tokenBucket = { date: key, input: 0, output: 0, cache_read: 0, cache_create: 0 };
|
|
10244
|
-
dailyTokenMap.set(key, tokenBucket);
|
|
10245
|
-
}
|
|
10246
|
-
const cacheRead = session.stats.total_cache_read_tokens ?? 0;
|
|
10247
|
-
const cacheCreate = session.stats.total_cache_create_tokens ?? 0;
|
|
10248
|
-
const pureInput = session.stats.total_input_tokens - cacheRead - cacheCreate;
|
|
10249
|
-
tokenBucket.input += Math.max(0, pureInput);
|
|
10250
|
-
tokenBucket.output += session.stats.total_output_tokens;
|
|
10251
|
-
tokenBucket.cache_read += cacheRead;
|
|
10252
|
-
tokenBucket.cache_create += cacheCreate;
|
|
10253
|
-
if (session.model_usage) {
|
|
10254
|
-
for (const [model, tokens] of Object.entries(session.model_usage)) {
|
|
10255
|
-
const entry = modelAgg.get(model);
|
|
10256
|
-
if (entry) {
|
|
10257
|
-
entry.tokens += tokens;
|
|
10258
|
-
entry.sessions += 1;
|
|
10259
|
-
} else {
|
|
10260
|
-
modelAgg.set(model, { tokens, sessions: 1 });
|
|
10261
|
-
}
|
|
10262
|
-
}
|
|
10263
|
-
}
|
|
10264
|
-
let recentIndex = recentCandidates.length;
|
|
10265
|
-
for (let i = 0; i < recentCandidates.length; i += 1) {
|
|
10266
|
-
if (activity > recentCandidates[i].activity) {
|
|
10267
|
-
recentIndex = i;
|
|
10268
|
-
break;
|
|
10269
|
-
}
|
|
10270
|
-
}
|
|
10271
|
-
if (recentIndex < DASHBOARD_RECENT_LIMIT) {
|
|
10272
|
-
recentCandidates.splice(recentIndex, 0, { session, activity });
|
|
10273
|
-
if (recentCandidates.length > DASHBOARD_RECENT_LIMIT) recentCandidates.pop();
|
|
12745
|
+
for (const node of scopedEntries(sessions, scope, from, to)) accumulate(node, acc);
|
|
12746
|
+
let previous;
|
|
12747
|
+
if (compare) {
|
|
12748
|
+
const compareAcc = createAccumulator(byAgentNames, scope, compare.to);
|
|
12749
|
+
for (const node of scopedEntries(sessions, scope, compare.from, compare.to)) {
|
|
12750
|
+
accumulate(node, compareAcc);
|
|
10274
12751
|
}
|
|
12752
|
+
previous = toPreviousTotals(compareAcc);
|
|
10275
12753
|
}
|
|
10276
|
-
const perAgent = [...
|
|
10277
|
-
const info = agentInfoMap?.get(name);
|
|
12754
|
+
const perAgent = [...acc.agents.values()].map((metrics) => {
|
|
12755
|
+
const info = agentInfoMap?.get(metrics.name);
|
|
10278
12756
|
return {
|
|
10279
|
-
name,
|
|
10280
|
-
displayName: info?.displayName ?? name,
|
|
12757
|
+
name: metrics.name,
|
|
12758
|
+
displayName: info?.displayName ?? metrics.name,
|
|
10281
12759
|
icon: info?.icon ?? "",
|
|
10282
12760
|
iconColored: info?.iconColored,
|
|
10283
12761
|
sessions: metrics.sessions,
|
|
10284
12762
|
messages: metrics.messages,
|
|
10285
|
-
tokens: metrics.tokens
|
|
12763
|
+
tokens: metrics.tokens,
|
|
12764
|
+
cost: metrics.cost
|
|
10286
12765
|
};
|
|
10287
12766
|
}).filter((item) => item.sessions > 0).sort((a, b) => b.sessions - a.sessions);
|
|
10288
|
-
const dailyActivity = [...
|
|
10289
|
-
const
|
|
10290
|
-
|
|
12767
|
+
const dailyActivity = [...acc.daily.values()].sort((a, b) => a.date.localeCompare(b.date));
|
|
12768
|
+
const modelDistribution = [...acc.models.entries()].map(([model, { tokens, sessions: count }]) => ({ model, tokens, sessions: count })).sort((a, b) => b.tokens - a.tokens);
|
|
12769
|
+
const rankedProjects = [...acc.projects.values()].sort((a, b) => b.cost - a.cost);
|
|
12770
|
+
const perProject = rankedProjects.slice(0, DASHBOARD_PROJECT_LIMIT).map(toProjectStat);
|
|
12771
|
+
const projectRollup = rankedProjects.slice(DASHBOARD_PROJECT_LIMIT).reduce(
|
|
12772
|
+
(rollup, project) => ({
|
|
12773
|
+
projects: rollup.projects + 1,
|
|
12774
|
+
sessions: rollup.sessions + project.sessions,
|
|
12775
|
+
tokens: rollup.tokens + project.tokens,
|
|
12776
|
+
cost: rollup.cost + project.cost
|
|
12777
|
+
}),
|
|
12778
|
+
{ projects: 0, sessions: 0, tokens: 0, cost: 0 }
|
|
10291
12779
|
);
|
|
10292
|
-
const
|
|
10293
|
-
|
|
10294
|
-
|
|
10295
|
-
|
|
10296
|
-
|
|
10297
|
-
session
|
|
10298
|
-
};
|
|
10299
|
-
});
|
|
12780
|
+
const recentSessions = acc.recent.map(({ session }) => ({
|
|
12781
|
+
reference: { agentName: getSessionAgentName(session), sessionId: session.id },
|
|
12782
|
+
session
|
|
12783
|
+
}));
|
|
12784
|
+
const latest = acc.recent[0];
|
|
10300
12785
|
return {
|
|
10301
12786
|
totals: {
|
|
10302
|
-
sessions:
|
|
10303
|
-
messages:
|
|
10304
|
-
tokens:
|
|
10305
|
-
cost:
|
|
10306
|
-
|
|
10307
|
-
|
|
12787
|
+
sessions: acc.sessions,
|
|
12788
|
+
messages: acc.messages,
|
|
12789
|
+
tokens: acc.tokens,
|
|
12790
|
+
cost: acc.cost,
|
|
12791
|
+
costRecorded: acc.costRecorded,
|
|
12792
|
+
costEstimated: acc.costEstimated,
|
|
12793
|
+
cacheReadTokens: acc.cacheReadTokens,
|
|
12794
|
+
cost_source: acc.cost > 0 ? acc.hasEstimatedCost ? "estimated" : "recorded" : void 0,
|
|
12795
|
+
latestActivity: latest?.activity || void 0,
|
|
12796
|
+
latestActivityProject: latest?.session.project_identity?.displayName,
|
|
12797
|
+
latestActivityAgent: latest ? getSessionAgentName(latest.session) : void 0,
|
|
12798
|
+
previous
|
|
10308
12799
|
},
|
|
12800
|
+
scopeCounts: { projects: acc.projects.size, agents: acc.agentKeys.size },
|
|
10309
12801
|
perAgent,
|
|
10310
12802
|
dailyActivity,
|
|
10311
|
-
dailyTokenActivity,
|
|
10312
12803
|
modelDistribution,
|
|
12804
|
+
perProject,
|
|
12805
|
+
projectRollup,
|
|
10313
12806
|
recentSessions
|
|
10314
12807
|
};
|
|
10315
12808
|
}
|
|
10316
12809
|
function emptyMetrics() {
|
|
10317
|
-
return {
|
|
12810
|
+
return {
|
|
12811
|
+
sessions: 0,
|
|
12812
|
+
messages: 0,
|
|
12813
|
+
tokens: 0,
|
|
12814
|
+
cost: 0,
|
|
12815
|
+
hasEstimatedCost: false,
|
|
12816
|
+
agentStats: /* @__PURE__ */ new Map()
|
|
12817
|
+
};
|
|
10318
12818
|
}
|
|
10319
12819
|
function attachProjectMetrics(projects, sessions) {
|
|
10320
12820
|
const metrics = /* @__PURE__ */ new Map();
|
|
10321
|
-
for (const
|
|
10322
|
-
const identity = session.project_identity;
|
|
12821
|
+
for (const node of buildSessionTree(sessions).entries) {
|
|
12822
|
+
const identity = node.session.project_identity;
|
|
10323
12823
|
if (!identity) continue;
|
|
10324
12824
|
const key = getProjectIdentityKey(identity);
|
|
10325
12825
|
let current = metrics.get(key);
|
|
@@ -10327,26 +12827,26 @@ function attachProjectMetrics(projects, sessions) {
|
|
|
10327
12827
|
current = emptyMetrics();
|
|
10328
12828
|
metrics.set(key, current);
|
|
10329
12829
|
}
|
|
10330
|
-
const
|
|
10331
|
-
|
|
10332
|
-
current.messages +=
|
|
10333
|
-
current.tokens +=
|
|
10334
|
-
current.cost += cost;
|
|
10335
|
-
if (
|
|
10336
|
-
const agentName = getSessionAgentName(session);
|
|
12830
|
+
const stats = node.inclusiveStats;
|
|
12831
|
+
current.sessions += 1;
|
|
12832
|
+
current.messages += stats.messageCount;
|
|
12833
|
+
current.tokens += stats.totalTokens;
|
|
12834
|
+
current.cost += stats.cost;
|
|
12835
|
+
if (stats.costSource === "estimated") current.hasEstimatedCost = true;
|
|
12836
|
+
const agentName = getSessionAgentName(node.session);
|
|
10337
12837
|
const agent = current.agentStats.get(agentName);
|
|
10338
12838
|
if (agent) {
|
|
10339
12839
|
agent.sessions += 1;
|
|
10340
|
-
agent.messages +=
|
|
10341
|
-
agent.tokens +=
|
|
10342
|
-
agent.cost += cost;
|
|
12840
|
+
agent.messages += stats.messageCount;
|
|
12841
|
+
agent.tokens += stats.totalTokens;
|
|
12842
|
+
agent.cost += stats.cost;
|
|
10343
12843
|
} else {
|
|
10344
12844
|
current.agentStats.set(agentName, {
|
|
10345
12845
|
name: agentName,
|
|
10346
12846
|
sessions: 1,
|
|
10347
|
-
messages:
|
|
10348
|
-
tokens,
|
|
10349
|
-
cost
|
|
12847
|
+
messages: stats.messageCount,
|
|
12848
|
+
tokens: stats.totalTokens,
|
|
12849
|
+
cost: stats.cost
|
|
10350
12850
|
});
|
|
10351
12851
|
}
|
|
10352
12852
|
}
|
|
@@ -10356,6 +12856,7 @@ function attachProjectMetrics(projects, sessions) {
|
|
|
10356
12856
|
);
|
|
10357
12857
|
return {
|
|
10358
12858
|
...project,
|
|
12859
|
+
sessionCount: metric?.sessions ?? 0,
|
|
10359
12860
|
messages: metric?.messages ?? 0,
|
|
10360
12861
|
tokens: metric?.tokens ?? 0,
|
|
10361
12862
|
cost: metric?.cost ?? 0,
|
|
@@ -10501,6 +13002,10 @@ export {
|
|
|
10501
13002
|
getSessionAgentKey,
|
|
10502
13003
|
sessionRoutePath,
|
|
10503
13004
|
mergeSortedSessions,
|
|
13005
|
+
getSessionRouteKey,
|
|
13006
|
+
isChildSession,
|
|
13007
|
+
getRootSessions,
|
|
13008
|
+
filterSessionTreeByActivityWindow,
|
|
10504
13009
|
registerAgent,
|
|
10505
13010
|
createRegisteredAgents,
|
|
10506
13011
|
getRegisteredAgents,
|
|
@@ -10529,6 +13034,9 @@ export {
|
|
|
10529
13034
|
loadCachedSessions,
|
|
10530
13035
|
isAgentCacheInitialized,
|
|
10531
13036
|
markAgentCacheInitialized,
|
|
13037
|
+
markAgentFullSyncStarted,
|
|
13038
|
+
getAgentFullSyncCursor,
|
|
13039
|
+
markAgentFullSyncProgress,
|
|
10532
13040
|
getAgentLastFullSyncAt,
|
|
10533
13041
|
markAgentFullSyncCompleted,
|
|
10534
13042
|
saveCachedSessions,
|
|
@@ -10543,6 +13051,7 @@ export {
|
|
|
10543
13051
|
scanSessions,
|
|
10544
13052
|
materializeSessionDetailResponse,
|
|
10545
13053
|
listCachedProjectGroups,
|
|
13054
|
+
listModelCostDistribution,
|
|
10546
13055
|
StateStorageUnavailableError,
|
|
10547
13056
|
listBookmarks,
|
|
10548
13057
|
upsertBookmark,
|
|
@@ -10558,4 +13067,4 @@ export {
|
|
|
10558
13067
|
executeSessionSearch,
|
|
10559
13068
|
filterSessionSearchCandidates
|
|
10560
13069
|
};
|
|
10561
|
-
//# sourceMappingURL=chunk-
|
|
13070
|
+
//# sourceMappingURL=chunk-BEF6LLRG.js.map
|