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/main.js
CHANGED
|
@@ -1572,7 +1572,7 @@ var exports_version = {};
|
|
|
1572
1572
|
__export(exports_version, {
|
|
1573
1573
|
NEGOTIUM_VERSION: () => NEGOTIUM_VERSION
|
|
1574
1574
|
});
|
|
1575
|
-
var NEGOTIUM_VERSION = "0.2.
|
|
1575
|
+
var NEGOTIUM_VERSION = "0.2.16";
|
|
1576
1576
|
|
|
1577
1577
|
// ../../packages/core/src/agents/codex-native-multi-agent.ts
|
|
1578
1578
|
import { spawn } from "child_process";
|
|
@@ -1925,7 +1925,7 @@ var init_canonical_bridge_config = __esm(() => {
|
|
|
1925
1925
|
});
|
|
1926
1926
|
|
|
1927
1927
|
// ../../packages/core/src/mcp/runtime-spec.ts
|
|
1928
|
-
import { createHmac, timingSafeEqual } from "crypto";
|
|
1928
|
+
import { createHash as createHash2, createHmac, timingSafeEqual } from "crypto";
|
|
1929
1929
|
function setRuntimeMcpPort(port) {
|
|
1930
1930
|
runtimePort = port;
|
|
1931
1931
|
}
|
|
@@ -2024,19 +2024,69 @@ function buildRuntimeMcpSpec(agent, ctx) {
|
|
|
2024
2024
|
return {
|
|
2025
2025
|
type: "sse",
|
|
2026
2026
|
url: `${base}/sse?${query}`,
|
|
2027
|
-
timeout: CLAUDE_MCP_TOOL_TIMEOUT_MS
|
|
2027
|
+
timeout: CLAUDE_MCP_TOOL_TIMEOUT_MS,
|
|
2028
|
+
...agent === "maestro" ? { lifecycle: "turn" } : {}
|
|
2028
2029
|
};
|
|
2029
2030
|
}
|
|
2031
|
+
function hostedMcpCacheIdentity(surface, ctx) {
|
|
2032
|
+
let semanticContext;
|
|
2033
|
+
switch (surface) {
|
|
2034
|
+
case "system-health":
|
|
2035
|
+
semanticContext = {};
|
|
2036
|
+
break;
|
|
2037
|
+
case "token-stats":
|
|
2038
|
+
case "agent-health":
|
|
2039
|
+
semanticContext = { userId: ctx.userId };
|
|
2040
|
+
break;
|
|
2041
|
+
case "task":
|
|
2042
|
+
semanticContext = {
|
|
2043
|
+
userId: ctx.userId,
|
|
2044
|
+
topicTitle: ctx.topicTitle,
|
|
2045
|
+
topicId: ctx.topicId ?? null
|
|
2046
|
+
};
|
|
2047
|
+
break;
|
|
2048
|
+
case "wiki":
|
|
2049
|
+
case "skills":
|
|
2050
|
+
semanticContext = {
|
|
2051
|
+
userId: ctx.userId,
|
|
2052
|
+
topicId: ctx.wikiTopicId ?? ctx.topicId ?? null
|
|
2053
|
+
};
|
|
2054
|
+
break;
|
|
2055
|
+
case "vault":
|
|
2056
|
+
semanticContext = { userId: ctx.userId, cwd: ctx.cwd, agent: ctx.agent };
|
|
2057
|
+
break;
|
|
2058
|
+
case "session-comm":
|
|
2059
|
+
semanticContext = {
|
|
2060
|
+
userId: ctx.userId,
|
|
2061
|
+
topicTitle: ctx.topicTitle,
|
|
2062
|
+
topicId: ctx.topicId ?? null,
|
|
2063
|
+
subagentParentTopicId: ctx.subagentParentTopicId ?? null,
|
|
2064
|
+
depth: ctx.depth ?? 0,
|
|
2065
|
+
silent: ctx.silent ?? false,
|
|
2066
|
+
agent: ctx.agent,
|
|
2067
|
+
peerBridge: ctx.peerBridge ?? null
|
|
2068
|
+
};
|
|
2069
|
+
break;
|
|
2070
|
+
}
|
|
2071
|
+
const digest = createHash2("sha256").update(JSON.stringify([surface, semanticContext])).digest("hex").slice(0, 24);
|
|
2072
|
+
return `hosted:${surface}:${digest}`;
|
|
2073
|
+
}
|
|
2030
2074
|
function buildHostedMcpSpec(agent, surface, ctx) {
|
|
2031
2075
|
const token = issueHostedMcpToken(surface, ctx);
|
|
2032
2076
|
const base = `http://127.0.0.1:${runtimePort}${RUNTIME_MCP_BASE_PATH}/${surface}`;
|
|
2033
2077
|
const query = `token=${encodeURIComponent(token)}`;
|
|
2034
2078
|
if (agent === "codex")
|
|
2035
2079
|
return { url: `${base}/mcp?${query}` };
|
|
2080
|
+
const queryBound = surface === "session-comm" && (ctx.silent === true || ctx.peerBridge !== undefined);
|
|
2081
|
+
const lifecycle = queryBound ? "turn" : surface === "session-comm" ? "session" : "process";
|
|
2036
2082
|
return {
|
|
2037
2083
|
type: "sse",
|
|
2038
2084
|
url: `${base}/sse?${query}`,
|
|
2039
|
-
timeout: CLAUDE_MCP_TOOL_TIMEOUT_MS
|
|
2085
|
+
timeout: CLAUDE_MCP_TOOL_TIMEOUT_MS,
|
|
2086
|
+
...agent === "maestro" ? {
|
|
2087
|
+
lifecycle,
|
|
2088
|
+
cacheKey: hostedMcpCacheIdentity(surface, ctx)
|
|
2089
|
+
} : {}
|
|
2040
2090
|
};
|
|
2041
2091
|
}
|
|
2042
2092
|
var RUNTIME_MCP_KEY = "runtime", RUNTIME_MCP_BASE_PATH = "/mcp/runtime", HOSTED_MCP_SURFACES, TOKEN_TTL_MS, CLAUDE_MCP_TOOL_TIMEOUT_MS = 600000, runtimePort;
|
|
@@ -3176,6 +3226,36 @@ function extractLatestCodexContextUsage(jsonl) {
|
|
|
3176
3226
|
}
|
|
3177
3227
|
return;
|
|
3178
3228
|
}
|
|
3229
|
+
function extractLatestCodexTokenTotals(jsonl) {
|
|
3230
|
+
const lines = jsonl.trimEnd().split(`
|
|
3231
|
+
`);
|
|
3232
|
+
for (let index = lines.length - 1;index >= 0; index -= 1) {
|
|
3233
|
+
try {
|
|
3234
|
+
const entry = JSON.parse(lines[index] ?? "");
|
|
3235
|
+
if (entry.type !== "event_msg" || entry.payload?.type !== "token_count")
|
|
3236
|
+
continue;
|
|
3237
|
+
const usage = entry.payload.info?.total_token_usage;
|
|
3238
|
+
if (!usage)
|
|
3239
|
+
continue;
|
|
3240
|
+
const values = [
|
|
3241
|
+
usage.input_tokens,
|
|
3242
|
+
usage.output_tokens,
|
|
3243
|
+
usage.cached_input_tokens,
|
|
3244
|
+
usage.cache_write_input_tokens
|
|
3245
|
+
];
|
|
3246
|
+
if (values.some((value) => value !== undefined && (!Number.isFinite(value) || value < 0))) {
|
|
3247
|
+
continue;
|
|
3248
|
+
}
|
|
3249
|
+
return {
|
|
3250
|
+
inputTokens: usage.input_tokens ?? 0,
|
|
3251
|
+
outputTokens: usage.output_tokens ?? 0,
|
|
3252
|
+
cachedInputTokens: usage.cached_input_tokens ?? 0,
|
|
3253
|
+
cacheWriteInputTokens: usage.cache_write_input_tokens ?? 0
|
|
3254
|
+
};
|
|
3255
|
+
} catch {}
|
|
3256
|
+
}
|
|
3257
|
+
return;
|
|
3258
|
+
}
|
|
3179
3259
|
function readLatestCodexContextUsage(threadId) {
|
|
3180
3260
|
const path = latestCodexRolloutPath(threadId);
|
|
3181
3261
|
if (!path)
|
|
@@ -3187,6 +3267,17 @@ function readLatestCodexContextUsage(threadId) {
|
|
|
3187
3267
|
return;
|
|
3188
3268
|
}
|
|
3189
3269
|
}
|
|
3270
|
+
function readLatestCodexTokenTotals(threadId) {
|
|
3271
|
+
const path = latestCodexRolloutPath(threadId);
|
|
3272
|
+
if (!path)
|
|
3273
|
+
return;
|
|
3274
|
+
try {
|
|
3275
|
+
return extractLatestCodexTokenTotals(readFileSync5(path, "utf8"));
|
|
3276
|
+
} catch (error) {
|
|
3277
|
+
logger.debug({ error, threadId }, "codex token totals: rollout read failed");
|
|
3278
|
+
return;
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3190
3281
|
function migrateCodexRolloutNativeMultiAgentMetadata(threadId) {
|
|
3191
3282
|
const path = latestCodexRolloutPath(threadId);
|
|
3192
3283
|
if (!path)
|
|
@@ -5554,6 +5645,7 @@ var init_tool_format = __esm(() => {
|
|
|
5554
5645
|
var exports_codex_provider = {};
|
|
5555
5646
|
__export(exports_codex_provider, {
|
|
5556
5647
|
toCodexMcpServers: () => toCodexMcpServers,
|
|
5648
|
+
normalizeCodexTurnUsage: () => normalizeCodexTurnUsage,
|
|
5557
5649
|
codexProvider: () => codexProvider
|
|
5558
5650
|
});
|
|
5559
5651
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
@@ -5561,6 +5653,20 @@ import { existsSync as existsSync13, readFileSync as readFileSync11, realpathSyn
|
|
|
5561
5653
|
import { homedir as homedir7 } from "os";
|
|
5562
5654
|
import { dirname as dirname9, isAbsolute as isAbsolute2, join as join13, relative, resolve as resolve8 } from "path";
|
|
5563
5655
|
import { Codex } from "@openai/codex-sdk";
|
|
5656
|
+
function sameCodexUsage(usage, total) {
|
|
5657
|
+
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;
|
|
5658
|
+
}
|
|
5659
|
+
function normalizeCodexTurnUsage(usage, baseline, rolloutTotal) {
|
|
5660
|
+
if (!baseline || !rolloutTotal || !sameCodexUsage(usage, rolloutTotal))
|
|
5661
|
+
return usage;
|
|
5662
|
+
const delta = (current, previous) => current >= previous ? current - previous : current;
|
|
5663
|
+
return {
|
|
5664
|
+
input_tokens: delta(rolloutTotal.inputTokens, baseline.inputTokens),
|
|
5665
|
+
output_tokens: delta(rolloutTotal.outputTokens, baseline.outputTokens),
|
|
5666
|
+
cached_input_tokens: delta(rolloutTotal.cachedInputTokens, baseline.cachedInputTokens),
|
|
5667
|
+
cache_write_input_tokens: delta(rolloutTotal.cacheWriteInputTokens, baseline.cacheWriteInputTokens)
|
|
5668
|
+
};
|
|
5669
|
+
}
|
|
5564
5670
|
function mapEffort(effort) {
|
|
5565
5671
|
if (!effort)
|
|
5566
5672
|
return;
|
|
@@ -5960,6 +6066,7 @@ async function* codexProvider(opts) {
|
|
|
5960
6066
|
...opts.effort ? { modelReasoningEffort: mapEffort(opts.effort) } : {}
|
|
5961
6067
|
};
|
|
5962
6068
|
let currentSessionId = opts.sessionId;
|
|
6069
|
+
let usageBaseline = currentSessionId ? readLatestCodexTokenTotals(currentSessionId) : undefined;
|
|
5963
6070
|
let thread = currentSessionId ? codex.resumeThread(currentSessionId, threadOptions) : codex.startThread(threadOptions);
|
|
5964
6071
|
let prompt = promptForThread(opts, true);
|
|
5965
6072
|
let agentTextSoFar = "";
|
|
@@ -6018,6 +6125,7 @@ async function* codexProvider(opts) {
|
|
|
6018
6125
|
thread = codex.startThread(threadOptions);
|
|
6019
6126
|
prompt = promptForThread(opts, true);
|
|
6020
6127
|
currentSessionId = undefined;
|
|
6128
|
+
usageBaseline = undefined;
|
|
6021
6129
|
startResult = await startStreamedWithTracking(thread, prompt, attemptAbort.signal, trackedPids);
|
|
6022
6130
|
}
|
|
6023
6131
|
if (attemptAbort.signal.aborted) {
|
|
@@ -6118,10 +6226,11 @@ async function* codexProvider(opts) {
|
|
|
6118
6226
|
break;
|
|
6119
6227
|
}
|
|
6120
6228
|
case "turn.completed": {
|
|
6121
|
-
const
|
|
6122
|
-
if (!
|
|
6229
|
+
const rawUsage = event.usage;
|
|
6230
|
+
if (!rawUsage)
|
|
6123
6231
|
break;
|
|
6124
6232
|
attemptCompleted = true;
|
|
6233
|
+
const usage = normalizeCodexTurnUsage(rawUsage, usageBaseline, currentSessionId ? readLatestCodexTokenTotals(currentSessionId) : undefined);
|
|
6125
6234
|
const contextUsage = currentSessionId ? readLatestCodexContextUsage(currentSessionId) : undefined;
|
|
6126
6235
|
yield {
|
|
6127
6236
|
type: "result",
|
|
@@ -6130,6 +6239,7 @@ async function* codexProvider(opts) {
|
|
|
6130
6239
|
usage: {
|
|
6131
6240
|
inputTokens: usage.input_tokens,
|
|
6132
6241
|
outputTokens: usage.output_tokens,
|
|
6242
|
+
cacheCreationInputTokens: usage.cache_write_input_tokens,
|
|
6133
6243
|
cacheReadInputTokens: usage.cached_input_tokens,
|
|
6134
6244
|
...contextUsage
|
|
6135
6245
|
}
|
|
@@ -8529,7 +8639,7 @@ var init_runtime_process_leases = __esm(async () => {
|
|
|
8529
8639
|
});
|
|
8530
8640
|
|
|
8531
8641
|
// ../../packages/core/src/agents/mcp-tools/ask-user.ts
|
|
8532
|
-
import { createHash as
|
|
8642
|
+
import { createHash as createHash3, randomUUID as randomUUID7 } from "crypto";
|
|
8533
8643
|
import { z } from "zod";
|
|
8534
8644
|
function normalizeAskText(value, maxChars) {
|
|
8535
8645
|
if (typeof value !== "string")
|
|
@@ -8566,7 +8676,7 @@ function normalizeAskUserQuestionInput(input) {
|
|
|
8566
8676
|
return { question, choices };
|
|
8567
8677
|
}
|
|
8568
8678
|
function askBodyHash(question, choices) {
|
|
8569
|
-
return
|
|
8679
|
+
return createHash3("sha256").update(JSON.stringify({ question, choices })).digest("hex");
|
|
8570
8680
|
}
|
|
8571
8681
|
function answerToolResult(answer) {
|
|
8572
8682
|
if (!answer.userId)
|
|
@@ -10070,7 +10180,7 @@ var init_browser_profiles = __esm(async () => {
|
|
|
10070
10180
|
|
|
10071
10181
|
// ../../packages/core/src/platform/playwright/manager.ts
|
|
10072
10182
|
import { execFileSync as execFileSync7, spawn as spawn5 } from "child_process";
|
|
10073
|
-
import { createHash as
|
|
10183
|
+
import { createHash as createHash4, randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
10074
10184
|
import {
|
|
10075
10185
|
cpSync,
|
|
10076
10186
|
existsSync as existsSync15,
|
|
@@ -10165,7 +10275,7 @@ function parseInstanceKey(instanceKey) {
|
|
|
10165
10275
|
};
|
|
10166
10276
|
}
|
|
10167
10277
|
function portFileName(instanceKey) {
|
|
10168
|
-
return
|
|
10278
|
+
return createHash4("sha256").update(instanceKey).digest("hex").slice(0, 24);
|
|
10169
10279
|
}
|
|
10170
10280
|
function writePortFile(instanceKey, port) {
|
|
10171
10281
|
try {
|
|
@@ -14109,7 +14219,7 @@ __export(exports_derive, {
|
|
|
14109
14219
|
TopicForkCompactionError: () => TopicForkCompactionError,
|
|
14110
14220
|
TopicDeriveBusyError: () => TopicDeriveBusyError
|
|
14111
14221
|
});
|
|
14112
|
-
import { createHash as
|
|
14222
|
+
import { createHash as createHash5, randomUUID as randomUUID12 } from "crypto";
|
|
14113
14223
|
import { mkdirSync as mkdirSync13, rmSync as rmSync5, unlinkSync as unlinkSync13 } from "fs";
|
|
14114
14224
|
function getTopics() {
|
|
14115
14225
|
return listTopics().filter((topic) => !isLegacySharedGeneral(topic.id));
|
|
@@ -14175,7 +14285,7 @@ function captureForkSnapshot(sourceTopicId, userId, topicTitle) {
|
|
|
14175
14285
|
maxRowid: messageRows.at(-1)?.rowid ?? 0
|
|
14176
14286
|
},
|
|
14177
14287
|
active: isTopicRunning(sourceTopicId),
|
|
14178
|
-
canonicalDigest:
|
|
14288
|
+
canonicalDigest: createHash5("sha256").update(entries.map((entry) => JSON.stringify(entry)).join(`
|
|
14179
14289
|
`)).digest("hex")
|
|
14180
14290
|
};
|
|
14181
14291
|
}
|
|
@@ -15251,7 +15361,7 @@ __export(exports_session_asks, {
|
|
|
15251
15361
|
clearPendingAsk: () => clearPendingAsk,
|
|
15252
15362
|
PENDING_ASK_TTL_MS: () => PENDING_ASK_TTL_MS
|
|
15253
15363
|
});
|
|
15254
|
-
import { createHash as
|
|
15364
|
+
import { createHash as createHash6 } from "crypto";
|
|
15255
15365
|
import {
|
|
15256
15366
|
closeSync as closeSync2,
|
|
15257
15367
|
mkdirSync as mkdirSync15,
|
|
@@ -15265,14 +15375,14 @@ import {
|
|
|
15265
15375
|
import { dirname as dirname13, join as join24 } from "path";
|
|
15266
15376
|
function pendingAskDir(userId) {
|
|
15267
15377
|
const rawUserId = String(userId);
|
|
15268
|
-
const safeUserId = /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) && !rawUserId.includes("..") ? rawUserId : `sha256-${
|
|
15378
|
+
const safeUserId = /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) && !rawUserId.includes("..") ? rawUserId : `sha256-${createHash6("sha256").update(rawUserId).digest("hex")}`;
|
|
15269
15379
|
return join24(resolveStorageSessionAsksDir(), safeUserId);
|
|
15270
15380
|
}
|
|
15271
15381
|
function encodeAskKey(key) {
|
|
15272
15382
|
return JSON.stringify([key.from, key.to]);
|
|
15273
15383
|
}
|
|
15274
15384
|
function pendingAskPath(key) {
|
|
15275
|
-
const digest =
|
|
15385
|
+
const digest = createHash6("sha256").update(encodeAskKey(key)).digest("hex");
|
|
15276
15386
|
return join24(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${digest}.pending`);
|
|
15277
15387
|
}
|
|
15278
15388
|
function v2PendingAskPath(key) {
|
|
@@ -16880,10 +16990,11 @@ var exports_token_stats = {};
|
|
|
16880
16990
|
__export(exports_token_stats, {
|
|
16881
16991
|
tokenStatsFileId: () => tokenStatsFileId,
|
|
16882
16992
|
recordUsage: () => recordUsage,
|
|
16993
|
+
getTopicStats: () => getTopicStats,
|
|
16883
16994
|
getStats: () => getStats,
|
|
16884
16995
|
calcCost: () => calcCost
|
|
16885
16996
|
});
|
|
16886
|
-
import { createHash as
|
|
16997
|
+
import { createHash as createHash7 } from "crypto";
|
|
16887
16998
|
import { mkdirSync as mkdirSync17 } from "fs";
|
|
16888
16999
|
import { join as join26 } from "path";
|
|
16889
17000
|
function emptyBucket() {
|
|
@@ -16892,12 +17003,13 @@ function emptyBucket() {
|
|
|
16892
17003
|
outputTokens: 0,
|
|
16893
17004
|
cacheCreationInputTokens: 0,
|
|
16894
17005
|
cacheReadInputTokens: 0,
|
|
16895
|
-
queries: 0
|
|
17006
|
+
queries: 0,
|
|
17007
|
+
estimatedCostUsd: 0
|
|
16896
17008
|
};
|
|
16897
17009
|
}
|
|
16898
17010
|
function tokenStatsFileId(userId) {
|
|
16899
17011
|
const rawUserId = String(userId);
|
|
16900
|
-
return /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) && !rawUserId.includes("..") ? rawUserId : `sha256-${
|
|
17012
|
+
return /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) && !rawUserId.includes("..") ? rawUserId : `sha256-${createHash7("sha256").update(rawUserId).digest("hex")}`;
|
|
16901
17013
|
}
|
|
16902
17014
|
function queriesPath(userId) {
|
|
16903
17015
|
const fileId = tokenStatsFileId(userId);
|
|
@@ -16919,16 +17031,41 @@ function loadRecords(userId) {
|
|
|
16919
17031
|
}
|
|
16920
17032
|
}
|
|
16921
17033
|
function calcCost(b) {
|
|
16922
|
-
return b.
|
|
17034
|
+
return b.estimatedCostUsd;
|
|
17035
|
+
}
|
|
17036
|
+
function estimateUsageCost(agent, model, usage) {
|
|
17037
|
+
const prices = TOKEN_PRICES[`${agent}:${model}`];
|
|
17038
|
+
if (!prices)
|
|
17039
|
+
return 0;
|
|
17040
|
+
return (usage.inputTokens * prices.input + usage.outputTokens * prices.output + (agent === "claude" ? usage.cacheCreationInputTokens * (prices.cacheWrite ?? prices.input) : 0) + usage.cacheReadInputTokens * prices.cacheRead) / 1e6;
|
|
16923
17041
|
}
|
|
16924
|
-
function
|
|
17042
|
+
function isQueryRecord(value) {
|
|
17043
|
+
if (!value || typeof value !== "object")
|
|
17044
|
+
return false;
|
|
17045
|
+
const record = value;
|
|
17046
|
+
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";
|
|
17047
|
+
}
|
|
17048
|
+
function recordUsage(userId, session, usage, context) {
|
|
17049
|
+
const cacheReadInputTokens = usage.cacheReadInputTokens ?? 0;
|
|
17050
|
+
const inputTokens = context.agent === "claude" ? usage.inputTokens : Math.max(0, usage.inputTokens - cacheReadInputTokens);
|
|
17051
|
+
const normalized = {
|
|
17052
|
+
inputTokens,
|
|
17053
|
+
outputTokens: usage.outputTokens,
|
|
17054
|
+
cacheCreationInputTokens: usage.cacheCreationInputTokens ?? 0,
|
|
17055
|
+
cacheReadInputTokens
|
|
17056
|
+
};
|
|
16925
17057
|
const record = {
|
|
17058
|
+
schemaVersion: 2,
|
|
16926
17059
|
timestamp: new Date().toISOString(),
|
|
16927
17060
|
session,
|
|
16928
|
-
|
|
16929
|
-
|
|
16930
|
-
|
|
16931
|
-
|
|
17061
|
+
topicId: context.topicId,
|
|
17062
|
+
...context.providerSessionId ? { providerSessionId: context.providerSessionId } : {},
|
|
17063
|
+
agent: context.agent,
|
|
17064
|
+
model: context.model,
|
|
17065
|
+
...normalized,
|
|
17066
|
+
...usage.contextTokens !== undefined ? { contextTokens: usage.contextTokens } : {},
|
|
17067
|
+
...usage.contextWindow !== undefined ? { contextWindow: usage.contextWindow } : {},
|
|
17068
|
+
estimatedCostUsd: usage.costUsd ?? estimateUsageCost(context.agent, context.model, normalized)
|
|
16932
17069
|
};
|
|
16933
17070
|
try {
|
|
16934
17071
|
appendJsonlEntry(queriesPath(userId), record);
|
|
@@ -16942,12 +17079,26 @@ function getStats(userId, from, to) {
|
|
|
16942
17079
|
const toTs = to ? new Date(to).getTime() : Infinity;
|
|
16943
17080
|
if (from && Number.isNaN(fromTs) || to && Number.isNaN(toTs)) {
|
|
16944
17081
|
logger.warn({ from, to }, "token-stats: Invalid date range, returning empty");
|
|
16945
|
-
return {
|
|
17082
|
+
return {
|
|
17083
|
+
total: emptyBucket(),
|
|
17084
|
+
byHour: {},
|
|
17085
|
+
bySession: {},
|
|
17086
|
+
currentSessions: [],
|
|
17087
|
+
ignoredLegacyRecords: 0,
|
|
17088
|
+
estimatedCostUsd: 0
|
|
17089
|
+
};
|
|
16946
17090
|
}
|
|
16947
17091
|
const total = emptyBucket();
|
|
16948
17092
|
const byHour = {};
|
|
16949
17093
|
const bySession = {};
|
|
16950
|
-
|
|
17094
|
+
const currentSessions = new Map;
|
|
17095
|
+
let ignoredLegacyRecords = 0;
|
|
17096
|
+
for (const raw of records) {
|
|
17097
|
+
if (!isQueryRecord(raw)) {
|
|
17098
|
+
ignoredLegacyRecords += 1;
|
|
17099
|
+
continue;
|
|
17100
|
+
}
|
|
17101
|
+
const r = raw;
|
|
16951
17102
|
const ts = new Date(r.timestamp).getTime();
|
|
16952
17103
|
if (ts < fromTs || ts > toTs)
|
|
16953
17104
|
continue;
|
|
@@ -16962,14 +17113,74 @@ function getStats(userId, from, to) {
|
|
|
16962
17113
|
bucket.cacheCreationInputTokens += r.cacheCreationInputTokens;
|
|
16963
17114
|
bucket.cacheReadInputTokens += r.cacheReadInputTokens;
|
|
16964
17115
|
bucket.queries += 1;
|
|
17116
|
+
bucket.estimatedCostUsd += r.estimatedCostUsd;
|
|
17117
|
+
}
|
|
17118
|
+
if (r.contextTokens !== undefined && r.contextWindow !== undefined && r.contextWindow > 0) {
|
|
17119
|
+
currentSessions.set(r.topicId, {
|
|
17120
|
+
timestamp: r.timestamp,
|
|
17121
|
+
topicId: r.topicId,
|
|
17122
|
+
topicTitle: r.session,
|
|
17123
|
+
...r.providerSessionId ? { providerSessionId: r.providerSessionId } : {},
|
|
17124
|
+
agent: r.agent,
|
|
17125
|
+
model: r.model,
|
|
17126
|
+
contextTokens: r.contextTokens,
|
|
17127
|
+
contextWindow: r.contextWindow
|
|
17128
|
+
});
|
|
17129
|
+
}
|
|
17130
|
+
}
|
|
17131
|
+
return {
|
|
17132
|
+
total,
|
|
17133
|
+
byHour,
|
|
17134
|
+
bySession,
|
|
17135
|
+
currentSessions: [...currentSessions.values()].sort((a, b) => b.timestamp.localeCompare(a.timestamp)),
|
|
17136
|
+
ignoredLegacyRecords,
|
|
17137
|
+
estimatedCostUsd: calcCost(total)
|
|
17138
|
+
};
|
|
17139
|
+
}
|
|
17140
|
+
function getTopicStats(userId, topicId) {
|
|
17141
|
+
const total = emptyBucket();
|
|
17142
|
+
let currentSession;
|
|
17143
|
+
for (const raw of loadRecords(userId)) {
|
|
17144
|
+
if (!isQueryRecord(raw) || raw.topicId !== topicId)
|
|
17145
|
+
continue;
|
|
17146
|
+
total.inputTokens += raw.inputTokens;
|
|
17147
|
+
total.outputTokens += raw.outputTokens;
|
|
17148
|
+
total.cacheCreationInputTokens += raw.cacheCreationInputTokens;
|
|
17149
|
+
total.cacheReadInputTokens += raw.cacheReadInputTokens;
|
|
17150
|
+
total.queries += 1;
|
|
17151
|
+
total.estimatedCostUsd += raw.estimatedCostUsd;
|
|
17152
|
+
if (raw.contextTokens !== undefined && raw.contextWindow !== undefined && raw.contextWindow > 0 && (!currentSession || raw.timestamp > currentSession.timestamp)) {
|
|
17153
|
+
currentSession = {
|
|
17154
|
+
timestamp: raw.timestamp,
|
|
17155
|
+
topicId: raw.topicId,
|
|
17156
|
+
topicTitle: raw.session,
|
|
17157
|
+
...raw.providerSessionId ? { providerSessionId: raw.providerSessionId } : {},
|
|
17158
|
+
agent: raw.agent,
|
|
17159
|
+
model: raw.model,
|
|
17160
|
+
contextTokens: raw.contextTokens,
|
|
17161
|
+
contextWindow: raw.contextWindow
|
|
17162
|
+
};
|
|
16965
17163
|
}
|
|
16966
17164
|
}
|
|
16967
|
-
return {
|
|
17165
|
+
return { topicId, ...total, ...currentSession ? { currentSession } : {} };
|
|
16968
17166
|
}
|
|
17167
|
+
var TOKEN_PRICES;
|
|
16969
17168
|
var init_token_stats = __esm(async () => {
|
|
16970
17169
|
init_jsonl();
|
|
16971
17170
|
init_logger();
|
|
16972
17171
|
await init_storage_host();
|
|
17172
|
+
TOKEN_PRICES = {
|
|
17173
|
+
"codex:gpt-5.6-sol": { input: 5, cacheRead: 0.5, output: 30 },
|
|
17174
|
+
"codex:gpt-5.6-terra": { input: 2.5, cacheRead: 0.25, output: 15 },
|
|
17175
|
+
"codex:gpt-5.6-luna": { input: 1, cacheRead: 0.1, output: 6 },
|
|
17176
|
+
"claude:fable": { input: 10, cacheWrite: 12.5, cacheRead: 1, output: 50 },
|
|
17177
|
+
"claude:opus": { input: 5, cacheWrite: 6.25, cacheRead: 0.5, output: 25 },
|
|
17178
|
+
"claude:sonnet": { input: 2, cacheWrite: 2.5, cacheRead: 0.2, output: 10 },
|
|
17179
|
+
"maestro:kimi-k3": { input: 3, cacheRead: 0.3, output: 15 },
|
|
17180
|
+
"maestro:kimi-k2.7-code": { input: 0.95, cacheRead: 0.19, output: 4 },
|
|
17181
|
+
"maestro:deepseek-pro": { input: 0.435, cacheRead: 0.003625, output: 0.87 },
|
|
17182
|
+
"maestro:deepseek-flash": { input: 0.14, cacheRead: 0.0028, output: 0.28 }
|
|
17183
|
+
};
|
|
16973
17184
|
});
|
|
16974
17185
|
|
|
16975
17186
|
// ../../packages/core/src/runtime/turn-event-stream.ts
|
|
@@ -17008,6 +17219,14 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
17008
17219
|
const toolSummaryOptions = {
|
|
17009
17220
|
cwd: control.injectParams?.cwd ?? workspaceCwdFor(topicId)
|
|
17010
17221
|
};
|
|
17222
|
+
const recordEventUsage = (usage) => {
|
|
17223
|
+
recordUsage(userId, topicTitle, usage, {
|
|
17224
|
+
topicId,
|
|
17225
|
+
...control.sessionId ? { providerSessionId: control.sessionId } : {},
|
|
17226
|
+
agent: agentType,
|
|
17227
|
+
model
|
|
17228
|
+
});
|
|
17229
|
+
};
|
|
17011
17230
|
const nextSyntheticToolUseId = () => `tool-${queryId}-${++syntheticToolCounter}`;
|
|
17012
17231
|
const bindToolUseId = (providerToolUseId, fallback) => {
|
|
17013
17232
|
const providerId = normalizeToolUseId(providerToolUseId);
|
|
@@ -17326,7 +17545,7 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
17326
17545
|
break;
|
|
17327
17546
|
case "result":
|
|
17328
17547
|
if (event.usage)
|
|
17329
|
-
|
|
17548
|
+
recordEventUsage(event.usage);
|
|
17330
17549
|
{
|
|
17331
17550
|
const usage = event.usage ? {
|
|
17332
17551
|
input: event.usage.inputTokens,
|
|
@@ -17376,6 +17595,8 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
17376
17595
|
}
|
|
17377
17596
|
break;
|
|
17378
17597
|
case "error":
|
|
17598
|
+
if (event.usage)
|
|
17599
|
+
recordEventUsage(event.usage);
|
|
17379
17600
|
logger.warn({ topicId, queryId, agentType, model, silent, error: event.content }, "ai: provider returned error");
|
|
17380
17601
|
terminalEmitted = true;
|
|
17381
17602
|
errorOccurred = true;
|
|
@@ -19929,7 +20150,7 @@ var init_runtime_gateway_submissions = __esm(async () => {
|
|
|
19929
20150
|
});
|
|
19930
20151
|
|
|
19931
20152
|
// ../../packages/core/src/application/submit-runtime-gateway-turn.ts
|
|
19932
|
-
import { createHash as
|
|
20153
|
+
import { createHash as createHash8, randomUUID as randomUUID17 } from "crypto";
|
|
19933
20154
|
function duplicateResult(submission, params, requestId, actorUserId, payloadHash) {
|
|
19934
20155
|
const message = getApiMessage(submission.topicId, submission.messageId);
|
|
19935
20156
|
if (!message)
|
|
@@ -19951,7 +20172,7 @@ function duplicateResult(submission, params, requestId, actorUserId, payloadHash
|
|
|
19951
20172
|
};
|
|
19952
20173
|
}
|
|
19953
20174
|
function gatewayPayloadHash(params, requestId, actorUserId) {
|
|
19954
|
-
return
|
|
20175
|
+
return createHash8("sha256").update(JSON.stringify([
|
|
19955
20176
|
params.topic.id,
|
|
19956
20177
|
params.userId,
|
|
19957
20178
|
actorUserId,
|
|
@@ -21431,7 +21652,7 @@ var init_file_ops = __esm(() => {
|
|
|
21431
21652
|
});
|
|
21432
21653
|
|
|
21433
21654
|
// ../../packages/core/src/runtime/inbox.ts
|
|
21434
|
-
import { createHash as
|
|
21655
|
+
import { createHash as createHash9, randomUUID as randomUUID20 } from "crypto";
|
|
21435
21656
|
import { readdirSync as readdirSync8, statSync as statSync13, writeFileSync as writeFileSync16 } from "fs";
|
|
21436
21657
|
import { join as join32 } from "path";
|
|
21437
21658
|
async function createAskForkPlan(options) {
|
|
@@ -21545,7 +21766,7 @@ async function notifyAskDrop(scope, entry, targetLabel, message) {
|
|
|
21545
21766
|
}
|
|
21546
21767
|
}
|
|
21547
21768
|
function stableLegacyRequestId(prefix, parts) {
|
|
21548
|
-
const hash =
|
|
21769
|
+
const hash = createHash9("sha256").update(parts.map((part) => String(part ?? "")).join("\x00")).digest("hex").slice(0, 16);
|
|
21549
21770
|
return `${prefix}-${hash}`;
|
|
21550
21771
|
}
|
|
21551
21772
|
async function flushSessionInbox() {
|
|
@@ -22553,6 +22774,7 @@ __export(exports_node_host, {
|
|
|
22553
22774
|
killAllBgBash: () => killAllBgBash,
|
|
22554
22775
|
isParticipant: () => isParticipant,
|
|
22555
22776
|
getVisibleTopics: () => getVisibleTopics,
|
|
22777
|
+
getTopicStats: () => getTopicStats,
|
|
22556
22778
|
getTopic: () => getTopic,
|
|
22557
22779
|
flushBashrsCompletions: () => flushBashrsCompletions,
|
|
22558
22780
|
executeVaultCommand: () => executeVaultCommand,
|
|
@@ -22607,6 +22829,7 @@ var init_node_host = __esm(async () => {
|
|
|
22607
22829
|
init_api_topics(),
|
|
22608
22830
|
init_runtime_events(),
|
|
22609
22831
|
init_runtime_process_leases(),
|
|
22832
|
+
init_token_stats(),
|
|
22610
22833
|
init_derive(),
|
|
22611
22834
|
init_personal_general(),
|
|
22612
22835
|
init_session()
|
|
@@ -23234,7 +23457,7 @@ function createTokenStatsMcpServer(context, host2 = defaultTokenStatsMcpHost) {
|
|
|
23234
23457
|
throw new Error("token-stats MCP requires userId");
|
|
23235
23458
|
const server = new McpServer2({ name: "token-stats", version: "1.0.0" });
|
|
23236
23459
|
server.tool("get_usage_stats", [
|
|
23237
|
-
"Claude \uC0AC\uC6A9\uB7C9
|
|
23460
|
+
"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.",
|
|
23238
23461
|
"",
|
|
23239
23462
|
"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.",
|
|
23240
23463
|
" \uC608: '\uC9C0\uB09C 3\uC2DC\uAC04' \u2192 from = \uD604\uC7AC -3h",
|
|
@@ -23248,18 +23471,36 @@ function createTokenStatsMcpServer(context, host2 = defaultTokenStatsMcpHost) {
|
|
|
23248
23471
|
to: z8.string().optional().describe("\uC885\uB8CC \uC2DC\uAC01 ISO 8601. \uC0DD\uB7B5 \uC2DC \uD604\uC7AC"),
|
|
23249
23472
|
groupBy: z8.enum(["hour", "session"]).optional().describe("\uC9D1\uACC4 \uAE30\uC900. \uAE30\uBCF8\uAC12: hour")
|
|
23250
23473
|
}, async ({ from, to, groupBy = "hour" }) => {
|
|
23251
|
-
const
|
|
23474
|
+
const snapshot = host2.getStats(context.userId, from, to);
|
|
23475
|
+
const {
|
|
23476
|
+
total,
|
|
23477
|
+
byHour,
|
|
23478
|
+
bySession,
|
|
23479
|
+
currentSessions = [],
|
|
23480
|
+
ignoredLegacyRecords = 0,
|
|
23481
|
+
estimatedCostUsd
|
|
23482
|
+
} = snapshot;
|
|
23252
23483
|
const lines = [
|
|
23253
|
-
"\uD83D\uDCCA
|
|
23484
|
+
"\uD83D\uDCCA Agent \uC0AC\uC6A9\uB7C9",
|
|
23254
23485
|
from || to ? `\uAE30\uAC04: ${from ?? "\uC804\uCCB4"} ~ ${to ?? "\uD604\uC7AC"}` : "\uAE30\uAC04: \uC804\uCCB4",
|
|
23255
23486
|
"",
|
|
23256
23487
|
`\uCFFC\uB9AC \uD69F\uC218: ${total.queries.toLocaleString()}\uD68C`,
|
|
23257
|
-
`\
|
|
23488
|
+
`\uCE90\uC2DC \uBBF8\uC801\uC911 \uC785\uB825: ${total.inputTokens.toLocaleString()}`,
|
|
23258
23489
|
`\uCD9C\uB825 \uD1A0\uD070: ${total.outputTokens.toLocaleString()}`,
|
|
23259
23490
|
`\uCE90\uC2DC \uC4F0\uAE30: ${total.cacheCreationInputTokens.toLocaleString()}`,
|
|
23260
23491
|
`\uCE90\uC2DC \uC77D\uAE30: ${total.cacheReadInputTokens.toLocaleString()}`,
|
|
23261
23492
|
`\uC608\uC0C1 \uBE44\uC6A9: $${estimatedCostUsd.toFixed(4)} USD`
|
|
23262
23493
|
];
|
|
23494
|
+
if (ignoredLegacyRecords > 0) {
|
|
23495
|
+
lines.push(`\uAD6C\uD615 \uB808\uCF54\uB4DC \uC81C\uC678: ${ignoredLegacyRecords.toLocaleString()}\uAC74`);
|
|
23496
|
+
}
|
|
23497
|
+
if (currentSessions.length > 0) {
|
|
23498
|
+
lines.push("", `\uD83D\uDCD0 \uD604\uC7AC context (${currentSessions.length}\uAC1C provider session)`);
|
|
23499
|
+
for (const current3 of currentSessions) {
|
|
23500
|
+
const percent = current3.contextTokens / current3.contextWindow * 100;
|
|
23501
|
+
lines.push(` ${current3.topicTitle} \xB7 ${current3.agent}/${current3.model} ${current3.contextTokens.toLocaleString()} / ${current3.contextWindow.toLocaleString()} (${percent.toFixed(1)}%)`);
|
|
23502
|
+
}
|
|
23503
|
+
}
|
|
23263
23504
|
const extraSummaryLines = host2.extraSummaryLines?.(context.userId) ?? [];
|
|
23264
23505
|
if (extraSummaryLines.length > 0)
|
|
23265
23506
|
lines.push("", ...extraSummaryLines);
|
|
@@ -23267,7 +23508,7 @@ function createTokenStatsMcpServer(context, host2 = defaultTokenStatsMcpHost) {
|
|
|
23267
23508
|
const sorted = Object.entries(bySession).map(([name, bucket]) => ({ name, ...bucket, cost: host2.calcCost(bucket) })).sort((left, right) => right.cost - left.cost);
|
|
23268
23509
|
lines.push("", `\uD83D\uDDC2 \uC138\uC158\uBCC4 \uC0AC\uC6A9\uB7C9 (${sorted.length}\uAC1C)`);
|
|
23269
23510
|
for (const session of sorted) {
|
|
23270
|
-
lines.push(` ${session.name} \uCFFC\uB9AC ${session.queries}\uD68C \uC785\uB825 ${session.inputTokens.toLocaleString()} / \uCD9C\uB825 ${session.outputTokens.toLocaleString()} $${session.cost.toFixed(4)}`);
|
|
23511
|
+
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)}`);
|
|
23271
23512
|
}
|
|
23272
23513
|
} else {
|
|
23273
23514
|
const hours = Object.keys(byHour).sort();
|
|
@@ -26199,6 +26440,7 @@ __export(exports_storage_public, {
|
|
|
26199
26440
|
grantSubagentTellTarget: () => grantSubagentTellTarget,
|
|
26200
26441
|
getWikiDir: () => getWikiDir,
|
|
26201
26442
|
getUserConfig: () => getUserConfig,
|
|
26443
|
+
getTopicStats: () => getTopicStats,
|
|
26202
26444
|
getTopicSessionId: () => getTopicSessionId,
|
|
26203
26445
|
getTopicNames: () => getTopicNames,
|
|
26204
26446
|
getTopicMemoryOrigin: () => getTopicMemoryOrigin,
|
|
@@ -26549,7 +26791,7 @@ data: ${JSON.stringify(message)}
|
|
|
26549
26791
|
var init_sse_transport = () => {};
|
|
26550
26792
|
|
|
26551
26793
|
// ../../packages/mcp/src/server.ts
|
|
26552
|
-
import { createHash as
|
|
26794
|
+
import { createHash as createHash10, randomUUID as randomUUID23 } from "crypto";
|
|
26553
26795
|
import { realpathSync as realpathSync6, statSync as statSync17 } from "fs";
|
|
26554
26796
|
import { basename as basename9, extname as extname3, resolve as resolve19 } from "path";
|
|
26555
26797
|
import { McpServer as McpServer7 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -26822,7 +27064,7 @@ async function closeNegotiumMcpSessions() {
|
|
|
26822
27064
|
await Promise.all([...servers].map((server) => closeServer(server)));
|
|
26823
27065
|
}
|
|
26824
27066
|
function tokenFingerprint(token) {
|
|
26825
|
-
return
|
|
27067
|
+
return createHash10("sha256").update(token).digest("hex");
|
|
26826
27068
|
}
|
|
26827
27069
|
async function enforceSessionBounds(reserve = 0) {
|
|
26828
27070
|
const now = Date.now();
|
|
@@ -28273,6 +28515,14 @@ function createNodeControlHandler(options) {
|
|
|
28273
28515
|
const events = listRecentRuntimeEventsForTopic(topicId).map(runtimeEvent);
|
|
28274
28516
|
return Response.json({ ok: true, events });
|
|
28275
28517
|
}
|
|
28518
|
+
const usageMatch = path.match(/^\/topics\/([^/]+)\/usage$/);
|
|
28519
|
+
if (usageMatch && req.method === "GET") {
|
|
28520
|
+
const topicId = decodeURIComponent(usageMatch[1]);
|
|
28521
|
+
const userId = requiredText(url.searchParams.get("user"), "user");
|
|
28522
|
+
if (!topicForUser(topicId, userId))
|
|
28523
|
+
return jsonError(404, "Topic not found");
|
|
28524
|
+
return Response.json({ ok: true, usage: getTopicStats(userId, topicId) });
|
|
28525
|
+
}
|
|
28276
28526
|
const modelMatch = path.match(/^\/topics\/([^/]+)\/model$/);
|
|
28277
28527
|
if (modelMatch && req.method === "POST") {
|
|
28278
28528
|
const topicId = decodeURIComponent(modelMatch[1]);
|
|
@@ -31160,6 +31410,7 @@ function createInitialState(userId) {
|
|
|
31160
31410
|
backgroundSessions: [],
|
|
31161
31411
|
activeTopicId: null,
|
|
31162
31412
|
messages: {},
|
|
31413
|
+
topicUsage: {},
|
|
31163
31414
|
messageHistory: {},
|
|
31164
31415
|
activity: {},
|
|
31165
31416
|
input: "",
|
|
@@ -31195,6 +31446,12 @@ function activeTopic(state) {
|
|
|
31195
31446
|
function activeMessages(state) {
|
|
31196
31447
|
return state.activeTopicId ? state.messages[state.activeTopicId] ?? [] : [];
|
|
31197
31448
|
}
|
|
31449
|
+
function setTopicUsage(state, usage) {
|
|
31450
|
+
return {
|
|
31451
|
+
...state,
|
|
31452
|
+
topicUsage: { ...state.topicUsage, [usage.topicId]: usage }
|
|
31453
|
+
};
|
|
31454
|
+
}
|
|
31198
31455
|
function activeQuestion(state) {
|
|
31199
31456
|
return activeMessages(state).slice().reverse().find((message) => message.kind === "ask_user_question" && message.askUserQuestion && !message.askUserQuestion.expired && !message.askUserQuestion.selectedLabel) ?? null;
|
|
31200
31457
|
}
|
|
@@ -32565,14 +32822,28 @@ function tokenCount(value) {
|
|
|
32565
32822
|
if (value >= 1e6)
|
|
32566
32823
|
return `${(value / 1e6).toFixed(2)}M`;
|
|
32567
32824
|
if (value >= 1000)
|
|
32568
|
-
return `${(value / 1000).toFixed(value >= 1e5 ? 0 : 1)}
|
|
32825
|
+
return `${(value / 1000).toFixed(value >= 1e5 ? 0 : 1)}k`;
|
|
32569
32826
|
return String(Math.round(value));
|
|
32570
32827
|
}
|
|
32828
|
+
function latestActiveUsage(state) {
|
|
32829
|
+
return activeMessages(state).slice().reverse().find((message) => message.authorId === "ai" && message.usage)?.usage;
|
|
32830
|
+
}
|
|
32831
|
+
function activeContextUsage(state) {
|
|
32832
|
+
const usage = latestActiveUsage(state);
|
|
32833
|
+
if (usage?.context !== undefined && usage.contextWindow) {
|
|
32834
|
+
return { context: usage.context, contextWindow: usage.contextWindow };
|
|
32835
|
+
}
|
|
32836
|
+
const topic = activeTopic(state);
|
|
32837
|
+
const current3 = topic ? state.topicUsage[topic.id]?.currentSession : undefined;
|
|
32838
|
+
return current3 ? { context: current3.contextTokens, contextWindow: current3.contextWindow } : undefined;
|
|
32839
|
+
}
|
|
32571
32840
|
function statusLines(state) {
|
|
32572
32841
|
const topic = activeTopic(state);
|
|
32573
|
-
const
|
|
32574
|
-
const
|
|
32575
|
-
const
|
|
32842
|
+
const usage = latestActiveUsage(state);
|
|
32843
|
+
const contextUsage = activeContextUsage(state);
|
|
32844
|
+
const total = topic ? state.topicUsage[topic.id] : undefined;
|
|
32845
|
+
const hasTopicUsage = total !== undefined && total.queries > 0;
|
|
32846
|
+
const ratio = contextUsage?.context !== undefined && contextUsage.contextWindow ? Math.round(contextUsage.context / contextUsage.contextWindow * 100) : undefined;
|
|
32576
32847
|
return [
|
|
32577
32848
|
line(" Status", { fg: theme.accent, bold: true }),
|
|
32578
32849
|
line(""),
|
|
@@ -32581,7 +32852,7 @@ function statusLines(state) {
|
|
|
32581
32852
|
line(` Model ${effectiveTopicModel(topic)}`),
|
|
32582
32853
|
line(` Effort ${topic?.effectiveEffort ?? topic?.defaultEffort ?? "-"}`),
|
|
32583
32854
|
line(""),
|
|
32584
|
-
line(` Context ${tokenCount(
|
|
32855
|
+
line(` Context ${tokenCount(contextUsage?.context)} / ${tokenCount(contextUsage?.contextWindow)}${ratio === undefined ? "" : ` (${ratio}%)`}`, { fg: ratio !== undefined && ratio >= 80 ? theme.amber : theme.text }),
|
|
32585
32856
|
line(" Measured on the latest model request", { fg: theme.muted, dim: true }),
|
|
32586
32857
|
line(""),
|
|
32587
32858
|
line(` Last turn input ${tokenCount(usage?.input)} \xB7 output ${tokenCount(usage?.output)}`),
|
|
@@ -32591,6 +32862,14 @@ function statusLines(state) {
|
|
|
32591
32862
|
dim: true
|
|
32592
32863
|
}),
|
|
32593
32864
|
line(""),
|
|
32865
|
+
line(" Topic cumulative", { fg: theme.cyan, bold: true }),
|
|
32866
|
+
line(` Queries ${hasTopicUsage ? total.queries.toLocaleString() : "unavailable"}`),
|
|
32867
|
+
line(` Input ${hasTopicUsage ? tokenCount(total.inputTokens) : "unavailable"} cache miss`),
|
|
32868
|
+
line(` Output ${hasTopicUsage ? tokenCount(total.outputTokens) : "unavailable"}`),
|
|
32869
|
+
line(` Cache write ${hasTopicUsage ? tokenCount(total.cacheCreationInputTokens) : "unavailable"}`),
|
|
32870
|
+
line(` Cache read ${hasTopicUsage ? tokenCount(total.cacheReadInputTokens) : "unavailable"}`),
|
|
32871
|
+
line(` Est. cost ${hasTopicUsage ? `$${total.estimatedCostUsd.toFixed(4)}` : "unavailable"}`),
|
|
32872
|
+
line(""),
|
|
32594
32873
|
line(" Esc close", { fg: theme.muted })
|
|
32595
32874
|
];
|
|
32596
32875
|
}
|
|
@@ -33179,6 +33458,39 @@ function footerStatusText(state) {
|
|
|
33179
33458
|
title
|
|
33180
33459
|
];
|
|
33181
33460
|
}
|
|
33461
|
+
function footerUsageText(state) {
|
|
33462
|
+
const topic = activeTopic(state);
|
|
33463
|
+
if (!topic)
|
|
33464
|
+
return [""];
|
|
33465
|
+
const contextUsage = activeContextUsage(state);
|
|
33466
|
+
const total = state.topicUsage[topic.id];
|
|
33467
|
+
const ratio = contextUsage?.context !== undefined && contextUsage.contextWindow ? Math.round(contextUsage.context / contextUsage.contextWindow * 100) : undefined;
|
|
33468
|
+
const context = contextUsage?.context !== undefined && contextUsage.contextWindow ? `${tokenCount(contextUsage.context)}/${tokenCount(contextUsage.contextWindow)} ${ratio}%` : "";
|
|
33469
|
+
const hasTopicUsage = total !== undefined && total.queries > 0;
|
|
33470
|
+
const cumulative = hasTopicUsage ? `\u03A3 ${tokenCount(total.inputTokens)} in/${tokenCount(total.outputTokens)} out` : "";
|
|
33471
|
+
const cache = hasTopicUsage && total.cacheReadInputTokens ? `cache ${tokenCount(total.cacheReadInputTokens)}` : "";
|
|
33472
|
+
const cost = hasTopicUsage ? `est $${total.estimatedCostUsd.toFixed(2)}` : "";
|
|
33473
|
+
const parts = [context, cumulative, cache, cost].filter(Boolean);
|
|
33474
|
+
return [
|
|
33475
|
+
`${parts.join(" \xB7 ")} `,
|
|
33476
|
+
`${[context, cumulative, cache].filter(Boolean).join(" \xB7 ")} `,
|
|
33477
|
+
`${[context, cumulative].filter(Boolean).join(" \xB7 ")} `,
|
|
33478
|
+
context ? `${context} ` : "",
|
|
33479
|
+
""
|
|
33480
|
+
];
|
|
33481
|
+
}
|
|
33482
|
+
function footerStatusLine(state, width) {
|
|
33483
|
+
const leftCandidates = footerStatusText(state);
|
|
33484
|
+
const rightCandidates = footerUsageText(state);
|
|
33485
|
+
for (const right of rightCandidates) {
|
|
33486
|
+
for (const left of leftCandidates) {
|
|
33487
|
+
if (displayWidth(left) + (right ? 1 + displayWidth(right) : 0) <= width) {
|
|
33488
|
+
return joinSides(left, right, width);
|
|
33489
|
+
}
|
|
33490
|
+
}
|
|
33491
|
+
}
|
|
33492
|
+
return joinSides(leftCandidates[leftCandidates.length - 1] ?? "", "", width);
|
|
33493
|
+
}
|
|
33182
33494
|
function noticeStyle(state) {
|
|
33183
33495
|
return NOTICE_STYLE[state.noticeLevel ?? "warn"];
|
|
33184
33496
|
}
|
|
@@ -33228,7 +33540,7 @@ function footerLines(state, width) {
|
|
|
33228
33540
|
];
|
|
33229
33541
|
}
|
|
33230
33542
|
return [
|
|
33231
|
-
paint(
|
|
33543
|
+
paint(footerStatusLine(state, width), {
|
|
33232
33544
|
fg: theme.accent,
|
|
33233
33545
|
bg: theme.surfaceRaised,
|
|
33234
33546
|
bold: true
|
|
@@ -33813,6 +34125,9 @@ class EmbeddedNegotiumClient {
|
|
|
33813
34125
|
const rows = getAllMessagesForTopic(topicId);
|
|
33814
34126
|
return rows.map((row) => typeof row.id === "string" ? getApiMessage(topicId, row.id) : null).filter((message) => message !== null);
|
|
33815
34127
|
}
|
|
34128
|
+
listTopicUsage(topicId) {
|
|
34129
|
+
return getTopicStats(this.#userId, topicId);
|
|
34130
|
+
}
|
|
33816
34131
|
listMessagePage(topicId, cursor, limit = MESSAGE_HISTORY_PAGE_SIZE) {
|
|
33817
34132
|
const result = listApiMessages(topicId, { cursor, limit });
|
|
33818
34133
|
return { messages: result.page, cursor: result.cursor, hasMore: result.hasMore };
|
|
@@ -34008,6 +34323,10 @@ class RemoteNegotiumClient {
|
|
|
34008
34323
|
const result = await this.#request(`/topics/${encodeURIComponent(topicId)}/messages?user=${encodeURIComponent(this.#userId)}`);
|
|
34009
34324
|
return result.messages ?? [];
|
|
34010
34325
|
}
|
|
34326
|
+
async listTopicUsage(topicId) {
|
|
34327
|
+
const result = await this.#request(`/topics/${encodeURIComponent(topicId)}/usage?user=${encodeURIComponent(this.#userId)}`);
|
|
34328
|
+
return result.usage;
|
|
34329
|
+
}
|
|
34011
34330
|
async listMessagePage(topicId, cursor, limit = MESSAGE_HISTORY_PAGE_SIZE) {
|
|
34012
34331
|
const query2 = new URLSearchParams({ user: this.#userId, limit: String(limit) });
|
|
34013
34332
|
if (cursor)
|
|
@@ -34261,6 +34580,7 @@ var MESSAGE_HISTORY_PAGE_SIZE = 50, INITIAL_MESSAGE_HISTORY_PAGE_COUNT = 3, INIT
|
|
|
34261
34580
|
var init_client = __esm(async () => {
|
|
34262
34581
|
await __promiseAll([
|
|
34263
34582
|
init_src(),
|
|
34583
|
+
init_storage_public(),
|
|
34264
34584
|
init_src5(),
|
|
34265
34585
|
init_history_store()
|
|
34266
34586
|
]);
|
|
@@ -35360,6 +35680,7 @@ class TerminalApp {
|
|
|
35360
35680
|
#backgroundRefreshRunning = false;
|
|
35361
35681
|
#animationFrame = animationFrameAt();
|
|
35362
35682
|
#topicsRefreshGeneration = 0;
|
|
35683
|
+
#topicUsageRefreshGeneration = new Map;
|
|
35363
35684
|
#messageLoadGeneration = new Map;
|
|
35364
35685
|
#messageHistory = new Map;
|
|
35365
35686
|
#queuedRuntimeEvents = new Map;
|
|
@@ -35471,6 +35792,9 @@ class TerminalApp {
|
|
|
35471
35792
|
return;
|
|
35472
35793
|
}
|
|
35473
35794
|
this.#applyRuntimeEvent(event);
|
|
35795
|
+
if (event.type === "message" && event.payload.usage || event.type === "message-updated" && event.payload.patch?.usage) {
|
|
35796
|
+
this.#refreshTopicUsage(event.topicId);
|
|
35797
|
+
}
|
|
35474
35798
|
if (event.type === "topic-created" || event.type === "topic-updated" || event.type === "topic-deleted") {
|
|
35475
35799
|
const previous = this.#state.activeTopicId;
|
|
35476
35800
|
const refreshSubagentGraph = this.#state.overlay === "subagents";
|
|
@@ -35578,6 +35902,20 @@ class TerminalApp {
|
|
|
35578
35902
|
this.#backgroundRefreshRunning = false;
|
|
35579
35903
|
}
|
|
35580
35904
|
}
|
|
35905
|
+
async#refreshTopicUsage(topicId) {
|
|
35906
|
+
if (!this.#client.listTopicUsage)
|
|
35907
|
+
return;
|
|
35908
|
+
const generation = (this.#topicUsageRefreshGeneration.get(topicId) ?? 0) + 1;
|
|
35909
|
+
this.#topicUsageRefreshGeneration.set(topicId, generation);
|
|
35910
|
+
try {
|
|
35911
|
+
const usage = await this.#client.listTopicUsage(topicId);
|
|
35912
|
+
if (this.#topicUsageRefreshGeneration.get(topicId) !== generation)
|
|
35913
|
+
return;
|
|
35914
|
+
this.#state = setTopicUsage(this.#state, usage);
|
|
35915
|
+
if (this.#state.activeTopicId === topicId)
|
|
35916
|
+
this.#queueRender();
|
|
35917
|
+
} catch {}
|
|
35918
|
+
}
|
|
35581
35919
|
async#loadActiveMessages() {
|
|
35582
35920
|
const topic = activeTopic(this.#state);
|
|
35583
35921
|
if (!topic)
|
|
@@ -35625,6 +35963,7 @@ class TerminalApp {
|
|
|
35625
35963
|
this.#queuedRuntimeEvents.delete(topic.id);
|
|
35626
35964
|
for (const event of queued)
|
|
35627
35965
|
this.#applyRuntimeEvent(event);
|
|
35966
|
+
this.#refreshTopicUsage(topic.id);
|
|
35628
35967
|
}
|
|
35629
35968
|
async#loadOlderMessages(topicId, targetOffset) {
|
|
35630
35969
|
const history = this.#messageHistory.get(topicId);
|
|
@@ -39900,7 +40239,7 @@ __export(exports_join, {
|
|
|
39900
40239
|
joinCredentialDigest: () => joinCredentialDigest,
|
|
39901
40240
|
isJoinPersisted: () => isJoinPersisted
|
|
39902
40241
|
});
|
|
39903
|
-
import { createHash as
|
|
40242
|
+
import { createHash as createHash11, randomUUID as randomUUID27 } from "crypto";
|
|
39904
40243
|
import {
|
|
39905
40244
|
chmodSync as chmodSync6,
|
|
39906
40245
|
closeSync as closeSync5,
|
|
@@ -40064,7 +40403,7 @@ function normalizedJoin(join42) {
|
|
|
40064
40403
|
});
|
|
40065
40404
|
}
|
|
40066
40405
|
function joinCredentialDigest(join42) {
|
|
40067
|
-
return
|
|
40406
|
+
return createHash11("sha256").update(JSON.stringify(normalizedJoin(join42))).digest("base64url");
|
|
40068
40407
|
}
|
|
40069
40408
|
function readPersistedJoin(path = joinFilePath()) {
|
|
40070
40409
|
if (!existsSync35(path))
|
|
@@ -40455,7 +40794,7 @@ var init_canonical_mcp_bridge = __esm(async () => {
|
|
|
40455
40794
|
});
|
|
40456
40795
|
|
|
40457
40796
|
// ../../adapters/otium/src/store.ts
|
|
40458
|
-
import { createHash as
|
|
40797
|
+
import { createHash as createHash12 } from "crypto";
|
|
40459
40798
|
function isPeerDetached(hubNodeId) {
|
|
40460
40799
|
const row = db.query("SELECT status FROM otium_peer_lifecycle WHERE hub_node_id = ?").get(hubNodeId);
|
|
40461
40800
|
return row?.status === "detached";
|
|
@@ -40638,7 +40977,7 @@ function acknowledgePeerTerminal(hostNodeId, requestId) {
|
|
|
40638
40977
|
})();
|
|
40639
40978
|
}
|
|
40640
40979
|
function peerInboxPayloadHash(value) {
|
|
40641
|
-
return
|
|
40980
|
+
return createHash12("sha256").update(JSON.stringify(value)).digest("hex");
|
|
40642
40981
|
}
|
|
40643
40982
|
function claimPeerInboxRequest(args) {
|
|
40644
40983
|
const inserted = db.run(`INSERT OR IGNORE INTO otium_peer_inbox_requests
|
|
@@ -45311,4 +45650,4 @@ switch (command) {
|
|
|
45311
45650
|
}
|
|
45312
45651
|
}
|
|
45313
45652
|
|
|
45314
|
-
//# debugId=
|
|
45653
|
+
//# debugId=5D6FE2D4C44E041364756E2164756E21
|