negotium 0.2.15 → 0.2.17
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 +220 -32
- package/dist/agent-helpers.js.map +12 -12
- package/dist/background-bash.js.map +1 -1
- package/dist/browser-runtime.js.map +1 -1
- package/dist/{chunk-5g5a63vv.js → chunk-5cpdbbeh.js} +56 -6
- package/dist/{chunk-5g5a63vv.js.map → chunk-5cpdbbeh.js.map} +5 -5
- package/dist/hosted-agent.js +119 -9
- package/dist/hosted-agent.js.map +8 -8
- package/dist/main.js +931 -419
- package/dist/main.js.map +25 -24
- package/dist/mcp-factories.js +289 -42
- package/dist/mcp-factories.js.map +13 -13
- package/dist/prompts.js.map +1 -1
- package/dist/query-runtime.js.map +1 -1
- 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/compaction-support.ts +1 -1
- package/dist/runtime/src/agents/maestro-provider.ts +3 -0
- package/dist/runtime/src/agents/rollout/codex.ts +62 -0
- package/dist/runtime/src/index.ts +1 -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/turn-event-stream.ts +43 -2
- package/dist/runtime/src/runtime/turn-runner.ts +20 -9
- package/dist/runtime/src/storage/token-stats.ts +200 -19
- package/dist/runtime/src/types.ts +6 -0
- package/dist/runtime/src/version.ts +1 -1
- package/dist/runtime-helpers.js.map +1 -1
- package/dist/storage.js +112 -11
- package/dist/storage.js.map +4 -4
- package/dist/types/packages/core/src/agents/codex-provider.d.ts +13 -0
- package/dist/types/packages/core/src/agents/compaction-support.d.ts +1 -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/turn-runner.d.ts +4 -0
- package/dist/types/packages/core/src/storage/token-stats.d.ts +35 -6
- package/dist/types/packages/core/src/types.d.ts +6 -0
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/dist/vault.js.map +1 -1
- package/package.json +2 -2
package/dist/main.js
CHANGED
|
@@ -1010,6 +1010,70 @@ var init_codex_tree_kill = __esm(() => {
|
|
|
1010
1010
|
killOwnedCodexTreesForShutdown = manager.killOwnedTreesForShutdown;
|
|
1011
1011
|
});
|
|
1012
1012
|
|
|
1013
|
+
// ../../packages/core/src/agents/compaction-support.ts
|
|
1014
|
+
function isCjkCodePoint(code) {
|
|
1015
|
+
return code >= 12288 && code <= 19903 || code >= 19968 && code <= 40959 || code >= 4352 && code <= 4607 || code >= 44032 && code <= 55203 || code >= 63744 && code <= 64255 || code >= 65280 && code <= 65519 || code >= 131072 && code <= 262143;
|
|
1016
|
+
}
|
|
1017
|
+
function estimateTextTokens(text) {
|
|
1018
|
+
let cjkChars = 0;
|
|
1019
|
+
let cjkUnits = 0;
|
|
1020
|
+
for (let index = 0;index < text.length; index++) {
|
|
1021
|
+
const code = text.codePointAt(index) ?? 0;
|
|
1022
|
+
const wide = code > 65535;
|
|
1023
|
+
if (wide)
|
|
1024
|
+
index++;
|
|
1025
|
+
if (isCjkCodePoint(code)) {
|
|
1026
|
+
cjkChars++;
|
|
1027
|
+
cjkUnits += wide ? 2 : 1;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
const latinUnits = text.length - cjkUnits;
|
|
1031
|
+
return Math.ceil(cjkChars / CHARS_PER_CJK_TOKEN + latinUnits / CHARS_PER_TEXT_TOKEN);
|
|
1032
|
+
}
|
|
1033
|
+
function estimateConversationTokens(messages) {
|
|
1034
|
+
return messages.reduce((sum, message) => sum + 4 + estimateTextTokens(message.content), 0);
|
|
1035
|
+
}
|
|
1036
|
+
var ACTIVE_TASK_TEMPLATE = `You are compressing a long agent conversation so the main agent can continue
|
|
1037
|
+
without losing context. Produce a single concise summary using EXACTLY these
|
|
1038
|
+
section headers, in this order:
|
|
1039
|
+
|
|
1040
|
+
## Active Task
|
|
1041
|
+
One sentence: what is the agent currently working on?
|
|
1042
|
+
|
|
1043
|
+
## Goal
|
|
1044
|
+
One or two sentences: the user's overall objective in this session.
|
|
1045
|
+
|
|
1046
|
+
## Constraints
|
|
1047
|
+
Bulleted list of durable requirements, user preferences, technical limits, or
|
|
1048
|
+
process rules that should continue to govern the work. Skip if none.
|
|
1049
|
+
|
|
1050
|
+
## Key Decisions
|
|
1051
|
+
Bulleted list of decisions already made that should not be reopened unless the
|
|
1052
|
+
user asks. Include the rationale when it is short and important. Skip if none.
|
|
1053
|
+
|
|
1054
|
+
## Pending
|
|
1055
|
+
Bulleted list of unresolved items, decisions to make, or work explicitly
|
|
1056
|
+
deferred. Use "(blocked: <reason>)" when applicable.
|
|
1057
|
+
|
|
1058
|
+
## Next Steps
|
|
1059
|
+
Bulleted list of the concrete next actions the main agent should take after
|
|
1060
|
+
compaction, in likely execution order. Skip if none.
|
|
1061
|
+
|
|
1062
|
+
## Files
|
|
1063
|
+
Bulleted list of \`absolute/paths\` touched or referenced (read, written,
|
|
1064
|
+
inspected). Skip if none.
|
|
1065
|
+
|
|
1066
|
+
## Recent context
|
|
1067
|
+
3-5 bullets capturing the most recent tool calls + their salient outputs.
|
|
1068
|
+
Prefer specifics (paths, line numbers, exit codes, key values) over generic
|
|
1069
|
+
recaps. Skip details that have no bearing on the next step.
|
|
1070
|
+
|
|
1071
|
+
RULES:
|
|
1072
|
+
- Output ONLY the eight sections above, with no preamble or postscript.
|
|
1073
|
+
- Do NOT echo the user's words verbatim - paraphrase tightly.
|
|
1074
|
+
- Do NOT invent file paths or facts not present in the transcript.
|
|
1075
|
+
- Keep the entire summary under 1500 words.`, CHARS_PER_TEXT_TOKEN = 3.5, CHARS_PER_CJK_TOKEN = 0.9;
|
|
1076
|
+
|
|
1013
1077
|
// ../../packages/core/src/runtime/user-turn-envelope.ts
|
|
1014
1078
|
function legacyUserTurnEnvelope(prompt, attachments) {
|
|
1015
1079
|
return attachments?.length ? { prompt, attachments } : { prompt };
|
|
@@ -1572,7 +1636,7 @@ var exports_version = {};
|
|
|
1572
1636
|
__export(exports_version, {
|
|
1573
1637
|
NEGOTIUM_VERSION: () => NEGOTIUM_VERSION
|
|
1574
1638
|
});
|
|
1575
|
-
var NEGOTIUM_VERSION = "0.2.
|
|
1639
|
+
var NEGOTIUM_VERSION = "0.2.17";
|
|
1576
1640
|
|
|
1577
1641
|
// ../../packages/core/src/agents/codex-native-multi-agent.ts
|
|
1578
1642
|
import { spawn } from "child_process";
|
|
@@ -1925,7 +1989,7 @@ var init_canonical_bridge_config = __esm(() => {
|
|
|
1925
1989
|
});
|
|
1926
1990
|
|
|
1927
1991
|
// ../../packages/core/src/mcp/runtime-spec.ts
|
|
1928
|
-
import { createHmac, timingSafeEqual } from "crypto";
|
|
1992
|
+
import { createHash as createHash2, createHmac, timingSafeEqual } from "crypto";
|
|
1929
1993
|
function setRuntimeMcpPort(port) {
|
|
1930
1994
|
runtimePort = port;
|
|
1931
1995
|
}
|
|
@@ -2024,19 +2088,69 @@ function buildRuntimeMcpSpec(agent, ctx) {
|
|
|
2024
2088
|
return {
|
|
2025
2089
|
type: "sse",
|
|
2026
2090
|
url: `${base}/sse?${query}`,
|
|
2027
|
-
timeout: CLAUDE_MCP_TOOL_TIMEOUT_MS
|
|
2091
|
+
timeout: CLAUDE_MCP_TOOL_TIMEOUT_MS,
|
|
2092
|
+
...agent === "maestro" ? { lifecycle: "turn" } : {}
|
|
2028
2093
|
};
|
|
2029
2094
|
}
|
|
2095
|
+
function hostedMcpCacheIdentity(surface, ctx) {
|
|
2096
|
+
let semanticContext;
|
|
2097
|
+
switch (surface) {
|
|
2098
|
+
case "system-health":
|
|
2099
|
+
semanticContext = {};
|
|
2100
|
+
break;
|
|
2101
|
+
case "token-stats":
|
|
2102
|
+
case "agent-health":
|
|
2103
|
+
semanticContext = { userId: ctx.userId };
|
|
2104
|
+
break;
|
|
2105
|
+
case "task":
|
|
2106
|
+
semanticContext = {
|
|
2107
|
+
userId: ctx.userId,
|
|
2108
|
+
topicTitle: ctx.topicTitle,
|
|
2109
|
+
topicId: ctx.topicId ?? null
|
|
2110
|
+
};
|
|
2111
|
+
break;
|
|
2112
|
+
case "wiki":
|
|
2113
|
+
case "skills":
|
|
2114
|
+
semanticContext = {
|
|
2115
|
+
userId: ctx.userId,
|
|
2116
|
+
topicId: ctx.wikiTopicId ?? ctx.topicId ?? null
|
|
2117
|
+
};
|
|
2118
|
+
break;
|
|
2119
|
+
case "vault":
|
|
2120
|
+
semanticContext = { userId: ctx.userId, cwd: ctx.cwd, agent: ctx.agent };
|
|
2121
|
+
break;
|
|
2122
|
+
case "session-comm":
|
|
2123
|
+
semanticContext = {
|
|
2124
|
+
userId: ctx.userId,
|
|
2125
|
+
topicTitle: ctx.topicTitle,
|
|
2126
|
+
topicId: ctx.topicId ?? null,
|
|
2127
|
+
subagentParentTopicId: ctx.subagentParentTopicId ?? null,
|
|
2128
|
+
depth: ctx.depth ?? 0,
|
|
2129
|
+
silent: ctx.silent ?? false,
|
|
2130
|
+
agent: ctx.agent,
|
|
2131
|
+
peerBridge: ctx.peerBridge ?? null
|
|
2132
|
+
};
|
|
2133
|
+
break;
|
|
2134
|
+
}
|
|
2135
|
+
const digest = createHash2("sha256").update(JSON.stringify([surface, semanticContext])).digest("hex").slice(0, 24);
|
|
2136
|
+
return `hosted:${surface}:${digest}`;
|
|
2137
|
+
}
|
|
2030
2138
|
function buildHostedMcpSpec(agent, surface, ctx) {
|
|
2031
2139
|
const token = issueHostedMcpToken(surface, ctx);
|
|
2032
2140
|
const base = `http://127.0.0.1:${runtimePort}${RUNTIME_MCP_BASE_PATH}/${surface}`;
|
|
2033
2141
|
const query = `token=${encodeURIComponent(token)}`;
|
|
2034
2142
|
if (agent === "codex")
|
|
2035
2143
|
return { url: `${base}/mcp?${query}` };
|
|
2144
|
+
const queryBound = surface === "session-comm" && (ctx.silent === true || ctx.peerBridge !== undefined);
|
|
2145
|
+
const lifecycle = queryBound ? "turn" : surface === "session-comm" ? "session" : "process";
|
|
2036
2146
|
return {
|
|
2037
2147
|
type: "sse",
|
|
2038
2148
|
url: `${base}/sse?${query}`,
|
|
2039
|
-
timeout: CLAUDE_MCP_TOOL_TIMEOUT_MS
|
|
2149
|
+
timeout: CLAUDE_MCP_TOOL_TIMEOUT_MS,
|
|
2150
|
+
...agent === "maestro" ? {
|
|
2151
|
+
lifecycle,
|
|
2152
|
+
cacheKey: hostedMcpCacheIdentity(surface, ctx)
|
|
2153
|
+
} : {}
|
|
2040
2154
|
};
|
|
2041
2155
|
}
|
|
2042
2156
|
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 +3290,36 @@ function extractLatestCodexContextUsage(jsonl) {
|
|
|
3176
3290
|
}
|
|
3177
3291
|
return;
|
|
3178
3292
|
}
|
|
3293
|
+
function extractLatestCodexTokenTotals(jsonl) {
|
|
3294
|
+
const lines = jsonl.trimEnd().split(`
|
|
3295
|
+
`);
|
|
3296
|
+
for (let index = lines.length - 1;index >= 0; index -= 1) {
|
|
3297
|
+
try {
|
|
3298
|
+
const entry = JSON.parse(lines[index] ?? "");
|
|
3299
|
+
if (entry.type !== "event_msg" || entry.payload?.type !== "token_count")
|
|
3300
|
+
continue;
|
|
3301
|
+
const usage = entry.payload.info?.total_token_usage;
|
|
3302
|
+
if (!usage)
|
|
3303
|
+
continue;
|
|
3304
|
+
const values = [
|
|
3305
|
+
usage.input_tokens,
|
|
3306
|
+
usage.output_tokens,
|
|
3307
|
+
usage.cached_input_tokens,
|
|
3308
|
+
usage.cache_write_input_tokens
|
|
3309
|
+
];
|
|
3310
|
+
if (values.some((value) => value !== undefined && (!Number.isFinite(value) || value < 0))) {
|
|
3311
|
+
continue;
|
|
3312
|
+
}
|
|
3313
|
+
return {
|
|
3314
|
+
inputTokens: usage.input_tokens ?? 0,
|
|
3315
|
+
outputTokens: usage.output_tokens ?? 0,
|
|
3316
|
+
cachedInputTokens: usage.cached_input_tokens ?? 0,
|
|
3317
|
+
cacheWriteInputTokens: usage.cache_write_input_tokens ?? 0
|
|
3318
|
+
};
|
|
3319
|
+
} catch {}
|
|
3320
|
+
}
|
|
3321
|
+
return;
|
|
3322
|
+
}
|
|
3179
3323
|
function readLatestCodexContextUsage(threadId) {
|
|
3180
3324
|
const path = latestCodexRolloutPath(threadId);
|
|
3181
3325
|
if (!path)
|
|
@@ -3187,6 +3331,17 @@ function readLatestCodexContextUsage(threadId) {
|
|
|
3187
3331
|
return;
|
|
3188
3332
|
}
|
|
3189
3333
|
}
|
|
3334
|
+
function readLatestCodexTokenTotals(threadId) {
|
|
3335
|
+
const path = latestCodexRolloutPath(threadId);
|
|
3336
|
+
if (!path)
|
|
3337
|
+
return;
|
|
3338
|
+
try {
|
|
3339
|
+
return extractLatestCodexTokenTotals(readFileSync5(path, "utf8"));
|
|
3340
|
+
} catch (error) {
|
|
3341
|
+
logger.debug({ error, threadId }, "codex token totals: rollout read failed");
|
|
3342
|
+
return;
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3190
3345
|
function migrateCodexRolloutNativeMultiAgentMetadata(threadId) {
|
|
3191
3346
|
const path = latestCodexRolloutPath(threadId);
|
|
3192
3347
|
if (!path)
|
|
@@ -5554,6 +5709,7 @@ var init_tool_format = __esm(() => {
|
|
|
5554
5709
|
var exports_codex_provider = {};
|
|
5555
5710
|
__export(exports_codex_provider, {
|
|
5556
5711
|
toCodexMcpServers: () => toCodexMcpServers,
|
|
5712
|
+
normalizeCodexTurnUsage: () => normalizeCodexTurnUsage,
|
|
5557
5713
|
codexProvider: () => codexProvider
|
|
5558
5714
|
});
|
|
5559
5715
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
@@ -5561,6 +5717,20 @@ import { existsSync as existsSync13, readFileSync as readFileSync11, realpathSyn
|
|
|
5561
5717
|
import { homedir as homedir7 } from "os";
|
|
5562
5718
|
import { dirname as dirname9, isAbsolute as isAbsolute2, join as join13, relative, resolve as resolve8 } from "path";
|
|
5563
5719
|
import { Codex } from "@openai/codex-sdk";
|
|
5720
|
+
function sameCodexUsage(usage, total) {
|
|
5721
|
+
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;
|
|
5722
|
+
}
|
|
5723
|
+
function normalizeCodexTurnUsage(usage, baseline, rolloutTotal) {
|
|
5724
|
+
if (!baseline || !rolloutTotal || !sameCodexUsage(usage, rolloutTotal))
|
|
5725
|
+
return usage;
|
|
5726
|
+
const delta = (current, previous) => current >= previous ? current - previous : current;
|
|
5727
|
+
return {
|
|
5728
|
+
input_tokens: delta(rolloutTotal.inputTokens, baseline.inputTokens),
|
|
5729
|
+
output_tokens: delta(rolloutTotal.outputTokens, baseline.outputTokens),
|
|
5730
|
+
cached_input_tokens: delta(rolloutTotal.cachedInputTokens, baseline.cachedInputTokens),
|
|
5731
|
+
cache_write_input_tokens: delta(rolloutTotal.cacheWriteInputTokens, baseline.cacheWriteInputTokens)
|
|
5732
|
+
};
|
|
5733
|
+
}
|
|
5564
5734
|
function mapEffort(effort) {
|
|
5565
5735
|
if (!effort)
|
|
5566
5736
|
return;
|
|
@@ -5960,6 +6130,7 @@ async function* codexProvider(opts) {
|
|
|
5960
6130
|
...opts.effort ? { modelReasoningEffort: mapEffort(opts.effort) } : {}
|
|
5961
6131
|
};
|
|
5962
6132
|
let currentSessionId = opts.sessionId;
|
|
6133
|
+
let usageBaseline = currentSessionId ? readLatestCodexTokenTotals(currentSessionId) : undefined;
|
|
5963
6134
|
let thread = currentSessionId ? codex.resumeThread(currentSessionId, threadOptions) : codex.startThread(threadOptions);
|
|
5964
6135
|
let prompt = promptForThread(opts, true);
|
|
5965
6136
|
let agentTextSoFar = "";
|
|
@@ -6018,6 +6189,7 @@ async function* codexProvider(opts) {
|
|
|
6018
6189
|
thread = codex.startThread(threadOptions);
|
|
6019
6190
|
prompt = promptForThread(opts, true);
|
|
6020
6191
|
currentSessionId = undefined;
|
|
6192
|
+
usageBaseline = undefined;
|
|
6021
6193
|
startResult = await startStreamedWithTracking(thread, prompt, attemptAbort.signal, trackedPids);
|
|
6022
6194
|
}
|
|
6023
6195
|
if (attemptAbort.signal.aborted) {
|
|
@@ -6118,10 +6290,11 @@ async function* codexProvider(opts) {
|
|
|
6118
6290
|
break;
|
|
6119
6291
|
}
|
|
6120
6292
|
case "turn.completed": {
|
|
6121
|
-
const
|
|
6122
|
-
if (!
|
|
6293
|
+
const rawUsage = event.usage;
|
|
6294
|
+
if (!rawUsage)
|
|
6123
6295
|
break;
|
|
6124
6296
|
attemptCompleted = true;
|
|
6297
|
+
const usage = normalizeCodexTurnUsage(rawUsage, usageBaseline, currentSessionId ? readLatestCodexTokenTotals(currentSessionId) : undefined);
|
|
6125
6298
|
const contextUsage = currentSessionId ? readLatestCodexContextUsage(currentSessionId) : undefined;
|
|
6126
6299
|
yield {
|
|
6127
6300
|
type: "result",
|
|
@@ -6130,6 +6303,7 @@ async function* codexProvider(opts) {
|
|
|
6130
6303
|
usage: {
|
|
6131
6304
|
inputTokens: usage.input_tokens,
|
|
6132
6305
|
outputTokens: usage.output_tokens,
|
|
6306
|
+
cacheCreationInputTokens: usage.cache_write_input_tokens,
|
|
6133
6307
|
cacheReadInputTokens: usage.cached_input_tokens,
|
|
6134
6308
|
...contextUsage
|
|
6135
6309
|
}
|
|
@@ -6317,6 +6491,7 @@ function maestroProvider(opts) {
|
|
|
6317
6491
|
...opts,
|
|
6318
6492
|
enableToolSearch: !opts.toolPolicy && opts.enableToolSearch !== false,
|
|
6319
6493
|
toolResultTruncation: buildMaestroToolResultTruncation(opts),
|
|
6494
|
+
ephemeralSystemPrompt: opts.ephemeralSystemPrompt,
|
|
6320
6495
|
apiKeyOverrides: resolveMaestroApiKeyOverrides(userId),
|
|
6321
6496
|
agent: "maestro",
|
|
6322
6497
|
disallowedTools: buildMaestroDisallowedTools(callerDisallowedTools, opts.toolPolicy),
|
|
@@ -8529,7 +8704,7 @@ var init_runtime_process_leases = __esm(async () => {
|
|
|
8529
8704
|
});
|
|
8530
8705
|
|
|
8531
8706
|
// ../../packages/core/src/agents/mcp-tools/ask-user.ts
|
|
8532
|
-
import { createHash as
|
|
8707
|
+
import { createHash as createHash3, randomUUID as randomUUID7 } from "crypto";
|
|
8533
8708
|
import { z } from "zod";
|
|
8534
8709
|
function normalizeAskText(value, maxChars) {
|
|
8535
8710
|
if (typeof value !== "string")
|
|
@@ -8566,7 +8741,7 @@ function normalizeAskUserQuestionInput(input) {
|
|
|
8566
8741
|
return { question, choices };
|
|
8567
8742
|
}
|
|
8568
8743
|
function askBodyHash(question, choices) {
|
|
8569
|
-
return
|
|
8744
|
+
return createHash3("sha256").update(JSON.stringify({ question, choices })).digest("hex");
|
|
8570
8745
|
}
|
|
8571
8746
|
function answerToolResult(answer) {
|
|
8572
8747
|
if (!answer.userId)
|
|
@@ -10070,7 +10245,7 @@ var init_browser_profiles = __esm(async () => {
|
|
|
10070
10245
|
|
|
10071
10246
|
// ../../packages/core/src/platform/playwright/manager.ts
|
|
10072
10247
|
import { execFileSync as execFileSync7, spawn as spawn5 } from "child_process";
|
|
10073
|
-
import { createHash as
|
|
10248
|
+
import { createHash as createHash4, randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
10074
10249
|
import {
|
|
10075
10250
|
cpSync,
|
|
10076
10251
|
existsSync as existsSync15,
|
|
@@ -10165,7 +10340,7 @@ function parseInstanceKey(instanceKey) {
|
|
|
10165
10340
|
};
|
|
10166
10341
|
}
|
|
10167
10342
|
function portFileName(instanceKey) {
|
|
10168
|
-
return
|
|
10343
|
+
return createHash4("sha256").update(instanceKey).digest("hex").slice(0, 24);
|
|
10169
10344
|
}
|
|
10170
10345
|
function writePortFile(instanceKey, port) {
|
|
10171
10346
|
try {
|
|
@@ -11211,70 +11386,6 @@ var init_personal_general = __esm(async () => {
|
|
|
11211
11386
|
]);
|
|
11212
11387
|
});
|
|
11213
11388
|
|
|
11214
|
-
// ../../packages/core/src/agents/compaction-support.ts
|
|
11215
|
-
function isCjkCodePoint(code) {
|
|
11216
|
-
return code >= 12288 && code <= 19903 || code >= 19968 && code <= 40959 || code >= 4352 && code <= 4607 || code >= 44032 && code <= 55203 || code >= 63744 && code <= 64255 || code >= 65280 && code <= 65519 || code >= 131072 && code <= 262143;
|
|
11217
|
-
}
|
|
11218
|
-
function estimateTextTokens(text) {
|
|
11219
|
-
let cjkChars = 0;
|
|
11220
|
-
let cjkUnits = 0;
|
|
11221
|
-
for (let index = 0;index < text.length; index++) {
|
|
11222
|
-
const code = text.codePointAt(index) ?? 0;
|
|
11223
|
-
const wide = code > 65535;
|
|
11224
|
-
if (wide)
|
|
11225
|
-
index++;
|
|
11226
|
-
if (isCjkCodePoint(code)) {
|
|
11227
|
-
cjkChars++;
|
|
11228
|
-
cjkUnits += wide ? 2 : 1;
|
|
11229
|
-
}
|
|
11230
|
-
}
|
|
11231
|
-
const latinUnits = text.length - cjkUnits;
|
|
11232
|
-
return Math.ceil(cjkChars / CHARS_PER_CJK_TOKEN + latinUnits / CHARS_PER_TEXT_TOKEN);
|
|
11233
|
-
}
|
|
11234
|
-
function estimateConversationTokens(messages) {
|
|
11235
|
-
return messages.reduce((sum, message) => sum + 4 + estimateTextTokens(message.content), 0);
|
|
11236
|
-
}
|
|
11237
|
-
var ACTIVE_TASK_TEMPLATE = `You are compressing a long agent conversation so the main agent can continue
|
|
11238
|
-
without losing context. Produce a single concise summary using EXACTLY these
|
|
11239
|
-
section headers, in this order:
|
|
11240
|
-
|
|
11241
|
-
## Active Task
|
|
11242
|
-
One sentence: what is the agent currently working on?
|
|
11243
|
-
|
|
11244
|
-
## Goal
|
|
11245
|
-
One or two sentences: the user's overall objective in this session.
|
|
11246
|
-
|
|
11247
|
-
## Constraints
|
|
11248
|
-
Bulleted list of durable requirements, user preferences, technical limits, or
|
|
11249
|
-
process rules that should continue to govern the work. Skip if none.
|
|
11250
|
-
|
|
11251
|
-
## Key Decisions
|
|
11252
|
-
Bulleted list of decisions already made that should not be reopened unless the
|
|
11253
|
-
user asks. Include the rationale when it is short and important. Skip if none.
|
|
11254
|
-
|
|
11255
|
-
## Pending
|
|
11256
|
-
Bulleted list of unresolved items, decisions to make, or work explicitly
|
|
11257
|
-
deferred. Use "(blocked: <reason>)" when applicable.
|
|
11258
|
-
|
|
11259
|
-
## Next Steps
|
|
11260
|
-
Bulleted list of the concrete next actions the main agent should take after
|
|
11261
|
-
compaction, in likely execution order. Skip if none.
|
|
11262
|
-
|
|
11263
|
-
## Files
|
|
11264
|
-
Bulleted list of \`absolute/paths\` touched or referenced (read, written,
|
|
11265
|
-
inspected). Skip if none.
|
|
11266
|
-
|
|
11267
|
-
## Recent context
|
|
11268
|
-
3-5 bullets capturing the most recent tool calls + their salient outputs.
|
|
11269
|
-
Prefer specifics (paths, line numbers, exit codes, key values) over generic
|
|
11270
|
-
recaps. Skip details that have no bearing on the next step.
|
|
11271
|
-
|
|
11272
|
-
RULES:
|
|
11273
|
-
- Output ONLY the eight sections above, with no preamble or postscript.
|
|
11274
|
-
- Do NOT echo the user's words verbatim - paraphrase tightly.
|
|
11275
|
-
- Do NOT invent file paths or facts not present in the transcript.
|
|
11276
|
-
- Keep the entire summary under 1500 words.`, CHARS_PER_TEXT_TOKEN = 3.5, CHARS_PER_CJK_TOKEN = 0.9;
|
|
11277
|
-
|
|
11278
11389
|
// ../../packages/core/src/prompts/builders.ts
|
|
11279
11390
|
import { readFileSync as readFileSync13 } from "fs";
|
|
11280
11391
|
import { resolve as resolve13 } from "path";
|
|
@@ -14109,7 +14220,7 @@ __export(exports_derive, {
|
|
|
14109
14220
|
TopicForkCompactionError: () => TopicForkCompactionError,
|
|
14110
14221
|
TopicDeriveBusyError: () => TopicDeriveBusyError
|
|
14111
14222
|
});
|
|
14112
|
-
import { createHash as
|
|
14223
|
+
import { createHash as createHash5, randomUUID as randomUUID12 } from "crypto";
|
|
14113
14224
|
import { mkdirSync as mkdirSync13, rmSync as rmSync5, unlinkSync as unlinkSync13 } from "fs";
|
|
14114
14225
|
function getTopics() {
|
|
14115
14226
|
return listTopics().filter((topic) => !isLegacySharedGeneral(topic.id));
|
|
@@ -14175,7 +14286,7 @@ function captureForkSnapshot(sourceTopicId, userId, topicTitle) {
|
|
|
14175
14286
|
maxRowid: messageRows.at(-1)?.rowid ?? 0
|
|
14176
14287
|
},
|
|
14177
14288
|
active: isTopicRunning(sourceTopicId),
|
|
14178
|
-
canonicalDigest:
|
|
14289
|
+
canonicalDigest: createHash5("sha256").update(entries.map((entry) => JSON.stringify(entry)).join(`
|
|
14179
14290
|
`)).digest("hex")
|
|
14180
14291
|
};
|
|
14181
14292
|
}
|
|
@@ -15251,7 +15362,7 @@ __export(exports_session_asks, {
|
|
|
15251
15362
|
clearPendingAsk: () => clearPendingAsk,
|
|
15252
15363
|
PENDING_ASK_TTL_MS: () => PENDING_ASK_TTL_MS
|
|
15253
15364
|
});
|
|
15254
|
-
import { createHash as
|
|
15365
|
+
import { createHash as createHash6 } from "crypto";
|
|
15255
15366
|
import {
|
|
15256
15367
|
closeSync as closeSync2,
|
|
15257
15368
|
mkdirSync as mkdirSync15,
|
|
@@ -15265,14 +15376,14 @@ import {
|
|
|
15265
15376
|
import { dirname as dirname13, join as join24 } from "path";
|
|
15266
15377
|
function pendingAskDir(userId) {
|
|
15267
15378
|
const rawUserId = String(userId);
|
|
15268
|
-
const safeUserId = /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) && !rawUserId.includes("..") ? rawUserId : `sha256-${
|
|
15379
|
+
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
15380
|
return join24(resolveStorageSessionAsksDir(), safeUserId);
|
|
15270
15381
|
}
|
|
15271
15382
|
function encodeAskKey(key) {
|
|
15272
15383
|
return JSON.stringify([key.from, key.to]);
|
|
15273
15384
|
}
|
|
15274
15385
|
function pendingAskPath(key) {
|
|
15275
|
-
const digest =
|
|
15386
|
+
const digest = createHash6("sha256").update(encodeAskKey(key)).digest("hex");
|
|
15276
15387
|
return join24(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${digest}.pending`);
|
|
15277
15388
|
}
|
|
15278
15389
|
function v2PendingAskPath(key) {
|
|
@@ -16880,10 +16991,11 @@ var exports_token_stats = {};
|
|
|
16880
16991
|
__export(exports_token_stats, {
|
|
16881
16992
|
tokenStatsFileId: () => tokenStatsFileId,
|
|
16882
16993
|
recordUsage: () => recordUsage,
|
|
16994
|
+
getTopicStats: () => getTopicStats,
|
|
16883
16995
|
getStats: () => getStats,
|
|
16884
16996
|
calcCost: () => calcCost
|
|
16885
16997
|
});
|
|
16886
|
-
import { createHash as
|
|
16998
|
+
import { createHash as createHash7 } from "crypto";
|
|
16887
16999
|
import { mkdirSync as mkdirSync17 } from "fs";
|
|
16888
17000
|
import { join as join26 } from "path";
|
|
16889
17001
|
function emptyBucket() {
|
|
@@ -16892,12 +17004,13 @@ function emptyBucket() {
|
|
|
16892
17004
|
outputTokens: 0,
|
|
16893
17005
|
cacheCreationInputTokens: 0,
|
|
16894
17006
|
cacheReadInputTokens: 0,
|
|
16895
|
-
queries: 0
|
|
17007
|
+
queries: 0,
|
|
17008
|
+
estimatedCostUsd: 0
|
|
16896
17009
|
};
|
|
16897
17010
|
}
|
|
16898
17011
|
function tokenStatsFileId(userId) {
|
|
16899
17012
|
const rawUserId = String(userId);
|
|
16900
|
-
return /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) && !rawUserId.includes("..") ? rawUserId : `sha256-${
|
|
17013
|
+
return /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) && !rawUserId.includes("..") ? rawUserId : `sha256-${createHash7("sha256").update(rawUserId).digest("hex")}`;
|
|
16901
17014
|
}
|
|
16902
17015
|
function queriesPath(userId) {
|
|
16903
17016
|
const fileId = tokenStatsFileId(userId);
|
|
@@ -16919,16 +17032,41 @@ function loadRecords(userId) {
|
|
|
16919
17032
|
}
|
|
16920
17033
|
}
|
|
16921
17034
|
function calcCost(b) {
|
|
16922
|
-
return b.
|
|
17035
|
+
return b.estimatedCostUsd;
|
|
17036
|
+
}
|
|
17037
|
+
function estimateUsageCost(agent, model, usage) {
|
|
17038
|
+
const prices = TOKEN_PRICES[`${agent}:${model}`];
|
|
17039
|
+
if (!prices)
|
|
17040
|
+
return 0;
|
|
17041
|
+
return (usage.inputTokens * prices.input + usage.outputTokens * prices.output + (agent === "claude" ? usage.cacheCreationInputTokens * (prices.cacheWrite ?? prices.input) : 0) + usage.cacheReadInputTokens * prices.cacheRead) / 1e6;
|
|
16923
17042
|
}
|
|
16924
|
-
function
|
|
17043
|
+
function isQueryRecord(value) {
|
|
17044
|
+
if (!value || typeof value !== "object")
|
|
17045
|
+
return false;
|
|
17046
|
+
const record = value;
|
|
17047
|
+
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";
|
|
17048
|
+
}
|
|
17049
|
+
function recordUsage(userId, session, usage, context) {
|
|
17050
|
+
const cacheReadInputTokens = usage.cacheReadInputTokens ?? 0;
|
|
17051
|
+
const inputTokens = context.agent === "claude" ? usage.inputTokens : Math.max(0, usage.inputTokens - cacheReadInputTokens);
|
|
17052
|
+
const normalized = {
|
|
17053
|
+
inputTokens,
|
|
17054
|
+
outputTokens: usage.outputTokens,
|
|
17055
|
+
cacheCreationInputTokens: usage.cacheCreationInputTokens ?? 0,
|
|
17056
|
+
cacheReadInputTokens
|
|
17057
|
+
};
|
|
16925
17058
|
const record = {
|
|
17059
|
+
schemaVersion: 2,
|
|
16926
17060
|
timestamp: new Date().toISOString(),
|
|
16927
17061
|
session,
|
|
16928
|
-
|
|
16929
|
-
|
|
16930
|
-
|
|
16931
|
-
|
|
17062
|
+
topicId: context.topicId,
|
|
17063
|
+
...context.providerSessionId ? { providerSessionId: context.providerSessionId } : {},
|
|
17064
|
+
agent: context.agent,
|
|
17065
|
+
model: context.model,
|
|
17066
|
+
...normalized,
|
|
17067
|
+
...usage.contextTokens !== undefined ? { contextTokens: usage.contextTokens } : {},
|
|
17068
|
+
...usage.contextWindow !== undefined ? { contextWindow: usage.contextWindow } : {},
|
|
17069
|
+
estimatedCostUsd: usage.costUsd ?? estimateUsageCost(context.agent, context.model, normalized)
|
|
16932
17070
|
};
|
|
16933
17071
|
try {
|
|
16934
17072
|
appendJsonlEntry(queriesPath(userId), record);
|
|
@@ -16942,12 +17080,26 @@ function getStats(userId, from, to) {
|
|
|
16942
17080
|
const toTs = to ? new Date(to).getTime() : Infinity;
|
|
16943
17081
|
if (from && Number.isNaN(fromTs) || to && Number.isNaN(toTs)) {
|
|
16944
17082
|
logger.warn({ from, to }, "token-stats: Invalid date range, returning empty");
|
|
16945
|
-
return {
|
|
17083
|
+
return {
|
|
17084
|
+
total: emptyBucket(),
|
|
17085
|
+
byHour: {},
|
|
17086
|
+
bySession: {},
|
|
17087
|
+
currentSessions: [],
|
|
17088
|
+
ignoredLegacyRecords: 0,
|
|
17089
|
+
estimatedCostUsd: 0
|
|
17090
|
+
};
|
|
16946
17091
|
}
|
|
16947
17092
|
const total = emptyBucket();
|
|
16948
17093
|
const byHour = {};
|
|
16949
17094
|
const bySession = {};
|
|
16950
|
-
|
|
17095
|
+
const currentSessions = new Map;
|
|
17096
|
+
let ignoredLegacyRecords = 0;
|
|
17097
|
+
for (const raw of records) {
|
|
17098
|
+
if (!isQueryRecord(raw)) {
|
|
17099
|
+
ignoredLegacyRecords += 1;
|
|
17100
|
+
continue;
|
|
17101
|
+
}
|
|
17102
|
+
const r = raw;
|
|
16951
17103
|
const ts = new Date(r.timestamp).getTime();
|
|
16952
17104
|
if (ts < fromTs || ts > toTs)
|
|
16953
17105
|
continue;
|
|
@@ -16962,14 +17114,74 @@ function getStats(userId, from, to) {
|
|
|
16962
17114
|
bucket.cacheCreationInputTokens += r.cacheCreationInputTokens;
|
|
16963
17115
|
bucket.cacheReadInputTokens += r.cacheReadInputTokens;
|
|
16964
17116
|
bucket.queries += 1;
|
|
17117
|
+
bucket.estimatedCostUsd += r.estimatedCostUsd;
|
|
17118
|
+
}
|
|
17119
|
+
if (r.contextTokens !== undefined && r.contextWindow !== undefined && r.contextWindow > 0) {
|
|
17120
|
+
currentSessions.set(r.topicId, {
|
|
17121
|
+
timestamp: r.timestamp,
|
|
17122
|
+
topicId: r.topicId,
|
|
17123
|
+
topicTitle: r.session,
|
|
17124
|
+
...r.providerSessionId ? { providerSessionId: r.providerSessionId } : {},
|
|
17125
|
+
agent: r.agent,
|
|
17126
|
+
model: r.model,
|
|
17127
|
+
contextTokens: r.contextTokens,
|
|
17128
|
+
contextWindow: r.contextWindow
|
|
17129
|
+
});
|
|
17130
|
+
}
|
|
17131
|
+
}
|
|
17132
|
+
return {
|
|
17133
|
+
total,
|
|
17134
|
+
byHour,
|
|
17135
|
+
bySession,
|
|
17136
|
+
currentSessions: [...currentSessions.values()].sort((a, b) => b.timestamp.localeCompare(a.timestamp)),
|
|
17137
|
+
ignoredLegacyRecords,
|
|
17138
|
+
estimatedCostUsd: calcCost(total)
|
|
17139
|
+
};
|
|
17140
|
+
}
|
|
17141
|
+
function getTopicStats(userId, topicId) {
|
|
17142
|
+
const total = emptyBucket();
|
|
17143
|
+
let currentSession;
|
|
17144
|
+
for (const raw of loadRecords(userId)) {
|
|
17145
|
+
if (!isQueryRecord(raw) || raw.topicId !== topicId)
|
|
17146
|
+
continue;
|
|
17147
|
+
total.inputTokens += raw.inputTokens;
|
|
17148
|
+
total.outputTokens += raw.outputTokens;
|
|
17149
|
+
total.cacheCreationInputTokens += raw.cacheCreationInputTokens;
|
|
17150
|
+
total.cacheReadInputTokens += raw.cacheReadInputTokens;
|
|
17151
|
+
total.queries += 1;
|
|
17152
|
+
total.estimatedCostUsd += raw.estimatedCostUsd;
|
|
17153
|
+
if (raw.contextTokens !== undefined && raw.contextWindow !== undefined && raw.contextWindow > 0 && (!currentSession || raw.timestamp > currentSession.timestamp)) {
|
|
17154
|
+
currentSession = {
|
|
17155
|
+
timestamp: raw.timestamp,
|
|
17156
|
+
topicId: raw.topicId,
|
|
17157
|
+
topicTitle: raw.session,
|
|
17158
|
+
...raw.providerSessionId ? { providerSessionId: raw.providerSessionId } : {},
|
|
17159
|
+
agent: raw.agent,
|
|
17160
|
+
model: raw.model,
|
|
17161
|
+
contextTokens: raw.contextTokens,
|
|
17162
|
+
contextWindow: raw.contextWindow
|
|
17163
|
+
};
|
|
16965
17164
|
}
|
|
16966
17165
|
}
|
|
16967
|
-
return {
|
|
17166
|
+
return { topicId, ...total, ...currentSession ? { currentSession } : {} };
|
|
16968
17167
|
}
|
|
17168
|
+
var TOKEN_PRICES;
|
|
16969
17169
|
var init_token_stats = __esm(async () => {
|
|
16970
17170
|
init_jsonl();
|
|
16971
17171
|
init_logger();
|
|
16972
17172
|
await init_storage_host();
|
|
17173
|
+
TOKEN_PRICES = {
|
|
17174
|
+
"codex:gpt-5.6-sol": { input: 5, cacheRead: 0.5, output: 30 },
|
|
17175
|
+
"codex:gpt-5.6-terra": { input: 2.5, cacheRead: 0.25, output: 15 },
|
|
17176
|
+
"codex:gpt-5.6-luna": { input: 1, cacheRead: 0.1, output: 6 },
|
|
17177
|
+
"claude:fable": { input: 10, cacheWrite: 12.5, cacheRead: 1, output: 50 },
|
|
17178
|
+
"claude:opus": { input: 5, cacheWrite: 6.25, cacheRead: 0.5, output: 25 },
|
|
17179
|
+
"claude:sonnet": { input: 2, cacheWrite: 2.5, cacheRead: 0.2, output: 10 },
|
|
17180
|
+
"maestro:kimi-k3": { input: 3, cacheRead: 0.3, output: 15 },
|
|
17181
|
+
"maestro:kimi-k2.7-code": { input: 0.95, cacheRead: 0.19, output: 4 },
|
|
17182
|
+
"maestro:deepseek-pro": { input: 0.435, cacheRead: 0.003625, output: 0.87 },
|
|
17183
|
+
"maestro:deepseek-flash": { input: 0.14, cacheRead: 0.0028, output: 0.28 }
|
|
17184
|
+
};
|
|
16973
17185
|
});
|
|
16974
17186
|
|
|
16975
17187
|
// ../../packages/core/src/runtime/turn-event-stream.ts
|
|
@@ -16998,6 +17210,9 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
16998
17210
|
let pendingSawDelta = false;
|
|
16999
17211
|
let accumulatedText = "";
|
|
17000
17212
|
let pendingText = "";
|
|
17213
|
+
let assistantContextTokens = 0;
|
|
17214
|
+
let toolContextTokens = 0;
|
|
17215
|
+
let lastContextProgressAt = 0;
|
|
17001
17216
|
let lastVisibleMessageId = null;
|
|
17002
17217
|
const visibleMessageIds = [];
|
|
17003
17218
|
let lastTaskPanelText = null;
|
|
@@ -17008,6 +17223,28 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
17008
17223
|
const toolSummaryOptions = {
|
|
17009
17224
|
cwd: control.injectParams?.cwd ?? workspaceCwdFor(topicId)
|
|
17010
17225
|
};
|
|
17226
|
+
const recordEventUsage = (usage) => {
|
|
17227
|
+
recordUsage(userId, topicTitle, usage, {
|
|
17228
|
+
topicId,
|
|
17229
|
+
...control.sessionId ? { providerSessionId: control.sessionId } : {},
|
|
17230
|
+
agent: agentType,
|
|
17231
|
+
model
|
|
17232
|
+
});
|
|
17233
|
+
};
|
|
17234
|
+
const broadcastContextProgress = (force = false) => {
|
|
17235
|
+
if (silent)
|
|
17236
|
+
return;
|
|
17237
|
+
const now = Date.now();
|
|
17238
|
+
if (!force && lastContextProgressAt > 0 && now - lastContextProgressAt < 250)
|
|
17239
|
+
return;
|
|
17240
|
+
lastContextProgressAt = now;
|
|
17241
|
+
hub.broadcastAiStatus(topicId, {
|
|
17242
|
+
kind: "context_progress",
|
|
17243
|
+
queryId,
|
|
17244
|
+
assistantTokens: assistantContextTokens,
|
|
17245
|
+
toolTokens: toolContextTokens
|
|
17246
|
+
});
|
|
17247
|
+
};
|
|
17011
17248
|
const nextSyntheticToolUseId = () => `tool-${queryId}-${++syntheticToolCounter}`;
|
|
17012
17249
|
const bindToolUseId = (providerToolUseId, fallback) => {
|
|
17013
17250
|
const providerId = normalizeToolUseId(providerToolUseId);
|
|
@@ -17117,6 +17354,8 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
17117
17354
|
const incoming = event.content;
|
|
17118
17355
|
accumulatedText += incoming;
|
|
17119
17356
|
pendingText += incoming;
|
|
17357
|
+
assistantContextTokens = estimateTextTokens(accumulatedText);
|
|
17358
|
+
broadcastContextProgress();
|
|
17120
17359
|
break;
|
|
17121
17360
|
}
|
|
17122
17361
|
case "text":
|
|
@@ -17124,10 +17363,15 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
17124
17363
|
const incoming = event.content;
|
|
17125
17364
|
accumulatedText += incoming;
|
|
17126
17365
|
pendingText += incoming;
|
|
17366
|
+
assistantContextTokens = estimateTextTokens(accumulatedText);
|
|
17367
|
+
broadcastContextProgress();
|
|
17127
17368
|
}
|
|
17128
17369
|
break;
|
|
17129
17370
|
case "tool_use":
|
|
17130
17371
|
emitPendingAssistantMessage();
|
|
17372
|
+
toolContextTokens += estimateTextTokens(`${event.name}
|
|
17373
|
+
${JSON.stringify(event.input ?? {})}`);
|
|
17374
|
+
broadcastContextProgress(true);
|
|
17131
17375
|
if (isVisualsShowHtmlTool(event.name)) {
|
|
17132
17376
|
const input = event.input;
|
|
17133
17377
|
if (typeof input.html !== "string" || input.html.trim().length === 0) {
|
|
@@ -17281,6 +17525,8 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
17281
17525
|
}
|
|
17282
17526
|
break;
|
|
17283
17527
|
case "tool_result":
|
|
17528
|
+
toolContextTokens += estimateTextTokens(event.content);
|
|
17529
|
+
broadcastContextProgress(true);
|
|
17284
17530
|
if (isVisualToolResultHandled(event.toolUseId))
|
|
17285
17531
|
break;
|
|
17286
17532
|
if (!silent) {
|
|
@@ -17326,7 +17572,7 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
17326
17572
|
break;
|
|
17327
17573
|
case "result":
|
|
17328
17574
|
if (event.usage)
|
|
17329
|
-
|
|
17575
|
+
recordEventUsage(event.usage);
|
|
17330
17576
|
{
|
|
17331
17577
|
const usage = event.usage ? {
|
|
17332
17578
|
input: event.usage.inputTokens,
|
|
@@ -17376,6 +17622,8 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
17376
17622
|
}
|
|
17377
17623
|
break;
|
|
17378
17624
|
case "error":
|
|
17625
|
+
if (event.usage)
|
|
17626
|
+
recordEventUsage(event.usage);
|
|
17379
17627
|
logger.warn({ topicId, queryId, agentType, model, silent, error: event.content }, "ai: provider returned error");
|
|
17380
17628
|
terminalEmitted = true;
|
|
17381
17629
|
errorOccurred = true;
|
|
@@ -17679,6 +17927,7 @@ __export(exports_turn_runner, {
|
|
|
17679
17927
|
channelTranscriptSpeaker: () => channelTranscriptSpeaker,
|
|
17680
17928
|
canonicalModelId: () => canonicalModelId,
|
|
17681
17929
|
buildVideoHtml: () => buildVideoHtml,
|
|
17930
|
+
buildTurnReminderQuery: () => buildTurnReminderQuery,
|
|
17682
17931
|
buildMermaidHtml: () => buildMermaidHtml,
|
|
17683
17932
|
buildMentionOnlyChannelPrompt: () => buildMentionOnlyChannelPrompt,
|
|
17684
17933
|
buildImageHtml: () => buildImageHtml,
|
|
@@ -17704,6 +17953,18 @@ function withDefaultPlaywright(configuredMcp, isManager) {
|
|
|
17704
17953
|
enabled.add("background-bash");
|
|
17705
17954
|
return [...enabled];
|
|
17706
17955
|
}
|
|
17956
|
+
function buildTurnReminderQuery(agent, prompt, reminders) {
|
|
17957
|
+
const reminderPrompt = reminders.join(`
|
|
17958
|
+
|
|
17959
|
+
`);
|
|
17960
|
+
if (!reminderPrompt)
|
|
17961
|
+
return { prompt };
|
|
17962
|
+
if (agent === "maestro")
|
|
17963
|
+
return { prompt, ephemeralSystemPrompt: reminderPrompt };
|
|
17964
|
+
return { prompt: `${prompt}
|
|
17965
|
+
|
|
17966
|
+
${reminderPrompt}` };
|
|
17967
|
+
}
|
|
17707
17968
|
function appendSystemMessage(topicId, text2) {
|
|
17708
17969
|
const message = {
|
|
17709
17970
|
id: randomUUID16(),
|
|
@@ -18651,15 +18912,11 @@ function startAiTurn(params) {
|
|
|
18651
18912
|
if (consumePlaywrightUnavailable(userId, topic.title)) {
|
|
18652
18913
|
turnReminders.push("<system-reminder>Playwright browser tools are UNAVAILABLE this turn. The `mcp__playwright__*` tools have been removed from this turn's catalog because the long-lived browser MCP could not be prepared. Do not attempt to call browser tools. If browser interaction is required, ask the user to retry shortly or use a non-browser alternative.</system-reminder>");
|
|
18653
18914
|
}
|
|
18654
|
-
const
|
|
18655
|
-
|
|
18656
|
-
${turnReminders.join(`
|
|
18657
|
-
|
|
18658
|
-
`)}` : agentPrompt;
|
|
18915
|
+
const reminderQuery = buildTurnReminderQuery(agentKind, agentPrompt, turnReminders);
|
|
18659
18916
|
try {
|
|
18660
18917
|
yield* runAgent({
|
|
18661
18918
|
agent: agentKind,
|
|
18662
|
-
|
|
18919
|
+
...reminderQuery,
|
|
18663
18920
|
attachments: promptAttachments,
|
|
18664
18921
|
cwd: workspaceCwd,
|
|
18665
18922
|
systemPrompt,
|
|
@@ -19929,7 +20186,7 @@ var init_runtime_gateway_submissions = __esm(async () => {
|
|
|
19929
20186
|
});
|
|
19930
20187
|
|
|
19931
20188
|
// ../../packages/core/src/application/submit-runtime-gateway-turn.ts
|
|
19932
|
-
import { createHash as
|
|
20189
|
+
import { createHash as createHash8, randomUUID as randomUUID17 } from "crypto";
|
|
19933
20190
|
function duplicateResult(submission, params, requestId, actorUserId, payloadHash) {
|
|
19934
20191
|
const message = getApiMessage(submission.topicId, submission.messageId);
|
|
19935
20192
|
if (!message)
|
|
@@ -19951,7 +20208,7 @@ function duplicateResult(submission, params, requestId, actorUserId, payloadHash
|
|
|
19951
20208
|
};
|
|
19952
20209
|
}
|
|
19953
20210
|
function gatewayPayloadHash(params, requestId, actorUserId) {
|
|
19954
|
-
return
|
|
20211
|
+
return createHash8("sha256").update(JSON.stringify([
|
|
19955
20212
|
params.topic.id,
|
|
19956
20213
|
params.userId,
|
|
19957
20214
|
actorUserId,
|
|
@@ -21431,7 +21688,7 @@ var init_file_ops = __esm(() => {
|
|
|
21431
21688
|
});
|
|
21432
21689
|
|
|
21433
21690
|
// ../../packages/core/src/runtime/inbox.ts
|
|
21434
|
-
import { createHash as
|
|
21691
|
+
import { createHash as createHash9, randomUUID as randomUUID20 } from "crypto";
|
|
21435
21692
|
import { readdirSync as readdirSync8, statSync as statSync13, writeFileSync as writeFileSync16 } from "fs";
|
|
21436
21693
|
import { join as join32 } from "path";
|
|
21437
21694
|
async function createAskForkPlan(options) {
|
|
@@ -21545,7 +21802,7 @@ async function notifyAskDrop(scope, entry, targetLabel, message) {
|
|
|
21545
21802
|
}
|
|
21546
21803
|
}
|
|
21547
21804
|
function stableLegacyRequestId(prefix, parts) {
|
|
21548
|
-
const hash =
|
|
21805
|
+
const hash = createHash9("sha256").update(parts.map((part) => String(part ?? "")).join("\x00")).digest("hex").slice(0, 16);
|
|
21549
21806
|
return `${prefix}-${hash}`;
|
|
21550
21807
|
}
|
|
21551
21808
|
async function flushSessionInbox() {
|
|
@@ -22240,6 +22497,7 @@ __export(exports_src, {
|
|
|
22240
22497
|
extractFileEvents: () => extractFileEvents,
|
|
22241
22498
|
executeVaultCommand: () => executeVaultCommand,
|
|
22242
22499
|
executeExternalUserTurn: () => executeExternalUserTurn,
|
|
22500
|
+
estimateTextTokens: () => estimateTextTokens,
|
|
22243
22501
|
errorResult: () => errorResult,
|
|
22244
22502
|
errMsg: () => errMsg,
|
|
22245
22503
|
ensurePersonalGeneral: () => ensurePersonalGeneral,
|
|
@@ -22553,6 +22811,7 @@ __export(exports_node_host, {
|
|
|
22553
22811
|
killAllBgBash: () => killAllBgBash,
|
|
22554
22812
|
isParticipant: () => isParticipant,
|
|
22555
22813
|
getVisibleTopics: () => getVisibleTopics,
|
|
22814
|
+
getTopicStats: () => getTopicStats,
|
|
22556
22815
|
getTopic: () => getTopic,
|
|
22557
22816
|
flushBashrsCompletions: () => flushBashrsCompletions,
|
|
22558
22817
|
executeVaultCommand: () => executeVaultCommand,
|
|
@@ -22607,6 +22866,7 @@ var init_node_host = __esm(async () => {
|
|
|
22607
22866
|
init_api_topics(),
|
|
22608
22867
|
init_runtime_events(),
|
|
22609
22868
|
init_runtime_process_leases(),
|
|
22869
|
+
init_token_stats(),
|
|
22610
22870
|
init_derive(),
|
|
22611
22871
|
init_personal_general(),
|
|
22612
22872
|
init_session()
|
|
@@ -23234,7 +23494,7 @@ function createTokenStatsMcpServer(context, host2 = defaultTokenStatsMcpHost) {
|
|
|
23234
23494
|
throw new Error("token-stats MCP requires userId");
|
|
23235
23495
|
const server = new McpServer2({ name: "token-stats", version: "1.0.0" });
|
|
23236
23496
|
server.tool("get_usage_stats", [
|
|
23237
|
-
"Claude \uC0AC\uC6A9\uB7C9
|
|
23497
|
+
"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
23498
|
"",
|
|
23239
23499
|
"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
23500
|
" \uC608: '\uC9C0\uB09C 3\uC2DC\uAC04' \u2192 from = \uD604\uC7AC -3h",
|
|
@@ -23248,18 +23508,36 @@ function createTokenStatsMcpServer(context, host2 = defaultTokenStatsMcpHost) {
|
|
|
23248
23508
|
to: z8.string().optional().describe("\uC885\uB8CC \uC2DC\uAC01 ISO 8601. \uC0DD\uB7B5 \uC2DC \uD604\uC7AC"),
|
|
23249
23509
|
groupBy: z8.enum(["hour", "session"]).optional().describe("\uC9D1\uACC4 \uAE30\uC900. \uAE30\uBCF8\uAC12: hour")
|
|
23250
23510
|
}, async ({ from, to, groupBy = "hour" }) => {
|
|
23251
|
-
const
|
|
23511
|
+
const snapshot = host2.getStats(context.userId, from, to);
|
|
23512
|
+
const {
|
|
23513
|
+
total,
|
|
23514
|
+
byHour,
|
|
23515
|
+
bySession,
|
|
23516
|
+
currentSessions = [],
|
|
23517
|
+
ignoredLegacyRecords = 0,
|
|
23518
|
+
estimatedCostUsd
|
|
23519
|
+
} = snapshot;
|
|
23252
23520
|
const lines = [
|
|
23253
|
-
"\uD83D\uDCCA
|
|
23521
|
+
"\uD83D\uDCCA Agent \uC0AC\uC6A9\uB7C9",
|
|
23254
23522
|
from || to ? `\uAE30\uAC04: ${from ?? "\uC804\uCCB4"} ~ ${to ?? "\uD604\uC7AC"}` : "\uAE30\uAC04: \uC804\uCCB4",
|
|
23255
23523
|
"",
|
|
23256
23524
|
`\uCFFC\uB9AC \uD69F\uC218: ${total.queries.toLocaleString()}\uD68C`,
|
|
23257
|
-
`\
|
|
23525
|
+
`\uCE90\uC2DC \uBBF8\uC801\uC911 \uC785\uB825: ${total.inputTokens.toLocaleString()}`,
|
|
23258
23526
|
`\uCD9C\uB825 \uD1A0\uD070: ${total.outputTokens.toLocaleString()}`,
|
|
23259
23527
|
`\uCE90\uC2DC \uC4F0\uAE30: ${total.cacheCreationInputTokens.toLocaleString()}`,
|
|
23260
23528
|
`\uCE90\uC2DC \uC77D\uAE30: ${total.cacheReadInputTokens.toLocaleString()}`,
|
|
23261
23529
|
`\uC608\uC0C1 \uBE44\uC6A9: $${estimatedCostUsd.toFixed(4)} USD`
|
|
23262
23530
|
];
|
|
23531
|
+
if (ignoredLegacyRecords > 0) {
|
|
23532
|
+
lines.push(`\uAD6C\uD615 \uB808\uCF54\uB4DC \uC81C\uC678: ${ignoredLegacyRecords.toLocaleString()}\uAC74`);
|
|
23533
|
+
}
|
|
23534
|
+
if (currentSessions.length > 0) {
|
|
23535
|
+
lines.push("", `\uD83D\uDCD0 \uD604\uC7AC context (${currentSessions.length}\uAC1C provider session)`);
|
|
23536
|
+
for (const current3 of currentSessions) {
|
|
23537
|
+
const percent = current3.contextTokens / current3.contextWindow * 100;
|
|
23538
|
+
lines.push(` ${current3.topicTitle} \xB7 ${current3.agent}/${current3.model} ${current3.contextTokens.toLocaleString()} / ${current3.contextWindow.toLocaleString()} (${percent.toFixed(1)}%)`);
|
|
23539
|
+
}
|
|
23540
|
+
}
|
|
23263
23541
|
const extraSummaryLines = host2.extraSummaryLines?.(context.userId) ?? [];
|
|
23264
23542
|
if (extraSummaryLines.length > 0)
|
|
23265
23543
|
lines.push("", ...extraSummaryLines);
|
|
@@ -23267,7 +23545,7 @@ function createTokenStatsMcpServer(context, host2 = defaultTokenStatsMcpHost) {
|
|
|
23267
23545
|
const sorted = Object.entries(bySession).map(([name, bucket]) => ({ name, ...bucket, cost: host2.calcCost(bucket) })).sort((left, right) => right.cost - left.cost);
|
|
23268
23546
|
lines.push("", `\uD83D\uDDC2 \uC138\uC158\uBCC4 \uC0AC\uC6A9\uB7C9 (${sorted.length}\uAC1C)`);
|
|
23269
23547
|
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)}`);
|
|
23548
|
+
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
23549
|
}
|
|
23272
23550
|
} else {
|
|
23273
23551
|
const hours = Object.keys(byHour).sort();
|
|
@@ -26199,6 +26477,7 @@ __export(exports_storage_public, {
|
|
|
26199
26477
|
grantSubagentTellTarget: () => grantSubagentTellTarget,
|
|
26200
26478
|
getWikiDir: () => getWikiDir,
|
|
26201
26479
|
getUserConfig: () => getUserConfig,
|
|
26480
|
+
getTopicStats: () => getTopicStats,
|
|
26202
26481
|
getTopicSessionId: () => getTopicSessionId,
|
|
26203
26482
|
getTopicNames: () => getTopicNames,
|
|
26204
26483
|
getTopicMemoryOrigin: () => getTopicMemoryOrigin,
|
|
@@ -26549,7 +26828,7 @@ data: ${JSON.stringify(message)}
|
|
|
26549
26828
|
var init_sse_transport = () => {};
|
|
26550
26829
|
|
|
26551
26830
|
// ../../packages/mcp/src/server.ts
|
|
26552
|
-
import { createHash as
|
|
26831
|
+
import { createHash as createHash10, randomUUID as randomUUID23 } from "crypto";
|
|
26553
26832
|
import { realpathSync as realpathSync6, statSync as statSync17 } from "fs";
|
|
26554
26833
|
import { basename as basename9, extname as extname3, resolve as resolve19 } from "path";
|
|
26555
26834
|
import { McpServer as McpServer7 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -26822,7 +27101,7 @@ async function closeNegotiumMcpSessions() {
|
|
|
26822
27101
|
await Promise.all([...servers].map((server) => closeServer(server)));
|
|
26823
27102
|
}
|
|
26824
27103
|
function tokenFingerprint(token) {
|
|
26825
|
-
return
|
|
27104
|
+
return createHash10("sha256").update(token).digest("hex");
|
|
26826
27105
|
}
|
|
26827
27106
|
async function enforceSessionBounds(reserve = 0) {
|
|
26828
27107
|
const now = Date.now();
|
|
@@ -28273,6 +28552,14 @@ function createNodeControlHandler(options) {
|
|
|
28273
28552
|
const events = listRecentRuntimeEventsForTopic(topicId).map(runtimeEvent);
|
|
28274
28553
|
return Response.json({ ok: true, events });
|
|
28275
28554
|
}
|
|
28555
|
+
const usageMatch = path.match(/^\/topics\/([^/]+)\/usage$/);
|
|
28556
|
+
if (usageMatch && req.method === "GET") {
|
|
28557
|
+
const topicId = decodeURIComponent(usageMatch[1]);
|
|
28558
|
+
const userId = requiredText(url.searchParams.get("user"), "user");
|
|
28559
|
+
if (!topicForUser(topicId, userId))
|
|
28560
|
+
return jsonError(404, "Topic not found");
|
|
28561
|
+
return Response.json({ ok: true, usage: getTopicStats(userId, topicId) });
|
|
28562
|
+
}
|
|
28276
28563
|
const modelMatch = path.match(/^\/topics\/([^/]+)\/model$/);
|
|
28277
28564
|
if (modelMatch && req.method === "POST") {
|
|
28278
28565
|
const topicId = decodeURIComponent(modelMatch[1]);
|
|
@@ -30664,6 +30951,7 @@ var init_commands = __esm(() => {
|
|
|
30664
30951
|
{ name: "new", usage: "/new", description: "reset the current session" },
|
|
30665
30952
|
{ name: "compact", usage: "/compact", description: "summarize and shrink context" },
|
|
30666
30953
|
{ name: "status", usage: "/status", description: "show model and token usage" },
|
|
30954
|
+
{ name: "context", usage: "/context", description: "show live context breakdown" },
|
|
30667
30955
|
{ name: "model", usage: "/model", description: "choose the model" },
|
|
30668
30956
|
{ name: "effort", usage: "/effort", description: "choose reasoning effort" },
|
|
30669
30957
|
{ name: "topics", usage: "/topics", description: "open topic picker" },
|
|
@@ -30692,297 +30980,6 @@ var init_commands = __esm(() => {
|
|
|
30692
30980
|
];
|
|
30693
30981
|
});
|
|
30694
30982
|
|
|
30695
|
-
// ../../adapters/terminal/src/path-suggest.ts
|
|
30696
|
-
import { execFile as execFile3 } from "child_process";
|
|
30697
|
-
import { existsSync as existsSync33, readdirSync as readdirSync14, statSync as statSync19 } from "fs";
|
|
30698
|
-
import { homedir as homedir10 } from "os";
|
|
30699
|
-
import { basename as basename11, dirname as dirname19, join as join40, sep as sep3 } from "path";
|
|
30700
|
-
import { promisify as promisify3 } from "util";
|
|
30701
|
-
function indexRecursivePaths(files) {
|
|
30702
|
-
const paths = [];
|
|
30703
|
-
const directories = new Set;
|
|
30704
|
-
for (const relPath of files) {
|
|
30705
|
-
const segments = relPath.split("/");
|
|
30706
|
-
if (segments.some((segment) => segment.startsWith(".")))
|
|
30707
|
-
continue;
|
|
30708
|
-
for (let index = 0;index < segments.length - 1; index += 1) {
|
|
30709
|
-
const directory = segments.slice(0, index + 1).join("/");
|
|
30710
|
-
if (directories.has(directory))
|
|
30711
|
-
continue;
|
|
30712
|
-
directories.add(directory);
|
|
30713
|
-
paths.push({
|
|
30714
|
-
relPath: directory,
|
|
30715
|
-
isDir: true,
|
|
30716
|
-
lowerName: (segments[index] ?? "").toLowerCase()
|
|
30717
|
-
});
|
|
30718
|
-
}
|
|
30719
|
-
paths.push({ relPath, isDir: false, lowerName: (segments.at(-1) ?? "").toLowerCase() });
|
|
30720
|
-
}
|
|
30721
|
-
return paths;
|
|
30722
|
-
}
|
|
30723
|
-
async function listFilesWithRipgrep(dir) {
|
|
30724
|
-
const args = ["--files", "--max-depth", String(RECURSIVE_MAX_DEPTH)];
|
|
30725
|
-
for (const excluded of RECURSIVE_EXCLUDES)
|
|
30726
|
-
args.push("-g", `!${excluded}`);
|
|
30727
|
-
args.push(".");
|
|
30728
|
-
let out = "";
|
|
30729
|
-
try {
|
|
30730
|
-
const result = await execFileAsync3("rg", args, {
|
|
30731
|
-
cwd: dir,
|
|
30732
|
-
encoding: "utf8",
|
|
30733
|
-
timeout: RECURSIVE_TIMEOUT_MS,
|
|
30734
|
-
maxBuffer: 32 * 1024 * 1024
|
|
30735
|
-
});
|
|
30736
|
-
out = result.stdout;
|
|
30737
|
-
} catch (err2) {
|
|
30738
|
-
const stdout = err2?.stdout;
|
|
30739
|
-
out = typeof stdout === "string" ? stdout : "";
|
|
30740
|
-
}
|
|
30741
|
-
return out.split(`
|
|
30742
|
-
`).filter(Boolean).map((path) => path.replaceAll(sep3, "/").replace(/^\.\//, ""));
|
|
30743
|
-
}
|
|
30744
|
-
function cacheRecursivePaths(dir, files) {
|
|
30745
|
-
const now = Date.now();
|
|
30746
|
-
for (const [key, entry] of recursiveCache) {
|
|
30747
|
-
if (now - entry.loadedAt >= RECURSIVE_CACHE_TTL_MS)
|
|
30748
|
-
recursiveCache.delete(key);
|
|
30749
|
-
}
|
|
30750
|
-
recursiveCache.delete(dir);
|
|
30751
|
-
while (recursiveCache.size >= MAX_RECURSIVE_CACHE_ENTRIES) {
|
|
30752
|
-
const oldest = recursiveCache.keys().next().value;
|
|
30753
|
-
if (oldest === undefined)
|
|
30754
|
-
break;
|
|
30755
|
-
recursiveCache.delete(oldest);
|
|
30756
|
-
}
|
|
30757
|
-
recursiveCache.set(dir, {
|
|
30758
|
-
loadedAt: now,
|
|
30759
|
-
paths: indexRecursivePaths(files),
|
|
30760
|
-
matches: new Map
|
|
30761
|
-
});
|
|
30762
|
-
}
|
|
30763
|
-
function loadFilesRecursive(dir, loader) {
|
|
30764
|
-
const cached = recursiveCache.get(dir);
|
|
30765
|
-
if (cached && Date.now() - cached.loadedAt < RECURSIVE_CACHE_TTL_MS) {
|
|
30766
|
-
recursiveCache.delete(dir);
|
|
30767
|
-
recursiveCache.set(dir, cached);
|
|
30768
|
-
return Promise.resolve(false);
|
|
30769
|
-
}
|
|
30770
|
-
const active2 = recursiveSearches.get(dir);
|
|
30771
|
-
if (active2)
|
|
30772
|
-
return active2;
|
|
30773
|
-
const search = (async () => {
|
|
30774
|
-
try {
|
|
30775
|
-
cacheRecursivePaths(dir, await loader(dir));
|
|
30776
|
-
} catch {
|
|
30777
|
-
cacheRecursivePaths(dir, []);
|
|
30778
|
-
}
|
|
30779
|
-
return true;
|
|
30780
|
-
})().finally(() => recursiveSearches.delete(dir));
|
|
30781
|
-
recursiveSearches.set(dir, search);
|
|
30782
|
-
return search;
|
|
30783
|
-
}
|
|
30784
|
-
function activeAtToken(lineText, col) {
|
|
30785
|
-
const upto = lineText.slice(0, col);
|
|
30786
|
-
const match = /(?:^|\s)@([^\s]*)$/.exec(upto);
|
|
30787
|
-
if (!match)
|
|
30788
|
-
return null;
|
|
30789
|
-
const frag = match[1] ?? "";
|
|
30790
|
-
return { start: col - frag.length - 1, frag };
|
|
30791
|
-
}
|
|
30792
|
-
function resolveFragment(frag) {
|
|
30793
|
-
const home = homedir10();
|
|
30794
|
-
let path;
|
|
30795
|
-
if (frag === "" || frag === "~")
|
|
30796
|
-
path = home;
|
|
30797
|
-
else if (frag === "~/")
|
|
30798
|
-
path = home;
|
|
30799
|
-
else if (frag.startsWith("~/"))
|
|
30800
|
-
path = join40(home, frag.slice(2));
|
|
30801
|
-
else if (frag.startsWith("/"))
|
|
30802
|
-
path = frag;
|
|
30803
|
-
else
|
|
30804
|
-
path = join40(home, frag);
|
|
30805
|
-
if (frag.endsWith("/") || frag === "" || frag === "~") {
|
|
30806
|
-
return { dir: path, prefix: "" };
|
|
30807
|
-
}
|
|
30808
|
-
return { dir: dirname19(path), prefix: basename11(path) };
|
|
30809
|
-
}
|
|
30810
|
-
function toToken(fullPath, isDir) {
|
|
30811
|
-
const home = homedir10();
|
|
30812
|
-
let shown = fullPath;
|
|
30813
|
-
if (fullPath === home)
|
|
30814
|
-
shown = "~";
|
|
30815
|
-
else if (fullPath.startsWith(`${home}/`))
|
|
30816
|
-
shown = `~/${fullPath.slice(home.length + 1)}`;
|
|
30817
|
-
return `@${shown}${isDir ? "/" : ""}`;
|
|
30818
|
-
}
|
|
30819
|
-
function rankAndSlice(dir, candidates) {
|
|
30820
|
-
candidates.sort((a, b) => {
|
|
30821
|
-
if (a.isPrefixMatch !== b.isPrefixMatch)
|
|
30822
|
-
return a.isPrefixMatch ? -1 : 1;
|
|
30823
|
-
if (a.isDir !== b.isDir)
|
|
30824
|
-
return a.isDir ? -1 : 1;
|
|
30825
|
-
const depthA = a.relPath.split("/").length;
|
|
30826
|
-
const depthB = b.relPath.split("/").length;
|
|
30827
|
-
if (depthA !== depthB)
|
|
30828
|
-
return depthA - depthB;
|
|
30829
|
-
return a.relPath.localeCompare(b.relPath);
|
|
30830
|
-
});
|
|
30831
|
-
return candidates.slice(0, MAX_SUGGESTIONS).map(({ relPath, isDir }) => ({
|
|
30832
|
-
label: `${relPath}${isDir ? "/" : ""}`,
|
|
30833
|
-
value: toToken(join40(dir, relPath), isDir),
|
|
30834
|
-
isDir
|
|
30835
|
-
}));
|
|
30836
|
-
}
|
|
30837
|
-
function recursiveCandidates(dir, lowerPrefix, seen) {
|
|
30838
|
-
const entry = recursiveCache.get(dir);
|
|
30839
|
-
if (!entry)
|
|
30840
|
-
return [];
|
|
30841
|
-
let matches = entry.matches.get(lowerPrefix);
|
|
30842
|
-
if (!matches) {
|
|
30843
|
-
matches = entry.paths.filter((path) => path.lowerName.includes(lowerPrefix)).map((path) => ({
|
|
30844
|
-
relPath: path.relPath,
|
|
30845
|
-
isDir: path.isDir,
|
|
30846
|
-
isPrefixMatch: path.lowerName.startsWith(lowerPrefix)
|
|
30847
|
-
}));
|
|
30848
|
-
if (entry.matches.size >= MAX_MATCH_CACHE_ENTRIES) {
|
|
30849
|
-
const oldest = entry.matches.keys().next().value;
|
|
30850
|
-
if (oldest !== undefined)
|
|
30851
|
-
entry.matches.delete(oldest);
|
|
30852
|
-
}
|
|
30853
|
-
entry.matches.set(lowerPrefix, matches);
|
|
30854
|
-
} else {
|
|
30855
|
-
entry.matches.delete(lowerPrefix);
|
|
30856
|
-
entry.matches.set(lowerPrefix, matches);
|
|
30857
|
-
}
|
|
30858
|
-
return matches.filter((candidate) => {
|
|
30859
|
-
if (seen.has(candidate.relPath))
|
|
30860
|
-
return false;
|
|
30861
|
-
seen.add(candidate.relPath);
|
|
30862
|
-
return true;
|
|
30863
|
-
});
|
|
30864
|
-
}
|
|
30865
|
-
function isRecursivePathQuery(lineText, col) {
|
|
30866
|
-
const token = activeAtToken(lineText, col);
|
|
30867
|
-
if (!token)
|
|
30868
|
-
return false;
|
|
30869
|
-
const { prefix } = resolveFragment(token.frag);
|
|
30870
|
-
return !prefix.startsWith(".") && prefix.length >= RECURSIVE_MIN_PREFIX_LEN;
|
|
30871
|
-
}
|
|
30872
|
-
async function warmPathSuggestions(lineText, col, loader = listFilesWithRipgrep) {
|
|
30873
|
-
if (!isRecursivePathQuery(lineText, col))
|
|
30874
|
-
return false;
|
|
30875
|
-
const token = activeAtToken(lineText, col);
|
|
30876
|
-
if (!token)
|
|
30877
|
-
return false;
|
|
30878
|
-
const { dir } = resolveFragment(token.frag);
|
|
30879
|
-
try {
|
|
30880
|
-
if (!statSync19(dir).isDirectory())
|
|
30881
|
-
return false;
|
|
30882
|
-
} catch {
|
|
30883
|
-
return false;
|
|
30884
|
-
}
|
|
30885
|
-
return loadFilesRecursive(dir, loader);
|
|
30886
|
-
}
|
|
30887
|
-
function pathSuggestions(lineText, col) {
|
|
30888
|
-
const token = activeAtToken(lineText, col);
|
|
30889
|
-
if (!token)
|
|
30890
|
-
return null;
|
|
30891
|
-
const { dir, prefix } = resolveFragment(token.frag);
|
|
30892
|
-
let entries;
|
|
30893
|
-
try {
|
|
30894
|
-
entries = readdirSync14(dir, { withFileTypes: true });
|
|
30895
|
-
} catch {
|
|
30896
|
-
return { start: token.start, end: col, items: [], truncated: 0, searching: false };
|
|
30897
|
-
}
|
|
30898
|
-
const lowerPrefix = prefix.toLowerCase();
|
|
30899
|
-
const showHidden = prefix.startsWith(".");
|
|
30900
|
-
const candidates = entries.filter((entry) => {
|
|
30901
|
-
const name = entry.name;
|
|
30902
|
-
if (!showHidden && name.startsWith("."))
|
|
30903
|
-
return false;
|
|
30904
|
-
if (lowerPrefix === "")
|
|
30905
|
-
return true;
|
|
30906
|
-
const lowerName = name.toLowerCase();
|
|
30907
|
-
return showHidden ? lowerName.startsWith(lowerPrefix) : lowerName.includes(lowerPrefix);
|
|
30908
|
-
}).map((entry) => {
|
|
30909
|
-
let isDir = entry.isDirectory();
|
|
30910
|
-
if (!isDir && entry.isSymbolicLink()) {
|
|
30911
|
-
try {
|
|
30912
|
-
isDir = statSync19(join40(dir, entry.name)).isDirectory();
|
|
30913
|
-
} catch {}
|
|
30914
|
-
}
|
|
30915
|
-
return {
|
|
30916
|
-
relPath: entry.name,
|
|
30917
|
-
isDir,
|
|
30918
|
-
isPrefixMatch: entry.name.toLowerCase().startsWith(lowerPrefix)
|
|
30919
|
-
};
|
|
30920
|
-
});
|
|
30921
|
-
if (!showHidden && lowerPrefix.length >= RECURSIVE_MIN_PREFIX_LEN) {
|
|
30922
|
-
const seen = new Set(candidates.map((c) => c.relPath));
|
|
30923
|
-
candidates.push(...recursiveCandidates(dir, lowerPrefix, seen));
|
|
30924
|
-
}
|
|
30925
|
-
const items = rankAndSlice(dir, candidates);
|
|
30926
|
-
return {
|
|
30927
|
-
start: token.start,
|
|
30928
|
-
end: col,
|
|
30929
|
-
items,
|
|
30930
|
-
truncated: Math.max(0, candidates.length - items.length),
|
|
30931
|
-
searching: recursiveSearches.has(dir)
|
|
30932
|
-
};
|
|
30933
|
-
}
|
|
30934
|
-
function fragmentToAbsolutePath(frag) {
|
|
30935
|
-
const home = homedir10();
|
|
30936
|
-
if (frag === "~" || frag === "~/")
|
|
30937
|
-
return home;
|
|
30938
|
-
if (frag.startsWith("~/"))
|
|
30939
|
-
return join40(home, frag.slice(2));
|
|
30940
|
-
if (frag.startsWith("/"))
|
|
30941
|
-
return frag;
|
|
30942
|
-
return join40(home, frag);
|
|
30943
|
-
}
|
|
30944
|
-
function fragmentResolves(frag) {
|
|
30945
|
-
try {
|
|
30946
|
-
return existsSync33(fragmentToAbsolutePath(frag));
|
|
30947
|
-
} catch {
|
|
30948
|
-
return false;
|
|
30949
|
-
}
|
|
30950
|
-
}
|
|
30951
|
-
function stripResolvedPathTriggers(text2) {
|
|
30952
|
-
return text2.replaceAll(AT_PATH_TOKEN, (whole, lead, frag) => {
|
|
30953
|
-
if (fragmentResolves(frag))
|
|
30954
|
-
return `${lead}${frag}`;
|
|
30955
|
-
const pathPart = frag.replace(TRAILING_PUNCT, "");
|
|
30956
|
-
if (pathPart && pathPart !== frag && fragmentResolves(pathPart)) {
|
|
30957
|
-
return `${lead}${pathPart}${frag.slice(pathPart.length)}`;
|
|
30958
|
-
}
|
|
30959
|
-
return whole;
|
|
30960
|
-
});
|
|
30961
|
-
}
|
|
30962
|
-
function completePathToken(lineText, col, suggestion, options = {}) {
|
|
30963
|
-
const token = activeAtToken(lineText, col);
|
|
30964
|
-
if (!token)
|
|
30965
|
-
return null;
|
|
30966
|
-
let tokenEnd = col;
|
|
30967
|
-
while (tokenEnd < lineText.length && !/\s/.test(lineText[tokenEnd] ?? " "))
|
|
30968
|
-
tokenEnd += 1;
|
|
30969
|
-
const trailingText = lineText.slice(col, tokenEnd);
|
|
30970
|
-
const pathTail = trailingText.replace(TRAILING_PUNCT, "");
|
|
30971
|
-
const end = col + pathTail.length;
|
|
30972
|
-
const replacement = options.keepTrigger ? suggestion.value : suggestion.value.slice(1);
|
|
30973
|
-
const line = lineText.slice(0, token.start) + replacement + lineText.slice(end);
|
|
30974
|
-
return { line, col: token.start + replacement.length };
|
|
30975
|
-
}
|
|
30976
|
-
var MAX_SUGGESTIONS = 8, RECURSIVE_MIN_PREFIX_LEN = 4, RECURSIVE_MAX_DEPTH = 10, RECURSIVE_TIMEOUT_MS = 2000, RECURSIVE_CACHE_TTL_MS = 30000, MAX_RECURSIVE_CACHE_ENTRIES = 3, MAX_MATCH_CACHE_ENTRIES = 24, RECURSIVE_EXCLUDES, recursiveCache, recursiveSearches, execFileAsync3, AT_PATH_TOKEN, TRAILING_PUNCT;
|
|
30977
|
-
var init_path_suggest = __esm(() => {
|
|
30978
|
-
RECURSIVE_EXCLUDES = ["node_modules", ".git", ".cache", "dist", "build", "Library", ".Trash"];
|
|
30979
|
-
recursiveCache = new Map;
|
|
30980
|
-
recursiveSearches = new Map;
|
|
30981
|
-
execFileAsync3 = promisify3(execFile3);
|
|
30982
|
-
AT_PATH_TOKEN = /(^|[\s([{<])@([^\s]+)/g;
|
|
30983
|
-
TRAILING_PUNCT = /[.,;:!?)\]}>"'`]+$/;
|
|
30984
|
-
});
|
|
30985
|
-
|
|
30986
30983
|
// ../../adapters/terminal/src/subagent-graph.ts
|
|
30987
30984
|
import {
|
|
30988
30985
|
layoutTerminalGraph,
|
|
@@ -31160,6 +31157,7 @@ function createInitialState(userId) {
|
|
|
31160
31157
|
backgroundSessions: [],
|
|
31161
31158
|
activeTopicId: null,
|
|
31162
31159
|
messages: {},
|
|
31160
|
+
topicUsage: {},
|
|
31163
31161
|
messageHistory: {},
|
|
31164
31162
|
activity: {},
|
|
31165
31163
|
input: "",
|
|
@@ -31195,6 +31193,12 @@ function activeTopic(state) {
|
|
|
31195
31193
|
function activeMessages(state) {
|
|
31196
31194
|
return state.activeTopicId ? state.messages[state.activeTopicId] ?? [] : [];
|
|
31197
31195
|
}
|
|
31196
|
+
function setTopicUsage(state, usage) {
|
|
31197
|
+
return {
|
|
31198
|
+
...state,
|
|
31199
|
+
topicUsage: { ...state.topicUsage, [usage.topicId]: usage }
|
|
31200
|
+
};
|
|
31201
|
+
}
|
|
31198
31202
|
function activeQuestion(state) {
|
|
31199
31203
|
return activeMessages(state).slice().reverse().find((message) => message.kind === "ask_user_question" && message.askUserQuestion && !message.askUserQuestion.expired && !message.askUserQuestion.selectedLabel) ?? null;
|
|
31200
31204
|
}
|
|
@@ -31610,6 +31614,19 @@ function applyAiStatus(state, topicId, raw, createdAt) {
|
|
|
31610
31614
|
tools: []
|
|
31611
31615
|
});
|
|
31612
31616
|
}
|
|
31617
|
+
if (kind === "context_progress") {
|
|
31618
|
+
if (isStaleTerminalStatus(current3, status))
|
|
31619
|
+
return state;
|
|
31620
|
+
return setActivity(state, topicId, {
|
|
31621
|
+
...liveCurrent,
|
|
31622
|
+
running: true,
|
|
31623
|
+
queryId: typeof status.queryId === "string" ? status.queryId : current3.queryId,
|
|
31624
|
+
contextProgress: {
|
|
31625
|
+
assistantTokens: Math.max(0, Number(status.assistantTokens) || 0),
|
|
31626
|
+
toolTokens: Math.max(0, Number(status.toolTokens) || 0)
|
|
31627
|
+
}
|
|
31628
|
+
});
|
|
31629
|
+
}
|
|
31613
31630
|
if (kind === "ai_done") {
|
|
31614
31631
|
if (isStaleTerminalStatus(current3, status))
|
|
31615
31632
|
return state;
|
|
@@ -31723,6 +31740,356 @@ var init_state2 = __esm(async () => {
|
|
|
31723
31740
|
await init_src();
|
|
31724
31741
|
});
|
|
31725
31742
|
|
|
31743
|
+
// ../../adapters/terminal/src/context-usage.ts
|
|
31744
|
+
function latestUsageMessage(messages) {
|
|
31745
|
+
for (let index = messages.length - 1;index >= 0; index--) {
|
|
31746
|
+
const usage = messages[index]?.authorId === "ai" ? messages[index]?.usage : undefined;
|
|
31747
|
+
if (usage?.context !== undefined && usage.contextWindow)
|
|
31748
|
+
return { index, usage };
|
|
31749
|
+
}
|
|
31750
|
+
return;
|
|
31751
|
+
}
|
|
31752
|
+
function estimateMessages(messages, predicate) {
|
|
31753
|
+
return messages.reduce((sum, message) => predicate(message) ? sum + 4 + estimateTextTokens(message.text) : sum, 0);
|
|
31754
|
+
}
|
|
31755
|
+
function activeContextBreakdown(state) {
|
|
31756
|
+
const topic = activeTopic(state);
|
|
31757
|
+
if (!topic)
|
|
31758
|
+
return;
|
|
31759
|
+
const messages = activeMessages(state);
|
|
31760
|
+
const latest = latestUsageMessage(messages);
|
|
31761
|
+
const stored = state.topicUsage[topic.id]?.currentSession;
|
|
31762
|
+
const confirmed = latest?.usage.context ?? stored?.contextTokens;
|
|
31763
|
+
const contextWindow = latest?.usage.contextWindow ?? stored?.contextWindow;
|
|
31764
|
+
if (confirmed === undefined || !contextWindow)
|
|
31765
|
+
return;
|
|
31766
|
+
const activity = state.activity[topic.id];
|
|
31767
|
+
if (!activity?.running) {
|
|
31768
|
+
return {
|
|
31769
|
+
context: confirmed,
|
|
31770
|
+
contextWindow,
|
|
31771
|
+
confirmed,
|
|
31772
|
+
user: 0,
|
|
31773
|
+
assistant: 0,
|
|
31774
|
+
tools: 0,
|
|
31775
|
+
free: Math.max(0, contextWindow - confirmed),
|
|
31776
|
+
estimated: false
|
|
31777
|
+
};
|
|
31778
|
+
}
|
|
31779
|
+
const afterBaseline = latest ? messages.slice(latest.index + 1) : stored?.timestamp ? messages.filter((message) => message.createdAt > stored.timestamp) : messages;
|
|
31780
|
+
const user = estimateMessages(afterBaseline, (message) => message.authorId !== "ai" && message.kind !== "system");
|
|
31781
|
+
const assistant = activity.contextProgress?.assistantTokens ?? estimateMessages(afterBaseline, (message) => message.authorId === "ai" && message.kind !== "tool");
|
|
31782
|
+
const tools = activity.contextProgress?.toolTokens ?? estimateMessages(afterBaseline, (message) => message.kind === "tool");
|
|
31783
|
+
const context = confirmed + user + assistant + tools;
|
|
31784
|
+
return {
|
|
31785
|
+
context,
|
|
31786
|
+
contextWindow,
|
|
31787
|
+
confirmed,
|
|
31788
|
+
user,
|
|
31789
|
+
assistant,
|
|
31790
|
+
tools,
|
|
31791
|
+
free: Math.max(0, contextWindow - context),
|
|
31792
|
+
estimated: true
|
|
31793
|
+
};
|
|
31794
|
+
}
|
|
31795
|
+
var init_context_usage = __esm(async () => {
|
|
31796
|
+
await __promiseAll([
|
|
31797
|
+
init_src(),
|
|
31798
|
+
init_state2()
|
|
31799
|
+
]);
|
|
31800
|
+
});
|
|
31801
|
+
|
|
31802
|
+
// ../../adapters/terminal/src/path-suggest.ts
|
|
31803
|
+
import { execFile as execFile3 } from "child_process";
|
|
31804
|
+
import { existsSync as existsSync33, readdirSync as readdirSync14, statSync as statSync19 } from "fs";
|
|
31805
|
+
import { homedir as homedir10 } from "os";
|
|
31806
|
+
import { basename as basename11, dirname as dirname19, join as join40, sep as sep3 } from "path";
|
|
31807
|
+
import { promisify as promisify3 } from "util";
|
|
31808
|
+
function indexRecursivePaths(files) {
|
|
31809
|
+
const paths = [];
|
|
31810
|
+
const directories = new Set;
|
|
31811
|
+
for (const relPath of files) {
|
|
31812
|
+
const segments = relPath.split("/");
|
|
31813
|
+
if (segments.some((segment) => segment.startsWith(".")))
|
|
31814
|
+
continue;
|
|
31815
|
+
for (let index = 0;index < segments.length - 1; index += 1) {
|
|
31816
|
+
const directory = segments.slice(0, index + 1).join("/");
|
|
31817
|
+
if (directories.has(directory))
|
|
31818
|
+
continue;
|
|
31819
|
+
directories.add(directory);
|
|
31820
|
+
paths.push({
|
|
31821
|
+
relPath: directory,
|
|
31822
|
+
isDir: true,
|
|
31823
|
+
lowerName: (segments[index] ?? "").toLowerCase()
|
|
31824
|
+
});
|
|
31825
|
+
}
|
|
31826
|
+
paths.push({ relPath, isDir: false, lowerName: (segments.at(-1) ?? "").toLowerCase() });
|
|
31827
|
+
}
|
|
31828
|
+
return paths;
|
|
31829
|
+
}
|
|
31830
|
+
async function listFilesWithRipgrep(dir) {
|
|
31831
|
+
const args = ["--files", "--max-depth", String(RECURSIVE_MAX_DEPTH)];
|
|
31832
|
+
for (const excluded of RECURSIVE_EXCLUDES)
|
|
31833
|
+
args.push("-g", `!${excluded}`);
|
|
31834
|
+
args.push(".");
|
|
31835
|
+
let out = "";
|
|
31836
|
+
try {
|
|
31837
|
+
const result = await execFileAsync3("rg", args, {
|
|
31838
|
+
cwd: dir,
|
|
31839
|
+
encoding: "utf8",
|
|
31840
|
+
timeout: RECURSIVE_TIMEOUT_MS,
|
|
31841
|
+
maxBuffer: 32 * 1024 * 1024
|
|
31842
|
+
});
|
|
31843
|
+
out = result.stdout;
|
|
31844
|
+
} catch (err2) {
|
|
31845
|
+
const stdout = err2?.stdout;
|
|
31846
|
+
out = typeof stdout === "string" ? stdout : "";
|
|
31847
|
+
}
|
|
31848
|
+
return out.split(`
|
|
31849
|
+
`).filter(Boolean).map((path) => path.replaceAll(sep3, "/").replace(/^\.\//, ""));
|
|
31850
|
+
}
|
|
31851
|
+
function cacheRecursivePaths(dir, files) {
|
|
31852
|
+
const now = Date.now();
|
|
31853
|
+
for (const [key, entry] of recursiveCache) {
|
|
31854
|
+
if (now - entry.loadedAt >= RECURSIVE_CACHE_TTL_MS)
|
|
31855
|
+
recursiveCache.delete(key);
|
|
31856
|
+
}
|
|
31857
|
+
recursiveCache.delete(dir);
|
|
31858
|
+
while (recursiveCache.size >= MAX_RECURSIVE_CACHE_ENTRIES) {
|
|
31859
|
+
const oldest = recursiveCache.keys().next().value;
|
|
31860
|
+
if (oldest === undefined)
|
|
31861
|
+
break;
|
|
31862
|
+
recursiveCache.delete(oldest);
|
|
31863
|
+
}
|
|
31864
|
+
recursiveCache.set(dir, {
|
|
31865
|
+
loadedAt: now,
|
|
31866
|
+
paths: indexRecursivePaths(files),
|
|
31867
|
+
matches: new Map
|
|
31868
|
+
});
|
|
31869
|
+
}
|
|
31870
|
+
function loadFilesRecursive(dir, loader) {
|
|
31871
|
+
const cached = recursiveCache.get(dir);
|
|
31872
|
+
if (cached && Date.now() - cached.loadedAt < RECURSIVE_CACHE_TTL_MS) {
|
|
31873
|
+
recursiveCache.delete(dir);
|
|
31874
|
+
recursiveCache.set(dir, cached);
|
|
31875
|
+
return Promise.resolve(false);
|
|
31876
|
+
}
|
|
31877
|
+
const active2 = recursiveSearches.get(dir);
|
|
31878
|
+
if (active2)
|
|
31879
|
+
return active2;
|
|
31880
|
+
const search = (async () => {
|
|
31881
|
+
try {
|
|
31882
|
+
cacheRecursivePaths(dir, await loader(dir));
|
|
31883
|
+
} catch {
|
|
31884
|
+
cacheRecursivePaths(dir, []);
|
|
31885
|
+
}
|
|
31886
|
+
return true;
|
|
31887
|
+
})().finally(() => recursiveSearches.delete(dir));
|
|
31888
|
+
recursiveSearches.set(dir, search);
|
|
31889
|
+
return search;
|
|
31890
|
+
}
|
|
31891
|
+
function activeAtToken(lineText, col) {
|
|
31892
|
+
const upto = lineText.slice(0, col);
|
|
31893
|
+
const match = /(?:^|\s)@([^\s]*)$/.exec(upto);
|
|
31894
|
+
if (!match)
|
|
31895
|
+
return null;
|
|
31896
|
+
const frag = match[1] ?? "";
|
|
31897
|
+
return { start: col - frag.length - 1, frag };
|
|
31898
|
+
}
|
|
31899
|
+
function resolveFragment(frag) {
|
|
31900
|
+
const home = homedir10();
|
|
31901
|
+
let path;
|
|
31902
|
+
if (frag === "" || frag === "~")
|
|
31903
|
+
path = home;
|
|
31904
|
+
else if (frag === "~/")
|
|
31905
|
+
path = home;
|
|
31906
|
+
else if (frag.startsWith("~/"))
|
|
31907
|
+
path = join40(home, frag.slice(2));
|
|
31908
|
+
else if (frag.startsWith("/"))
|
|
31909
|
+
path = frag;
|
|
31910
|
+
else
|
|
31911
|
+
path = join40(home, frag);
|
|
31912
|
+
if (frag.endsWith("/") || frag === "" || frag === "~") {
|
|
31913
|
+
return { dir: path, prefix: "" };
|
|
31914
|
+
}
|
|
31915
|
+
return { dir: dirname19(path), prefix: basename11(path) };
|
|
31916
|
+
}
|
|
31917
|
+
function toToken(fullPath, isDir) {
|
|
31918
|
+
const home = homedir10();
|
|
31919
|
+
let shown = fullPath;
|
|
31920
|
+
if (fullPath === home)
|
|
31921
|
+
shown = "~";
|
|
31922
|
+
else if (fullPath.startsWith(`${home}/`))
|
|
31923
|
+
shown = `~/${fullPath.slice(home.length + 1)}`;
|
|
31924
|
+
return `@${shown}${isDir ? "/" : ""}`;
|
|
31925
|
+
}
|
|
31926
|
+
function rankAndSlice(dir, candidates) {
|
|
31927
|
+
candidates.sort((a, b) => {
|
|
31928
|
+
if (a.isPrefixMatch !== b.isPrefixMatch)
|
|
31929
|
+
return a.isPrefixMatch ? -1 : 1;
|
|
31930
|
+
if (a.isDir !== b.isDir)
|
|
31931
|
+
return a.isDir ? -1 : 1;
|
|
31932
|
+
const depthA = a.relPath.split("/").length;
|
|
31933
|
+
const depthB = b.relPath.split("/").length;
|
|
31934
|
+
if (depthA !== depthB)
|
|
31935
|
+
return depthA - depthB;
|
|
31936
|
+
return a.relPath.localeCompare(b.relPath);
|
|
31937
|
+
});
|
|
31938
|
+
return candidates.slice(0, MAX_SUGGESTIONS).map(({ relPath, isDir }) => ({
|
|
31939
|
+
label: `${relPath}${isDir ? "/" : ""}`,
|
|
31940
|
+
value: toToken(join40(dir, relPath), isDir),
|
|
31941
|
+
isDir
|
|
31942
|
+
}));
|
|
31943
|
+
}
|
|
31944
|
+
function recursiveCandidates(dir, lowerPrefix, seen) {
|
|
31945
|
+
const entry = recursiveCache.get(dir);
|
|
31946
|
+
if (!entry)
|
|
31947
|
+
return [];
|
|
31948
|
+
let matches = entry.matches.get(lowerPrefix);
|
|
31949
|
+
if (!matches) {
|
|
31950
|
+
matches = entry.paths.filter((path) => path.lowerName.includes(lowerPrefix)).map((path) => ({
|
|
31951
|
+
relPath: path.relPath,
|
|
31952
|
+
isDir: path.isDir,
|
|
31953
|
+
isPrefixMatch: path.lowerName.startsWith(lowerPrefix)
|
|
31954
|
+
}));
|
|
31955
|
+
if (entry.matches.size >= MAX_MATCH_CACHE_ENTRIES) {
|
|
31956
|
+
const oldest = entry.matches.keys().next().value;
|
|
31957
|
+
if (oldest !== undefined)
|
|
31958
|
+
entry.matches.delete(oldest);
|
|
31959
|
+
}
|
|
31960
|
+
entry.matches.set(lowerPrefix, matches);
|
|
31961
|
+
} else {
|
|
31962
|
+
entry.matches.delete(lowerPrefix);
|
|
31963
|
+
entry.matches.set(lowerPrefix, matches);
|
|
31964
|
+
}
|
|
31965
|
+
return matches.filter((candidate) => {
|
|
31966
|
+
if (seen.has(candidate.relPath))
|
|
31967
|
+
return false;
|
|
31968
|
+
seen.add(candidate.relPath);
|
|
31969
|
+
return true;
|
|
31970
|
+
});
|
|
31971
|
+
}
|
|
31972
|
+
function isRecursivePathQuery(lineText, col) {
|
|
31973
|
+
const token = activeAtToken(lineText, col);
|
|
31974
|
+
if (!token)
|
|
31975
|
+
return false;
|
|
31976
|
+
const { prefix } = resolveFragment(token.frag);
|
|
31977
|
+
return !prefix.startsWith(".") && prefix.length >= RECURSIVE_MIN_PREFIX_LEN;
|
|
31978
|
+
}
|
|
31979
|
+
async function warmPathSuggestions(lineText, col, loader = listFilesWithRipgrep) {
|
|
31980
|
+
if (!isRecursivePathQuery(lineText, col))
|
|
31981
|
+
return false;
|
|
31982
|
+
const token = activeAtToken(lineText, col);
|
|
31983
|
+
if (!token)
|
|
31984
|
+
return false;
|
|
31985
|
+
const { dir } = resolveFragment(token.frag);
|
|
31986
|
+
try {
|
|
31987
|
+
if (!statSync19(dir).isDirectory())
|
|
31988
|
+
return false;
|
|
31989
|
+
} catch {
|
|
31990
|
+
return false;
|
|
31991
|
+
}
|
|
31992
|
+
return loadFilesRecursive(dir, loader);
|
|
31993
|
+
}
|
|
31994
|
+
function pathSuggestions(lineText, col) {
|
|
31995
|
+
const token = activeAtToken(lineText, col);
|
|
31996
|
+
if (!token)
|
|
31997
|
+
return null;
|
|
31998
|
+
const { dir, prefix } = resolveFragment(token.frag);
|
|
31999
|
+
let entries;
|
|
32000
|
+
try {
|
|
32001
|
+
entries = readdirSync14(dir, { withFileTypes: true });
|
|
32002
|
+
} catch {
|
|
32003
|
+
return { start: token.start, end: col, items: [], truncated: 0, searching: false };
|
|
32004
|
+
}
|
|
32005
|
+
const lowerPrefix = prefix.toLowerCase();
|
|
32006
|
+
const showHidden = prefix.startsWith(".");
|
|
32007
|
+
const candidates = entries.filter((entry) => {
|
|
32008
|
+
const name = entry.name;
|
|
32009
|
+
if (!showHidden && name.startsWith("."))
|
|
32010
|
+
return false;
|
|
32011
|
+
if (lowerPrefix === "")
|
|
32012
|
+
return true;
|
|
32013
|
+
const lowerName = name.toLowerCase();
|
|
32014
|
+
return showHidden ? lowerName.startsWith(lowerPrefix) : lowerName.includes(lowerPrefix);
|
|
32015
|
+
}).map((entry) => {
|
|
32016
|
+
let isDir = entry.isDirectory();
|
|
32017
|
+
if (!isDir && entry.isSymbolicLink()) {
|
|
32018
|
+
try {
|
|
32019
|
+
isDir = statSync19(join40(dir, entry.name)).isDirectory();
|
|
32020
|
+
} catch {}
|
|
32021
|
+
}
|
|
32022
|
+
return {
|
|
32023
|
+
relPath: entry.name,
|
|
32024
|
+
isDir,
|
|
32025
|
+
isPrefixMatch: entry.name.toLowerCase().startsWith(lowerPrefix)
|
|
32026
|
+
};
|
|
32027
|
+
});
|
|
32028
|
+
if (!showHidden && lowerPrefix.length >= RECURSIVE_MIN_PREFIX_LEN) {
|
|
32029
|
+
const seen = new Set(candidates.map((c) => c.relPath));
|
|
32030
|
+
candidates.push(...recursiveCandidates(dir, lowerPrefix, seen));
|
|
32031
|
+
}
|
|
32032
|
+
const items = rankAndSlice(dir, candidates);
|
|
32033
|
+
return {
|
|
32034
|
+
start: token.start,
|
|
32035
|
+
end: col,
|
|
32036
|
+
items,
|
|
32037
|
+
truncated: Math.max(0, candidates.length - items.length),
|
|
32038
|
+
searching: recursiveSearches.has(dir)
|
|
32039
|
+
};
|
|
32040
|
+
}
|
|
32041
|
+
function fragmentToAbsolutePath(frag) {
|
|
32042
|
+
const home = homedir10();
|
|
32043
|
+
if (frag === "~" || frag === "~/")
|
|
32044
|
+
return home;
|
|
32045
|
+
if (frag.startsWith("~/"))
|
|
32046
|
+
return join40(home, frag.slice(2));
|
|
32047
|
+
if (frag.startsWith("/"))
|
|
32048
|
+
return frag;
|
|
32049
|
+
return join40(home, frag);
|
|
32050
|
+
}
|
|
32051
|
+
function fragmentResolves(frag) {
|
|
32052
|
+
try {
|
|
32053
|
+
return existsSync33(fragmentToAbsolutePath(frag));
|
|
32054
|
+
} catch {
|
|
32055
|
+
return false;
|
|
32056
|
+
}
|
|
32057
|
+
}
|
|
32058
|
+
function stripResolvedPathTriggers(text2) {
|
|
32059
|
+
return text2.replaceAll(AT_PATH_TOKEN, (whole, lead, frag) => {
|
|
32060
|
+
if (fragmentResolves(frag))
|
|
32061
|
+
return `${lead}${frag}`;
|
|
32062
|
+
const pathPart = frag.replace(TRAILING_PUNCT, "");
|
|
32063
|
+
if (pathPart && pathPart !== frag && fragmentResolves(pathPart)) {
|
|
32064
|
+
return `${lead}${pathPart}${frag.slice(pathPart.length)}`;
|
|
32065
|
+
}
|
|
32066
|
+
return whole;
|
|
32067
|
+
});
|
|
32068
|
+
}
|
|
32069
|
+
function completePathToken(lineText, col, suggestion, options = {}) {
|
|
32070
|
+
const token = activeAtToken(lineText, col);
|
|
32071
|
+
if (!token)
|
|
32072
|
+
return null;
|
|
32073
|
+
let tokenEnd = col;
|
|
32074
|
+
while (tokenEnd < lineText.length && !/\s/.test(lineText[tokenEnd] ?? " "))
|
|
32075
|
+
tokenEnd += 1;
|
|
32076
|
+
const trailingText = lineText.slice(col, tokenEnd);
|
|
32077
|
+
const pathTail = trailingText.replace(TRAILING_PUNCT, "");
|
|
32078
|
+
const end = col + pathTail.length;
|
|
32079
|
+
const replacement = options.keepTrigger ? suggestion.value : suggestion.value.slice(1);
|
|
32080
|
+
const line = lineText.slice(0, token.start) + replacement + lineText.slice(end);
|
|
32081
|
+
return { line, col: token.start + replacement.length };
|
|
32082
|
+
}
|
|
32083
|
+
var MAX_SUGGESTIONS = 8, RECURSIVE_MIN_PREFIX_LEN = 4, RECURSIVE_MAX_DEPTH = 10, RECURSIVE_TIMEOUT_MS = 2000, RECURSIVE_CACHE_TTL_MS = 30000, MAX_RECURSIVE_CACHE_ENTRIES = 3, MAX_MATCH_CACHE_ENTRIES = 24, RECURSIVE_EXCLUDES, recursiveCache, recursiveSearches, execFileAsync3, AT_PATH_TOKEN, TRAILING_PUNCT;
|
|
32084
|
+
var init_path_suggest = __esm(() => {
|
|
32085
|
+
RECURSIVE_EXCLUDES = ["node_modules", ".git", ".cache", "dist", "build", "Library", ".Trash"];
|
|
32086
|
+
recursiveCache = new Map;
|
|
32087
|
+
recursiveSearches = new Map;
|
|
32088
|
+
execFileAsync3 = promisify3(execFile3);
|
|
32089
|
+
AT_PATH_TOKEN = /(^|[\s([{<])@([^\s]+)/g;
|
|
32090
|
+
TRAILING_PUNCT = /[.,;:!?)\]}>"'`]+$/;
|
|
32091
|
+
});
|
|
32092
|
+
|
|
31726
32093
|
// ../../adapters/terminal/src/terminal-width.ts
|
|
31727
32094
|
function stripAnsi(value) {
|
|
31728
32095
|
return value.replace(new RegExp("\\u001b\\]8;[^\\u0007\\u001b]*(?:\\u0007|\\u001b\\\\)", "g"), "").replace(new RegExp("\\u001b\\[[0-?]*[ -/]*[@-~]", "g"), "");
|
|
@@ -32554,9 +32921,9 @@ function helpLines() {
|
|
|
32554
32921
|
line(" Esc/Ctrl-C stop active turn \xB7 Ctrl-C twice when idle to quit"),
|
|
32555
32922
|
line(""),
|
|
32556
32923
|
line(" Commands", { fg: theme.cyan, bold: true }),
|
|
32557
|
-
line(" /new /model /effort /
|
|
32558
|
-
line(" /
|
|
32559
|
-
line(" /abort /help /quit", { fg: theme.muted })
|
|
32924
|
+
line(" /new /model /effort /status /context"),
|
|
32925
|
+
line(" /topics /public /private /fork /spawn"),
|
|
32926
|
+
line(" /del /copy /abort /help /quit", { fg: theme.muted })
|
|
32560
32927
|
];
|
|
32561
32928
|
}
|
|
32562
32929
|
function tokenCount(value) {
|
|
@@ -32565,14 +32932,26 @@ function tokenCount(value) {
|
|
|
32565
32932
|
if (value >= 1e6)
|
|
32566
32933
|
return `${(value / 1e6).toFixed(2)}M`;
|
|
32567
32934
|
if (value >= 1000)
|
|
32568
|
-
return `${(value / 1000).toFixed(value >= 1e5 ? 0 : 1)}
|
|
32935
|
+
return `${(value / 1000).toFixed(value >= 1e5 ? 0 : 1)}k`;
|
|
32569
32936
|
return String(Math.round(value));
|
|
32570
32937
|
}
|
|
32938
|
+
function latestActiveUsage(state) {
|
|
32939
|
+
return activeMessages(state).slice().reverse().find((message) => message.authorId === "ai" && message.usage)?.usage;
|
|
32940
|
+
}
|
|
32941
|
+
function contextUsageColor(ratio) {
|
|
32942
|
+
if (ratio !== undefined && ratio >= 90)
|
|
32943
|
+
return theme.red;
|
|
32944
|
+
if (ratio !== undefined && ratio >= 80)
|
|
32945
|
+
return theme.amber;
|
|
32946
|
+
return theme.text;
|
|
32947
|
+
}
|
|
32571
32948
|
function statusLines(state) {
|
|
32572
32949
|
const topic = activeTopic(state);
|
|
32573
|
-
const
|
|
32574
|
-
const
|
|
32575
|
-
const
|
|
32950
|
+
const usage = latestActiveUsage(state);
|
|
32951
|
+
const contextUsage = activeContextBreakdown(state);
|
|
32952
|
+
const total = topic ? state.topicUsage[topic.id] : undefined;
|
|
32953
|
+
const hasTopicUsage = total !== undefined && total.queries > 0;
|
|
32954
|
+
const ratio = contextUsage?.context !== undefined && contextUsage.contextWindow ? Math.round(contextUsage.context / contextUsage.contextWindow * 100) : undefined;
|
|
32576
32955
|
return [
|
|
32577
32956
|
line(" Status", { fg: theme.accent, bold: true }),
|
|
32578
32957
|
line(""),
|
|
@@ -32581,8 +32960,8 @@ function statusLines(state) {
|
|
|
32581
32960
|
line(` Model ${effectiveTopicModel(topic)}`),
|
|
32582
32961
|
line(` Effort ${topic?.effectiveEffort ?? topic?.defaultEffort ?? "-"}`),
|
|
32583
32962
|
line(""),
|
|
32584
|
-
line(` Context ${tokenCount(
|
|
32585
|
-
line(" Measured on the latest model request", { fg: theme.muted, dim: true }),
|
|
32963
|
+
line(` Context ${contextUsage?.estimated ? "~" : ""}${tokenCount(contextUsage?.context)} / ${tokenCount(contextUsage?.contextWindow)}${ratio === undefined ? "" : ` (${ratio}%)`}`, { fg: contextUsageColor(ratio) }),
|
|
32964
|
+
line(contextUsage?.estimated ? " Live estimate; replaced by provider usage when the turn finishes" : " Measured on the latest model request", { fg: theme.muted, dim: true }),
|
|
32586
32965
|
line(""),
|
|
32587
32966
|
line(` Last turn input ${tokenCount(usage?.input)} \xB7 output ${tokenCount(usage?.output)}`),
|
|
32588
32967
|
line(` Cache read ${tokenCount(usage?.cachedInput)}`),
|
|
@@ -32591,6 +32970,39 @@ function statusLines(state) {
|
|
|
32591
32970
|
dim: true
|
|
32592
32971
|
}),
|
|
32593
32972
|
line(""),
|
|
32973
|
+
line(" Topic cumulative", { fg: theme.cyan, bold: true }),
|
|
32974
|
+
line(` Queries ${hasTopicUsage ? total.queries.toLocaleString() : "unavailable"}`),
|
|
32975
|
+
line(` Input ${hasTopicUsage ? tokenCount(total.inputTokens) : "unavailable"} cache miss`),
|
|
32976
|
+
line(` Output ${hasTopicUsage ? tokenCount(total.outputTokens) : "unavailable"}`),
|
|
32977
|
+
line(` Cache write ${hasTopicUsage ? tokenCount(total.cacheCreationInputTokens) : "unavailable"}`),
|
|
32978
|
+
line(` Cache read ${hasTopicUsage ? tokenCount(total.cacheReadInputTokens) : "unavailable"}`),
|
|
32979
|
+
line(` Est. cost ${hasTopicUsage ? `$${total.estimatedCostUsd.toFixed(4)}` : "unavailable"}`),
|
|
32980
|
+
line(""),
|
|
32981
|
+
line(" Esc close", { fg: theme.muted })
|
|
32982
|
+
];
|
|
32983
|
+
}
|
|
32984
|
+
function contextLines(state) {
|
|
32985
|
+
const usage = activeContextBreakdown(state);
|
|
32986
|
+
const ratio = usage ? Math.round(usage.context / usage.contextWindow * 100) : undefined;
|
|
32987
|
+
const value = (tokens) => tokenCount(tokens);
|
|
32988
|
+
return [
|
|
32989
|
+
line(" Context", { fg: theme.accent, bold: true }),
|
|
32990
|
+
line(""),
|
|
32991
|
+
line(` ${usage?.estimated ? "Estimated" : "Current"} ${usage?.estimated ? "~" : ""}${value(usage?.context)} / ${value(usage?.contextWindow)}${ratio === undefined ? "" : ` (${ratio}%)`}`, { fg: contextUsageColor(ratio) }),
|
|
32992
|
+
line(""),
|
|
32993
|
+
line(" Breakdown", { fg: theme.cyan, bold: true }),
|
|
32994
|
+
line(` Confirmed ${value(usage?.confirmed)}`),
|
|
32995
|
+
line(` New user ${value(usage?.user)}`),
|
|
32996
|
+
line(` Assistant ${value(usage?.assistant)}`),
|
|
32997
|
+
line(` Tools ${value(usage?.tools)}`),
|
|
32998
|
+
line(` Free ${value(usage?.free)}`),
|
|
32999
|
+
line(""),
|
|
33000
|
+
line(usage?.estimated ? " Live estimate; final provider usage replaces it" : " Provider-confirmed latest request; live categories appear during a turn", { fg: theme.muted, dim: true }),
|
|
33001
|
+
line(" System prompt and tool schemas are included in Confirmed", {
|
|
33002
|
+
fg: theme.muted,
|
|
33003
|
+
dim: true
|
|
33004
|
+
}),
|
|
33005
|
+
line(""),
|
|
32594
33006
|
line(" Esc close", { fg: theme.muted })
|
|
32595
33007
|
];
|
|
32596
33008
|
}
|
|
@@ -32962,6 +33374,8 @@ function conversationLines(state, width, height, animationFrame = 0, nowMs = ter
|
|
|
32962
33374
|
return helpLines().slice(0, height);
|
|
32963
33375
|
if (state.overlay === "status")
|
|
32964
33376
|
return statusLines(state).slice(0, height);
|
|
33377
|
+
if (state.overlay === "context")
|
|
33378
|
+
return contextLines(state).slice(0, height);
|
|
32965
33379
|
if (state.overlay === "topics")
|
|
32966
33380
|
return topicOverlayLines(state, width, height, animationFrame).slice(0, height);
|
|
32967
33381
|
if (state.overlay === "subagents")
|
|
@@ -33179,6 +33593,75 @@ function footerStatusText(state) {
|
|
|
33179
33593
|
title
|
|
33180
33594
|
];
|
|
33181
33595
|
}
|
|
33596
|
+
function footerUsageText(state) {
|
|
33597
|
+
const topic = activeTopic(state);
|
|
33598
|
+
if (!topic)
|
|
33599
|
+
return [{ text: "", spans: [] }];
|
|
33600
|
+
const contextUsage = activeContextBreakdown(state);
|
|
33601
|
+
const total = state.topicUsage[topic.id];
|
|
33602
|
+
const ratio = contextUsage?.context !== undefined && contextUsage.contextWindow ? Math.round(contextUsage.context / contextUsage.contextWindow * 100) : undefined;
|
|
33603
|
+
const context = contextUsage?.context !== undefined && contextUsage.contextWindow ? `${contextUsage.estimated ? "~" : ""}${tokenCount(contextUsage.context)}/${tokenCount(contextUsage.contextWindow)} ${ratio}%` : "";
|
|
33604
|
+
const hasTopicUsage = total !== undefined && total.queries > 0;
|
|
33605
|
+
const cumulative = hasTopicUsage ? `\u03A3 ${tokenCount(total.inputTokens)} in/${tokenCount(total.outputTokens)} out` : "";
|
|
33606
|
+
const cache = hasTopicUsage && total.cacheReadInputTokens ? `cache ${tokenCount(total.cacheReadInputTokens)}` : "";
|
|
33607
|
+
const cost = hasTopicUsage ? `est $${total.estimatedCostUsd.toFixed(2)}` : "";
|
|
33608
|
+
const variant = (values2) => {
|
|
33609
|
+
const present = values2.filter((value) => value.text);
|
|
33610
|
+
const spans = present.flatMap((value, index) => [
|
|
33611
|
+
...index > 0 ? [{ text: " \xB7 ", fg: theme.subtle }] : [],
|
|
33612
|
+
{ text: value.text, fg: value.fg }
|
|
33613
|
+
]);
|
|
33614
|
+
if (spans.length > 0)
|
|
33615
|
+
spans.push({ text: " ", fg: theme.muted });
|
|
33616
|
+
return {
|
|
33617
|
+
text: `${present.map((value) => value.text).join(" \xB7 ")}${present.length ? " " : ""}`,
|
|
33618
|
+
spans
|
|
33619
|
+
};
|
|
33620
|
+
};
|
|
33621
|
+
const values = {
|
|
33622
|
+
context: { text: context, fg: contextUsageColor(ratio) },
|
|
33623
|
+
cumulative: { text: cumulative, fg: theme.muted },
|
|
33624
|
+
cache: { text: cache, fg: theme.muted },
|
|
33625
|
+
cost: { text: cost, fg: theme.muted }
|
|
33626
|
+
};
|
|
33627
|
+
return [
|
|
33628
|
+
variant([values.context, values.cumulative, values.cache, values.cost]),
|
|
33629
|
+
variant([values.context, values.cumulative, values.cache]),
|
|
33630
|
+
variant([values.context, values.cumulative]),
|
|
33631
|
+
variant([values.context]),
|
|
33632
|
+
variant([])
|
|
33633
|
+
];
|
|
33634
|
+
}
|
|
33635
|
+
function footerStatusLine(state, width) {
|
|
33636
|
+
const leftCandidates = footerStatusText(state);
|
|
33637
|
+
const rightCandidates = footerUsageText(state);
|
|
33638
|
+
for (const right of rightCandidates) {
|
|
33639
|
+
for (const left2 of leftCandidates) {
|
|
33640
|
+
const rightWidth = displayWidth(right.text);
|
|
33641
|
+
if (displayWidth(left2) + (rightWidth ? 1 + rightWidth : 0) <= width) {
|
|
33642
|
+
const gap = width - displayWidth(left2) - rightWidth;
|
|
33643
|
+
return [
|
|
33644
|
+
{ text: left2, fg: theme.accent, bold: true },
|
|
33645
|
+
{ text: " ".repeat(gap), bg: theme.surfaceRaised },
|
|
33646
|
+
...right.spans
|
|
33647
|
+
];
|
|
33648
|
+
}
|
|
33649
|
+
}
|
|
33650
|
+
}
|
|
33651
|
+
const left = sliceWidth(leftCandidates[leftCandidates.length - 1] ?? "", width);
|
|
33652
|
+
return [
|
|
33653
|
+
{ text: left, fg: theme.accent, bold: true },
|
|
33654
|
+
{ text: " ".repeat(Math.max(0, width - displayWidth(left))), bg: theme.surfaceRaised }
|
|
33655
|
+
];
|
|
33656
|
+
}
|
|
33657
|
+
function paintFooterSpans(spans) {
|
|
33658
|
+
return spans.map((span) => paint(span.text, {
|
|
33659
|
+
fg: span.fg ?? theme.text,
|
|
33660
|
+
bg: span.bg ?? theme.surfaceRaised,
|
|
33661
|
+
bold: span.bold,
|
|
33662
|
+
dim: span.dim
|
|
33663
|
+
})).join("");
|
|
33664
|
+
}
|
|
33182
33665
|
function noticeStyle(state) {
|
|
33183
33666
|
return NOTICE_STYLE[state.noticeLevel ?? "warn"];
|
|
33184
33667
|
}
|
|
@@ -33228,11 +33711,7 @@ function footerLines(state, width) {
|
|
|
33228
33711
|
];
|
|
33229
33712
|
}
|
|
33230
33713
|
return [
|
|
33231
|
-
|
|
33232
|
-
fg: theme.accent,
|
|
33233
|
-
bg: theme.surfaceRaised,
|
|
33234
|
-
bold: true
|
|
33235
|
-
}),
|
|
33714
|
+
paintFooterSpans(footerStatusLine(state, width)),
|
|
33236
33715
|
paint(joinSides("", pickFitting(footerHintText(state), width), width), {
|
|
33237
33716
|
fg: state.notice ? noticeStyle(state).fg : theme.muted,
|
|
33238
33717
|
bg: theme.canvas
|
|
@@ -33367,6 +33846,7 @@ var init_render = __esm(async () => {
|
|
|
33367
33846
|
init_terminal_width();
|
|
33368
33847
|
await __promiseAll([
|
|
33369
33848
|
init_src(),
|
|
33849
|
+
init_context_usage(),
|
|
33370
33850
|
init_state2()
|
|
33371
33851
|
]);
|
|
33372
33852
|
theme = {
|
|
@@ -33813,6 +34293,9 @@ class EmbeddedNegotiumClient {
|
|
|
33813
34293
|
const rows = getAllMessagesForTopic(topicId);
|
|
33814
34294
|
return rows.map((row) => typeof row.id === "string" ? getApiMessage(topicId, row.id) : null).filter((message) => message !== null);
|
|
33815
34295
|
}
|
|
34296
|
+
listTopicUsage(topicId) {
|
|
34297
|
+
return getTopicStats(this.#userId, topicId);
|
|
34298
|
+
}
|
|
33816
34299
|
listMessagePage(topicId, cursor, limit = MESSAGE_HISTORY_PAGE_SIZE) {
|
|
33817
34300
|
const result = listApiMessages(topicId, { cursor, limit });
|
|
33818
34301
|
return { messages: result.page, cursor: result.cursor, hasMore: result.hasMore };
|
|
@@ -34008,6 +34491,10 @@ class RemoteNegotiumClient {
|
|
|
34008
34491
|
const result = await this.#request(`/topics/${encodeURIComponent(topicId)}/messages?user=${encodeURIComponent(this.#userId)}`);
|
|
34009
34492
|
return result.messages ?? [];
|
|
34010
34493
|
}
|
|
34494
|
+
async listTopicUsage(topicId) {
|
|
34495
|
+
const result = await this.#request(`/topics/${encodeURIComponent(topicId)}/usage?user=${encodeURIComponent(this.#userId)}`);
|
|
34496
|
+
return result.usage;
|
|
34497
|
+
}
|
|
34011
34498
|
async listMessagePage(topicId, cursor, limit = MESSAGE_HISTORY_PAGE_SIZE) {
|
|
34012
34499
|
const query2 = new URLSearchParams({ user: this.#userId, limit: String(limit) });
|
|
34013
34500
|
if (cursor)
|
|
@@ -34261,6 +34748,7 @@ var MESSAGE_HISTORY_PAGE_SIZE = 50, INITIAL_MESSAGE_HISTORY_PAGE_COUNT = 3, INIT
|
|
|
34261
34748
|
var init_client = __esm(async () => {
|
|
34262
34749
|
await __promiseAll([
|
|
34263
34750
|
init_src(),
|
|
34751
|
+
init_storage_public(),
|
|
34264
34752
|
init_src5(),
|
|
34265
34753
|
init_history_store()
|
|
34266
34754
|
]);
|
|
@@ -34501,6 +34989,11 @@ async function runTerminalCommand(commandLine, context) {
|
|
|
34501
34989
|
context.queueRender();
|
|
34502
34990
|
return;
|
|
34503
34991
|
}
|
|
34992
|
+
if (command === "context") {
|
|
34993
|
+
context.state = { ...context.state, overlay: "context" };
|
|
34994
|
+
context.queueRender();
|
|
34995
|
+
return;
|
|
34996
|
+
}
|
|
34504
34997
|
if (command === "model") {
|
|
34505
34998
|
if (args.length > 0) {
|
|
34506
34999
|
context.state = { ...context.state, notice: "Usage: /model", noticeLevel: "warn" };
|
|
@@ -35360,6 +35853,7 @@ class TerminalApp {
|
|
|
35360
35853
|
#backgroundRefreshRunning = false;
|
|
35361
35854
|
#animationFrame = animationFrameAt();
|
|
35362
35855
|
#topicsRefreshGeneration = 0;
|
|
35856
|
+
#topicUsageRefreshGeneration = new Map;
|
|
35363
35857
|
#messageLoadGeneration = new Map;
|
|
35364
35858
|
#messageHistory = new Map;
|
|
35365
35859
|
#queuedRuntimeEvents = new Map;
|
|
@@ -35471,6 +35965,9 @@ class TerminalApp {
|
|
|
35471
35965
|
return;
|
|
35472
35966
|
}
|
|
35473
35967
|
this.#applyRuntimeEvent(event);
|
|
35968
|
+
if (event.type === "message" && event.payload.usage || event.type === "message-updated" && event.payload.patch?.usage) {
|
|
35969
|
+
this.#refreshTopicUsage(event.topicId);
|
|
35970
|
+
}
|
|
35474
35971
|
if (event.type === "topic-created" || event.type === "topic-updated" || event.type === "topic-deleted") {
|
|
35475
35972
|
const previous = this.#state.activeTopicId;
|
|
35476
35973
|
const refreshSubagentGraph = this.#state.overlay === "subagents";
|
|
@@ -35578,6 +36075,20 @@ class TerminalApp {
|
|
|
35578
36075
|
this.#backgroundRefreshRunning = false;
|
|
35579
36076
|
}
|
|
35580
36077
|
}
|
|
36078
|
+
async#refreshTopicUsage(topicId) {
|
|
36079
|
+
if (!this.#client.listTopicUsage)
|
|
36080
|
+
return;
|
|
36081
|
+
const generation = (this.#topicUsageRefreshGeneration.get(topicId) ?? 0) + 1;
|
|
36082
|
+
this.#topicUsageRefreshGeneration.set(topicId, generation);
|
|
36083
|
+
try {
|
|
36084
|
+
const usage = await this.#client.listTopicUsage(topicId);
|
|
36085
|
+
if (this.#topicUsageRefreshGeneration.get(topicId) !== generation)
|
|
36086
|
+
return;
|
|
36087
|
+
this.#state = setTopicUsage(this.#state, usage);
|
|
36088
|
+
if (this.#state.activeTopicId === topicId)
|
|
36089
|
+
this.#queueRender();
|
|
36090
|
+
} catch {}
|
|
36091
|
+
}
|
|
35581
36092
|
async#loadActiveMessages() {
|
|
35582
36093
|
const topic = activeTopic(this.#state);
|
|
35583
36094
|
if (!topic)
|
|
@@ -35625,6 +36136,7 @@ class TerminalApp {
|
|
|
35625
36136
|
this.#queuedRuntimeEvents.delete(topic.id);
|
|
35626
36137
|
for (const event of queued)
|
|
35627
36138
|
this.#applyRuntimeEvent(event);
|
|
36139
|
+
this.#refreshTopicUsage(topic.id);
|
|
35628
36140
|
}
|
|
35629
36141
|
async#loadOlderMessages(topicId, targetOffset) {
|
|
35630
36142
|
const history = this.#messageHistory.get(topicId);
|
|
@@ -39900,7 +40412,7 @@ __export(exports_join, {
|
|
|
39900
40412
|
joinCredentialDigest: () => joinCredentialDigest,
|
|
39901
40413
|
isJoinPersisted: () => isJoinPersisted
|
|
39902
40414
|
});
|
|
39903
|
-
import { createHash as
|
|
40415
|
+
import { createHash as createHash11, randomUUID as randomUUID27 } from "crypto";
|
|
39904
40416
|
import {
|
|
39905
40417
|
chmodSync as chmodSync6,
|
|
39906
40418
|
closeSync as closeSync5,
|
|
@@ -40064,7 +40576,7 @@ function normalizedJoin(join42) {
|
|
|
40064
40576
|
});
|
|
40065
40577
|
}
|
|
40066
40578
|
function joinCredentialDigest(join42) {
|
|
40067
|
-
return
|
|
40579
|
+
return createHash11("sha256").update(JSON.stringify(normalizedJoin(join42))).digest("base64url");
|
|
40068
40580
|
}
|
|
40069
40581
|
function readPersistedJoin(path = joinFilePath()) {
|
|
40070
40582
|
if (!existsSync35(path))
|
|
@@ -40455,7 +40967,7 @@ var init_canonical_mcp_bridge = __esm(async () => {
|
|
|
40455
40967
|
});
|
|
40456
40968
|
|
|
40457
40969
|
// ../../adapters/otium/src/store.ts
|
|
40458
|
-
import { createHash as
|
|
40970
|
+
import { createHash as createHash12 } from "crypto";
|
|
40459
40971
|
function isPeerDetached(hubNodeId) {
|
|
40460
40972
|
const row = db.query("SELECT status FROM otium_peer_lifecycle WHERE hub_node_id = ?").get(hubNodeId);
|
|
40461
40973
|
return row?.status === "detached";
|
|
@@ -40638,7 +41150,7 @@ function acknowledgePeerTerminal(hostNodeId, requestId) {
|
|
|
40638
41150
|
})();
|
|
40639
41151
|
}
|
|
40640
41152
|
function peerInboxPayloadHash(value) {
|
|
40641
|
-
return
|
|
41153
|
+
return createHash12("sha256").update(JSON.stringify(value)).digest("hex");
|
|
40642
41154
|
}
|
|
40643
41155
|
function claimPeerInboxRequest(args) {
|
|
40644
41156
|
const inserted = db.run(`INSERT OR IGNORE INTO otium_peer_inbox_requests
|
|
@@ -45311,4 +45823,4 @@ switch (command) {
|
|
|
45311
45823
|
}
|
|
45312
45824
|
}
|
|
45313
45825
|
|
|
45314
|
-
//# debugId=
|
|
45826
|
+
//# debugId=C4FD6B649361AE3464756E2164756E21
|