negotium 0.2.14 → 0.2.16

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.
Files changed (34) hide show
  1. package/dist/agent-helpers.js +178 -26
  2. package/dist/agent-helpers.js.map +8 -8
  3. package/dist/{chunk-5g5a63vv.js → chunk-5cpdbbeh.js} +56 -6
  4. package/dist/{chunk-5g5a63vv.js.map → chunk-5cpdbbeh.js.map} +4 -4
  5. package/dist/hosted-agent.js +118 -9
  6. package/dist/hosted-agent.js.map +6 -6
  7. package/dist/main.js +389 -50
  8. package/dist/main.js.map +15 -15
  9. package/dist/mcp-factories.js +247 -36
  10. package/dist/mcp-factories.js.map +9 -9
  11. package/dist/registry.js +3 -3
  12. package/dist/registry.js.map +2 -2
  13. package/dist/rollout.js +1 -1
  14. package/dist/runtime/src/agents/codex-provider.ts +53 -2
  15. package/dist/runtime/src/agents/rollout/codex.ts +62 -0
  16. package/dist/runtime/src/mcp/factories/token-stats.ts +33 -11
  17. package/dist/runtime/src/mcp/runtime-spec.ts +58 -1
  18. package/dist/runtime/src/node-host.ts +1 -0
  19. package/dist/runtime/src/runtime/public-helpers.ts +8 -0
  20. package/dist/runtime/src/runtime/turn-event-stream.ts +17 -2
  21. package/dist/runtime/src/storage/token-stats.ts +200 -19
  22. package/dist/runtime/src/version.ts +1 -1
  23. package/dist/runtime-helpers.js +443 -30
  24. package/dist/runtime-helpers.js.map +9 -3
  25. package/dist/storage.js +112 -11
  26. package/dist/storage.js.map +3 -3
  27. package/dist/types/packages/core/src/agents/codex-provider.d.ts +13 -0
  28. package/dist/types/packages/core/src/agents/rollout/codex.d.ts +9 -0
  29. package/dist/types/packages/core/src/mcp/factories/token-stats.d.ts +4 -2
  30. package/dist/types/packages/core/src/runtime/bashrs-completions.d.ts +50 -0
  31. package/dist/types/packages/core/src/runtime/public-helpers.d.ts +2 -0
  32. package/dist/types/packages/core/src/storage/token-stats.d.ts +35 -6
  33. package/dist/types/packages/core/src/version.d.ts +1 -1
  34. package/package.json +1 -1
@@ -952,7 +952,7 @@ var init_claude_registry = __esm(() => {
952
952
  });
953
953
 
954
954
  // ../../packages/core/src/version.ts
955
- var NEGOTIUM_VERSION = "0.2.14";
955
+ var NEGOTIUM_VERSION = "0.2.16";
956
956
 
957
957
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
958
958
  import { spawn as spawn2 } from "child_process";
@@ -1292,7 +1292,7 @@ var init_canonical_bridge_config = __esm(() => {
1292
1292
  });
1293
1293
 
1294
1294
  // ../../packages/core/src/mcp/runtime-spec.ts
1295
- import { createHmac, timingSafeEqual } from "crypto";
1295
+ import { createHash, createHmac, timingSafeEqual } from "crypto";
1296
1296
  function encodeTokenPart(value2) {
1297
1297
  return Buffer.from(JSON.stringify(value2), "utf-8").toString("base64url");
1298
1298
  }
@@ -1325,19 +1325,69 @@ function buildRuntimeMcpSpec(agent, ctx) {
1325
1325
  return {
1326
1326
  type: "sse",
1327
1327
  url: `${base}/sse?${query}`,
1328
- timeout: CLAUDE_MCP_TOOL_TIMEOUT_MS
1328
+ timeout: CLAUDE_MCP_TOOL_TIMEOUT_MS,
1329
+ ...agent === "maestro" ? { lifecycle: "turn" } : {}
1329
1330
  };
1330
1331
  }
