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.
- package/dist/agent-helpers.js +178 -26
- package/dist/agent-helpers.js.map +8 -8
- package/dist/{chunk-5g5a63vv.js → chunk-5cpdbbeh.js} +56 -6
- package/dist/{chunk-5g5a63vv.js.map → chunk-5cpdbbeh.js.map} +4 -4
- package/dist/hosted-agent.js +118 -9
- package/dist/hosted-agent.js.map +6 -6
- package/dist/main.js +389 -50
- package/dist/main.js.map +15 -15
- package/dist/mcp-factories.js +247 -36
- package/dist/mcp-factories.js.map +9 -9
- package/dist/registry.js +3 -3
- package/dist/registry.js.map +2 -2
- package/dist/rollout.js +1 -1
- package/dist/runtime/src/agents/codex-provider.ts +53 -2
- package/dist/runtime/src/agents/rollout/codex.ts +62 -0
- package/dist/runtime/src/mcp/factories/token-stats.ts +33 -11
- package/dist/runtime/src/mcp/runtime-spec.ts +58 -1
- package/dist/runtime/src/node-host.ts +1 -0
- package/dist/runtime/src/runtime/public-helpers.ts +8 -0
- package/dist/runtime/src/runtime/turn-event-stream.ts +17 -2
- package/dist/runtime/src/storage/token-stats.ts +200 -19
- package/dist/runtime/src/version.ts +1 -1
- package/dist/runtime-helpers.js +443 -30
- package/dist/runtime-helpers.js.map +9 -3
- package/dist/storage.js +112 -11
- package/dist/storage.js.map +3 -3
- package/dist/types/packages/core/src/agents/codex-provider.d.ts +13 -0
- package/dist/types/packages/core/src/agents/rollout/codex.d.ts +9 -0
- package/dist/types/packages/core/src/mcp/factories/token-stats.d.ts +4 -2
- package/dist/types/packages/core/src/runtime/bashrs-completions.d.ts +50 -0
- package/dist/types/packages/core/src/runtime/public-helpers.d.ts +2 -0
- package/dist/types/packages/core/src/storage/token-stats.d.ts +35 -6
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/package.json +1 -1
package/dist/mcp-factories.js
CHANGED
|
@@ -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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
9477
|
-
if (!
|
|
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
|
|
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:
|
|
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
|
|
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-${
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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-${
|
|
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.
|
|
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
|
|
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
|
-
|
|
14631
|
-
|
|
14632
|
-
|
|
14633
|
-
|
|
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 {
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
`\
|
|
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=
|
|
19910
|
+
//# debugId=AD21E471FB1B87CE64756E2164756E21
|