codesesh 0.4.1 → 0.5.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 -3
- package/dist/{chunk-BGQXQTWM.js → chunk-FZNZAMTZ.js} +1321 -173
- package/dist/chunk-FZNZAMTZ.js.map +1 -0
- package/dist/{dist-5NKHH33A.js → dist-DMEDEJ2D.js} +38 -4
- package/dist/index.js +378 -59
- package/dist/index.js.map +1 -1
- package/dist/web/assets/index-BRW_TBMw.js +106 -0
- package/dist/web/assets/index-CCgk7cPa.css +2 -0
- package/dist/web/assets/markdown-CnUlvKkZ.js +14 -0
- package/dist/web/assets/react-DT3QPCDf.js +1821 -0
- package/dist/web/assets/rolldown-runtime-Dw2cE7zH.js +1 -0
- package/dist/web/assets/syntax-DcanuzfQ.js +6 -0
- package/dist/web/assets/vendor-CWmLg_mG.js +43 -0
- package/dist/web/index.html +7 -2
- package/package.json +2 -3
- package/dist/chunk-BGQXQTWM.js.map +0 -1
- package/dist/web/assets/index-B78jpjIp.js +0 -68
- package/dist/web/assets/index-gSYgPU_H.css +0 -2
- /package/dist/{dist-5NKHH33A.js.map → dist-DMEDEJ2D.js.map} +0 -0
|
@@ -1,31 +1,48 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// ../core/dist/index.mjs
|
|
4
|
-
import { existsSync as
|
|
5
|
-
import { join as
|
|
4
|
+
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3, statSync } from "fs";
|
|
5
|
+
import { join as join3, basename as basename2, dirname } from "path";
|
|
6
6
|
import { existsSync } from "fs";
|
|
7
7
|
import { homedir, platform } from "os";
|
|
8
8
|
import { join } from "path";
|
|
9
9
|
import { readFileSync } from "fs";
|
|
10
10
|
import { basename } from "path";
|
|
11
|
-
import { existsSync as
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
11
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
12
|
+
import { homedir as homedir2 } from "os";
|
|
13
|
+
import { join as join2 } from "path";
|
|
14
|
+
import { existsSync as existsSync4, statSync as statSync2 } from "fs";
|
|
15
|
+
import { join as join4 } from "path";
|
|
16
|
+
import { mkdirSync as mkdirSync2 } from "fs";
|
|
14
17
|
import { dirname as dirname2 } from "path";
|
|
15
18
|
import { createRequire } from "module";
|
|
16
19
|
import { createHash } from "crypto";
|
|
17
|
-
import { existsSync as
|
|
18
|
-
import { join as
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync3 } from "fs";
|
|
21
|
+
import { join as join5, basename as basename3, dirname as dirname3 } from "path";
|
|
22
|
+
import {
|
|
23
|
+
closeSync,
|
|
24
|
+
existsSync as existsSync6,
|
|
25
|
+
openSync,
|
|
26
|
+
readFileSync as readFileSync5,
|
|
27
|
+
readSync,
|
|
28
|
+
readdirSync as readdirSync3,
|
|
29
|
+
statSync as statSync4
|
|
30
|
+
} from "fs";
|
|
31
|
+
import { join as join6, basename as basename4 } from "path";
|
|
32
|
+
import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync6, statSync as statSync5 } from "fs";
|
|
33
|
+
import { join as join7, normalize } from "path";
|
|
23
34
|
import { resolve, sep } from "path";
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
35
|
+
import { availableParallelism } from "os";
|
|
36
|
+
import { Worker } from "worker_threads";
|
|
37
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
|
|
38
|
+
import { spawnSync } from "child_process";
|
|
39
|
+
import { homedir as homedir3 } from "os";
|
|
40
|
+
import * as path from "path";
|
|
41
|
+
import { existsSync as existsSync9, rmSync, unlinkSync } from "fs";
|
|
28
42
|
import { join as join8 } from "path";
|
|
43
|
+
import { homedir as homedir4 } from "os";
|
|
44
|
+
import { homedir as homedir5, platform as platform2 } from "os";
|
|
45
|
+
import { join as join9 } from "path";
|
|
29
46
|
var registrations = [];
|
|
30
47
|
function registerAgent(reg) {
|
|
31
48
|
registrations.push(reg);
|
|
@@ -47,6 +64,11 @@ function getAgentInfoMap(sessionsByAgent) {
|
|
|
47
64
|
function getAgentByName(name) {
|
|
48
65
|
return registrations.find((r) => r.name === name);
|
|
49
66
|
}
|
|
67
|
+
function matchesScanWindow(activityTime, options) {
|
|
68
|
+
if (options?.from != null && activityTime < options.from) return false;
|
|
69
|
+
if (options?.to != null && activityTime > options.to) return false;
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
50
72
|
var BaseAgent = class {
|
|
51
73
|
getUri(sessionId) {
|
|
52
74
|
return `${this.name}://${sessionId}`;
|
|
@@ -119,9 +141,9 @@ function normalizeTitleText(text) {
|
|
|
119
141
|
if (!cleaned) return null;
|
|
120
142
|
return cleaned.slice(0, TITLE_MAX_LENGTH);
|
|
121
143
|
}
|
|
122
|
-
function basenameTitle(
|
|
123
|
-
if (!
|
|
124
|
-
const normalized =
|
|
144
|
+
function basenameTitle(path2) {
|
|
145
|
+
if (!path2) return null;
|
|
146
|
+
const normalized = path2.trim().replace(/[/\\]+$/, "");
|
|
125
147
|
if (!normalized) return null;
|
|
126
148
|
const name = basename(normalized).trim();
|
|
127
149
|
return name || null;
|
|
@@ -209,6 +231,329 @@ var PerfTracer = class {
|
|
|
209
231
|
}
|
|
210
232
|
};
|
|
211
233
|
var perf = new PerfTracer();
|
|
234
|
+
var aliases_default = {
|
|
235
|
+
"anthropic--claude-4.6-opus": "claude-opus-4-6",
|
|
236
|
+
"anthropic--claude-4.6-sonnet": "claude-sonnet-4-6",
|
|
237
|
+
"claude-4.6-sonnet": "claude-sonnet-4-6",
|
|
238
|
+
"anthropic--claude-4.5-opus": "claude-opus-4-5",
|
|
239
|
+
"anthropic--claude-4.5-sonnet": "claude-sonnet-4-5",
|
|
240
|
+
"anthropic--claude-4.5-haiku": "claude-haiku-4-5",
|
|
241
|
+
"claude-4.5-sonnet-thinking": "claude-sonnet-4-5",
|
|
242
|
+
"claude-4-sonnet-thinking": "claude-sonnet-4-5",
|
|
243
|
+
"claude-4-opus": "claude-opus-4-5",
|
|
244
|
+
"cursor-auto": "claude-sonnet-4-5",
|
|
245
|
+
"cursor-agent-auto": "claude-sonnet-4-5",
|
|
246
|
+
"copilot-auto": "claude-sonnet-4-5",
|
|
247
|
+
"copilot-openai-auto": "gpt-5.3-codex",
|
|
248
|
+
"copilot-anthropic-auto": "claude-sonnet-4-5",
|
|
249
|
+
"kimi-for-coding": "moonshotai/kimi-k2-0905",
|
|
250
|
+
"kimi-k2.5": "moonshotai/kimi-k2-0905",
|
|
251
|
+
"moonshot-cn/kimi-k2.5": "moonshotai/kimi-k2-0905",
|
|
252
|
+
"gpt-5-codex": "gpt-5",
|
|
253
|
+
"gpt-5.1-codex-high": "gpt-5.3-codex",
|
|
254
|
+
"gpt-5.2-low": "gpt-5.2"
|
|
255
|
+
};
|
|
256
|
+
var snapshot_default = {
|
|
257
|
+
"claude-3-5-haiku": [8e-7, 4e-6, 1e-6, 8e-8],
|
|
258
|
+
"claude-3-5-sonnet": [3e-6, 15e-6, 375e-8, 3e-7],
|
|
259
|
+
"claude-3-7-sonnet": [3e-6, 15e-6, 375e-8, 3e-7],
|
|
260
|
+
"claude-haiku-4-5": [1e-6, 5e-6, 125e-8, 1e-7],
|
|
261
|
+
"claude-sonnet-4": [3e-6, 15e-6, 375e-8, 3e-7],
|
|
262
|
+
"claude-sonnet-4-5": [3e-6, 15e-6, 375e-8, 3e-7],
|
|
263
|
+
"claude-sonnet-4-6": [3e-6, 15e-6, 375e-8, 3e-7],
|
|
264
|
+
"claude-opus-4": [15e-6, 75e-6, 1875e-8, 15e-7],
|
|
265
|
+
"claude-opus-4-1": [15e-6, 75e-6, 1875e-8, 15e-7],
|
|
266
|
+
"claude-opus-4-5": [5e-6, 25e-6, 625e-8, 5e-7],
|
|
267
|
+
"claude-opus-4-6": [5e-6, 25e-6, 625e-8, 5e-7],
|
|
268
|
+
"claude-opus-4-7": [5e-6, 25e-6, 625e-8, 5e-7],
|
|
269
|
+
"anthropic/claude-sonnet-4-5": [3e-6, 15e-6, 375e-8, 3e-7],
|
|
270
|
+
"anthropic/claude-sonnet-4-6": [3e-6, 15e-6, 375e-8, 3e-7],
|
|
271
|
+
"anthropic/claude-opus-4-6": [5e-6, 25e-6, 625e-8, 5e-7],
|
|
272
|
+
"gpt-4.1": [2e-6, 8e-6, null, 5e-7],
|
|
273
|
+
"gpt-4.1-mini": [4e-7, 16e-7, null, 1e-7],
|
|
274
|
+
"gpt-4.1-nano": [1e-7, 4e-7, null, 25e-9],
|
|
275
|
+
"gpt-4o": [25e-7, 1e-5, null, 125e-8],
|
|
276
|
+
"gpt-4o-mini": [15e-8, 6e-7, null, 75e-9],
|
|
277
|
+
"gpt-5": [125e-8, 1e-5, null, 125e-9],
|
|
278
|
+
"gpt-5.1": [125e-8, 1e-5, null, 125e-9],
|
|
279
|
+
"gpt-5.1-codex": [125e-8, 1e-5, null, 125e-9],
|
|
280
|
+
"gpt-5.1-codex-mini": [25e-8, 2e-6, null, 25e-9],
|
|
281
|
+
"gpt-5.2": [175e-8, 14e-6, null, 175e-9],
|
|
282
|
+
"gpt-5.2-codex": [175e-8, 14e-6, null, 175e-9],
|
|
283
|
+
"gpt-5.3-codex": [175e-8, 14e-6, null, 175e-9],
|
|
284
|
+
"gpt-5.4": [25e-7, 15e-6, null, 25e-8],
|
|
285
|
+
"gpt-5.4-mini": [75e-8, 45e-7, null, 75e-9],
|
|
286
|
+
"gpt-5.4-nano": [2e-7, 125e-8, null, 2e-8],
|
|
287
|
+
"gpt-5.5": [5e-6, 3e-5, null, 5e-7],
|
|
288
|
+
"gpt-5-mini": [25e-8, 2e-6, null, 25e-9],
|
|
289
|
+
"gpt-5-nano": [5e-8, 4e-7, null, 5e-9],
|
|
290
|
+
"moonshotai/kimi-k2-0905": [6e-7, 25e-7, null, null],
|
|
291
|
+
"moonshotai/kimi-k2-instruct": [57e-8, 23e-7, null, null],
|
|
292
|
+
"moonshotai/kimi-k2-thinking": [6e-7, 25e-7, null, null],
|
|
293
|
+
"deepseek-chat": [28e-8, 42e-8, null, 28e-9],
|
|
294
|
+
"deepseek-reasoner": [28e-8, 42e-8, null, 28e-9],
|
|
295
|
+
"qwen3-coder-480b-a35b-instruct": [3e-7, 13e-7, null, null],
|
|
296
|
+
"gemini-2.5-pro": [125e-8, 1e-5, null, 125e-9],
|
|
297
|
+
"gemini-2.5-flash": [3e-7, 25e-7, null, 3e-8]
|
|
298
|
+
};
|
|
299
|
+
var LITELLM_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
|
|
300
|
+
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
301
|
+
var WEB_SEARCH_COST = 0.01;
|
|
302
|
+
var pricingCache = loadSnapshot();
|
|
303
|
+
loadDiskCache();
|
|
304
|
+
function normalizeKey(key) {
|
|
305
|
+
return key.trim().toLowerCase();
|
|
306
|
+
}
|
|
307
|
+
function costNumber(value, fallback) {
|
|
308
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
309
|
+
}
|
|
310
|
+
function getCacheDir() {
|
|
311
|
+
return join2(homedir2(), ".cache", "codesesh");
|
|
312
|
+
}
|
|
313
|
+
function getCachePath() {
|
|
314
|
+
return join2(getCacheDir(), "litellm-pricing.json");
|
|
315
|
+
}
|
|
316
|
+
function loadSnapshot() {
|
|
317
|
+
const map = /* @__PURE__ */ new Map();
|
|
318
|
+
const snapshot = snapshot_default;
|
|
319
|
+
for (const [name, entry] of Object.entries(snapshot)) {
|
|
320
|
+
const [input, output, cacheCreate, cacheRead, reasoning, webSearch] = entry;
|
|
321
|
+
map.set(normalizeKey(name), {
|
|
322
|
+
inputCostPerToken: input,
|
|
323
|
+
outputCostPerToken: output,
|
|
324
|
+
cacheCreateCostPerToken: cacheCreate ?? input * 1.25,
|
|
325
|
+
cacheReadCostPerToken: cacheRead ?? input * 0.1,
|
|
326
|
+
reasoningCostPerToken: reasoning ?? output,
|
|
327
|
+
webSearchCostPerRequest: webSearch ?? WEB_SEARCH_COST
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
return map;
|
|
331
|
+
}
|
|
332
|
+
function parseLiteLLMEntry(entry) {
|
|
333
|
+
const input = entry.input_cost_per_token;
|
|
334
|
+
const output = entry.output_cost_per_token;
|
|
335
|
+
if (typeof input !== "number" || typeof output !== "number") return null;
|
|
336
|
+
return {
|
|
337
|
+
inputCostPerToken: input,
|
|
338
|
+
outputCostPerToken: output,
|
|
339
|
+
cacheCreateCostPerToken: entry.cache_creation_input_token_cost ?? input * 1.25,
|
|
340
|
+
cacheReadCostPerToken: entry.cache_read_input_token_cost ?? input * 0.1,
|
|
341
|
+
reasoningCostPerToken: entry.output_reasoning_cost_per_token ?? output,
|
|
342
|
+
webSearchCostPerRequest: costNumber(
|
|
343
|
+
entry.web_search_cost_per_request ?? entry.search_context_cost_per_query,
|
|
344
|
+
WEB_SEARCH_COST
|
|
345
|
+
)
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function normalizeCachedPricing(raw) {
|
|
349
|
+
const input = costNumber(raw["inputCostPerToken"], 0);
|
|
350
|
+
const output = costNumber(raw["outputCostPerToken"], 0);
|
|
351
|
+
if (input <= 0 || output <= 0) return null;
|
|
352
|
+
return {
|
|
353
|
+
inputCostPerToken: input,
|
|
354
|
+
outputCostPerToken: output,
|
|
355
|
+
cacheCreateCostPerToken: costNumber(raw["cacheCreateCostPerToken"], input * 1.25),
|
|
356
|
+
cacheReadCostPerToken: costNumber(raw["cacheReadCostPerToken"], input * 0.1),
|
|
357
|
+
reasoningCostPerToken: costNumber(raw["reasoningCostPerToken"], output),
|
|
358
|
+
webSearchCostPerRequest: costNumber(raw["webSearchCostPerRequest"], WEB_SEARCH_COST)
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
function indexPricing(map, name, pricing) {
|
|
362
|
+
const normalized = normalizeKey(name);
|
|
363
|
+
map.set(normalized, pricing);
|
|
364
|
+
const slashIndex = normalized.indexOf("/");
|
|
365
|
+
if (slashIndex >= 0) {
|
|
366
|
+
const stripped = normalized.slice(slashIndex + 1);
|
|
367
|
+
if (!map.has(stripped)) map.set(stripped, pricing);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
function parseLiteLLMData(data) {
|
|
371
|
+
const map = /* @__PURE__ */ new Map();
|
|
372
|
+
for (const [name, entry] of Object.entries(data)) {
|
|
373
|
+
const pricing = parseLiteLLMEntry(entry);
|
|
374
|
+
if (pricing) indexPricing(map, name, pricing);
|
|
375
|
+
}
|
|
376
|
+
return map;
|
|
377
|
+
}
|
|
378
|
+
function loadDiskCache() {
|
|
379
|
+
const path2 = getCachePath();
|
|
380
|
+
if (!existsSync2(path2)) return;
|
|
381
|
+
try {
|
|
382
|
+
const cached = JSON.parse(readFileSync2(path2, "utf-8"));
|
|
383
|
+
if (Date.now() - cached.timestamp <= CACHE_TTL_MS) {
|
|
384
|
+
const next = loadSnapshot();
|
|
385
|
+
for (const [name, rawPricing] of Object.entries(cached.data)) {
|
|
386
|
+
const pricing = normalizeCachedPricing(rawPricing);
|
|
387
|
+
if (!pricing) continue;
|
|
388
|
+
next.set(normalizeKey(name), pricing);
|
|
389
|
+
}
|
|
390
|
+
pricingCache = next;
|
|
391
|
+
}
|
|
392
|
+
} catch {
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
function getPricingRegistry() {
|
|
396
|
+
return pricingCache;
|
|
397
|
+
}
|
|
398
|
+
function hasBillablePricing(pricing) {
|
|
399
|
+
return pricing.inputCostPerToken > 0 || pricing.outputCostPerToken > 0 || pricing.cacheReadCostPerToken > 0 || pricing.cacheCreateCostPerToken > 0;
|
|
400
|
+
}
|
|
401
|
+
async function refreshPricingCache() {
|
|
402
|
+
const path2 = getCachePath();
|
|
403
|
+
if (existsSync2(path2)) {
|
|
404
|
+
try {
|
|
405
|
+
const cached = JSON.parse(readFileSync2(path2, "utf-8"));
|
|
406
|
+
if (typeof cached.timestamp === "number" && Date.now() - cached.timestamp <= CACHE_TTL_MS) {
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
} catch {
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
try {
|
|
413
|
+
const response = await fetch(LITELLM_URL);
|
|
414
|
+
if (!response.ok) return false;
|
|
415
|
+
const data = await response.json();
|
|
416
|
+
const remote = parseLiteLLMData(data);
|
|
417
|
+
if (remote.size === 0) return false;
|
|
418
|
+
const next = loadSnapshot();
|
|
419
|
+
for (const [name, pricing] of remote.entries()) {
|
|
420
|
+
next.set(name, pricing);
|
|
421
|
+
}
|
|
422
|
+
pricingCache = next;
|
|
423
|
+
mkdirSync(getCacheDir(), { recursive: true });
|
|
424
|
+
writeFileSync(path2, JSON.stringify({ timestamp: Date.now(), data: Object.fromEntries(next) }));
|
|
425
|
+
return true;
|
|
426
|
+
} catch {
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
var BUILTIN_ALIASES = Object.fromEntries(
|
|
431
|
+
Object.entries(aliases_default).map(([key, value]) => [
|
|
432
|
+
normalizeModelKey(key),
|
|
433
|
+
normalizeModelKey(value)
|
|
434
|
+
])
|
|
435
|
+
);
|
|
436
|
+
function normalizeModelKey(model) {
|
|
437
|
+
return model.trim().toLowerCase().replaceAll("_", "-");
|
|
438
|
+
}
|
|
439
|
+
function stripVersion(model) {
|
|
440
|
+
return model.replace(/@.*$/, "").replace(/-\d{8}$/, "");
|
|
441
|
+
}
|
|
442
|
+
function stripProviderPrefix(model) {
|
|
443
|
+
const parts = model.split("/");
|
|
444
|
+
return parts[parts.length - 1] ?? model;
|
|
445
|
+
}
|
|
446
|
+
function resolveAlias(model) {
|
|
447
|
+
return BUILTIN_ALIASES[model] ?? model;
|
|
448
|
+
}
|
|
449
|
+
function getCandidates(rawModel) {
|
|
450
|
+
const withPrefix = stripVersion(normalizeModelKey(rawModel));
|
|
451
|
+
const stripped = stripProviderPrefix(withPrefix);
|
|
452
|
+
const aliased = resolveAlias(stripped);
|
|
453
|
+
const candidates = [
|
|
454
|
+
withPrefix,
|
|
455
|
+
resolveAlias(withPrefix),
|
|
456
|
+
stripped,
|
|
457
|
+
aliased,
|
|
458
|
+
`anthropic/${stripped}`,
|
|
459
|
+
`openai/${stripped}`,
|
|
460
|
+
`openrouter/openai/${stripped}`,
|
|
461
|
+
`openrouter/anthropic/${stripped}`,
|
|
462
|
+
`moonshotai/${stripped}`,
|
|
463
|
+
`novita/moonshotai/${stripped}`
|
|
464
|
+
];
|
|
465
|
+
return [...new Set(candidates.filter(Boolean))];
|
|
466
|
+
}
|
|
467
|
+
function lookupCandidate(model, registry) {
|
|
468
|
+
const direct = registry.get(model);
|
|
469
|
+
if (direct && hasBillablePricing(direct)) return direct;
|
|
470
|
+
const alias = resolveAlias(model);
|
|
471
|
+
if (alias !== model) {
|
|
472
|
+
const aliased = registry.get(alias);
|
|
473
|
+
if (aliased && hasBillablePricing(aliased)) return aliased;
|
|
474
|
+
}
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
function fuzzyLookup(model, registry) {
|
|
478
|
+
let best = null;
|
|
479
|
+
for (const [key, pricing] of registry.entries()) {
|
|
480
|
+
if (!hasBillablePricing(pricing)) continue;
|
|
481
|
+
if (model.startsWith(`${key}-`) || model.startsWith(`${key}@`) || model === key) {
|
|
482
|
+
if (!best || key.length > best[0].length) best = [key, pricing];
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return best?.[1] ?? null;
|
|
486
|
+
}
|
|
487
|
+
var pricingResolver = {
|
|
488
|
+
resolve(rawModelName) {
|
|
489
|
+
const registry = getPricingRegistry();
|
|
490
|
+
const candidates = getCandidates(rawModelName);
|
|
491
|
+
for (const candidate of candidates) {
|
|
492
|
+
const pricing = lookupCandidate(candidate, registry);
|
|
493
|
+
if (pricing) return pricing;
|
|
494
|
+
}
|
|
495
|
+
for (const candidate of candidates) {
|
|
496
|
+
const pricing = fuzzyLookup(candidate, registry);
|
|
497
|
+
if (pricing) return pricing;
|
|
498
|
+
}
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
function positive(value) {
|
|
503
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
504
|
+
}
|
|
505
|
+
function estimateCostForTokens(model, usage) {
|
|
506
|
+
if (!model || !usage) return null;
|
|
507
|
+
const pricing = pricingResolver.resolve(model);
|
|
508
|
+
if (!pricing) return null;
|
|
509
|
+
const cacheRead = positive(usage.cache_read);
|
|
510
|
+
const cacheCreate = positive(usage.cache_create);
|
|
511
|
+
const input = Math.max(0, positive(usage.input) - cacheRead - cacheCreate);
|
|
512
|
+
const output = positive(usage.output);
|
|
513
|
+
const reasoning = positive(usage.reasoning);
|
|
514
|
+
const webSearchRequests = positive(usage.web_search_requests);
|
|
515
|
+
const cost = input * pricing.inputCostPerToken + output * pricing.outputCostPerToken + reasoning * pricing.reasoningCostPerToken + cacheRead * pricing.cacheReadCostPerToken + cacheCreate * pricing.cacheCreateCostPerToken + webSearchRequests * pricing.webSearchCostPerRequest;
|
|
516
|
+
return cost > 0 ? { cost: Number(cost.toFixed(8)), source: "estimated" } : null;
|
|
517
|
+
}
|
|
518
|
+
function applyMessageCost(message) {
|
|
519
|
+
if ((message.cost ?? 0) > 0) {
|
|
520
|
+
message.cost_source = "recorded";
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
const estimate = estimateCostForTokens(message.model, message.tokens);
|
|
524
|
+
if (!estimate) return;
|
|
525
|
+
message.cost = estimate.cost;
|
|
526
|
+
message.cost_source = estimate.source;
|
|
527
|
+
}
|
|
528
|
+
function applyMessageCosts(messages) {
|
|
529
|
+
let totalCost = 0;
|
|
530
|
+
let source;
|
|
531
|
+
for (const message of messages) {
|
|
532
|
+
applyMessageCost(message);
|
|
533
|
+
const cost = message.cost ?? 0;
|
|
534
|
+
if (cost <= 0) continue;
|
|
535
|
+
totalCost += cost;
|
|
536
|
+
if (message.cost_source === "estimated") source = "estimated";
|
|
537
|
+
else if (!source) source = "recorded";
|
|
538
|
+
}
|
|
539
|
+
return { totalCost: Number(totalCost.toFixed(8)), source };
|
|
540
|
+
}
|
|
541
|
+
function withEstimatedSessionCost(stats, model) {
|
|
542
|
+
if (stats.total_cost > 0) {
|
|
543
|
+
return { ...stats, cost_source: stats.cost_source ?? "recorded" };
|
|
544
|
+
}
|
|
545
|
+
const estimate = estimateCostForTokens(model, {
|
|
546
|
+
input: stats.total_input_tokens,
|
|
547
|
+
output: stats.total_output_tokens,
|
|
548
|
+
cache_read: stats.total_cache_read_tokens,
|
|
549
|
+
cache_create: stats.total_cache_create_tokens
|
|
550
|
+
});
|
|
551
|
+
if (!estimate) return stats;
|
|
552
|
+
return { ...stats, total_cost: estimate.cost, cost_source: estimate.source };
|
|
553
|
+
}
|
|
554
|
+
function estimateTokenCost(model, tokens) {
|
|
555
|
+
return estimateCostForTokens(model, tokens)?.cost ?? null;
|
|
556
|
+
}
|
|
212
557
|
var RECENT_SESSION_REVALIDATION_WINDOW_MS = 24 * 60 * 60 * 1e3;
|
|
213
558
|
function parseTimestampMs(data) {
|
|
214
559
|
const raw = String(data["timestamp"] ?? "").trim();
|
|
@@ -232,15 +577,15 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
232
577
|
sessionMetaMap = /* @__PURE__ */ new Map();
|
|
233
578
|
findBasePath() {
|
|
234
579
|
const roots = resolveProviderRoots();
|
|
235
|
-
return firstExisting(
|
|
580
|
+
return firstExisting(join3(roots.claudeRoot, "projects"), "data/claudecode");
|
|
236
581
|
}
|
|
237
582
|
isAvailable() {
|
|
238
583
|
this.basePath = this.findBasePath();
|
|
239
584
|
if (!this.basePath) return false;
|
|
240
585
|
try {
|
|
241
586
|
for (const entry of readdirSync(this.basePath)) {
|
|
242
|
-
const dir =
|
|
243
|
-
if (
|
|
587
|
+
const dir = join3(this.basePath, entry);
|
|
588
|
+
if (existsSync3(dir) && readdirSync(dir).some((f) => f.endsWith(".jsonl"))) {
|
|
244
589
|
return true;
|
|
245
590
|
}
|
|
246
591
|
}
|
|
@@ -248,7 +593,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
248
593
|
}
|
|
249
594
|
return false;
|
|
250
595
|
}
|
|
251
|
-
scan() {
|
|
596
|
+
scan(options) {
|
|
252
597
|
if (!this.basePath) return [];
|
|
253
598
|
const scanMarker = perf.start("claudecode:scan");
|
|
254
599
|
const heads = [];
|
|
@@ -261,6 +606,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
261
606
|
perf.end(fileMarker);
|
|
262
607
|
for (const file of files) {
|
|
263
608
|
try {
|
|
609
|
+
if (!matchesScanWindow(statSync(file).mtimeMs, options)) continue;
|
|
264
610
|
const parseMarker = perf.start(`parseSessionHead:${basename2(file)}`);
|
|
265
611
|
const head = this.parseSessionHead(file, projectDir);
|
|
266
612
|
perf.end(parseMarker);
|
|
@@ -295,10 +641,10 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
295
641
|
if (!meta) {
|
|
296
642
|
throw new Error(`Session not found: ${sessionId}`);
|
|
297
643
|
}
|
|
298
|
-
if (!
|
|
644
|
+
if (!existsSync3(meta.sourcePath)) {
|
|
299
645
|
throw new Error(`Session file missing: ${meta.sourcePath}`);
|
|
300
646
|
}
|
|
301
|
-
const content =
|
|
647
|
+
const content = readFileSync3(meta.sourcePath, "utf-8");
|
|
302
648
|
const messages = [];
|
|
303
649
|
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
304
650
|
const ignoredToolCallIds = /* @__PURE__ */ new Set();
|
|
@@ -345,6 +691,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
345
691
|
total_input_tokens: totalInputTokens,
|
|
346
692
|
total_output_tokens: totalOutputTokens,
|
|
347
693
|
total_cost: totalCost,
|
|
694
|
+
cost_source: totalCost > 0 ? "estimated" : void 0,
|
|
348
695
|
total_cache_read_tokens: totalCacheRead,
|
|
349
696
|
total_cache_create_tokens: totalCacheCreate
|
|
350
697
|
},
|
|
@@ -464,14 +811,14 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
464
811
|
listProjectDirs() {
|
|
465
812
|
if (!this.basePath) return [];
|
|
466
813
|
try {
|
|
467
|
-
return readdirSync(this.basePath).map((e) =>
|
|
814
|
+
return readdirSync(this.basePath).map((e) => join3(this.basePath, e)).filter((p) => existsSync3(p));
|
|
468
815
|
} catch {
|
|
469
816
|
return [];
|
|
470
817
|
}
|
|
471
818
|
}
|
|
472
819
|
listJsonlFiles(dir) {
|
|
473
820
|
try {
|
|
474
|
-
return readdirSync(dir).filter((f) => f.endsWith(".jsonl") && f !== "sessions-index.json").map((f) =>
|
|
821
|
+
return readdirSync(dir).filter((f) => f.endsWith(".jsonl") && f !== "sessions-index.json").map((f) => join3(dir, f));
|
|
475
822
|
} catch {
|
|
476
823
|
return [];
|
|
477
824
|
}
|
|
@@ -481,11 +828,11 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
481
828
|
if (cacheKey in this.sessionsIndexCache) {
|
|
482
829
|
return this.sessionsIndexCache[cacheKey];
|
|
483
830
|
}
|
|
484
|
-
const indexPath =
|
|
831
|
+
const indexPath = join3(projectDir, "sessions-index.json");
|
|
485
832
|
const map = /* @__PURE__ */ new Map();
|
|
486
|
-
if (
|
|
833
|
+
if (existsSync3(indexPath)) {
|
|
487
834
|
try {
|
|
488
|
-
const data = JSON.parse(
|
|
835
|
+
const data = JSON.parse(readFileSync3(indexPath, "utf-8"));
|
|
489
836
|
const entries = data?.entries ?? [];
|
|
490
837
|
for (const entry of entries) {
|
|
491
838
|
const sid = entry?.sessionId;
|
|
@@ -500,7 +847,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
500
847
|
return map;
|
|
501
848
|
}
|
|
502
849
|
parseSessionHead(filePath, projectDir) {
|
|
503
|
-
const content =
|
|
850
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
504
851
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
505
852
|
if (lines.length === 0) return null;
|
|
506
853
|
const sessionId = basename2(filePath, ".jsonl");
|
|
@@ -522,6 +869,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
522
869
|
let totalOutputTokens = 0;
|
|
523
870
|
let totalCacheReadTokens = 0;
|
|
524
871
|
let totalCacheCreateTokens = 0;
|
|
872
|
+
let totalCost = 0;
|
|
525
873
|
const modelUsageMap = {};
|
|
526
874
|
for (const line of lines) {
|
|
527
875
|
try {
|
|
@@ -557,6 +905,13 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
557
905
|
const name = m.trim();
|
|
558
906
|
const msgTotal = inputTokens + cacheRead + cacheCreate + outputTokens;
|
|
559
907
|
modelUsageMap[name] = (modelUsageMap[name] ?? 0) + msgTotal;
|
|
908
|
+
const cost = estimateTokenCost(name, {
|
|
909
|
+
input: inputTokens + cacheRead + cacheCreate,
|
|
910
|
+
output: outputTokens,
|
|
911
|
+
cache_read: cacheRead,
|
|
912
|
+
cache_create: cacheCreate
|
|
913
|
+
});
|
|
914
|
+
if (cost !== null) totalCost += cost;
|
|
560
915
|
}
|
|
561
916
|
}
|
|
562
917
|
}
|
|
@@ -580,7 +935,8 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
580
935
|
message_count: messageCount,
|
|
581
936
|
total_input_tokens: totalInputTokens,
|
|
582
937
|
total_output_tokens: totalOutputTokens,
|
|
583
|
-
total_cost:
|
|
938
|
+
total_cost: totalCost,
|
|
939
|
+
cost_source: totalCost > 0 ? "estimated" : void 0,
|
|
584
940
|
total_cache_read_tokens: totalCacheReadTokens,
|
|
585
941
|
total_cache_create_tokens: totalCacheCreateTokens
|
|
586
942
|
},
|
|
@@ -781,6 +1137,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
781
1137
|
provider: opts.provider ?? null,
|
|
782
1138
|
tokens: opts.tokens ? opts.tokens : void 0,
|
|
783
1139
|
cost: opts.cost ?? 0,
|
|
1140
|
+
cost_source: opts.cost_source,
|
|
784
1141
|
parts: opts.parts
|
|
785
1142
|
};
|
|
786
1143
|
}
|
|
@@ -820,6 +1177,11 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
820
1177
|
cache_read: cacheRead,
|
|
821
1178
|
cache_create: cacheCreate
|
|
822
1179
|
};
|
|
1180
|
+
const cost = estimateTokenCost(message.model, message.tokens);
|
|
1181
|
+
if (cost !== null) {
|
|
1182
|
+
message.cost = cost;
|
|
1183
|
+
message.cost_source = "estimated";
|
|
1184
|
+
}
|
|
823
1185
|
}
|
|
824
1186
|
}
|
|
825
1187
|
// --- Assistant message grouping ---
|
|
@@ -1018,7 +1380,7 @@ function openDbReadOnly(dbPath) {
|
|
|
1018
1380
|
function openDb(dbPath) {
|
|
1019
1381
|
if (!DatabaseConstructor) return null;
|
|
1020
1382
|
try {
|
|
1021
|
-
|
|
1383
|
+
mkdirSync2(dirname2(dbPath), { recursive: true });
|
|
1022
1384
|
const db = DatabaseConstructor(dbPath);
|
|
1023
1385
|
db.pragma("journal_mode = WAL");
|
|
1024
1386
|
db.pragma("synchronous = NORMAL");
|
|
@@ -1040,18 +1402,18 @@ var OpenCodeAgent = class extends BaseAgent {
|
|
|
1040
1402
|
findDbPath() {
|
|
1041
1403
|
if (!isSqliteAvailable()) return null;
|
|
1042
1404
|
const roots = resolveProviderRoots();
|
|
1043
|
-
return firstExisting(
|
|
1405
|
+
return firstExisting(join4(roots.opencodeRoot, "opencode.db"), "data/opencode/opencode.db");
|
|
1044
1406
|
}
|
|
1045
1407
|
isAvailable() {
|
|
1046
1408
|
this.dbPath = this.findDbPath();
|
|
1047
1409
|
return this.dbPath !== null;
|
|
1048
1410
|
}
|
|
1049
|
-
scan() {
|
|
1411
|
+
scan(options) {
|
|
1050
1412
|
if (!this.dbPath) return [];
|
|
1051
1413
|
const db = openDbReadOnly(this.dbPath);
|
|
1052
1414
|
if (!db) return [];
|
|
1053
1415
|
try {
|
|
1054
|
-
const cutoffTime = Date.now() - 3650 * 24 * 60 * 60 * 1e3;
|
|
1416
|
+
const cutoffTime = options?.from ?? Date.now() - 3650 * 24 * 60 * 60 * 1e3;
|
|
1055
1417
|
const hasMessageTable = db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'message'").get();
|
|
1056
1418
|
let rows;
|
|
1057
1419
|
if (hasMessageTable) {
|
|
@@ -1064,7 +1426,7 @@ var OpenCodeAgent = class extends BaseAgent {
|
|
|
1064
1426
|
WHERE m.session_id = s.id AND m.data LIKE '%"modelID"%'
|
|
1065
1427
|
ORDER BY m.time_created DESC LIMIT 1) AS model_message_data
|
|
1066
1428
|
FROM session s
|
|
1067
|
-
WHERE s.time_created >= ?
|
|
1429
|
+
WHERE COALESCE(s.time_updated, s.time_created) >= ?
|
|
1068
1430
|
ORDER BY s.time_created DESC
|
|
1069
1431
|
`).all(cutoffTime);
|
|
1070
1432
|
} else {
|
|
@@ -1072,7 +1434,7 @@ var OpenCodeAgent = class extends BaseAgent {
|
|
|
1072
1434
|
SELECT s.id, s.title, s.time_created, s.time_updated, s.slug, s.directory,
|
|
1073
1435
|
s.version, s.summary_files, 0 AS message_count, NULL AS model_message_data
|
|
1074
1436
|
FROM session s
|
|
1075
|
-
WHERE s.time_created >= ?
|
|
1437
|
+
WHERE COALESCE(s.time_updated, s.time_created) >= ?
|
|
1076
1438
|
ORDER BY s.time_created DESC
|
|
1077
1439
|
`).all(cutoffTime);
|
|
1078
1440
|
}
|
|
@@ -1096,7 +1458,8 @@ var OpenCodeAgent = class extends BaseAgent {
|
|
|
1096
1458
|
message_count: stats?.message_count ?? Number(row.message_count ?? 0),
|
|
1097
1459
|
total_input_tokens: stats?.total_input_tokens ?? 0,
|
|
1098
1460
|
total_output_tokens: stats?.total_output_tokens ?? 0,
|
|
1099
|
-
total_cost: stats?.total_cost ?? 0
|
|
1461
|
+
total_cost: stats?.total_cost ?? 0,
|
|
1462
|
+
cost_source: stats?.cost_source
|
|
1100
1463
|
}
|
|
1101
1464
|
});
|
|
1102
1465
|
if (this.dbPath) {
|
|
@@ -1127,7 +1490,7 @@ var OpenCodeAgent = class extends BaseAgent {
|
|
|
1127
1490
|
if (!this.dbPath) {
|
|
1128
1491
|
this.dbPath = this.findDbPath();
|
|
1129
1492
|
}
|
|
1130
|
-
if (!this.dbPath || !
|
|
1493
|
+
if (!this.dbPath || !existsSync4(this.dbPath)) {
|
|
1131
1494
|
return { hasChanges: false, timestamp: Date.now() };
|
|
1132
1495
|
}
|
|
1133
1496
|
try {
|
|
@@ -1155,19 +1518,26 @@ var OpenCodeAgent = class extends BaseAgent {
|
|
|
1155
1518
|
let totalCost = 0;
|
|
1156
1519
|
let totalInputTokens = 0;
|
|
1157
1520
|
let totalOutputTokens = 0;
|
|
1521
|
+
let hasEstimatedCost = false;
|
|
1158
1522
|
for (const row of rows) {
|
|
1159
1523
|
const msgData = JSON.parse(String(row.data ?? "{}"));
|
|
1160
1524
|
const cost = Number(msgData.cost ?? 0);
|
|
1161
1525
|
const tokens = msgData.tokens;
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1526
|
+
const inputTokens = Number(tokens?.input ?? 0);
|
|
1527
|
+
const outputTokens = Number(tokens?.output ?? 0);
|
|
1528
|
+
const model = msgData.modelID ?? null;
|
|
1529
|
+
const estimatedCost = cost > 0 ? null : estimateTokenCost(model, { input: inputTokens, output: outputTokens });
|
|
1530
|
+
if (estimatedCost !== null) hasEstimatedCost = true;
|
|
1531
|
+
totalCost += cost || estimatedCost || 0;
|
|
1532
|
+
totalInputTokens += inputTokens;
|
|
1533
|
+
totalOutputTokens += outputTokens;
|
|
1165
1534
|
}
|
|
1166
1535
|
return {
|
|
1167
1536
|
message_count: rows.length,
|
|
1168
1537
|
total_input_tokens: totalInputTokens,
|
|
1169
1538
|
total_output_tokens: totalOutputTokens,
|
|
1170
|
-
total_cost: totalCost
|
|
1539
|
+
total_cost: totalCost,
|
|
1540
|
+
cost_source: totalCost > 0 ? hasEstimatedCost ? "estimated" : "recorded" : void 0
|
|
1171
1541
|
};
|
|
1172
1542
|
} catch {
|
|
1173
1543
|
return null;
|
|
@@ -1199,6 +1569,7 @@ var OpenCodeAgent = class extends BaseAgent {
|
|
|
1199
1569
|
let totalCost = 0;
|
|
1200
1570
|
let totalInputTokens = 0;
|
|
1201
1571
|
let totalOutputTokens = 0;
|
|
1572
|
+
let hasEstimatedCost = false;
|
|
1202
1573
|
const msgRows = db.prepare("SELECT * FROM message WHERE session_id = ? ORDER BY time_created ASC").all(sessionId);
|
|
1203
1574
|
for (const msgRow of msgRows) {
|
|
1204
1575
|
const msgData = JSON.parse(String(msgRow.data ?? "{}"));
|
|
@@ -1207,7 +1578,11 @@ var OpenCodeAgent = class extends BaseAgent {
|
|
|
1207
1578
|
const tokens = msgData.tokens;
|
|
1208
1579
|
const inputTokens = Number(tokens?.input ?? 0);
|
|
1209
1580
|
const outputTokens = Number(tokens?.output ?? 0);
|
|
1210
|
-
|
|
1581
|
+
const model = msgData.modelID ?? null;
|
|
1582
|
+
const estimatedCost = cost > 0 ? null : estimateTokenCost(model, { input: inputTokens, output: outputTokens });
|
|
1583
|
+
const resolvedCost = cost || estimatedCost || 0;
|
|
1584
|
+
if (estimatedCost !== null) hasEstimatedCost = true;
|
|
1585
|
+
totalCost += resolvedCost;
|
|
1211
1586
|
totalInputTokens += inputTokens;
|
|
1212
1587
|
totalOutputTokens += outputTokens;
|
|
1213
1588
|
const partRows = db.prepare("SELECT * FROM part WHERE message_id = ? ORDER BY time_created ASC").all(msgRow.id);
|
|
@@ -1236,11 +1611,12 @@ var OpenCodeAgent = class extends BaseAgent {
|
|
|
1236
1611
|
role: String(msgData.role ?? "assistant"),
|
|
1237
1612
|
agent: msgData.agent ?? null,
|
|
1238
1613
|
mode: msgData.mode ?? null,
|
|
1239
|
-
model
|
|
1614
|
+
model,
|
|
1240
1615
|
provider: msgData.providerID ?? null,
|
|
1241
1616
|
time_created: Number(msgRow.time_created ?? 0),
|
|
1242
1617
|
tokens: tokens ? { input: inputTokens, output: outputTokens } : void 0,
|
|
1243
|
-
cost,
|
|
1618
|
+
cost: resolvedCost,
|
|
1619
|
+
cost_source: resolvedCost > 0 ? cost > 0 ? "recorded" : "estimated" : void 0,
|
|
1244
1620
|
parts
|
|
1245
1621
|
});
|
|
1246
1622
|
}
|
|
@@ -1257,7 +1633,8 @@ var OpenCodeAgent = class extends BaseAgent {
|
|
|
1257
1633
|
message_count: messages.length,
|
|
1258
1634
|
total_input_tokens: totalInputTokens,
|
|
1259
1635
|
total_output_tokens: totalOutputTokens,
|
|
1260
|
-
total_cost: totalCost
|
|
1636
|
+
total_cost: totalCost,
|
|
1637
|
+
cost_source: totalCost > 0 ? hasEstimatedCost ? "estimated" : "recorded" : void 0
|
|
1261
1638
|
},
|
|
1262
1639
|
messages
|
|
1263
1640
|
};
|
|
@@ -1327,24 +1704,30 @@ var KimiAgent = class extends BaseAgent {
|
|
|
1327
1704
|
basePath = null;
|
|
1328
1705
|
sessionMetaMap = /* @__PURE__ */ new Map();
|
|
1329
1706
|
projectMap = /* @__PURE__ */ new Map();
|
|
1707
|
+
defaultModel = null;
|
|
1330
1708
|
findBasePath() {
|
|
1331
1709
|
const roots = resolveProviderRoots();
|
|
1332
|
-
return firstExisting(
|
|
1710
|
+
return firstExisting(join5(roots.kimiRoot, "sessions"), "data/kimi");
|
|
1333
1711
|
}
|
|
1334
1712
|
/** Parse kimi.json and build md5(project_path) → cwd mapping */
|
|
1335
1713
|
loadKimiConfig() {
|
|
1336
1714
|
const roots = resolveProviderRoots();
|
|
1337
|
-
const configPath =
|
|
1338
|
-
|
|
1715
|
+
const configPath = join5(roots.kimiRoot, "kimi.json");
|
|
1716
|
+
const tomlPath = join5(roots.kimiRoot, "config.toml");
|
|
1717
|
+
if (existsSync5(tomlPath)) {
|
|
1718
|
+
const configText = readFileSync4(tomlPath, "utf-8");
|
|
1719
|
+
this.defaultModel = configText.match(/^default_model\s*=\s*"([^"]+)"/m)?.[1] ?? null;
|
|
1720
|
+
}
|
|
1721
|
+
if (!existsSync5(configPath)) return;
|
|
1339
1722
|
try {
|
|
1340
|
-
const raw = JSON.parse(
|
|
1723
|
+
const raw = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
1341
1724
|
const workDirs = raw?.work_dirs;
|
|
1342
1725
|
if (!Array.isArray(workDirs)) return;
|
|
1343
1726
|
for (const wd of workDirs) {
|
|
1344
|
-
const
|
|
1345
|
-
if (typeof
|
|
1346
|
-
const hash = createHash("md5").update(
|
|
1347
|
-
this.projectMap.set(hash,
|
|
1727
|
+
const path2 = wd.path;
|
|
1728
|
+
if (typeof path2 !== "string") continue;
|
|
1729
|
+
const hash = createHash("md5").update(path2).digest("hex");
|
|
1730
|
+
this.projectMap.set(hash, path2);
|
|
1348
1731
|
}
|
|
1349
1732
|
} catch {
|
|
1350
1733
|
}
|
|
@@ -1366,12 +1749,12 @@ var KimiAgent = class extends BaseAgent {
|
|
|
1366
1749
|
try {
|
|
1367
1750
|
for (const hashEntry of readdirSync2(this.basePath, { withFileTypes: true })) {
|
|
1368
1751
|
if (!hashEntry.isDirectory()) continue;
|
|
1369
|
-
const hashPath =
|
|
1752
|
+
const hashPath = join5(this.basePath, hashEntry.name);
|
|
1370
1753
|
try {
|
|
1371
1754
|
for (const sessionEntry of readdirSync2(hashPath, { withFileTypes: true })) {
|
|
1372
1755
|
if (!sessionEntry.isDirectory()) continue;
|
|
1373
|
-
const sessionPath =
|
|
1374
|
-
if (
|
|
1756
|
+
const sessionPath = join5(hashPath, sessionEntry.name);
|
|
1757
|
+
if (existsSync5(join5(sessionPath, "metadata.json")) || existsSync5(join5(sessionPath, "state.json"))) {
|
|
1375
1758
|
dirs.push(sessionPath);
|
|
1376
1759
|
}
|
|
1377
1760
|
}
|
|
@@ -1387,21 +1770,21 @@ var KimiAgent = class extends BaseAgent {
|
|
|
1387
1770
|
try {
|
|
1388
1771
|
const sessionId = basename3(sessionDir);
|
|
1389
1772
|
const projectHash = basename3(dirname3(sessionDir));
|
|
1390
|
-
const contextFile =
|
|
1391
|
-
const wireFile =
|
|
1392
|
-
if (!
|
|
1393
|
-
const statePath =
|
|
1394
|
-
const metaPath =
|
|
1773
|
+
const contextFile = join5(sessionDir, "context.jsonl");
|
|
1774
|
+
const wireFile = join5(sessionDir, "wire.jsonl");
|
|
1775
|
+
if (!existsSync5(contextFile) && !existsSync5(wireFile)) return null;
|
|
1776
|
+
const statePath = join5(sessionDir, "state.json");
|
|
1777
|
+
const metaPath = join5(sessionDir, "metadata.json");
|
|
1395
1778
|
let title = "";
|
|
1396
1779
|
let wireMtime = null;
|
|
1397
1780
|
let metaFile = "";
|
|
1398
|
-
if (
|
|
1399
|
-
const state = JSON.parse(
|
|
1781
|
+
if (existsSync5(statePath)) {
|
|
1782
|
+
const state = JSON.parse(readFileSync4(statePath, "utf-8"));
|
|
1400
1783
|
title = String(state.custom_title ?? "");
|
|
1401
1784
|
wireMtime = typeof state.wire_mtime === "number" ? state.wire_mtime : null;
|
|
1402
1785
|
metaFile = statePath;
|
|
1403
|
-
} else if (
|
|
1404
|
-
const meta = JSON.parse(
|
|
1786
|
+
} else if (existsSync5(metaPath)) {
|
|
1787
|
+
const meta = JSON.parse(readFileSync4(metaPath, "utf-8"));
|
|
1405
1788
|
title = String(meta.title ?? "");
|
|
1406
1789
|
wireMtime = typeof meta.wire_mtime === "number" ? meta.wire_mtime : null;
|
|
1407
1790
|
metaFile = metaPath;
|
|
@@ -1413,8 +1796,8 @@ var KimiAgent = class extends BaseAgent {
|
|
|
1413
1796
|
title: title || "Untitled Session",
|
|
1414
1797
|
sourcePath: sessionDir,
|
|
1415
1798
|
cwd,
|
|
1416
|
-
contextFile:
|
|
1417
|
-
wireFile:
|
|
1799
|
+
contextFile: existsSync5(contextFile) ? contextFile : null,
|
|
1800
|
+
wireFile: existsSync5(wireFile) ? wireFile : null,
|
|
1418
1801
|
createdAt,
|
|
1419
1802
|
metaFile
|
|
1420
1803
|
};
|
|
@@ -1422,7 +1805,7 @@ var KimiAgent = class extends BaseAgent {
|
|
|
1422
1805
|
return null;
|
|
1423
1806
|
}
|
|
1424
1807
|
}
|
|
1425
|
-
scan() {
|
|
1808
|
+
scan(options) {
|
|
1426
1809
|
if (!this.basePath) return [];
|
|
1427
1810
|
const scanMarker = perf.start("kimi:scan");
|
|
1428
1811
|
const listMarker = perf.start("listSessionDirs");
|
|
@@ -1435,6 +1818,7 @@ var KimiAgent = class extends BaseAgent {
|
|
|
1435
1818
|
const meta = this.parseSessionDir(dir);
|
|
1436
1819
|
perf.end(parseMarker);
|
|
1437
1820
|
if (!meta) continue;
|
|
1821
|
+
if (!matchesScanWindow(meta.createdAt, options)) continue;
|
|
1438
1822
|
this.sessionMetaMap.set(meta.id, meta);
|
|
1439
1823
|
const stats = this.extractStats(meta.sourcePath);
|
|
1440
1824
|
heads.push({
|
|
@@ -1462,32 +1846,42 @@ var KimiAgent = class extends BaseAgent {
|
|
|
1462
1846
|
* 检测文件系统变更
|
|
1463
1847
|
*/
|
|
1464
1848
|
checkForChanges(sinceTimestamp, cachedSessions) {
|
|
1465
|
-
const changedIds =
|
|
1466
|
-
|
|
1467
|
-
|
|
1849
|
+
const changedIds = /* @__PURE__ */ new Set();
|
|
1850
|
+
const cachedIds = new Set(cachedSessions.map((session) => session.id));
|
|
1851
|
+
const currentIds = /* @__PURE__ */ new Set();
|
|
1852
|
+
for (const dir of this.listSessionDirs()) {
|
|
1853
|
+
const meta = this.parseSessionDir(dir);
|
|
1468
1854
|
if (!meta) continue;
|
|
1855
|
+
currentIds.add(meta.id);
|
|
1856
|
+
this.sessionMetaMap.set(meta.id, meta);
|
|
1857
|
+
if (!cachedIds.has(meta.id)) {
|
|
1858
|
+
changedIds.add(meta.id);
|
|
1859
|
+
continue;
|
|
1860
|
+
}
|
|
1861
|
+
const dataFile = meta.wireFile || meta.contextFile;
|
|
1469
1862
|
try {
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
continue;
|
|
1475
|
-
}
|
|
1863
|
+
const metaStat = statSync3(meta.metaFile);
|
|
1864
|
+
if (metaStat.mtimeMs > sinceTimestamp) {
|
|
1865
|
+
changedIds.add(meta.id);
|
|
1866
|
+
continue;
|
|
1476
1867
|
}
|
|
1477
|
-
const dataFile = meta.wireFile || meta.contextFile;
|
|
1478
1868
|
if (dataFile) {
|
|
1479
1869
|
const dataStat = statSync3(dataFile);
|
|
1480
1870
|
if (dataStat.mtimeMs > sinceTimestamp) {
|
|
1481
|
-
changedIds.
|
|
1871
|
+
changedIds.add(meta.id);
|
|
1482
1872
|
}
|
|
1483
1873
|
}
|
|
1484
1874
|
} catch {
|
|
1485
|
-
changedIds.
|
|
1875
|
+
changedIds.add(meta.id);
|
|
1486
1876
|
}
|
|
1487
1877
|
}
|
|
1878
|
+
for (const session of cachedSessions) {
|
|
1879
|
+
if (!currentIds.has(session.id)) changedIds.add(session.id);
|
|
1880
|
+
}
|
|
1881
|
+
const changedIdList = Array.from(changedIds);
|
|
1488
1882
|
return {
|
|
1489
|
-
hasChanges:
|
|
1490
|
-
changedIds,
|
|
1883
|
+
hasChanges: changedIdList.length > 0,
|
|
1884
|
+
changedIds: changedIdList,
|
|
1491
1885
|
timestamp: Date.now()
|
|
1492
1886
|
};
|
|
1493
1887
|
}
|
|
@@ -1496,11 +1890,16 @@ var KimiAgent = class extends BaseAgent {
|
|
|
1496
1890
|
*/
|
|
1497
1891
|
incrementalScan(cachedSessions, changedIds) {
|
|
1498
1892
|
const sessionMap = new Map(cachedSessions.map((s) => [s.id, s]));
|
|
1893
|
+
const changedIdSet = new Set(changedIds);
|
|
1894
|
+
for (const id of changedIdSet) {
|
|
1895
|
+
sessionMap.delete(id);
|
|
1896
|
+
this.sessionMetaMap.delete(id);
|
|
1897
|
+
}
|
|
1499
1898
|
for (const dir of this.listSessionDirs()) {
|
|
1500
1899
|
try {
|
|
1501
1900
|
const meta = this.parseSessionDir(dir);
|
|
1502
1901
|
if (!meta) continue;
|
|
1503
|
-
if (
|
|
1902
|
+
if (changedIdSet.has(meta.id)) {
|
|
1504
1903
|
this.sessionMetaMap.set(meta.id, meta);
|
|
1505
1904
|
const stats = this.extractStats(meta.sourcePath);
|
|
1506
1905
|
sessionMap.set(meta.id, {
|
|
@@ -1528,7 +1927,7 @@ var KimiAgent = class extends BaseAgent {
|
|
|
1528
1927
|
}
|
|
1529
1928
|
getSessionDataFromContext(meta) {
|
|
1530
1929
|
if (!meta.contextFile) throw new Error("context.jsonl is missing");
|
|
1531
|
-
const content =
|
|
1930
|
+
const content = readFileSync4(meta.contextFile, "utf-8");
|
|
1532
1931
|
const messages = [];
|
|
1533
1932
|
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
1534
1933
|
const ignoredToolCallIds = /* @__PURE__ */ new Set();
|
|
@@ -1593,9 +1992,9 @@ var KimiAgent = class extends BaseAgent {
|
|
|
1593
1992
|
return this.buildSessionData(meta, messages, stats);
|
|
1594
1993
|
}
|
|
1595
1994
|
getSessionDataFromWire(meta) {
|
|
1596
|
-
const wirePath = meta.wireFile ??
|
|
1597
|
-
if (!
|
|
1598
|
-
const content =
|
|
1995
|
+
const wirePath = meta.wireFile ?? join5(meta.sourcePath, "wire.jsonl");
|
|
1996
|
+
if (!existsSync5(wirePath)) throw new Error("wire.jsonl is missing");
|
|
1997
|
+
const content = readFileSync4(wirePath, "utf-8");
|
|
1599
1998
|
const messages = [];
|
|
1600
1999
|
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
1601
2000
|
const ignoredToolCallIds = /* @__PURE__ */ new Set();
|
|
@@ -1620,6 +2019,12 @@ var KimiAgent = class extends BaseAgent {
|
|
|
1620
2019
|
const msg = messages[i];
|
|
1621
2020
|
if (msg.role === "assistant" && !msg.tokens) {
|
|
1622
2021
|
msg.tokens = { input: inputTokens, output: outputTokens };
|
|
2022
|
+
msg.model ??= this.defaultModel;
|
|
2023
|
+
const cost = estimateTokenCost(msg.model, msg.tokens);
|
|
2024
|
+
if (cost !== null) {
|
|
2025
|
+
msg.cost = cost;
|
|
2026
|
+
msg.cost_source = "estimated";
|
|
2027
|
+
}
|
|
1623
2028
|
break;
|
|
1624
2029
|
}
|
|
1625
2030
|
}
|
|
@@ -1862,6 +2267,7 @@ var KimiAgent = class extends BaseAgent {
|
|
|
1862
2267
|
return true;
|
|
1863
2268
|
}
|
|
1864
2269
|
extractStats(sessionDir) {
|
|
2270
|
+
let totalCost = 0;
|
|
1865
2271
|
const stats = {
|
|
1866
2272
|
total_cost: 0,
|
|
1867
2273
|
total_input_tokens: 0,
|
|
@@ -1869,27 +2275,34 @@ var KimiAgent = class extends BaseAgent {
|
|
|
1869
2275
|
total_tokens: 0,
|
|
1870
2276
|
message_count: 0
|
|
1871
2277
|
};
|
|
1872
|
-
const wirePath =
|
|
1873
|
-
if (!
|
|
2278
|
+
const wirePath = join5(sessionDir, "wire.jsonl");
|
|
2279
|
+
if (!existsSync5(wirePath)) return stats;
|
|
1874
2280
|
try {
|
|
1875
|
-
const content =
|
|
2281
|
+
const content = readFileSync4(wirePath, "utf-8");
|
|
1876
2282
|
for (const line of content.split("\n").filter((l) => l.trim())) {
|
|
1877
2283
|
try {
|
|
1878
2284
|
const data = JSON.parse(line);
|
|
1879
2285
|
const tokenUsage = data.message?.usage;
|
|
1880
2286
|
if (!tokenUsage) continue;
|
|
1881
|
-
|
|
1882
|
-
|
|
2287
|
+
const inputTokens = Number(tokenUsage.input_tokens ?? 0);
|
|
2288
|
+
const outputTokens = Number(tokenUsage.output_tokens ?? 0);
|
|
2289
|
+
stats.total_input_tokens += inputTokens;
|
|
2290
|
+
stats.total_output_tokens += outputTokens;
|
|
2291
|
+
const cost = estimateTokenCost(this.defaultModel, {
|
|
2292
|
+
input: inputTokens,
|
|
2293
|
+
output: outputTokens
|
|
2294
|
+
});
|
|
2295
|
+
if (cost !== null) totalCost += cost;
|
|
1883
2296
|
} catch {
|
|
1884
2297
|
}
|
|
1885
2298
|
}
|
|
1886
2299
|
} catch {
|
|
1887
2300
|
}
|
|
1888
|
-
const contextPath =
|
|
1889
|
-
const rawPath =
|
|
1890
|
-
if (!
|
|
2301
|
+
const contextPath = join5(sessionDir, "context.jsonl");
|
|
2302
|
+
const rawPath = existsSync5(contextPath) ? contextPath : wirePath;
|
|
2303
|
+
if (!existsSync5(rawPath)) return stats;
|
|
1891
2304
|
try {
|
|
1892
|
-
const rawContent =
|
|
2305
|
+
const rawContent = readFileSync4(rawPath, "utf-8");
|
|
1893
2306
|
for (const line of rawContent.split("\n").filter((l) => l.trim())) {
|
|
1894
2307
|
try {
|
|
1895
2308
|
const data = JSON.parse(line);
|
|
@@ -1901,10 +2314,19 @@ var KimiAgent = class extends BaseAgent {
|
|
|
1901
2314
|
}
|
|
1902
2315
|
} catch {
|
|
1903
2316
|
}
|
|
2317
|
+
stats.total_cost = Number(totalCost.toFixed(8));
|
|
2318
|
+
if (stats.total_cost > 0) {
|
|
2319
|
+
stats.cost_source = "estimated";
|
|
2320
|
+
}
|
|
1904
2321
|
return stats;
|
|
1905
2322
|
}
|
|
1906
2323
|
buildSessionData(meta, messages, stats) {
|
|
1907
2324
|
stats.message_count = messages.length;
|
|
2325
|
+
const totalCost = messages.reduce((sum, message) => sum + (message.cost ?? 0), 0);
|
|
2326
|
+
if (totalCost > 0) {
|
|
2327
|
+
stats.total_cost = Number(totalCost.toFixed(8));
|
|
2328
|
+
stats.cost_source = "estimated";
|
|
2329
|
+
}
|
|
1908
2330
|
return {
|
|
1909
2331
|
id: meta.id,
|
|
1910
2332
|
title: meta.title,
|
|
@@ -1959,6 +2381,10 @@ function parseTimestampMs2(data) {
|
|
|
1959
2381
|
function extractModelName(raw) {
|
|
1960
2382
|
return typeof raw === "string" && raw.trim() ? raw.trim() : null;
|
|
1961
2383
|
}
|
|
2384
|
+
function extractCachedInputTokens(usage) {
|
|
2385
|
+
if (!usage) return 0;
|
|
2386
|
+
return Number(usage["cached_input_tokens"] ?? usage["cache_read_input_tokens"] ?? 0);
|
|
2387
|
+
}
|
|
1962
2388
|
function normalizeTitleText3(text) {
|
|
1963
2389
|
const line = text.split("\n").find((l) => l.trim());
|
|
1964
2390
|
return line?.trim().slice(0, 80) || "";
|
|
@@ -2073,7 +2499,7 @@ var CodexAgent = class extends BaseAgent {
|
|
|
2073
2499
|
// ---- BaseAgent implementation ----
|
|
2074
2500
|
findBasePath() {
|
|
2075
2501
|
const roots = resolveProviderRoots();
|
|
2076
|
-
return firstExisting(
|
|
2502
|
+
return firstExisting(join6(roots.codexRoot, "sessions"));
|
|
2077
2503
|
}
|
|
2078
2504
|
isAvailable() {
|
|
2079
2505
|
this.basePath = this.findBasePath();
|
|
@@ -2085,7 +2511,7 @@ var CodexAgent = class extends BaseAgent {
|
|
|
2085
2511
|
return false;
|
|
2086
2512
|
}
|
|
2087
2513
|
}
|
|
2088
|
-
scan() {
|
|
2514
|
+
scan(options) {
|
|
2089
2515
|
if (!this.basePath) return [];
|
|
2090
2516
|
const scanMarker = perf.start("codex:scan");
|
|
2091
2517
|
const indexMarker = perf.start("loadSessionIndex");
|
|
@@ -2093,12 +2519,12 @@ var CodexAgent = class extends BaseAgent {
|
|
|
2093
2519
|
perf.end(indexMarker);
|
|
2094
2520
|
const heads = [];
|
|
2095
2521
|
const listMarker = perf.start("listRolloutFiles");
|
|
2096
|
-
const files = this.listRolloutFiles();
|
|
2522
|
+
const files = this.listRolloutFiles(options);
|
|
2097
2523
|
perf.end(listMarker);
|
|
2098
2524
|
for (const file of files) {
|
|
2099
2525
|
try {
|
|
2100
2526
|
const parseMarker = perf.start(`parseSessionHead:${basename4(file)}`);
|
|
2101
|
-
const head = this.parseSessionHead(file);
|
|
2527
|
+
const head = this.parseSessionHead(file, options);
|
|
2102
2528
|
perf.end(parseMarker);
|
|
2103
2529
|
if (head) {
|
|
2104
2530
|
heads.push(head);
|
|
@@ -2234,12 +2660,14 @@ var CodexAgent = class extends BaseAgent {
|
|
|
2234
2660
|
getSessionData(sessionId) {
|
|
2235
2661
|
const meta = this.sessionMetaMap.get(sessionId);
|
|
2236
2662
|
if (!meta) throw new Error(`Session not found: ${sessionId}`);
|
|
2237
|
-
if (!
|
|
2238
|
-
const content =
|
|
2663
|
+
if (!existsSync6(meta.sourcePath)) throw new Error(`Session file missing: ${meta.sourcePath}`);
|
|
2664
|
+
const content = readFileSync5(meta.sourcePath, "utf-8");
|
|
2239
2665
|
const messages = [];
|
|
2240
2666
|
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
2241
2667
|
let totalInputTokens = 0;
|
|
2242
2668
|
let totalOutputTokens = 0;
|
|
2669
|
+
let totalCacheReadTokens = 0;
|
|
2670
|
+
let totalCost = 0;
|
|
2243
2671
|
let currentAssistantIndex = null;
|
|
2244
2672
|
let latestAssistantTextIndex = null;
|
|
2245
2673
|
let pendingPlan = null;
|
|
@@ -2248,6 +2676,7 @@ var CodexAgent = class extends BaseAgent {
|
|
|
2248
2676
|
let prevInput = 0;
|
|
2249
2677
|
let prevOutput = 0;
|
|
2250
2678
|
let prevReasoning = 0;
|
|
2679
|
+
let prevCachedInput = 0;
|
|
2251
2680
|
for (const record of parseJsonlLines(content)) {
|
|
2252
2681
|
try {
|
|
2253
2682
|
const recordType = String(record["type"] ?? "");
|
|
@@ -2286,29 +2715,43 @@ var CodexAgent = class extends BaseAgent {
|
|
|
2286
2715
|
let inputTokens = 0;
|
|
2287
2716
|
let outputTokens = 0;
|
|
2288
2717
|
let reasoningTokens = 0;
|
|
2718
|
+
let cacheReadTokens = 0;
|
|
2289
2719
|
if (lastUsage) {
|
|
2290
2720
|
inputTokens = Number(lastUsage["input_tokens"] ?? 0);
|
|
2291
2721
|
outputTokens = Number(lastUsage["output_tokens"] ?? 0);
|
|
2292
2722
|
reasoningTokens = Number(lastUsage["reasoning_output_tokens"] ?? 0);
|
|
2723
|
+
cacheReadTokens = extractCachedInputTokens(lastUsage);
|
|
2293
2724
|
} else if (cumulativeTotal > 0 && totalUsage) {
|
|
2294
2725
|
inputTokens = Number(totalUsage["input_tokens"] ?? 0) - prevInput;
|
|
2295
2726
|
outputTokens = Number(totalUsage["output_tokens"] ?? 0) - prevOutput;
|
|
2296
2727
|
reasoningTokens = Number(totalUsage["reasoning_output_tokens"] ?? 0) - prevReasoning;
|
|
2728
|
+
cacheReadTokens = extractCachedInputTokens(totalUsage) - prevCachedInput;
|
|
2297
2729
|
prevInput = Number(totalUsage["input_tokens"] ?? 0);
|
|
2298
2730
|
prevOutput = Number(totalUsage["output_tokens"] ?? 0);
|
|
2299
2731
|
prevReasoning = Number(totalUsage["reasoning_output_tokens"] ?? 0);
|
|
2732
|
+
prevCachedInput = extractCachedInputTokens(totalUsage);
|
|
2300
2733
|
}
|
|
2301
2734
|
const totalInput = Math.max(0, inputTokens);
|
|
2735
|
+
const totalCacheRead = Math.max(0, cacheReadTokens);
|
|
2302
2736
|
if (totalInput || outputTokens || reasoningTokens) {
|
|
2303
2737
|
totalInputTokens += totalInput;
|
|
2304
2738
|
totalOutputTokens += outputTokens + reasoningTokens;
|
|
2739
|
+
totalCacheReadTokens += totalCacheRead;
|
|
2305
2740
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2306
2741
|
const msg = messages[i];
|
|
2307
2742
|
if (msg.role === "assistant" && !msg.tokens) {
|
|
2308
2743
|
msg.tokens = {
|
|
2309
2744
|
input: totalInput,
|
|
2310
|
-
output: outputTokens
|
|
2745
|
+
output: outputTokens,
|
|
2746
|
+
reasoning: reasoningTokens || void 0,
|
|
2747
|
+
cache_read: totalCacheRead || void 0
|
|
2311
2748
|
};
|
|
2749
|
+
const cost = estimateTokenCost(msg.model ?? activeModel, msg.tokens);
|
|
2750
|
+
if (cost !== null) {
|
|
2751
|
+
msg.cost = cost;
|
|
2752
|
+
msg.cost_source = "estimated";
|
|
2753
|
+
totalCost += cost;
|
|
2754
|
+
}
|
|
2312
2755
|
break;
|
|
2313
2756
|
}
|
|
2314
2757
|
}
|
|
@@ -2333,29 +2776,32 @@ var CodexAgent = class extends BaseAgent {
|
|
|
2333
2776
|
message_count: messages.length,
|
|
2334
2777
|
total_input_tokens: totalInputTokens,
|
|
2335
2778
|
total_output_tokens: totalOutputTokens,
|
|
2336
|
-
|
|
2779
|
+
total_cache_read_tokens: totalCacheReadTokens || void 0,
|
|
2780
|
+
total_cost: totalCost,
|
|
2781
|
+
cost_source: totalCost > 0 ? "estimated" : void 0
|
|
2337
2782
|
},
|
|
2338
2783
|
messages
|
|
2339
2784
|
};
|
|
2340
2785
|
}
|
|
2341
2786
|
// ---- File listing ----
|
|
2342
|
-
listRolloutFiles() {
|
|
2787
|
+
listRolloutFiles(options) {
|
|
2343
2788
|
if (!this.basePath) return [];
|
|
2344
2789
|
try {
|
|
2345
|
-
return this.walkDirForRolloutFiles(this.basePath);
|
|
2790
|
+
return this.walkDirForRolloutFiles(this.basePath, options);
|
|
2346
2791
|
} catch {
|
|
2347
2792
|
return [];
|
|
2348
2793
|
}
|
|
2349
2794
|
}
|
|
2350
|
-
walkDirForRolloutFiles(dir) {
|
|
2795
|
+
walkDirForRolloutFiles(dir, options) {
|
|
2351
2796
|
const files = [];
|
|
2352
2797
|
try {
|
|
2353
2798
|
for (const entry of readdirSync3(dir)) {
|
|
2354
|
-
const fullPath =
|
|
2799
|
+
const fullPath = join6(dir, entry);
|
|
2355
2800
|
const stat = statSync4(fullPath);
|
|
2356
2801
|
if (stat.isDirectory()) {
|
|
2357
|
-
files.push(...this.walkDirForRolloutFiles(fullPath));
|
|
2802
|
+
files.push(...this.walkDirForRolloutFiles(fullPath, options));
|
|
2358
2803
|
} else if (entry.endsWith(".jsonl") && entry.startsWith("rollout-")) {
|
|
2804
|
+
if (!matchesScanWindow(stat.mtimeMs, options)) continue;
|
|
2359
2805
|
files.push(fullPath);
|
|
2360
2806
|
}
|
|
2361
2807
|
}
|
|
@@ -2367,10 +2813,10 @@ var CodexAgent = class extends BaseAgent {
|
|
|
2367
2813
|
loadSessionIndex() {
|
|
2368
2814
|
if (this.sessionIndexCache.size > 0) return;
|
|
2369
2815
|
const roots = resolveProviderRoots();
|
|
2370
|
-
const indexPath =
|
|
2371
|
-
if (!
|
|
2816
|
+
const indexPath = join6(roots.codexRoot, "session_index.jsonl");
|
|
2817
|
+
if (!existsSync6(indexPath)) return;
|
|
2372
2818
|
try {
|
|
2373
|
-
const content =
|
|
2819
|
+
const content = readFileSync5(indexPath, "utf-8");
|
|
2374
2820
|
for (const record of parseJsonlLines(content)) {
|
|
2375
2821
|
const sid = String(record["id"] ?? "").trim();
|
|
2376
2822
|
const threadName = String(record["thread_name"] ?? "").trim();
|
|
@@ -2386,8 +2832,21 @@ var CodexAgent = class extends BaseAgent {
|
|
|
2386
2832
|
return this.sessionIndexCache.get(sessionId) ?? null;
|
|
2387
2833
|
}
|
|
2388
2834
|
// ---- Session head parsing ----
|
|
2389
|
-
|
|
2390
|
-
const
|
|
2835
|
+
readFilePrefix(filePath, bytes = 64 * 1024) {
|
|
2836
|
+
const fd = openSync(filePath, "r");
|
|
2837
|
+
try {
|
|
2838
|
+
const buffer = Buffer.alloc(bytes);
|
|
2839
|
+
const bytesRead = readSync(fd, buffer, 0, bytes, 0);
|
|
2840
|
+
return buffer.subarray(0, bytesRead).toString("utf-8");
|
|
2841
|
+
} finally {
|
|
2842
|
+
closeSync(fd);
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
parseSessionHead(filePath, options) {
|
|
2846
|
+
if (options?.fast) {
|
|
2847
|
+
return this.parseFastSessionHead(filePath);
|
|
2848
|
+
}
|
|
2849
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
2391
2850
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
2392
2851
|
if (lines.length === 0) return null;
|
|
2393
2852
|
const sessionId = extractSessionId(filePath);
|
|
@@ -2406,12 +2865,17 @@ var CodexAgent = class extends BaseAgent {
|
|
|
2406
2865
|
let updatedAt = createdAt;
|
|
2407
2866
|
let messageCount = 0;
|
|
2408
2867
|
let model = null;
|
|
2868
|
+
let activeModel = null;
|
|
2869
|
+
const modelUsageMap = {};
|
|
2409
2870
|
let totalInputTokens = 0;
|
|
2410
2871
|
let totalOutputTokens = 0;
|
|
2872
|
+
let totalCacheReadTokens = 0;
|
|
2873
|
+
let totalCost = 0;
|
|
2411
2874
|
let scanPrevCumulativeTotal = 0;
|
|
2412
2875
|
let scanPrevInput = 0;
|
|
2413
2876
|
let scanPrevOutput = 0;
|
|
2414
2877
|
let scanPrevReasoning = 0;
|
|
2878
|
+
let scanPrevCachedInput = 0;
|
|
2415
2879
|
const COUNTED_TYPES = /* @__PURE__ */ new Set(["message", "function_call", "function_call_output"]);
|
|
2416
2880
|
for (const line of lines) {
|
|
2417
2881
|
try {
|
|
@@ -2421,8 +2885,10 @@ var CodexAgent = class extends BaseAgent {
|
|
|
2421
2885
|
if (recordTs > updatedAt) updatedAt = recordTs;
|
|
2422
2886
|
if (recordType === "session_meta" || recordType === "turn_context") {
|
|
2423
2887
|
const payload2 = data["payload"] ?? {};
|
|
2424
|
-
|
|
2425
|
-
|
|
2888
|
+
const nextModel = extractModelName(payload2["model"]);
|
|
2889
|
+
if (nextModel) {
|
|
2890
|
+
activeModel = nextModel;
|
|
2891
|
+
model ??= nextModel;
|
|
2426
2892
|
}
|
|
2427
2893
|
continue;
|
|
2428
2894
|
}
|
|
@@ -2432,10 +2898,11 @@ var CodexAgent = class extends BaseAgent {
|
|
|
2432
2898
|
if (COUNTED_TYPES.has(pType)) {
|
|
2433
2899
|
messageCount++;
|
|
2434
2900
|
}
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2901
|
+
const info = p["info"];
|
|
2902
|
+
const m = info?.["model"] ?? p["model"];
|
|
2903
|
+
if (typeof m === "string" && m.trim()) {
|
|
2904
|
+
activeModel = m.trim();
|
|
2905
|
+
model ??= activeModel;
|
|
2439
2906
|
}
|
|
2440
2907
|
}
|
|
2441
2908
|
if (recordType === "event_msg") {
|
|
@@ -2450,21 +2917,38 @@ var CodexAgent = class extends BaseAgent {
|
|
|
2450
2917
|
let inputTokens = 0;
|
|
2451
2918
|
let outputTokens = 0;
|
|
2452
2919
|
let reasoningTokens = 0;
|
|
2920
|
+
let cacheReadTokens = 0;
|
|
2453
2921
|
if (lastUsage) {
|
|
2454
2922
|
inputTokens = Number(lastUsage["input_tokens"] ?? 0);
|
|
2455
2923
|
outputTokens = Number(lastUsage["output_tokens"] ?? 0);
|
|
2456
2924
|
reasoningTokens = Number(lastUsage["reasoning_output_tokens"] ?? 0);
|
|
2925
|
+
cacheReadTokens = extractCachedInputTokens(lastUsage);
|
|
2457
2926
|
} else if (cumulativeTotal > 0 && totalUsage) {
|
|
2458
2927
|
inputTokens = Number(totalUsage["input_tokens"] ?? 0) - scanPrevInput;
|
|
2459
2928
|
outputTokens = Number(totalUsage["output_tokens"] ?? 0) - scanPrevOutput;
|
|
2460
2929
|
reasoningTokens = Number(totalUsage["reasoning_output_tokens"] ?? 0) - scanPrevReasoning;
|
|
2930
|
+
cacheReadTokens = extractCachedInputTokens(totalUsage) - scanPrevCachedInput;
|
|
2461
2931
|
scanPrevInput = Number(totalUsage["input_tokens"] ?? 0);
|
|
2462
2932
|
scanPrevOutput = Number(totalUsage["output_tokens"] ?? 0);
|
|
2463
2933
|
scanPrevReasoning = Number(totalUsage["reasoning_output_tokens"] ?? 0);
|
|
2934
|
+
scanPrevCachedInput = extractCachedInputTokens(totalUsage);
|
|
2464
2935
|
}
|
|
2465
2936
|
const totalInput = Math.max(0, inputTokens);
|
|
2937
|
+
const totalCacheRead = Math.max(0, cacheReadTokens);
|
|
2466
2938
|
totalInputTokens += totalInput;
|
|
2467
2939
|
totalOutputTokens += outputTokens + reasoningTokens;
|
|
2940
|
+
totalCacheReadTokens += totalCacheRead;
|
|
2941
|
+
const totalForModel = totalInput + outputTokens + reasoningTokens;
|
|
2942
|
+
if (activeModel && totalForModel > 0) {
|
|
2943
|
+
modelUsageMap[activeModel] = (modelUsageMap[activeModel] ?? 0) + totalForModel;
|
|
2944
|
+
}
|
|
2945
|
+
const cost = estimateTokenCost(activeModel, {
|
|
2946
|
+
input: totalInput,
|
|
2947
|
+
output: outputTokens,
|
|
2948
|
+
reasoning: reasoningTokens || void 0,
|
|
2949
|
+
cache_read: totalCacheRead || void 0
|
|
2950
|
+
});
|
|
2951
|
+
if (cost !== null) totalCost += cost;
|
|
2468
2952
|
}
|
|
2469
2953
|
}
|
|
2470
2954
|
}
|
|
@@ -2483,6 +2967,43 @@ var CodexAgent = class extends BaseAgent {
|
|
|
2483
2967
|
message_count: messageCount,
|
|
2484
2968
|
total_input_tokens: totalInputTokens,
|
|
2485
2969
|
total_output_tokens: totalOutputTokens,
|
|
2970
|
+
total_cache_read_tokens: totalCacheReadTokens || void 0,
|
|
2971
|
+
total_cost: totalCost,
|
|
2972
|
+
cost_source: totalCost > 0 ? "estimated" : void 0
|
|
2973
|
+
},
|
|
2974
|
+
model_usage: Object.keys(modelUsageMap).length > 0 ? modelUsageMap : void 0
|
|
2975
|
+
};
|
|
2976
|
+
}
|
|
2977
|
+
parseFastSessionHead(filePath) {
|
|
2978
|
+
const prefix = this.readFilePrefix(filePath);
|
|
2979
|
+
const lines = prefix.split("\n").filter((l) => l.trim());
|
|
2980
|
+
if (lines.length === 0) return null;
|
|
2981
|
+
const sessionId = extractSessionId(filePath);
|
|
2982
|
+
let firstRecord;
|
|
2983
|
+
try {
|
|
2984
|
+
firstRecord = JSON.parse(lines[0]);
|
|
2985
|
+
} catch {
|
|
2986
|
+
return null;
|
|
2987
|
+
}
|
|
2988
|
+
const payload = firstRecord["payload"] ?? {};
|
|
2989
|
+
const stat = statSync4(filePath);
|
|
2990
|
+
const createdAt = parseTimestampMs2(firstRecord) || parseTimestampMs2(payload) || stat.mtimeMs;
|
|
2991
|
+
const indexTitle = this.getTitleForSession(sessionId);
|
|
2992
|
+
const messageTitle = this.extractTitleFromLines(lines);
|
|
2993
|
+
const directory = payload["cwd"] ? String(payload["cwd"]) : "";
|
|
2994
|
+
const directoryTitle = basenameTitle(directory || null);
|
|
2995
|
+
const title = resolveSessionTitle(indexTitle, messageTitle, directoryTitle);
|
|
2996
|
+
return {
|
|
2997
|
+
id: sessionId,
|
|
2998
|
+
slug: `codex/${sessionId}`,
|
|
2999
|
+
title,
|
|
3000
|
+
directory,
|
|
3001
|
+
time_created: createdAt,
|
|
3002
|
+
time_updated: stat.mtimeMs,
|
|
3003
|
+
stats: {
|
|
3004
|
+
message_count: 0,
|
|
3005
|
+
total_input_tokens: 0,
|
|
3006
|
+
total_output_tokens: 0,
|
|
2486
3007
|
total_cost: 0
|
|
2487
3008
|
}
|
|
2488
3009
|
};
|
|
@@ -3016,7 +3537,7 @@ var CursorAgent = class extends BaseAgent {
|
|
|
3016
3537
|
if (!isSqliteAvailable()) return null;
|
|
3017
3538
|
const dataPath = getCursorDataPath();
|
|
3018
3539
|
if (!dataPath) return null;
|
|
3019
|
-
return
|
|
3540
|
+
return join7(dataPath, "globalStorage", "state.vscdb");
|
|
3020
3541
|
}
|
|
3021
3542
|
/**
|
|
3022
3543
|
* Build a map of composerId → workspace folder path by reading
|
|
@@ -3026,8 +3547,8 @@ var CursorAgent = class extends BaseAgent {
|
|
|
3026
3547
|
const map = /* @__PURE__ */ new Map();
|
|
3027
3548
|
const dataPath = getCursorDataPath();
|
|
3028
3549
|
if (!dataPath) return map;
|
|
3029
|
-
const wsStoragePath =
|
|
3030
|
-
if (!
|
|
3550
|
+
const wsStoragePath = join7(dataPath, "workspaceStorage");
|
|
3551
|
+
if (!existsSync7(wsStoragePath)) return map;
|
|
3031
3552
|
let entryNames;
|
|
3032
3553
|
try {
|
|
3033
3554
|
entryNames = readdirSync4(wsStoragePath);
|
|
@@ -3035,25 +3556,25 @@ var CursorAgent = class extends BaseAgent {
|
|
|
3035
3556
|
return map;
|
|
3036
3557
|
}
|
|
3037
3558
|
for (const name of entryNames) {
|
|
3038
|
-
const wsDir =
|
|
3559
|
+
const wsDir = join7(wsStoragePath, name);
|
|
3039
3560
|
try {
|
|
3040
3561
|
if (!statSync5(wsDir).isDirectory()) continue;
|
|
3041
3562
|
} catch {
|
|
3042
3563
|
continue;
|
|
3043
3564
|
}
|
|
3044
|
-
const wsJsonPath =
|
|
3045
|
-
if (!
|
|
3565
|
+
const wsJsonPath = join7(wsDir, "workspace.json");
|
|
3566
|
+
if (!existsSync7(wsJsonPath)) continue;
|
|
3046
3567
|
let workspacePath;
|
|
3047
3568
|
try {
|
|
3048
|
-
const data = JSON.parse(
|
|
3569
|
+
const data = JSON.parse(readFileSync6(wsJsonPath, "utf-8"));
|
|
3049
3570
|
const uri = data.folder ?? data.workspace ?? "";
|
|
3050
3571
|
if (!uri) continue;
|
|
3051
3572
|
workspacePath = normalize(decodeURIComponent(uri.replace(/^file:\/\//, "")));
|
|
3052
3573
|
} catch {
|
|
3053
3574
|
continue;
|
|
3054
3575
|
}
|
|
3055
|
-
const wsDbPath =
|
|
3056
|
-
if (!
|
|
3576
|
+
const wsDbPath = join7(wsDir, "state.vscdb");
|
|
3577
|
+
if (!existsSync7(wsDbPath)) continue;
|
|
3057
3578
|
const wsDb = openDbReadOnly(wsDbPath);
|
|
3058
3579
|
if (!wsDb) continue;
|
|
3059
3580
|
try {
|
|
@@ -3081,9 +3602,9 @@ var CursorAgent = class extends BaseAgent {
|
|
|
3081
3602
|
}
|
|
3082
3603
|
isAvailable() {
|
|
3083
3604
|
this.dbPath = this.findDbPath();
|
|
3084
|
-
return this.dbPath !== null &&
|
|
3605
|
+
return this.dbPath !== null && existsSync7(this.dbPath);
|
|
3085
3606
|
}
|
|
3086
|
-
scan() {
|
|
3607
|
+
scan(options) {
|
|
3087
3608
|
if (!this.dbPath) return [];
|
|
3088
3609
|
const scanMarker = perf.start("cursor:scan");
|
|
3089
3610
|
const dbMarker = perf.start("openDatabase");
|
|
@@ -3101,25 +3622,57 @@ var CursorAgent = class extends BaseAgent {
|
|
|
3101
3622
|
const composer = JSON.parse(row.value);
|
|
3102
3623
|
if (!composer.id && !composer.composerId) continue;
|
|
3103
3624
|
const composerId = composer.id || composer.composerId || "";
|
|
3625
|
+
const createdAt = composer.createdAt ?? 0;
|
|
3626
|
+
const updatedAt = composer.updatedAt ?? composer.lastUpdatedAt ?? composer.lastSendTime ?? createdAt;
|
|
3627
|
+
if (!matchesScanWindow(updatedAt, options)) continue;
|
|
3628
|
+
const title = this.extractTitle(composer);
|
|
3629
|
+
const fastMessageCount = composer.chatMessages?.length ?? 0;
|
|
3630
|
+
const hasSubagents = Array.isArray(composer.subagentInfos) && composer.subagentInfos.length > 0;
|
|
3631
|
+
if (options?.fast) {
|
|
3632
|
+
const directory2 = workspacePathMap.get(composerId) ?? "";
|
|
3633
|
+
const totalCost2 = estimateTokenCost(composer.modelConfig?.modelName ?? composer.model, {
|
|
3634
|
+
input: composer.inputTokenCount ?? 0,
|
|
3635
|
+
output: composer.outputTokenCount ?? 0
|
|
3636
|
+
}) ?? 0;
|
|
3637
|
+
heads.push({
|
|
3638
|
+
id: composerId,
|
|
3639
|
+
slug: `cursor/${composerId}`,
|
|
3640
|
+
title,
|
|
3641
|
+
directory: directory2,
|
|
3642
|
+
time_created: createdAt,
|
|
3643
|
+
time_updated: updatedAt || void 0,
|
|
3644
|
+
stats: {
|
|
3645
|
+
message_count: fastMessageCount,
|
|
3646
|
+
total_input_tokens: composer.inputTokenCount ?? 0,
|
|
3647
|
+
total_output_tokens: composer.outputTokenCount ?? 0,
|
|
3648
|
+
total_cost: totalCost2,
|
|
3649
|
+
cost_source: totalCost2 > 0 ? "estimated" : void 0
|
|
3650
|
+
}
|
|
3651
|
+
});
|
|
3652
|
+
this.composerCache.set(composerId, composer);
|
|
3653
|
+
this.sessionMetaMap.set(composerId, {
|
|
3654
|
+
id: composerId,
|
|
3655
|
+
sourcePath: this.dbPath || ""
|
|
3656
|
+
});
|
|
3657
|
+
continue;
|
|
3658
|
+
}
|
|
3104
3659
|
const requestId = this.extractRequestIdFromBubbles(db, composerId);
|
|
3105
3660
|
const sessionId = requestId || composerId;
|
|
3106
|
-
const title = this.extractTitle(composer);
|
|
3107
|
-
const createdAt = composer.createdAt ?? 0;
|
|
3108
|
-
const updatedAt = composer.updatedAt ?? createdAt;
|
|
3109
3661
|
const messages = this.loadMessagesFromBubbles(
|
|
3110
3662
|
db,
|
|
3111
3663
|
composerId,
|
|
3112
3664
|
sessionId,
|
|
3113
3665
|
composer.modelConfig?.modelName ?? composer.model ?? null
|
|
3114
3666
|
);
|
|
3115
|
-
const hasSubagents = Array.isArray(composer.subagentInfos) && composer.subagentInfos.length > 0;
|
|
3116
3667
|
if (messages.length === 0 && !hasSubagents) {
|
|
3117
3668
|
continue;
|
|
3118
3669
|
}
|
|
3119
3670
|
const messageCount = messages.length;
|
|
3120
3671
|
const directory = workspacePathMap.get(composerId) ?? "";
|
|
3121
3672
|
const modelUsageMap = {};
|
|
3673
|
+
let totalCost = 0;
|
|
3122
3674
|
for (const msg of messages) {
|
|
3675
|
+
totalCost += msg.cost ?? 0;
|
|
3123
3676
|
if (msg.model) {
|
|
3124
3677
|
const msgTokens = (msg.tokens?.input ?? 0) + (msg.tokens?.output ?? 0);
|
|
3125
3678
|
if (msgTokens > 0) {
|
|
@@ -3139,7 +3692,8 @@ var CursorAgent = class extends BaseAgent {
|
|
|
3139
3692
|
message_count: messageCount,
|
|
3140
3693
|
total_input_tokens: composer.inputTokenCount ?? 0,
|
|
3141
3694
|
total_output_tokens: composer.outputTokenCount ?? 0,
|
|
3142
|
-
total_cost:
|
|
3695
|
+
total_cost: totalCost,
|
|
3696
|
+
cost_source: totalCost > 0 ? "estimated" : void 0
|
|
3143
3697
|
},
|
|
3144
3698
|
model_usage: hasModelUsage ? modelUsageMap : void 0
|
|
3145
3699
|
});
|
|
@@ -3181,7 +3735,7 @@ var CursorAgent = class extends BaseAgent {
|
|
|
3181
3735
|
if (!this.dbPath) {
|
|
3182
3736
|
this.dbPath = this.findDbPath();
|
|
3183
3737
|
}
|
|
3184
|
-
if (!this.dbPath || !
|
|
3738
|
+
if (!this.dbPath || !existsSync7(this.dbPath)) {
|
|
3185
3739
|
return { hasChanges: false, timestamp: Date.now() };
|
|
3186
3740
|
}
|
|
3187
3741
|
try {
|
|
@@ -3242,12 +3796,20 @@ var CursorAgent = class extends BaseAgent {
|
|
|
3242
3796
|
);
|
|
3243
3797
|
let totalInputTokens = 0;
|
|
3244
3798
|
let totalOutputTokens = 0;
|
|
3799
|
+
let totalCost = 0;
|
|
3245
3800
|
for (const msg of messages) {
|
|
3246
3801
|
totalInputTokens += msg.tokens?.input ?? 0;
|
|
3247
3802
|
totalOutputTokens += msg.tokens?.output ?? 0;
|
|
3803
|
+
totalCost += msg.cost ?? 0;
|
|
3248
3804
|
}
|
|
3249
3805
|
if (totalInputTokens === 0) totalInputTokens = composer.inputTokenCount ?? 0;
|
|
3250
3806
|
if (totalOutputTokens === 0) totalOutputTokens = composer.outputTokenCount ?? 0;
|
|
3807
|
+
if (totalCost === 0) {
|
|
3808
|
+
totalCost = estimateTokenCost(composer.modelConfig?.modelName ?? composer.model, {
|
|
3809
|
+
input: totalInputTokens,
|
|
3810
|
+
output: totalOutputTokens
|
|
3811
|
+
}) ?? 0;
|
|
3812
|
+
}
|
|
3251
3813
|
this.appendSubagentMessages(db, composer, messages);
|
|
3252
3814
|
const cachedDir = this.composerCache.get(`__dir__${composerId}`);
|
|
3253
3815
|
const directory = cachedDir?.directory ?? this.buildWorkspacePathMap().get(composerId) ?? "";
|
|
@@ -3262,7 +3824,8 @@ var CursorAgent = class extends BaseAgent {
|
|
|
3262
3824
|
message_count: messages.length,
|
|
3263
3825
|
total_input_tokens: totalInputTokens,
|
|
3264
3826
|
total_output_tokens: totalOutputTokens,
|
|
3265
|
-
total_cost:
|
|
3827
|
+
total_cost: totalCost,
|
|
3828
|
+
cost_source: totalCost > 0 ? "estimated" : void 0
|
|
3266
3829
|
},
|
|
3267
3830
|
messages
|
|
3268
3831
|
};
|
|
@@ -3383,6 +3946,9 @@ var CursorAgent = class extends BaseAgent {
|
|
|
3383
3946
|
}
|
|
3384
3947
|
}
|
|
3385
3948
|
if (parts.length === 0) continue;
|
|
3949
|
+
const modelName = bubble.modelInfo?.modelName ?? activeModelName;
|
|
3950
|
+
const tokens = { input: inputTokens, output: outputTokens };
|
|
3951
|
+
const cost = estimateTokenCost(modelName, tokens);
|
|
3386
3952
|
messages.push({
|
|
3387
3953
|
id: `cursor-${composerId}-${bubbleId}`,
|
|
3388
3954
|
role,
|
|
@@ -3390,10 +3956,11 @@ var CursorAgent = class extends BaseAgent {
|
|
|
3390
3956
|
time_created: timestampMs,
|
|
3391
3957
|
time_completed: null,
|
|
3392
3958
|
mode: role === "assistant" && parts.some((p) => p.type === "tool") ? "tool" : null,
|
|
3393
|
-
model:
|
|
3959
|
+
model: modelName,
|
|
3394
3960
|
provider: null,
|
|
3395
|
-
tokens
|
|
3396
|
-
cost: 0,
|
|
3961
|
+
tokens,
|
|
3962
|
+
cost: cost ?? 0,
|
|
3963
|
+
cost_source: cost !== null ? "estimated" : void 0,
|
|
3397
3964
|
parts
|
|
3398
3965
|
});
|
|
3399
3966
|
messageIndex++;
|
|
@@ -3549,24 +4116,309 @@ registerAgent({
|
|
|
3549
4116
|
icon: "/icon/agent/cursor.svg",
|
|
3550
4117
|
create: () => new CursorAgent()
|
|
3551
4118
|
});
|
|
3552
|
-
|
|
4119
|
+
function fallbackDisplayName(input) {
|
|
4120
|
+
if (input === "/") return "(root)";
|
|
4121
|
+
const trimmed = input.replace(/[/\\]+$/, "");
|
|
4122
|
+
const parts = trimmed.split(/[/\\]+/).filter(Boolean);
|
|
4123
|
+
return parts.at(-1) ?? trimmed;
|
|
4124
|
+
}
|
|
4125
|
+
var realFs = {
|
|
4126
|
+
exists(path2) {
|
|
4127
|
+
return existsSync8(path2);
|
|
4128
|
+
},
|
|
4129
|
+
readText(path2) {
|
|
4130
|
+
try {
|
|
4131
|
+
return readFileSync7(path2, "utf8");
|
|
4132
|
+
} catch {
|
|
4133
|
+
return null;
|
|
4134
|
+
}
|
|
4135
|
+
},
|
|
4136
|
+
spawn(cmd, args, opts) {
|
|
4137
|
+
const result = spawnSync(cmd, args, { cwd: opts.cwd, encoding: "utf8", timeout: 1e3 });
|
|
4138
|
+
return {
|
|
4139
|
+
stdout: typeof result.stdout === "string" ? result.stdout : "",
|
|
4140
|
+
exitCode: result.status ?? 1
|
|
4141
|
+
};
|
|
4142
|
+
}
|
|
4143
|
+
};
|
|
4144
|
+
function getAgentName(session) {
|
|
4145
|
+
return session.slug.split("/")[0]?.toLowerCase() || "unknown";
|
|
4146
|
+
}
|
|
4147
|
+
function buildProjectGroups(sessions) {
|
|
4148
|
+
const groups = /* @__PURE__ */ new Map();
|
|
4149
|
+
for (const session of sessions) {
|
|
4150
|
+
const identity = session.project_identity;
|
|
4151
|
+
if (!identity) continue;
|
|
4152
|
+
const activity = session.time_updated ?? session.time_created;
|
|
4153
|
+
const groupKey = `${identity.kind}:${identity.key}`;
|
|
4154
|
+
const current = groups.get(groupKey);
|
|
4155
|
+
if (current) {
|
|
4156
|
+
current.sources.add(getAgentName(session));
|
|
4157
|
+
current.sessionCount += 1;
|
|
4158
|
+
current.lastActivity = Math.max(current.lastActivity, activity);
|
|
4159
|
+
} else {
|
|
4160
|
+
groups.set(groupKey, {
|
|
4161
|
+
identity,
|
|
4162
|
+
sources: /* @__PURE__ */ new Set([getAgentName(session)]),
|
|
4163
|
+
sessionCount: 1,
|
|
4164
|
+
lastActivity: activity
|
|
4165
|
+
});
|
|
4166
|
+
}
|
|
4167
|
+
}
|
|
4168
|
+
return [...groups.values()].map((group) => ({
|
|
4169
|
+
identityKind: group.identity.kind,
|
|
4170
|
+
identityKey: group.identity.key,
|
|
4171
|
+
displayName: group.identity.displayName,
|
|
4172
|
+
sources: [...group.sources].sort(),
|
|
4173
|
+
sessionCount: group.sessionCount,
|
|
4174
|
+
lastActivity: group.lastActivity || null
|
|
4175
|
+
})).sort((a, b) => {
|
|
4176
|
+
if (a.identityKind === "loose" && b.identityKind !== "loose") return 1;
|
|
4177
|
+
if (b.identityKind === "loose" && a.identityKind !== "loose") return -1;
|
|
4178
|
+
return (b.lastActivity ?? 0) - (a.lastActivity ?? 0);
|
|
4179
|
+
});
|
|
4180
|
+
}
|
|
4181
|
+
var MANIFESTS = [
|
|
4182
|
+
"package.json",
|
|
4183
|
+
"Cargo.toml",
|
|
4184
|
+
"pyproject.toml",
|
|
4185
|
+
"go.mod",
|
|
4186
|
+
"Gemfile",
|
|
4187
|
+
"pom.xml",
|
|
4188
|
+
"build.gradle"
|
|
4189
|
+
];
|
|
4190
|
+
var PARSEABLE_MANIFESTS = ["package.json", "Cargo.toml", "pyproject.toml"];
|
|
4191
|
+
var LOOSE_DIRS = /* @__PURE__ */ new Set(["/tmp", "/private/tmp"]);
|
|
4192
|
+
var LOOSE_HOME_DIRS = ["Desktop", "Downloads", "Documents"];
|
|
4193
|
+
function normalizeGitRemote(url) {
|
|
4194
|
+
if (!url) return null;
|
|
4195
|
+
let value = url.trim().replace(/\.git$/, "");
|
|
4196
|
+
const sshMatch = value.match(/^[^@]+@([^:]+):(.+)$/);
|
|
4197
|
+
if (sshMatch) value = `${sshMatch[1]}/${sshMatch[2]}`;
|
|
4198
|
+
value = value.replace(/^[a-z]+:\/\/(?:[^@/]*@)?/i, "");
|
|
4199
|
+
if (!value.includes("/")) return null;
|
|
4200
|
+
return value.toLowerCase();
|
|
4201
|
+
}
|
|
4202
|
+
function computeIdentity(cwd, fs) {
|
|
4203
|
+
if (!cwd) return loose();
|
|
4204
|
+
const pathOps = getPathOps(cwd);
|
|
4205
|
+
const absoluteCwd = pathOps.resolve(cwd);
|
|
4206
|
+
const homeDir = homedir3();
|
|
4207
|
+
const homePathOps = getPathOps(homeDir);
|
|
4208
|
+
const home = homePathOps === pathOps ? pathOps.resolve(homeDir) : homeDir;
|
|
4209
|
+
if (absoluteCwd === home || LOOSE_DIRS.has(absoluteCwd)) return loose();
|
|
4210
|
+
if (homePathOps === pathOps && LOOSE_HOME_DIRS.some((dir) => absoluteCwd === pathOps.join(home, dir))) {
|
|
4211
|
+
return loose();
|
|
4212
|
+
}
|
|
4213
|
+
const gitRoot = findGitRoot(absoluteCwd, fs, pathOps);
|
|
4214
|
+
if (gitRoot) {
|
|
4215
|
+
const remote = fs.spawn("git", ["config", "--get", "remote.origin.url"], { cwd: gitRoot });
|
|
4216
|
+
if (remote.exitCode === 0) {
|
|
4217
|
+
const normalized = normalizeGitRemote(remote.stdout.trim());
|
|
4218
|
+
if (normalized) {
|
|
4219
|
+
return {
|
|
4220
|
+
kind: "git_remote",
|
|
4221
|
+
key: normalized,
|
|
4222
|
+
displayName: deriveDisplayName({ kind: "git_remote", key: normalized, gitRoot, fs })
|
|
4223
|
+
};
|
|
4224
|
+
}
|
|
4225
|
+
}
|
|
4226
|
+
const common = fs.spawn("git", ["rev-parse", "--git-common-dir"], { cwd: gitRoot });
|
|
4227
|
+
if (common.exitCode === 0) {
|
|
4228
|
+
const raw = common.stdout.trim();
|
|
4229
|
+
if (raw) {
|
|
4230
|
+
const key = pathOps.isAbsolute(raw) ? raw : pathOps.resolve(gitRoot, raw);
|
|
4231
|
+
return {
|
|
4232
|
+
kind: "git_common_dir",
|
|
4233
|
+
key,
|
|
4234
|
+
displayName: deriveDisplayName({ kind: "git_common_dir", key, gitRoot, fs })
|
|
4235
|
+
};
|
|
4236
|
+
}
|
|
4237
|
+
}
|
|
4238
|
+
}
|
|
4239
|
+
const manifestDir = findManifestDir(absoluteCwd, fs, pathOps);
|
|
4240
|
+
if (manifestDir) {
|
|
4241
|
+
return {
|
|
4242
|
+
kind: "manifest_path",
|
|
4243
|
+
key: manifestDir,
|
|
4244
|
+
displayName: deriveDisplayName({ kind: "manifest_path", key: manifestDir, fs })
|
|
4245
|
+
};
|
|
4246
|
+
}
|
|
4247
|
+
return {
|
|
4248
|
+
kind: "path",
|
|
4249
|
+
key: absoluteCwd,
|
|
4250
|
+
displayName: fallbackDisplayName(absoluteCwd)
|
|
4251
|
+
};
|
|
4252
|
+
}
|
|
4253
|
+
function loose() {
|
|
4254
|
+
return { kind: "loose", key: "loose", displayName: "Loose" };
|
|
4255
|
+
}
|
|
4256
|
+
function getPathOps(input) {
|
|
4257
|
+
if (/^[a-zA-Z]:[\\/]/.test(input) || input.startsWith("\\\\") || input.includes("\\")) {
|
|
4258
|
+
return path.win32;
|
|
4259
|
+
}
|
|
4260
|
+
if (input.startsWith("/")) return path.posix;
|
|
4261
|
+
return path;
|
|
4262
|
+
}
|
|
4263
|
+
function findGitRoot(start, fs, pathOps) {
|
|
4264
|
+
let current = start;
|
|
4265
|
+
while (current) {
|
|
4266
|
+
if (fs.exists(pathOps.join(current, ".git"))) return current;
|
|
4267
|
+
const parent = pathOps.dirname(current);
|
|
4268
|
+
if (parent === current) break;
|
|
4269
|
+
current = parent;
|
|
4270
|
+
}
|
|
4271
|
+
return null;
|
|
4272
|
+
}
|
|
4273
|
+
function findManifestDir(start, fs, pathOps) {
|
|
4274
|
+
let current = start;
|
|
4275
|
+
while (current) {
|
|
4276
|
+
for (const manifest of MANIFESTS) {
|
|
4277
|
+
if (fs.exists(pathOps.join(current, manifest))) return current;
|
|
4278
|
+
}
|
|
4279
|
+
const parent = pathOps.dirname(current);
|
|
4280
|
+
if (parent === current) break;
|
|
4281
|
+
current = parent;
|
|
4282
|
+
}
|
|
4283
|
+
return null;
|
|
4284
|
+
}
|
|
4285
|
+
function deriveDisplayName(input) {
|
|
4286
|
+
const pathOps = getPathOps(input.gitRoot ?? input.key);
|
|
4287
|
+
const dir = input.gitRoot ?? (input.kind === "manifest_path" ? input.key : null);
|
|
4288
|
+
if (dir) {
|
|
4289
|
+
for (const manifest of PARSEABLE_MANIFESTS) {
|
|
4290
|
+
const manifestPath = pathOps.join(dir, manifest);
|
|
4291
|
+
if (input.fs.exists(manifestPath)) {
|
|
4292
|
+
const name = parseManifestName(manifest, input.fs.readText(manifestPath) ?? "");
|
|
4293
|
+
if (name) return name;
|
|
4294
|
+
}
|
|
4295
|
+
}
|
|
4296
|
+
}
|
|
4297
|
+
if (input.kind === "git_remote") {
|
|
4298
|
+
return input.key.split("/").at(-1) || input.key;
|
|
4299
|
+
}
|
|
4300
|
+
if (input.gitRoot) return fallbackDisplayName(input.gitRoot);
|
|
4301
|
+
return fallbackDisplayName(input.key);
|
|
4302
|
+
}
|
|
4303
|
+
function parseManifestName(file, text) {
|
|
4304
|
+
if (!text) return null;
|
|
4305
|
+
if (file === "package.json" || file === "Cargo.toml" || file === "pyproject.toml") {
|
|
4306
|
+
const match = text.match(/"name"\s*:\s*"([^"]+)"/) || text.match(/^\s*name\s*=\s*"([^"]+)"/m);
|
|
4307
|
+
if (match?.[1]) return match[1];
|
|
4308
|
+
}
|
|
4309
|
+
return null;
|
|
4310
|
+
}
|
|
4311
|
+
var TAG_ORDER = [
|
|
4312
|
+
"bugfix",
|
|
4313
|
+
"refactoring",
|
|
4314
|
+
"feature-dev",
|
|
4315
|
+
"testing",
|
|
4316
|
+
"docs",
|
|
4317
|
+
"git-ops",
|
|
4318
|
+
"build-deploy",
|
|
4319
|
+
"exploration",
|
|
4320
|
+
"planning"
|
|
4321
|
+
];
|
|
4322
|
+
var USER_RULES = [
|
|
4323
|
+
["bugfix", /\b(fix|bug|error|crash|exception|fail(?:ed|ure)?)\b|修复|错误|报错|崩溃|异常/i],
|
|
4324
|
+
["refactoring", /\b(refactor|rename|simplify|clean up|cleanup)\b|重构|重命名|简化|清理/i],
|
|
4325
|
+
["feature-dev", /\b(add|create|implement|new|support|build)\b|新增|创建|实现|增加|开发|支持/i],
|
|
4326
|
+
["docs", /\b(document|documentation|readme|docs?)\b|文档|说明/i]
|
|
4327
|
+
];
|
|
4328
|
+
var TESTING_COMMAND_RE = /\b(pytest|vitest|jest|mocha|pnpm\s+test|npm\s+test|yarn\s+test)\b/i;
|
|
4329
|
+
var GIT_COMMAND_RE = /\bgit\s+(push|commit|merge|branch|checkout|switch|rebase|tag)\b/i;
|
|
4330
|
+
var BUILD_DEPLOY_COMMAND_RE = /\b((npm|pnpm|yarn|bun)\s+(run\s+)?build|docker|pm2|deploy|vercel|netlify)\b/i;
|
|
4331
|
+
var DOC_PATH_RE = /\.(md|mdx|txt|rst|adoc)$/i;
|
|
4332
|
+
var READ_TOOL_RE = /\b(read|grep|glob|websearch|web_search|search|find|rg)\b/i;
|
|
4333
|
+
var EDIT_TOOL_RE = /\b(edit|write|apply_patch|patch|multiedit|notebookedit)\b/i;
|
|
4334
|
+
var PLAN_TOOL_RE = /\b(enterplanmode|taskcreate|update_plan|plan)\b/i;
|
|
4335
|
+
function getSmartTagSourceTimestamp(session) {
|
|
4336
|
+
return session.time_updated ?? session.time_created;
|
|
4337
|
+
}
|
|
4338
|
+
function classifySessionTags(session) {
|
|
4339
|
+
const tags = /* @__PURE__ */ new Set();
|
|
4340
|
+
let readToolCount = 0;
|
|
4341
|
+
let editToolCount = 0;
|
|
4342
|
+
for (const message of session.messages) {
|
|
4343
|
+
if (message.role === "user") {
|
|
4344
|
+
const text = message.parts.map(partText).join("\n");
|
|
4345
|
+
for (const [tag, pattern] of USER_RULES) {
|
|
4346
|
+
if (pattern.test(text)) tags.add(tag);
|
|
4347
|
+
}
|
|
4348
|
+
}
|
|
4349
|
+
for (const part of message.parts) {
|
|
4350
|
+
if (part.type === "plan") tags.add("planning");
|
|
4351
|
+
if (part.type !== "tool") continue;
|
|
4352
|
+
const toolName = `${part.tool ?? ""} ${part.title ?? ""}`;
|
|
4353
|
+
const toolPayload = stringifyToolPayload(part);
|
|
4354
|
+
if (PLAN_TOOL_RE.test(toolName)) tags.add("planning");
|
|
4355
|
+
if (READ_TOOL_RE.test(toolName)) readToolCount += 1;
|
|
4356
|
+
if (EDIT_TOOL_RE.test(toolName)) editToolCount += 1;
|
|
4357
|
+
if (TESTING_COMMAND_RE.test(toolPayload)) tags.add("testing");
|
|
4358
|
+
if (GIT_COMMAND_RE.test(toolPayload)) tags.add("git-ops");
|
|
4359
|
+
if (BUILD_DEPLOY_COMMAND_RE.test(toolPayload)) tags.add("build-deploy");
|
|
4360
|
+
if (hasEditedDocPath(part.state?.arguments) || hasEditedDocPath(part.state?.input)) {
|
|
4361
|
+
tags.add("docs");
|
|
4362
|
+
}
|
|
4363
|
+
}
|
|
4364
|
+
}
|
|
4365
|
+
if (readToolCount >= 3 && editToolCount <= 1) {
|
|
4366
|
+
tags.add("exploration");
|
|
4367
|
+
}
|
|
4368
|
+
return TAG_ORDER.filter((tag) => tags.has(tag));
|
|
4369
|
+
}
|
|
4370
|
+
function partText(part) {
|
|
4371
|
+
return typeof part.text === "string" ? part.text : "";
|
|
4372
|
+
}
|
|
4373
|
+
function stringifyToolPayload(part) {
|
|
4374
|
+
return [
|
|
4375
|
+
part.tool,
|
|
4376
|
+
part.title,
|
|
4377
|
+
valueToText(part.input),
|
|
4378
|
+
valueToText(part.output),
|
|
4379
|
+
valueToText(part.state)
|
|
4380
|
+
].filter(Boolean).join("\n");
|
|
4381
|
+
}
|
|
4382
|
+
function valueToText(value) {
|
|
4383
|
+
if (value == null) return "";
|
|
4384
|
+
if (typeof value === "string") return value;
|
|
4385
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
4386
|
+
try {
|
|
4387
|
+
return JSON.stringify(value);
|
|
4388
|
+
} catch {
|
|
4389
|
+
return "";
|
|
4390
|
+
}
|
|
4391
|
+
}
|
|
4392
|
+
function hasEditedDocPath(value) {
|
|
4393
|
+
if (value == null) return false;
|
|
4394
|
+
if (typeof value === "string") return DOC_PATH_RE.test(value);
|
|
4395
|
+
if (Array.isArray(value)) return value.some(hasEditedDocPath);
|
|
4396
|
+
if (typeof value !== "object") return false;
|
|
4397
|
+
const record = value;
|
|
4398
|
+
for (const key of ["path", "file", "filePath", "file_path", "targetPath", "target_path"]) {
|
|
4399
|
+
const raw = record[key];
|
|
4400
|
+
if (typeof raw === "string" && DOC_PATH_RE.test(raw)) return true;
|
|
4401
|
+
}
|
|
4402
|
+
return Object.values(record).some(hasEditedDocPath);
|
|
4403
|
+
}
|
|
4404
|
+
var CACHE_VERSION = 6;
|
|
3553
4405
|
var CACHE_TTL = 7 * 24 * 60 * 60 * 1e3;
|
|
3554
4406
|
var CACHE_FILENAME = "codesesh.db";
|
|
3555
4407
|
var LEGACY_CACHE_FILENAME = "scan-cache.json";
|
|
3556
|
-
function
|
|
3557
|
-
return
|
|
4408
|
+
function getCacheDir2() {
|
|
4409
|
+
return join8(homedir4(), ".cache", "codesesh");
|
|
3558
4410
|
}
|
|
3559
|
-
function
|
|
3560
|
-
return
|
|
4411
|
+
function getCachePath2() {
|
|
4412
|
+
return join8(getCacheDir2(), CACHE_FILENAME);
|
|
3561
4413
|
}
|
|
3562
4414
|
function getLegacyCachePath() {
|
|
3563
|
-
return
|
|
4415
|
+
return join8(getCacheDir2(), LEGACY_CACHE_FILENAME);
|
|
3564
4416
|
}
|
|
3565
4417
|
function hasCacheStorage() {
|
|
3566
|
-
return
|
|
4418
|
+
return existsSync9(getCachePath2());
|
|
3567
4419
|
}
|
|
3568
4420
|
function withCacheDb(fn) {
|
|
3569
|
-
const db = openDb(
|
|
4421
|
+
const db = openDb(getCachePath2());
|
|
3570
4422
|
if (!db) return null;
|
|
3571
4423
|
try {
|
|
3572
4424
|
ensureSchema(db);
|
|
@@ -3604,6 +4456,9 @@ function ensureSchema(db) {
|
|
|
3604
4456
|
slug TEXT NOT NULL,
|
|
3605
4457
|
title TEXT NOT NULL,
|
|
3606
4458
|
directory TEXT NOT NULL,
|
|
4459
|
+
project_identity_kind TEXT NOT NULL DEFAULT 'path',
|
|
4460
|
+
project_identity_key TEXT NOT NULL DEFAULT '',
|
|
4461
|
+
project_display_name TEXT NOT NULL DEFAULT '',
|
|
3607
4462
|
time_created INTEGER NOT NULL,
|
|
3608
4463
|
time_updated INTEGER,
|
|
3609
4464
|
activity_time INTEGER NOT NULL,
|
|
@@ -3613,6 +4468,31 @@ function ensureSchema(db) {
|
|
|
3613
4468
|
UNIQUE(agent_name, session_id)
|
|
3614
4469
|
);
|
|
3615
4470
|
|
|
4471
|
+
CREATE TABLE IF NOT EXISTS project_sessions (
|
|
4472
|
+
agent_name TEXT NOT NULL,
|
|
4473
|
+
session_id TEXT NOT NULL,
|
|
4474
|
+
identity_kind TEXT NOT NULL,
|
|
4475
|
+
identity_key TEXT NOT NULL,
|
|
4476
|
+
display_name TEXT NOT NULL,
|
|
4477
|
+
directory TEXT NOT NULL,
|
|
4478
|
+
activity_time INTEGER NOT NULL,
|
|
4479
|
+
PRIMARY KEY (agent_name, session_id)
|
|
4480
|
+
);
|
|
4481
|
+
|
|
4482
|
+
CREATE INDEX IF NOT EXISTS idx_project_sessions_identity
|
|
4483
|
+
ON project_sessions(identity_kind, identity_key);
|
|
4484
|
+
|
|
4485
|
+
CREATE VIEW IF NOT EXISTS project_groups_v AS
|
|
4486
|
+
SELECT
|
|
4487
|
+
identity_kind,
|
|
4488
|
+
identity_key,
|
|
4489
|
+
MIN(display_name) AS display_name,
|
|
4490
|
+
GROUP_CONCAT(DISTINCT agent_name) AS sources_csv,
|
|
4491
|
+
COUNT(*) AS session_count,
|
|
4492
|
+
MAX(activity_time) AS last_activity
|
|
4493
|
+
FROM project_sessions
|
|
4494
|
+
GROUP BY identity_kind, identity_key;
|
|
4495
|
+
|
|
3616
4496
|
CREATE VIRTUAL TABLE IF NOT EXISTS session_documents_fts USING fts5(
|
|
3617
4497
|
title,
|
|
3618
4498
|
content_text,
|
|
@@ -3637,6 +4517,18 @@ function ensureSchema(db) {
|
|
|
3637
4517
|
VALUES (new.id, new.title, new.content_text);
|
|
3638
4518
|
END;
|
|
3639
4519
|
`);
|
|
4520
|
+
const sessionDocumentColumns = new Set(
|
|
4521
|
+
db.prepare("PRAGMA table_info(session_documents)").all().map(
|
|
4522
|
+
(row) => String(row.name)
|
|
4523
|
+
)
|
|
4524
|
+
);
|
|
4525
|
+
if (!sessionDocumentColumns.has("project_identity_kind")) {
|
|
4526
|
+
db.exec(`
|
|
4527
|
+
ALTER TABLE session_documents ADD COLUMN project_identity_kind TEXT NOT NULL DEFAULT 'path';
|
|
4528
|
+
ALTER TABLE session_documents ADD COLUMN project_identity_key TEXT NOT NULL DEFAULT '';
|
|
4529
|
+
ALTER TABLE session_documents ADD COLUMN project_display_name TEXT NOT NULL DEFAULT '';
|
|
4530
|
+
`);
|
|
4531
|
+
}
|
|
3640
4532
|
const versionRow = db.prepare("SELECT value FROM cache_meta WHERE key = 'version'").get();
|
|
3641
4533
|
const version = Number(versionRow?.value ?? 0);
|
|
3642
4534
|
if (version === CACHE_VERSION) {
|
|
@@ -3646,6 +4538,7 @@ function ensureSchema(db) {
|
|
|
3646
4538
|
DELETE FROM agent_cache;
|
|
3647
4539
|
DELETE FROM cached_sessions;
|
|
3648
4540
|
DELETE FROM session_documents;
|
|
4541
|
+
DELETE FROM project_sessions;
|
|
3649
4542
|
INSERT INTO session_documents_fts(session_documents_fts) VALUES ('rebuild');
|
|
3650
4543
|
INSERT INTO cache_meta(key, value)
|
|
3651
4544
|
VALUES ('version', '${CACHE_VERSION}')
|
|
@@ -3662,7 +4555,10 @@ function sessionContentHash(session) {
|
|
|
3662
4555
|
session.stats.message_count,
|
|
3663
4556
|
session.stats.total_input_tokens,
|
|
3664
4557
|
session.stats.total_output_tokens,
|
|
4558
|
+
session.stats.total_cache_read_tokens ?? 0,
|
|
4559
|
+
session.stats.total_cache_create_tokens ?? 0,
|
|
3665
4560
|
session.stats.total_cost,
|
|
4561
|
+
session.stats.cost_source ?? "",
|
|
3666
4562
|
session.stats.total_tokens ?? 0
|
|
3667
4563
|
]);
|
|
3668
4564
|
}
|
|
@@ -3728,7 +4624,7 @@ function buildSessionContent(session) {
|
|
|
3728
4624
|
}
|
|
3729
4625
|
function deleteLegacyCacheFile() {
|
|
3730
4626
|
const legacyPath = getLegacyCachePath();
|
|
3731
|
-
if (!
|
|
4627
|
+
if (!existsSync9(legacyPath)) {
|
|
3732
4628
|
return;
|
|
3733
4629
|
}
|
|
3734
4630
|
try {
|
|
@@ -3773,6 +4669,7 @@ function saveCachedSessions(agentName, sessions, meta = {}) {
|
|
|
3773
4669
|
withCacheDb((db) => {
|
|
3774
4670
|
const deleteAgent = db.prepare("DELETE FROM agent_cache WHERE agent_name = ?");
|
|
3775
4671
|
const deleteSessions = db.prepare("DELETE FROM cached_sessions WHERE agent_name = ?");
|
|
4672
|
+
const deleteProjectSessions = db.prepare("DELETE FROM project_sessions WHERE agent_name = ?");
|
|
3776
4673
|
const upsertAgent = db.prepare(`
|
|
3777
4674
|
INSERT INTO agent_cache(agent_name, timestamp)
|
|
3778
4675
|
VALUES (?, ?)
|
|
@@ -3782,18 +4679,40 @@ function saveCachedSessions(agentName, sessions, meta = {}) {
|
|
|
3782
4679
|
INSERT INTO cached_sessions(agent_name, session_id, session_json, meta_json)
|
|
3783
4680
|
VALUES (?, ?, ?, ?)
|
|
3784
4681
|
`);
|
|
4682
|
+
const insertProjectSession = db.prepare(`
|
|
4683
|
+
INSERT INTO project_sessions(
|
|
4684
|
+
agent_name,
|
|
4685
|
+
session_id,
|
|
4686
|
+
identity_kind,
|
|
4687
|
+
identity_key,
|
|
4688
|
+
display_name,
|
|
4689
|
+
directory,
|
|
4690
|
+
activity_time
|
|
4691
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
4692
|
+
`);
|
|
3785
4693
|
const write = db.transaction(() => {
|
|
3786
4694
|
const timestamp = Date.now();
|
|
3787
4695
|
deleteAgent.run(agentName);
|
|
3788
4696
|
deleteSessions.run(agentName);
|
|
4697
|
+
deleteProjectSessions.run(agentName);
|
|
3789
4698
|
upsertAgent.run(agentName, timestamp);
|
|
3790
4699
|
for (const session of sessions) {
|
|
4700
|
+
const identity = session.project_identity ?? computeIdentity(session.directory, realFs);
|
|
3791
4701
|
insertSession.run(
|
|
3792
4702
|
agentName,
|
|
3793
4703
|
session.id,
|
|
3794
4704
|
JSON.stringify(session),
|
|
3795
4705
|
meta[session.id] ? JSON.stringify(meta[session.id]) : null
|
|
3796
4706
|
);
|
|
4707
|
+
insertProjectSession.run(
|
|
4708
|
+
agentName,
|
|
4709
|
+
session.id,
|
|
4710
|
+
identity.kind,
|
|
4711
|
+
identity.key,
|
|
4712
|
+
identity.displayName,
|
|
4713
|
+
session.directory,
|
|
4714
|
+
session.time_updated ?? session.time_created
|
|
4715
|
+
);
|
|
3797
4716
|
}
|
|
3798
4717
|
});
|
|
3799
4718
|
write();
|
|
@@ -3809,14 +4728,15 @@ function clearCache() {
|
|
|
3809
4728
|
db.exec(`
|
|
3810
4729
|
DELETE FROM agent_cache;
|
|
3811
4730
|
DELETE FROM cached_sessions;
|
|
4731
|
+
DELETE FROM project_sessions;
|
|
3812
4732
|
`);
|
|
3813
4733
|
});
|
|
3814
4734
|
deleteLegacyCacheFile();
|
|
3815
|
-
const cachePath =
|
|
4735
|
+
const cachePath = getCachePath2();
|
|
3816
4736
|
const walPath = `${cachePath}-wal`;
|
|
3817
4737
|
const shmPath = `${cachePath}-shm`;
|
|
3818
4738
|
for (const filePath of [walPath, shmPath]) {
|
|
3819
|
-
if (!
|
|
4739
|
+
if (!existsSync9(filePath)) {
|
|
3820
4740
|
continue;
|
|
3821
4741
|
}
|
|
3822
4742
|
try {
|
|
@@ -3876,17 +4796,23 @@ function syncSessionSearchIndex(agentName, sessions, loadSessionData) {
|
|
|
3876
4796
|
slug,
|
|
3877
4797
|
title,
|
|
3878
4798
|
directory,
|
|
4799
|
+
project_identity_kind,
|
|
4800
|
+
project_identity_key,
|
|
4801
|
+
project_display_name,
|
|
3879
4802
|
time_created,
|
|
3880
4803
|
time_updated,
|
|
3881
4804
|
activity_time,
|
|
3882
4805
|
content_text,
|
|
3883
4806
|
content_hash,
|
|
3884
4807
|
indexed_at
|
|
3885
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
4808
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3886
4809
|
ON CONFLICT(agent_name, session_id) DO UPDATE SET
|
|
3887
4810
|
slug = excluded.slug,
|
|
3888
4811
|
title = excluded.title,
|
|
3889
4812
|
directory = excluded.directory,
|
|
4813
|
+
project_identity_kind = excluded.project_identity_kind,
|
|
4814
|
+
project_identity_key = excluded.project_identity_key,
|
|
4815
|
+
project_display_name = excluded.project_display_name,
|
|
3890
4816
|
time_created = excluded.time_created,
|
|
3891
4817
|
time_updated = excluded.time_updated,
|
|
3892
4818
|
activity_time = excluded.activity_time,
|
|
@@ -3900,12 +4826,16 @@ function syncSessionSearchIndex(agentName, sessions, loadSessionData) {
|
|
|
3900
4826
|
}
|
|
3901
4827
|
for (const entry of loaded) {
|
|
3902
4828
|
const activityTime = entry.session.time_updated ?? entry.session.time_created;
|
|
4829
|
+
const identity = entry.session.project_identity ?? computeIdentity(entry.session.directory, realFs);
|
|
3903
4830
|
upsertRow.run(
|
|
3904
4831
|
agentName,
|
|
3905
4832
|
entry.session.id,
|
|
3906
4833
|
entry.session.slug,
|
|
3907
4834
|
entry.session.title,
|
|
3908
4835
|
entry.session.directory,
|
|
4836
|
+
identity.kind,
|
|
4837
|
+
identity.key,
|
|
4838
|
+
identity.displayName,
|
|
3909
4839
|
entry.session.time_created,
|
|
3910
4840
|
entry.session.time_updated ?? null,
|
|
3911
4841
|
activityTime,
|
|
@@ -3943,7 +4873,7 @@ function searchSessions(query, options = {}) {
|
|
|
3943
4873
|
JOIN session_documents d ON d.id = session_documents_fts.rowid
|
|
3944
4874
|
WHERE session_documents_fts MATCH ?
|
|
3945
4875
|
AND (? IS NULL OR d.agent_name = ?)
|
|
3946
|
-
AND (? IS NULL OR LOWER(d.directory) LIKE ?)
|
|
4876
|
+
AND (? IS NULL OR d.project_identity_key = ? OR LOWER(d.directory) LIKE ?)
|
|
3947
4877
|
AND (? IS NULL OR d.activity_time >= ?)
|
|
3948
4878
|
AND (? IS NULL OR d.activity_time <= ?)
|
|
3949
4879
|
ORDER BY bm25(session_documents_fts, 8.0, 1.0), d.activity_time DESC
|
|
@@ -3953,7 +4883,8 @@ function searchSessions(query, options = {}) {
|
|
|
3953
4883
|
ftsQuery,
|
|
3954
4884
|
options.agent ?? null,
|
|
3955
4885
|
options.agent ?? null,
|
|
3956
|
-
options.cwd
|
|
4886
|
+
options.cwd ?? null,
|
|
4887
|
+
options.cwd ? computeIdentity(options.cwd, realFs).key : null,
|
|
3957
4888
|
options.cwd ? `%${options.cwd.toLowerCase()}%` : null,
|
|
3958
4889
|
options.from ?? null,
|
|
3959
4890
|
options.from ?? null,
|
|
@@ -3982,6 +4913,35 @@ function searchSessions(query, options = {}) {
|
|
|
3982
4913
|
});
|
|
3983
4914
|
return results ?? [];
|
|
3984
4915
|
}
|
|
4916
|
+
function listCachedProjectGroups(sessions) {
|
|
4917
|
+
if (sessions) {
|
|
4918
|
+
return buildProjectGroups(sessions);
|
|
4919
|
+
}
|
|
4920
|
+
if (!hasCacheStorage()) {
|
|
4921
|
+
return [];
|
|
4922
|
+
}
|
|
4923
|
+
const groups = withCacheDb((db) => {
|
|
4924
|
+
const rows = db.prepare(
|
|
4925
|
+
`
|
|
4926
|
+
SELECT identity_kind, identity_key, display_name, sources_csv, session_count, last_activity
|
|
4927
|
+
FROM project_groups_v
|
|
4928
|
+
ORDER BY
|
|
4929
|
+
CASE identity_kind WHEN 'loose' THEN 1 ELSE 0 END,
|
|
4930
|
+
last_activity IS NULL,
|
|
4931
|
+
last_activity DESC
|
|
4932
|
+
`
|
|
4933
|
+
).all();
|
|
4934
|
+
return rows.map((row) => ({
|
|
4935
|
+
identityKind: row.identity_kind ?? "path",
|
|
4936
|
+
identityKey: String(row.identity_key ?? ""),
|
|
4937
|
+
displayName: String(row.display_name ?? ""),
|
|
4938
|
+
sources: String(row.sources_csv ?? "").split(",").filter(Boolean).sort(),
|
|
4939
|
+
sessionCount: Number(row.session_count ?? 0),
|
|
4940
|
+
lastActivity: row.last_activity == null ? null : Number(row.last_activity)
|
|
4941
|
+
}));
|
|
4942
|
+
});
|
|
4943
|
+
return groups ?? [];
|
|
4944
|
+
}
|
|
3985
4945
|
function isPathScopeMatch(queryPath, sessionPath) {
|
|
3986
4946
|
if (!sessionPath) return false;
|
|
3987
4947
|
const q = resolve(queryPath);
|
|
@@ -3991,17 +4951,44 @@ function isPathScopeMatch(queryPath, sessionPath) {
|
|
|
3991
4951
|
const qn = sepNorm(q);
|
|
3992
4952
|
return sn === qn || sn.startsWith(qn + "/") || qn.startsWith(sn + "/");
|
|
3993
4953
|
}
|
|
4954
|
+
function createIdentityResolver() {
|
|
4955
|
+
const cache = /* @__PURE__ */ new Map();
|
|
4956
|
+
return (directory) => {
|
|
4957
|
+
const key = directory || "";
|
|
4958
|
+
const cached = cache.get(key);
|
|
4959
|
+
if (cached) return cached;
|
|
4960
|
+
const identity = computeIdentity(directory, realFs);
|
|
4961
|
+
cache.set(key, identity);
|
|
4962
|
+
return identity;
|
|
4963
|
+
};
|
|
4964
|
+
}
|
|
4965
|
+
function attachProjectIdentities(sessions) {
|
|
4966
|
+
const resolveIdentity = createIdentityResolver();
|
|
4967
|
+
return sessions.map((session) => {
|
|
4968
|
+
if (session.project_identity) return session;
|
|
4969
|
+
return {
|
|
4970
|
+
...session,
|
|
4971
|
+
project_identity: resolveIdentity(session.directory)
|
|
4972
|
+
};
|
|
4973
|
+
});
|
|
4974
|
+
}
|
|
4975
|
+
function isProjectScopeMatch(queryPath, session) {
|
|
4976
|
+
if (!session.directory) return false;
|
|
4977
|
+
const queryIdentity = computeIdentity(queryPath, realFs);
|
|
4978
|
+
if (session.project_identity?.key === queryIdentity.key) return true;
|
|
4979
|
+
return isPathScopeMatch(queryPath, session.directory);
|
|
4980
|
+
}
|
|
3994
4981
|
function filterSessions(sessions, options) {
|
|
3995
4982
|
let result = sessions;
|
|
3996
4983
|
if (options.cwd) {
|
|
3997
4984
|
const cwd = options.cwd;
|
|
3998
|
-
result = result.filter((s) =>
|
|
4985
|
+
result = result.filter((s) => isProjectScopeMatch(cwd, s));
|
|
3999
4986
|
}
|
|
4000
4987
|
if (options.from != null) {
|
|
4001
|
-
result = result.filter((s) => s.time_created >= options.from);
|
|
4988
|
+
result = result.filter((s) => (s.time_updated ?? s.time_created) >= options.from);
|
|
4002
4989
|
}
|
|
4003
4990
|
if (options.to != null) {
|
|
4004
|
-
result = result.filter((s) => s.time_created <= options.to);
|
|
4991
|
+
result = result.filter((s) => (s.time_updated ?? s.time_created) <= options.to);
|
|
4005
4992
|
}
|
|
4006
4993
|
return result;
|
|
4007
4994
|
}
|
|
@@ -4014,6 +5001,137 @@ function buildAgentCacheMeta(agent) {
|
|
|
4014
5001
|
}
|
|
4015
5002
|
return meta;
|
|
4016
5003
|
}
|
|
5004
|
+
function getSmartTagWorkerCount(sessionCount) {
|
|
5005
|
+
if (sessionCount < 8) return 1;
|
|
5006
|
+
return Math.min(sessionCount, Math.max(1, Math.min(4, availableParallelism() - 1)));
|
|
5007
|
+
}
|
|
5008
|
+
function chunkSessions(items, chunkCount) {
|
|
5009
|
+
const chunks = Array.from({ length: chunkCount }, () => []);
|
|
5010
|
+
items.forEach((item, index) => {
|
|
5011
|
+
chunks[index % chunkCount].push(item);
|
|
5012
|
+
});
|
|
5013
|
+
return chunks.filter((chunk) => chunk.length > 0);
|
|
5014
|
+
}
|
|
5015
|
+
function ensureSessionTagsSync(agent, sessions) {
|
|
5016
|
+
let changed = false;
|
|
5017
|
+
const tagged = sessions.map((session) => {
|
|
5018
|
+
const sourceUpdatedAt = session.time_updated ?? session.time_created;
|
|
5019
|
+
const currentTags = Array.isArray(session.smart_tags) ? session.smart_tags : null;
|
|
5020
|
+
if (currentTags && session.smart_tags_source_updated_at === sourceUpdatedAt) {
|
|
5021
|
+
return session;
|
|
5022
|
+
}
|
|
5023
|
+
try {
|
|
5024
|
+
const data = agent.getSessionData(session.id);
|
|
5025
|
+
const tags = classifySessionTags(data);
|
|
5026
|
+
changed = true;
|
|
5027
|
+
return {
|
|
5028
|
+
...session,
|
|
5029
|
+
smart_tags: tags,
|
|
5030
|
+
smart_tags_source_updated_at: getSmartTagSourceTimestamp(data)
|
|
5031
|
+
};
|
|
5032
|
+
} catch {
|
|
5033
|
+
return session;
|
|
5034
|
+
}
|
|
5035
|
+
});
|
|
5036
|
+
return { sessions: tagged, changed };
|
|
5037
|
+
}
|
|
5038
|
+
async function classifySessionTagsInWorker(agentName, sessionIds) {
|
|
5039
|
+
return new Promise((resolveWorker, rejectWorker) => {
|
|
5040
|
+
const worker = new Worker(
|
|
5041
|
+
`
|
|
5042
|
+
const { parentPort, workerData } = require("node:worker_threads");
|
|
5043
|
+
|
|
5044
|
+
(async () => {
|
|
5045
|
+
const {
|
|
5046
|
+
createRegisteredAgents,
|
|
5047
|
+
classifySessionTags,
|
|
5048
|
+
getSmartTagSourceTimestamp,
|
|
5049
|
+
} = await import("@codesesh/core");
|
|
5050
|
+
|
|
5051
|
+
const agent = createRegisteredAgents().find((item) => item.name === workerData.agentName);
|
|
5052
|
+
const results = [];
|
|
5053
|
+
|
|
5054
|
+
if (agent) {
|
|
5055
|
+
for (const sessionId of workerData.sessionIds) {
|
|
5056
|
+
try {
|
|
5057
|
+
const data = agent.getSessionData(sessionId);
|
|
5058
|
+
results.push({
|
|
5059
|
+
id: sessionId,
|
|
5060
|
+
tags: classifySessionTags(data),
|
|
5061
|
+
sourceUpdatedAt: getSmartTagSourceTimestamp(data),
|
|
5062
|
+
});
|
|
5063
|
+
} catch (error) {
|
|
5064
|
+
results.push({
|
|
5065
|
+
id: sessionId,
|
|
5066
|
+
error: error instanceof Error ? error.message : String(error),
|
|
5067
|
+
});
|
|
5068
|
+
}
|
|
5069
|
+
}
|
|
5070
|
+
}
|
|
5071
|
+
|
|
5072
|
+
parentPort?.postMessage(results);
|
|
5073
|
+
})().catch((error) => {
|
|
5074
|
+
parentPort?.postMessage([
|
|
5075
|
+
{
|
|
5076
|
+
id: "",
|
|
5077
|
+
error: error instanceof Error ? error.message : String(error),
|
|
5078
|
+
},
|
|
5079
|
+
]);
|
|
5080
|
+
});
|
|
5081
|
+
`,
|
|
5082
|
+
{
|
|
5083
|
+
eval: true,
|
|
5084
|
+
workerData: { agentName, sessionIds }
|
|
5085
|
+
}
|
|
5086
|
+
);
|
|
5087
|
+
worker.once("message", (results) => {
|
|
5088
|
+
resolveWorker(results);
|
|
5089
|
+
});
|
|
5090
|
+
worker.once("error", rejectWorker);
|
|
5091
|
+
worker.once("exit", (code) => {
|
|
5092
|
+
if (code !== 0) {
|
|
5093
|
+
rejectWorker(new Error(`Smart tag worker exited with code ${code}`));
|
|
5094
|
+
}
|
|
5095
|
+
});
|
|
5096
|
+
});
|
|
5097
|
+
}
|
|
5098
|
+
async function ensureSessionTags(agent, sessions) {
|
|
5099
|
+
const staleSessions = sessions.filter((session) => {
|
|
5100
|
+
const sourceUpdatedAt = session.time_updated ?? session.time_created;
|
|
5101
|
+
const currentTags = Array.isArray(session.smart_tags) ? session.smart_tags : null;
|
|
5102
|
+
return !currentTags || session.smart_tags_source_updated_at !== sourceUpdatedAt;
|
|
5103
|
+
});
|
|
5104
|
+
if (staleSessions.length === 0) {
|
|
5105
|
+
return { sessions, changed: false };
|
|
5106
|
+
}
|
|
5107
|
+
const workerCount = getSmartTagWorkerCount(staleSessions.length);
|
|
5108
|
+
if (workerCount <= 1) {
|
|
5109
|
+
return ensureSessionTagsSync(agent, sessions);
|
|
5110
|
+
}
|
|
5111
|
+
try {
|
|
5112
|
+
const results = (await Promise.all(
|
|
5113
|
+
chunkSessions(
|
|
5114
|
+
staleSessions.map((session) => session.id),
|
|
5115
|
+
workerCount
|
|
5116
|
+
).map((sessionIds) => classifySessionTagsInWorker(agent.name, sessionIds))
|
|
5117
|
+
)).flat();
|
|
5118
|
+
const resultMap = new Map(results.filter((item) => item.tags).map((item) => [item.id, item]));
|
|
5119
|
+
return {
|
|
5120
|
+
changed: resultMap.size > 0,
|
|
5121
|
+
sessions: sessions.map((session) => {
|
|
5122
|
+
const result = resultMap.get(session.id);
|
|
5123
|
+
if (!result?.tags || result.sourceUpdatedAt == null) return session;
|
|
5124
|
+
return {
|
|
5125
|
+
...session,
|
|
5126
|
+
smart_tags: result.tags,
|
|
5127
|
+
smart_tags_source_updated_at: result.sourceUpdatedAt
|
|
5128
|
+
};
|
|
5129
|
+
})
|
|
5130
|
+
};
|
|
5131
|
+
} catch {
|
|
5132
|
+
return ensureSessionTagsSync(agent, sessions);
|
|
5133
|
+
}
|
|
5134
|
+
}
|
|
4017
5135
|
async function scanAgentSmart(agent, options, onProgress) {
|
|
4018
5136
|
const useCache = options.useCache ?? true;
|
|
4019
5137
|
const canValidateCache = Boolean(agent.checkForChanges && agent.incrementalScan);
|
|
@@ -4050,18 +5168,27 @@ async function scanAgentSmart(agent, options, onProgress) {
|
|
|
4050
5168
|
const updatedSessions = await Promise.resolve(
|
|
4051
5169
|
agent.incrementalScan(cached.sessions, checkResult.changedIds || [])
|
|
4052
5170
|
);
|
|
4053
|
-
|
|
5171
|
+
const sessionsWithIdentity = attachProjectIdentities(updatedSessions);
|
|
5172
|
+
const tagged2 = options.includeSmartTags === false ? { sessions: sessionsWithIdentity, changed: false } : await ensureSessionTags(agent, sessionsWithIdentity);
|
|
5173
|
+
if (options.writeCache !== false && options.from == null && options.to == null) {
|
|
5174
|
+
saveCachedSessions(agent.name, tagged2.sessions, buildAgentCacheMeta(agent));
|
|
5175
|
+
}
|
|
4054
5176
|
onProgress?.({
|
|
4055
5177
|
agent: agent.name,
|
|
4056
5178
|
phase: "complete",
|
|
4057
|
-
newCount:
|
|
5179
|
+
newCount: tagged2.sessions.length
|
|
4058
5180
|
});
|
|
4059
|
-
const filtered2 = filterSessions(
|
|
5181
|
+
const filtered2 = filterSessions(tagged2.sessions, options);
|
|
4060
5182
|
return { agent, heads: filtered2, fromCache: true, refreshed: true };
|
|
4061
5183
|
}
|
|
4062
5184
|
onProgress?.({ agent: agent.name, phase: "complete", newCount: cached.sessions.length });
|
|
4063
5185
|
}
|
|
4064
|
-
const
|
|
5186
|
+
const cachedWithIdentity = attachProjectIdentities(cached.sessions);
|
|
5187
|
+
const tagged = options.includeSmartTags === false ? { sessions: cachedWithIdentity, changed: false } : await ensureSessionTags(agent, cachedWithIdentity);
|
|
5188
|
+
if (tagged.changed && options.writeCache !== false && options.from == null && options.to == null) {
|
|
5189
|
+
saveCachedSessions(agent.name, tagged.sessions, buildAgentCacheMeta(agent));
|
|
5190
|
+
}
|
|
5191
|
+
const filtered = filterSessions(tagged.sessions, options);
|
|
4065
5192
|
return { agent, heads: filtered, fromCache: true };
|
|
4066
5193
|
}
|
|
4067
5194
|
}
|
|
@@ -4076,12 +5203,16 @@ async function scanAgentFull(agent, options, onProgress) {
|
|
|
4076
5203
|
}
|
|
4077
5204
|
try {
|
|
4078
5205
|
const scanMarker = perf.start(`agent:${agent.name}:scan`);
|
|
4079
|
-
const heads = agent.scan();
|
|
5206
|
+
const heads = agent.scan({ from: options.from, to: options.to, fast: options.fast });
|
|
4080
5207
|
perf.end(scanMarker);
|
|
5208
|
+
const headsWithIdentity = attachProjectIdentities(heads);
|
|
5209
|
+
const tagged = options.includeSmartTags === false ? { sessions: headsWithIdentity, changed: false } : await ensureSessionTags(agent, headsWithIdentity);
|
|
4081
5210
|
const meta = buildAgentCacheMeta(agent);
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
5211
|
+
if (options.writeCache !== false && options.from == null && options.to == null) {
|
|
5212
|
+
saveCachedSessions(agent.name, tagged.sessions, meta);
|
|
5213
|
+
}
|
|
5214
|
+
onProgress?.({ agent: agent.name, phase: "complete", newCount: tagged.sessions.length });
|
|
5215
|
+
const filtered = filterSessions(tagged.sessions, options);
|
|
4085
5216
|
return { agent, heads: filtered, fromCache: false };
|
|
4086
5217
|
} catch (err) {
|
|
4087
5218
|
console.error(`Error scanning ${agent.name}:`, err);
|
|
@@ -4127,16 +5258,16 @@ var BookmarkStorageUnavailableError = class extends Error {
|
|
|
4127
5258
|
function getStateDir() {
|
|
4128
5259
|
const p = platform2();
|
|
4129
5260
|
if (p === "darwin") {
|
|
4130
|
-
return
|
|
5261
|
+
return join9(homedir5(), "Library", "Application Support", "codesesh");
|
|
4131
5262
|
}
|
|
4132
5263
|
if (p === "win32") {
|
|
4133
5264
|
const appData = process.env.APPDATA ?? process.env.LOCALAPPDATA;
|
|
4134
|
-
return
|
|
5265
|
+
return join9(appData ?? join9(homedir5(), "AppData", "Roaming"), "codesesh");
|
|
4135
5266
|
}
|
|
4136
|
-
return
|
|
5267
|
+
return join9(process.env.XDG_DATA_HOME ?? join9(homedir5(), ".local", "share"), "codesesh");
|
|
4137
5268
|
}
|
|
4138
5269
|
function getStateDbPath() {
|
|
4139
|
-
return
|
|
5270
|
+
return join9(getStateDir(), BOOKMARK_DB_FILENAME);
|
|
4140
5271
|
}
|
|
4141
5272
|
function ensureSchema2(db) {
|
|
4142
5273
|
db.exec(`
|
|
@@ -4354,15 +5485,32 @@ export {
|
|
|
4354
5485
|
basenameTitle,
|
|
4355
5486
|
resolveSessionTitle,
|
|
4356
5487
|
perf,
|
|
5488
|
+
getPricingRegistry,
|
|
5489
|
+
hasBillablePricing,
|
|
5490
|
+
refreshPricingCache,
|
|
5491
|
+
pricingResolver,
|
|
5492
|
+
estimateCostForTokens,
|
|
5493
|
+
applyMessageCost,
|
|
5494
|
+
applyMessageCosts,
|
|
5495
|
+
withEstimatedSessionCost,
|
|
5496
|
+
estimateTokenCost,
|
|
4357
5497
|
openDbReadOnly,
|
|
4358
5498
|
openDb,
|
|
4359
5499
|
isSqliteAvailable,
|
|
5500
|
+
fallbackDisplayName,
|
|
5501
|
+
realFs,
|
|
5502
|
+
buildProjectGroups,
|
|
5503
|
+
normalizeGitRemote,
|
|
5504
|
+
computeIdentity,
|
|
5505
|
+
getSmartTagSourceTimestamp,
|
|
5506
|
+
classifySessionTags,
|
|
4360
5507
|
loadCachedSessions,
|
|
4361
5508
|
saveCachedSessions,
|
|
4362
5509
|
clearCache,
|
|
4363
5510
|
getCacheInfo,
|
|
4364
5511
|
syncSessionSearchIndex,
|
|
4365
5512
|
searchSessions,
|
|
5513
|
+
listCachedProjectGroups,
|
|
4366
5514
|
filterSessions,
|
|
4367
5515
|
scanSessions,
|
|
4368
5516
|
scanSessionsAsync,
|
|
@@ -4372,4 +5520,4 @@ export {
|
|
|
4372
5520
|
importBookmarks,
|
|
4373
5521
|
deleteBookmark
|
|
4374
5522
|
};
|
|
4375
|
-
//# sourceMappingURL=chunk-
|
|
5523
|
+
//# sourceMappingURL=chunk-FZNZAMTZ.js.map
|