1332
+ function hostedMcpCacheIdentity(surface, ctx) {
1333
+ let semanticContext;
1334
+ switch (surface) {
1335
+ case "system-health":
1336
+ semanticContext = {};
1337
+ break;
1338
+ case "token-stats":
1339
+ case "agent-health":
1340
+ semanticContext = { userId: ctx.userId };
1341
+ break;
1342
+ case "task":
1343
+ semanticContext = {
1344
+ userId: ctx.userId,
1345
+ topicTitle: ctx.topicTitle,
1346
+ topicId: ctx.topicId ?? null
1347
+ };
1348
+ break;
1349
+ case "wiki":
1350
+ case "skills":
1351
+ semanticContext = {
1352
+ userId: ctx.userId,
1353
+ topicId: ctx.wikiTopicId ?? ctx.topicId ?? null
1354
+ };
1355
+ break;
1356
+ case "vault":
1357
+ semanticContext = { userId: ctx.userId, cwd: ctx.cwd, agent: ctx.agent };
1358
+ break;
1359
+ case "session-comm":
1360
+ semanticContext = {
1361
+ userId: ctx.userId,
1362
+ topicTitle: ctx.topicTitle,
1363
+ topicId: ctx.topicId ?? null,
1364
+ subagentParentTopicId: ctx.subagentParentTopicId ?? null,
1365
+ depth: ctx.depth ?? 0,
1366
+ silent: ctx.silent ?? false,
1367
+ agent: ctx.agent,
1368
+ peerBridge: ctx.peerBridge ?? null
1369
+ };
1370
+ break;
1371
+ }
1372
+ const digest = createHash("sha256").update(JSON.stringify([surface, semanticContext])).digest("hex").slice(0, 24);
1373
+ return `hosted:${surface}:${digest}`;
1374
+ }
1331
1375
  function buildHostedMcpSpec(agent, surface, ctx) {
1332
1376
  const token = issueHostedMcpToken(surface, ctx);
1333
1377
  const base = `http://127.0.0.1:${runtimePort}${RUNTIME_MCP_BASE_PATH}/${surface}`;
1334
1378
  const query = `token=${encodeURIComponent(token)}`;
1335
1379
  if (agent === "codex")
1336
1380
  return { url: `${base}/mcp?${query}` };
1381
+ const queryBound = surface === "session-comm" && (ctx.silent === true || ctx.peerBridge !== undefined);
1382
+ const lifecycle = queryBound ? "turn" : surface === "session-comm" ? "session" : "process";
1337
1383
  return {
1338
1384
  type: "sse",
1339
1385
  url: `${base}/sse?${query}`,
1340
- timeout: CLAUDE_MCP_TOOL_TIMEOUT_MS
1386
+ timeout: CLAUDE_MCP_TOOL_TIMEOUT_MS,
1387
+ ...agent === "maestro" ? {
1388
+ lifecycle,
1389
+ cacheKey: hostedMcpCacheIdentity(surface, ctx)
1390
+ } : {}
1341
1391
  };
1342
1392
  }
1343
1393
  var RUNTIME_MCP_KEY = "runtime", RUNTIME_MCP_BASE_PATH = "/mcp/runtime", TOKEN_TTL_MS, CLAUDE_MCP_TOOL_TIMEOUT_MS = 600000, runtimePort;
@@ -2190,9 +2240,9 @@ var init_sqlite = __esm(async () => {
2190
2240
  });
2191
2241
 
2192
2242
  // ../../packages/core/src/storage/vault-crypto-core.ts
2193
- import { createCipheriv, createDecipheriv, createHash, randomBytes as randomBytes3 } from "crypto";
2243
+ import { createCipheriv, createDecipheriv, createHash as createHash2, randomBytes as randomBytes3 } from "crypto";
2194
2244
  function encryptionKey(masterKey) {
2195
- return createHash("sha256").update("otium-vault-value-v1\x00", "utf8").update(masterKey, "utf8").digest().subarray(0, KEY_BYTES);
2245
+ return createHash2("sha256").update("otium-vault-value-v1\x00", "utf8").update(masterKey, "utf8").digest().subarray(0, KEY_BYTES);
2196
2246
  }
2197
2247
  function aad(userId, key) {
2198
2248
  return Buffer.from(`${userId}\x00${key.toUpperCase()}`, "utf8");
@@ -2674,6 +2724,36 @@ function extractLatestCodexContextUsage(jsonl) {
2674
2724
  }
2675
2725
  return;
2676
2726
  }
2727
+ function extractLatestCodexTokenTotals(jsonl) {
2728
+ const lines = jsonl.trimEnd().split(`
2729
+ `);
2730
+ for (let index = lines.length - 1;index >= 0; index -= 1) {
2731
+ try {
2732
+ const entry = JSON.parse(lines[index] ?? "");
2733
+ if (entry.type !== "event_msg" || entry.payload?.type !== "token_count")
2734
+ continue;
2735
+ const usage = entry.payload.info?.total_token_usage;
2736
+ if (!usage)
2737
+ continue;
2738
+ const values = [
2739
+ usage.input_tokens,
2740
+ usage.output_tokens,
2741
+ usage.cached_input_tokens,
2742
+ usage.cache_write_input_tokens
2743
+ ];
2744
+ if (values.some((value2) => value2 !== undefined && (!Number.isFinite(value2) || value2 < 0))) {
2745
+ continue;
2746
+ }
2747
+ return {
2748
+ inputTokens: usage.input_tokens ?? 0,
2749
+ outputTokens: usage.output_tokens ?? 0,
2750
+ cachedInputTokens: usage.cached_input_tokens ?? 0,
2751
+ cacheWriteInputTokens: usage.cache_write_input_tokens ?? 0
2752
+ };
2753
+ } catch {}
2754
+ }
2755
+ return;
2756
+ }
2677
2757
  function readLatestCodexContextUsage(threadId) {
2678
2758
  const path = latestCodexRolloutPath(threadId);
2679
2759
  if (!path)
@@ -2685,6 +2765,17 @@ function readLatestCodexContextUsage(threadId) {
2685
2765
  return;
2686
2766
  }
2687
2767
  }
2768
+ function readLatestCodexTokenTotals(threadId) {
2769
+ const path = latestCodexRolloutPath(threadId);
2770
+ if (!path)
2771
+ return;
2772
+ try {
2773
+ return extractLatestCodexTokenTotals(readFileSync6(path, "utf8"));
2774
+ } catch (error) {
2775
+ logger.debug({ error, threadId }, "codex token totals: rollout read failed");
2776
+ return;
2777
+ }
2778
+ }
2688
2779
  function migrateCodexRolloutNativeMultiAgentMetadata(threadId) {
2689
2780
  const path = latestCodexRolloutPath(threadId);
2690
2781
  if (!path)
@@ -6013,7 +6104,7 @@ var init_browser_profiles = __esm(async () => {
6013
6104
 
6014
6105
  // ../../packages/core/src/platform/playwright/manager.ts
6015
6106
  import { execFileSync as execFileSync4, spawn as spawn6 } from "child_process";
6016
- import { createHash as createHash2, randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual3 } from "crypto";
6107
+ import { createHash as createHash3, randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual3 } from "crypto";
6017
6108
  import {
6018
6109
  cpSync,
6019
6110
  existsSync as existsSync11,
@@ -6108,7 +6199,7 @@ function parseInstanceKey(instanceKey) {
6108
6199
  };
6109
6200
  }
6110
6201
  function portFileName(instanceKey) {
6111
- return createHash2("sha256").update(instanceKey).digest("hex").slice(0, 24);
6202
+ return createHash3("sha256").update(instanceKey).digest("hex").slice(0, 24);
6112
6203
  }
6113
6204
  function writePortFile(instanceKey, port) {
6114
6205
  try {
@@ -8909,6 +9000,7 @@ var init_tool_format = __esm(() => {
8909
9000
  var exports_codex_provider = {};
8910
9001
  __export(exports_codex_provider, {
8911
9002
  toCodexMcpServers: () => toCodexMcpServers,
9003
+ normalizeCodexTurnUsage: () => normalizeCodexTurnUsage,
8912
9004
  codexProvider: () => codexProvider
8913
9005
  });
8914
9006
  import { execFileSync as execFileSync6 } from "child_process";
@@ -8916,6 +9008,20 @@ import { existsSync as existsSync15, readFileSync as readFileSync14, realpathSyn
8916
9008
  import { homedir as homedir6 } from "os";
8917
9009
  import { dirname as dirname11, isAbsolute as isAbsolute3, join as join16, relative, resolve as resolve12 } from "path";
8918
9010
  import { Codex } from "@openai/codex-sdk";
9011
+ function sameCodexUsage(usage, total) {
9012
+ return usage.input_tokens === total.inputTokens && usage.output_tokens === total.outputTokens && (usage.cached_input_tokens ?? 0) === total.cachedInputTokens && (usage.cache_write_input_tokens ?? 0) === total.cacheWriteInputTokens;
9013
+ }
9014
+ function normalizeCodexTurnUsage(usage, baseline, rolloutTotal) {
9015
+ if (!baseline || !rolloutTotal || !sameCodexUsage(usage, rolloutTotal))
9016
+ return usage;
9017
+ const delta = (current2, previous) => current2 >= previous ? current2 - previous : current2;
9018
+ return {
9019
+ input_tokens: delta(rolloutTotal.inputTokens, baseline.inputTokens),
9020
+ output_tokens: delta(rolloutTotal.outputTokens, baseline.outputTokens),
9021
+ cached_input_tokens: delta(rolloutTotal.cachedInputTokens, baseline.cachedInputTokens),
9022
+ cache_write_input_tokens: delta(rolloutTotal.cacheWriteInputTokens, baseline.cacheWriteInputTokens)
9023
+ };
9024
+ }
8919
9025
  function mapEffort(effort) {
8920
9026
  if (!effort)
8921
9027
  return;
@@ -9315,6 +9421,7 @@ async function* codexProvider(opts) {
9315
9421
  ...opts.effort ? { modelReasoningEffort: mapEffort(opts.effort) } : {}
9316
9422
  };
9317
9423
  let currentSessionId = opts.sessionId;
9424
+ let usageBaseline = currentSessionId ? readLatestCodexTokenTotals(currentSessionId) : undefined;
9318
9425
  let thread = currentSessionId ? codex.resumeThread(currentSessionId, threadOptions) : codex.startThread(threadOptions);
9319
9426
  let prompt = promptForThread(opts, true);
9320
9427
  let agentTextSoFar = "";
@@ -9373,6 +9480,7 @@ async function* codexProvider(opts) {
9373
9480
  thread = codex.startThread(threadOptions);
9374
9481
  prompt = promptForThread(opts, true);
9375
9482
  currentSessionId = undefined;
9483
+ usageBaseline = undefined;
9376
9484
  startResult = await startStreamedWithTracking(thread, prompt, attemptAbort.signal, trackedPids);
9377
9485
  }
9378
9486
  if (attemptAbort.signal.aborted) {
@@ -9473,10 +9581,11 @@ async function* codexProvider(opts) {
9473
9581
  break;
9474
9582
  }
9475
9583
  case "turn.completed": {
9476
- const usage = event.usage;
9477
- if (!usage)
9584
+ const rawUsage = event.usage;
9585
+ if (!rawUsage)
9478
9586
  break;
9479
9587
  attemptCompleted = true;
9588
+ const usage = normalizeCodexTurnUsage(rawUsage, usageBaseline, currentSessionId ? readLatestCodexTokenTotals(currentSessionId) : undefined);
9480
9589
  const contextUsage = currentSessionId ? readLatestCodexContextUsage(currentSessionId) : undefined;
9481
9590
  yield {
9482
9591
  type: "result",
@@ -9485,6 +9594,7 @@ async function* codexProvider(opts) {
9485
9594
  usage: {
9486
9595
  inputTokens: usage.input_tokens,
9487
9596
  outputTokens: usage.output_tokens,
9597
+ cacheCreationInputTokens: usage.cache_write_input_tokens,
9488
9598
  cacheReadInputTokens: usage.cached_input_tokens,
9489
9599
  ...contextUsage
9490
9600
  }
@@ -11839,7 +11949,7 @@ __export(exports_derive, {
11839
11949
  TopicForkCompactionError: () => TopicForkCompactionError,
11840
11950
  TopicDeriveBusyError: () => TopicDeriveBusyError
11841
11951
  });
11842
- import { createHash as createHash3, randomUUID as randomUUID11 } from "crypto";
11952
+ import { createHash as createHash4, randomUUID as randomUUID11 } from "crypto";
11843
11953
  import { mkdirSync as mkdirSync14, rmSync as rmSync5, unlinkSync as unlinkSync13 } from "fs";
11844
11954
  function getTopics() {
11845
11955
  return listTopics().filter((topic) => !isLegacySharedGeneral(topic.id));
@@ -11905,7 +12015,7 @@ function captureForkSnapshot(sourceTopicId, userId, topicTitle) {
11905
12015
  maxRowid: messageRows.at(-1)?.rowid ?? 0
11906
12016
  },
11907
12017
  active: isTopicRunning(sourceTopicId),
11908
- canonicalDigest: createHash3("sha256").update(entries.map((entry) => JSON.stringify(entry)).join(`
12018
+ canonicalDigest: createHash4("sha256").update(entries.map((entry) => JSON.stringify(entry)).join(`
11909
12019
  `)).digest("hex")
11910
12020
  };
11911
12021
  }
@@ -12398,7 +12508,7 @@ __export(exports_session_asks, {
12398
12508
  clearPendingAsk: () => clearPendingAsk,
12399
12509
  PENDING_ASK_TTL_MS: () => PENDING_ASK_TTL_MS
12400
12510
  });
12401
- import { createHash as createHash4 } from "crypto";
12511
+ import { createHash as createHash5 } from "crypto";
12402
12512
  import {
12403
12513
  closeSync as closeSync3,
12404
12514
  mkdirSync as mkdirSync16,
@@ -12412,14 +12522,14 @@ import {
12412
12522
  import { dirname as dirname14, join as join25 } from "path";
12413
12523
  function pendingAskDir(userId) {
12414
12524
  const rawUserId = String(userId);
12415
- const safeUserId = /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) && !rawUserId.includes("..") ? rawUserId : `sha256-${createHash4("sha256").update(rawUserId).digest("hex")}`;
12525
+ const safeUserId = /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) && !rawUserId.includes("..") ? rawUserId : `sha256-${createHash5("sha256").update(rawUserId).digest("hex")}`;
12416
12526
  return join25(resolveStorageSessionAsksDir(), safeUserId);
12417
12527
  }
12418
12528
  function encodeAskKey(key) {
12419
12529
  return JSON.stringify([key.from, key.to]);
12420
12530
  }
12421
12531
  function pendingAskPath(key) {
12422
- const digest = createHash4("sha256").update(encodeAskKey(key)).digest("hex");
12532
+ const digest = createHash5("sha256").update(encodeAskKey(key)).digest("hex");
12423
12533
  return join25(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${digest}.pending`);
12424
12534
  }
12425
12535
  function v2PendingAskPath(key) {
@@ -13920,7 +14030,7 @@ var init_ask_user_gates = __esm(async () => {
13920
14030
  });
13921
14031
 
13922
14032
  // ../../packages/core/src/agents/mcp-tools/ask-user.ts
13923
- import { createHash as createHash5, randomUUID as randomUUID14 } from "crypto";
14033
+ import { createHash as createHash6, randomUUID as randomUUID14 } from "crypto";
13924
14034
  import { z as z3 } from "zod";
13925
14035
  function normalizeAskText(value2, maxChars) {
13926
14036
  if (typeof value2 !== "string")
@@ -13957,7 +14067,7 @@ function normalizeAskUserQuestionInput(input) {
13957
14067
  return { question, choices };
13958
14068
  }
13959
14069
  function askBodyHash(question, choices) {
13960
- return createHash5("sha256").update(JSON.stringify({ question, choices })).digest("hex");
14070
+ return createHash6("sha256").update(JSON.stringify({ question, choices })).digest("hex");
13961
14071
  }
13962
14072
  function answerToolResult(answer) {
13963
14073
  if (!answer.userId)
@@ -14585,7 +14695,7 @@ var init_visuals = __esm(async () => {
14585
14695
  });
14586
14696
 
14587
14697
  // ../../packages/core/src/storage/token-stats.ts
14588
- import { createHash as createHash6 } from "crypto";
14698
+ import { createHash as createHash7 } from "crypto";
14589
14699
  import { mkdirSync as mkdirSync18 } from "fs";
14590
14700
  import { join as join27 } from "path";
14591
14701
  function emptyBucket() {
@@ -14594,12 +14704,13 @@ function emptyBucket() {
14594
14704
  outputTokens: 0,
14595
14705
  cacheCreationInputTokens: 0,
14596
14706
  cacheReadInputTokens: 0,
14597
- queries: 0
14707
+ queries: 0,
14708
+ estimatedCostUsd: 0
14598
14709
  };
14599
14710
  }
14600
14711
  function tokenStatsFileId(userId) {
14601
14712
  const rawUserId = String(userId);
14602
- return /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) && !rawUserId.includes("..") ? rawUserId : `sha256-${createHash6("sha256").update(rawUserId).digest("hex")}`;
14713
+ return /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) && !rawUserId.includes("..") ? rawUserId : `sha256-${createHash7("sha256").update(rawUserId).digest("hex")}`;
14603
14714
  }
14604
14715
  function queriesPath(userId) {
14605
14716
  const fileId = tokenStatsFileId(userId);
@@ -14621,16 +14732,41 @@ function loadRecords(userId) {
14621
14732
  }
14622
14733
  }
14623
14734
  function calcCost(b) {
14624
- return b.inputTokens / 1e6 * 3 + b.outputTokens / 1e6 * 15 + b.cacheCreationInputTokens / 1e6 * 3.75 + b.cacheReadInputTokens / 1e6 * 0.3;
14735
+ return b.estimatedCostUsd;
14736
+ }
14737
+ function estimateUsageCost(agent, model, usage) {
14738
+ const prices = TOKEN_PRICES[`${agent}:${model}`];
14739
+ if (!prices)
14740
+ return 0;
14741
+ return (usage.inputTokens * prices.input + usage.outputTokens * prices.output + (agent === "claude" ? usage.cacheCreationInputTokens * (prices.cacheWrite ?? prices.input) : 0) + usage.cacheReadInputTokens * prices.cacheRead) / 1e6;
14625
14742
  }
14626
- function recordUsage(userId, session, usage) {
14743
+ function isQueryRecord(value2) {
14744
+ if (!value2 || typeof value2 !== "object")
14745
+ return false;
14746
+ const record = value2;
14747
+ return record.schemaVersion === 2 && typeof record.timestamp === "string" && typeof record.session === "string" && typeof record.topicId === "string" && typeof record.agent === "string" && typeof record.model === "string" && typeof record.inputTokens === "number" && typeof record.outputTokens === "number" && typeof record.cacheCreationInputTokens === "number" && typeof record.cacheReadInputTokens === "number" && typeof record.estimatedCostUsd === "number";
14748
+ }
14749
+ function recordUsage(userId, session, usage, context) {
14750
+ const cacheReadInputTokens = usage.cacheReadInputTokens ?? 0;
14751
+ const inputTokens = context.agent === "claude" ? usage.inputTokens : Math.max(0, usage.inputTokens - cacheReadInputTokens);
14752
+ const normalized = {
14753
+ inputTokens,
14754
+ outputTokens: usage.outputTokens,
14755
+ cacheCreationInputTokens: usage.cacheCreationInputTokens ?? 0,
14756
+ cacheReadInputTokens
14757
+ };
14627
14758
  const record = {
14759
+ schemaVersion: 2,
14628
14760
  timestamp: new Date().toISOString(),
14629
14761
  session,
14630
- inputTokens: usage.inputTokens,
14631
- outputTokens: usage.outputTokens,
14632
- cacheCreationInputTokens: usage.cacheCreationInputTokens ?? 0,
14633
- cacheReadInputTokens: usage.cacheReadInputTokens ?? 0
14762
+ topicId: context.topicId,
14763
+ ...context.providerSessionId ? { providerSessionId: context.providerSessionId } : {},
14764
+ agent: context.agent,
14765
+ model: context.model,
14766
+ ...normalized,
14767
+ ...usage.contextTokens !== undefined ? { contextTokens: usage.contextTokens } : {},
14768
+ ...usage.contextWindow !== undefined ? { contextWindow: usage.contextWindow } : {},
14769
+ estimatedCostUsd: usage.costUsd ?? estimateUsageCost(context.agent, context.model, normalized)
14634
14770
  };
14635
14771
  try {
14636
14772
  appendJsonlEntry(queriesPath(userId), record);
@@ -14644,12 +14780,26 @@ function getStats(userId, from, to) {
14644
14780
  const toTs = to ? new Date(to).getTime() : Infinity;
14645
14781
  if (from && Number.isNaN(fromTs) || to && Number.isNaN(toTs)) {
14646
14782
  logger.warn({ from, to }, "token-stats: Invalid date range, returning empty");
14647
- return { total: emptyBucket(), byHour: {}, bySession: {}, estimatedCostUsd: 0 };
14783
+ return {
14784
+ total: emptyBucket(),
14785
+ byHour: {},
14786
+ bySession: {},
14787
+ currentSessions: [],
14788
+ ignoredLegacyRecords: 0,
14789
+ estimatedCostUsd: 0
14790
+ };
14648
14791
  }
14649
14792
  const total = emptyBucket();
14650
14793
  const byHour = {};
14651
14794
  const bySession = {};
14652
- for (const r of records) {
14795
+ const currentSessions = new Map;
14796
+ let ignoredLegacyRecords = 0;
14797
+ for (const raw of records) {
14798
+ if (!isQueryRecord(raw)) {
14799
+ ignoredLegacyRecords += 1;
14800
+ continue;
14801
+ }
14802
+ const r = raw;
14653
14803
  const ts = new Date(r.timestamp).getTime();
14654
14804
  if (ts < fromTs || ts > toTs)
14655
14805
  continue;
@@ -14664,14 +14814,47 @@ function getStats(userId, from, to) {
14664
14814
  bucket.cacheCreationInputTokens += r.cacheCreationInputTokens;
14665
14815
  bucket.cacheReadInputTokens += r.cacheReadInputTokens;
14666
14816
  bucket.queries += 1;
14817
+ bucket.estimatedCostUsd += r.estimatedCostUsd;
14818
+ }
14819
+ if (r.contextTokens !== undefined && r.contextWindow !== undefined && r.contextWindow > 0) {
14820
+ currentSessions.set(r.topicId, {
14821
+ timestamp: r.timestamp,
14822
+ topicId: r.topicId,
14823
+ topicTitle: r.session,
14824
+ ...r.providerSessionId ? { providerSessionId: r.providerSessionId } : {},
14825
+ agent: r.agent,
14826
+ model: r.model,
14827
+ contextTokens: r.contextTokens,
14828
+ contextWindow: r.contextWindow
14829
+ });
14667
14830
  }
14668
14831
  }
14669
- return { total, byHour, bySession, estimatedCostUsd: calcCost(total) };
14832
+ return {
14833
+ total,
14834
+ byHour,
14835
+ bySession,
14836
+ currentSessions: [...currentSessions.values()].sort((a, b) => b.timestamp.localeCompare(a.timestamp)),
14837
+ ignoredLegacyRecords,
14838
+ estimatedCostUsd: calcCost(total)
14839
+ };
14670
14840
  }
14841
+ var TOKEN_PRICES;
14671
14842
  var init_token_stats = __esm(async () => {
14672
14843
  init_jsonl();
14673
14844
  init_logger();
14674
14845
  await init_storage_host();
14846
+ TOKEN_PRICES = {
14847
+ "codex:gpt-5.6-sol": { input: 5, cacheRead: 0.5, output: 30 },
14848
+ "codex:gpt-5.6-terra": { input: 2.5, cacheRead: 0.25, output: 15 },
14849
+ "codex:gpt-5.6-luna": { input: 1, cacheRead: 0.1, output: 6 },
14850
+ "claude:fable": { input: 10, cacheWrite: 12.5, cacheRead: 1, output: 50 },
14851
+ "claude:opus": { input: 5, cacheWrite: 6.25, cacheRead: 0.5, output: 25 },
14852
+ "claude:sonnet": { input: 2, cacheWrite: 2.5, cacheRead: 0.2, output: 10 },
14853
+ "maestro:kimi-k3": { input: 3, cacheRead: 0.3, output: 15 },
14854
+ "maestro:kimi-k2.7-code": { input: 0.95, cacheRead: 0.19, output: 4 },
14855
+ "maestro:deepseek-pro": { input: 0.435, cacheRead: 0.003625, output: 0.87 },
14856
+ "maestro:deepseek-flash": { input: 0.14, cacheRead: 0.0028, output: 0.28 }
14857
+ };
14675
14858
  });
14676
14859
 
14677
14860
  // ../../packages/core/src/runtime/turn-event-stream.ts
@@ -14710,6 +14893,14 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
14710
14893
  const toolSummaryOptions = {
14711
14894
  cwd: control.injectParams?.cwd ?? workspaceCwdFor(topicId)
14712
14895
  };
14896
+ const recordEventUsage = (usage) => {
14897
+ recordUsage(userId, topicTitle, usage, {
14898
+ topicId,
14899
+ ...control.sessionId ? { providerSessionId: control.sessionId } : {},
14900
+ agent: agentType,
14901
+ model
14902
+ });
14903
+ };
14713
14904
  const nextSyntheticToolUseId = () => `tool-${queryId}-${++syntheticToolCounter}`;
14714
14905
  const bindToolUseId = (providerToolUseId, fallback) => {
14715
14906
  const providerId = normalizeToolUseId(providerToolUseId);
@@ -15028,7 +15219,7 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
15028
15219
  break;
15029
15220
  case "result":
15030
15221
  if (event.usage)
15031
- recordUsage(userId, topicTitle, event.usage);
15222
+ recordEventUsage(event.usage);
15032
15223
  {
15033
15224
  const usage = event.usage ? {
15034
15225
  input: event.usage.inputTokens,
@@ -15078,6 +15269,8 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
15078
15269
  }
15079
15270
  break;
15080
15271
  case "error":
15272
+ if (event.usage)
15273
+ recordEventUsage(event.usage);
15081
15274
  logger.warn({ topicId, queryId, agentType, model, silent, error: event.content }, "ai: provider returned error");
15082
15275
  terminalEmitted = true;
15083
15276
  errorOccurred = true;
@@ -19551,7 +19744,7 @@ function createTokenStatsMcpServer(context, host = defaultTokenStatsMcpHost) {
19551
19744
  throw new Error("token-stats MCP requires userId");
19552
19745
  const server = new McpServer6({ name: "token-stats", version: "1.0.0" });
19553
19746
  server.tool("get_usage_stats", [
19554
- "Claude \uC0AC\uC6A9\uB7C9 \uD1B5\uACC4\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4. \uC644\uB8CC\uB41C \uCFFC\uB9AC\uB9C8\uB2E4 \uC138\uC158/\uC2DC\uAC04 \uC815\uBCF4\uAC00 \uAE30\uB85D\uB429\uB2C8\uB2E4. \uD604\uC7AC \uC2E4\uD589 \uC911\uC778 turn\uC740 \uC644\uB8CC \uD6C4 \uC9D1\uACC4\uB429\uB2C8\uB2E4.",
19747
+ "Claude/Codex/Maestro \uC0AC\uC6A9\uB7C9\uACFC provider session\uC758 \uCD5C\uC2E0 context \uC810\uC720\uC728\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. \uD604\uC7AC \uC2E4\uD589 \uC911\uC778 turn\uC740 \uC644\uB8CC \uD6C4 \uC9D1\uACC4\uB429\uB2C8\uB2E4.",
19555
19748
  "",
19556
19749
  "from/to \uC0DD\uB7B5 \uC2DC \uC804\uCCB4 \uAE30\uAC04. \uC790\uC5F0\uC5B4 \uC2DC\uAC04 \uD45C\uD604\uC744 ISO \uD615\uC2DD\uC73C\uB85C \uBCC0\uD658\uD574\uC11C \uB118\uACA8\uC8FC\uC138\uC694.",
19557
19750
  " \uC608: '\uC9C0\uB09C 3\uC2DC\uAC04' \u2192 from = \uD604\uC7AC -3h",
@@ -19565,18 +19758,36 @@ function createTokenStatsMcpServer(context, host = defaultTokenStatsMcpHost) {
19565
19758
  to: z7.string().optional().describe("\uC885\uB8CC \uC2DC\uAC01 ISO 8601. \uC0DD\uB7B5 \uC2DC \uD604\uC7AC"),
19566
19759
  groupBy: z7.enum(["hour", "session"]).optional().describe("\uC9D1\uACC4 \uAE30\uC900. \uAE30\uBCF8\uAC12: hour")
19567
19760
  }, async ({ from, to, groupBy = "hour" }) => {
19568
- const { total, byHour, bySession, estimatedCostUsd } = host.getStats(context.userId, from, to);
19761
+ const snapshot = host.getStats(context.userId, from, to);
19762
+ const {
19763
+ total,
19764
+ byHour,
19765
+ bySession,
19766
+ currentSessions = [],
19767
+ ignoredLegacyRecords = 0,
19768
+ estimatedCostUsd
19769
+ } = snapshot;
19569
19770
  const lines = [
19570
- "\uD83D\uDCCA Claude \uC0AC\uC6A9\uB7C9",
19771
+ "\uD83D\uDCCA Agent \uC0AC\uC6A9\uB7C9",
19571
19772
  from || to ? `\uAE30\uAC04: ${from ?? "\uC804\uCCB4"} ~ ${to ?? "\uD604\uC7AC"}` : "\uAE30\uAC04: \uC804\uCCB4",
19572
19773
  "",
19573
19774
  `\uCFFC\uB9AC \uD69F\uC218: ${total.queries.toLocaleString()}\uD68C`,
19574
- `\uC785\uB825 \uD1A0\uD070: ${total.inputTokens.toLocaleString()}`,
19775
+ `\uCE90\uC2DC \uBBF8\uC801\uC911 \uC785\uB825: ${total.inputTokens.toLocaleString()}`,
19575
19776
  `\uCD9C\uB825 \uD1A0\uD070: ${total.outputTokens.toLocaleString()}`,
19576
19777
  `\uCE90\uC2DC \uC4F0\uAE30: ${total.cacheCreationInputTokens.toLocaleString()}`,
19577
19778
  `\uCE90\uC2DC \uC77D\uAE30: ${total.cacheReadInputTokens.toLocaleString()}`,
19578
19779
  `\uC608\uC0C1 \uBE44\uC6A9: $${estimatedCostUsd.toFixed(4)} USD`
19579
19780
  ];
19781
+ if (ignoredLegacyRecords > 0) {
19782
+ lines.push(`\uAD6C\uD615 \uB808\uCF54\uB4DC \uC81C\uC678: ${ignoredLegacyRecords.toLocaleString()}\uAC74`);
19783
+ }
19784
+ if (currentSessions.length > 0) {
19785
+ lines.push("", `\uD83D\uDCD0 \uD604\uC7AC context (${currentSessions.length}\uAC1C provider session)`);
19786
+ for (const current3 of currentSessions) {
19787
+ const percent = current3.contextTokens / current3.contextWindow * 100;
19788
+ lines.push(` ${current3.topicTitle} \xB7 ${current3.agent}/${current3.model} ${current3.contextTokens.toLocaleString()} / ${current3.contextWindow.toLocaleString()} (${percent.toFixed(1)}%)`);
19789
+ }
19790
+ }
19580
19791
  const extraSummaryLines = host.extraSummaryLines?.(context.userId) ?? [];
19581
19792
  if (extraSummaryLines.length > 0)
19582
19793
  lines.push("", ...extraSummaryLines);
@@ -19584,7 +19795,7 @@ function createTokenStatsMcpServer(context, host = defaultTokenStatsMcpHost) {
19584
19795
  const sorted = Object.entries(bySession).map(([name, bucket]) => ({ name, ...bucket, cost: host.calcCost(bucket) })).sort((left, right) => right.cost - left.cost);
19585
19796
  lines.push("", `\uD83D\uDDC2 \uC138\uC158\uBCC4 \uC0AC\uC6A9\uB7C9 (${sorted.length}\uAC1C)`);
19586
19797
  for (const session of sorted) {
19587
- lines.push(` ${session.name} \uCFFC\uB9AC ${session.queries}\uD68C \uC785\uB825 ${session.inputTokens.toLocaleString()} / \uCD9C\uB825 ${session.outputTokens.toLocaleString()} $${session.cost.toFixed(4)}`);
19798
+ lines.push(` ${session.name} \uCFFC\uB9AC ${session.queries}\uD68C \uCE90\uC2DC \uBBF8\uC801\uC911 \uC785\uB825 ${session.inputTokens.toLocaleString()} / \uCE90\uC2DC \uC77D\uAE30 ${session.cacheReadInputTokens.toLocaleString()} / \uCD9C\uB825 ${session.outputTokens.toLocaleString()} $${session.cost.toFixed(4)}`);
19588
19799
  }
19589
19800
  } else {
19590
19801
  const hours = Object.keys(byHour).sort();
@@ -19696,4 +19907,4 @@ export {
19696
19907
  createAgentHealthMcpServer
19697
19908
  };
19698
19909
 
19699
- //# debugId=4E9AA09E3DBFE2A464756E2164756E21
19910
+ //# debugId=AD21E471FB1B87CE64756E2164756E21