@skein-code/cli 0.3.11 → 0.3.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -1
- package/dist/cli.js +484 -142
- package/dist/cli.js.map +1 -1
- package/docs/ARCHITECTURE.md +22 -4
- package/docs/NEXT_STEPS.md +9 -10
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -220,7 +220,7 @@ function isSqliteBusy(error) {
|
|
|
220
220
|
// package.json
|
|
221
221
|
var package_default = {
|
|
222
222
|
name: "@skein-code/cli",
|
|
223
|
-
version: "0.3.
|
|
223
|
+
version: "0.3.12",
|
|
224
224
|
description: "A context-first, model-agnostic coding agent with an auditable terminal workspace.",
|
|
225
225
|
type: "module",
|
|
226
226
|
license: "MIT",
|
|
@@ -236,9 +236,9 @@ var package_default = {
|
|
|
236
236
|
},
|
|
237
237
|
skein: {
|
|
238
238
|
releaseNotes: [
|
|
239
|
-
"
|
|
240
|
-
"
|
|
241
|
-
"
|
|
239
|
+
"Each model request records privacy-safe token partitions and actual-versus-estimated provenance",
|
|
240
|
+
"Local retrieval now selects focused, standard, broad, or maximum evidence budgets",
|
|
241
|
+
"Terminal and JSON diagnostics expose budget decisions without storing prompt or source content"
|
|
242
242
|
]
|
|
243
243
|
},
|
|
244
244
|
bin: {
|
|
@@ -2484,6 +2484,112 @@ function shouldUseProviderEnvironmentKey(provider, baseUrl) {
|
|
|
2484
2484
|
return baseUrl.replace(/\/+$/u, "") === official[provider];
|
|
2485
2485
|
}
|
|
2486
2486
|
|
|
2487
|
+
// src/context/budget.ts
|
|
2488
|
+
var tierTokens = {
|
|
2489
|
+
focused: 2e3,
|
|
2490
|
+
standard: 4e3,
|
|
2491
|
+
broad: 8e3,
|
|
2492
|
+
maximum: 12e3
|
|
2493
|
+
};
|
|
2494
|
+
function selectContextBudget(query, config, options = {}) {
|
|
2495
|
+
if (options.trivial) return noContextBudget("trivial turn; retrieval skipped");
|
|
2496
|
+
const configuredCeiling = positiveFloor(options.maxTokens ?? config.context.maxTokens);
|
|
2497
|
+
const configuredTopK = positiveFloor(options.topK ?? config.context.topK);
|
|
2498
|
+
if (configuredCeiling === 0 || configuredTopK === 0) {
|
|
2499
|
+
return noContextBudget("retrieval disabled by the configured context ceiling");
|
|
2500
|
+
}
|
|
2501
|
+
const normalized = query.trim().toLocaleLowerCase();
|
|
2502
|
+
const explicitPaths = countExplicitPaths(query);
|
|
2503
|
+
const breadthSignals = countMatches(normalized, [
|
|
2504
|
+
/\b(?:cross[- ]module|cross[- ]package|across modules|across packages|entire repository|whole repository|whole codebase|all modules|all packages|end[- ]to[- ]end)\b/gu,
|
|
2505
|
+
/跨模块|跨包|全仓库|全代码库|整个(?:项目|仓库|代码库)|所有(?:模块|包)|端到端/gu
|
|
2506
|
+
]);
|
|
2507
|
+
const exhaustiveSignals = countMatches(normalized, [
|
|
2508
|
+
/\b(?:exhaustive|comprehensive|repository[- ]wide|codebase[- ]wide|all call sites|all usages|architecture migration|security audit)\b/gu,
|
|
2509
|
+
/全面|完整审计|全量|全局迁移|所有调用|所有引用|架构迁移|安全审计/gu
|
|
2510
|
+
]);
|
|
2511
|
+
const coordinationSignals = countMatches(normalized, [
|
|
2512
|
+
/\b(?:and|then|also|plus|including|migrate|redesign|architecture|integration|workflow)\b/gu,
|
|
2513
|
+
/并且|然后|同时|以及|包括|迁移|重设计|架构|集成|工作流/gu
|
|
2514
|
+
]);
|
|
2515
|
+
let tier;
|
|
2516
|
+
let reason;
|
|
2517
|
+
if (breadthSignals > 0 && exhaustiveSignals > 0 && (coordinationSignals >= 2 || normalized.length >= 180)) {
|
|
2518
|
+
tier = "maximum";
|
|
2519
|
+
reason = "explicit repository-wide work with exhaustive, multi-part scope";
|
|
2520
|
+
} else if (breadthSignals > 0 || exhaustiveSignals >= 2) {
|
|
2521
|
+
tier = "broad";
|
|
2522
|
+
reason = "cross-module or repository-wide evidence requested";
|
|
2523
|
+
} else if (explicitPaths > 0 && coordinationSignals < 2) {
|
|
2524
|
+
tier = "focused";
|
|
2525
|
+
reason = "one or more explicit paths bound the evidence surface";
|
|
2526
|
+
} else if ((options.intent === "explain" || options.intent === "review" || options.intent === "test") && normalized.length <= 160 && coordinationSignals < 2) {
|
|
2527
|
+
tier = "focused";
|
|
2528
|
+
reason = `short ${options.intent} request with a narrow evidence surface`;
|
|
2529
|
+
} else {
|
|
2530
|
+
tier = "standard";
|
|
2531
|
+
reason = "ordinary implementation, debugging, or refactoring request";
|
|
2532
|
+
}
|
|
2533
|
+
const requestedTokens = tierTokens[tier];
|
|
2534
|
+
const budgetTokens = Math.min(configuredCeiling, requestedTokens);
|
|
2535
|
+
const baseBudgetTokens = Math.min(budgetTokens, tierTokens.focused);
|
|
2536
|
+
const incrementalBudgetTokens = Math.max(0, budgetTokens - baseBudgetTokens);
|
|
2537
|
+
const topKByTier = {
|
|
2538
|
+
focused: 4,
|
|
2539
|
+
standard: 8,
|
|
2540
|
+
broad: 12,
|
|
2541
|
+
maximum: 16
|
|
2542
|
+
};
|
|
2543
|
+
const topK = Math.min(configuredTopK, topKByTier[tier]);
|
|
2544
|
+
const capped = budgetTokens < requestedTokens ? `; capped by configuration at ${budgetTokens} tokens` : "";
|
|
2545
|
+
return {
|
|
2546
|
+
tier,
|
|
2547
|
+
budgetTokens,
|
|
2548
|
+
baseBudgetTokens,
|
|
2549
|
+
incrementalBudgetTokens,
|
|
2550
|
+
topK,
|
|
2551
|
+
reason: `${reason}${capped}`
|
|
2552
|
+
};
|
|
2553
|
+
}
|
|
2554
|
+
function emptyPackedContext(decision, engine = "local") {
|
|
2555
|
+
return {
|
|
2556
|
+
text: "",
|
|
2557
|
+
hits: [],
|
|
2558
|
+
estimatedTokens: 0,
|
|
2559
|
+
engine,
|
|
2560
|
+
truncated: false,
|
|
2561
|
+
budgetTier: decision.tier,
|
|
2562
|
+
budgetTokens: decision.budgetTokens,
|
|
2563
|
+
baseBudgetTokens: decision.baseBudgetTokens,
|
|
2564
|
+
incrementalBudgetTokens: decision.incrementalBudgetTokens,
|
|
2565
|
+
budgetReason: decision.reason,
|
|
2566
|
+
candidateHits: 0,
|
|
2567
|
+
selectedHits: 0,
|
|
2568
|
+
duplicateHits: 0,
|
|
2569
|
+
incrementalEvidenceTokens: 0
|
|
2570
|
+
};
|
|
2571
|
+
}
|
|
2572
|
+
function noContextBudget(reason) {
|
|
2573
|
+
return {
|
|
2574
|
+
tier: "none",
|
|
2575
|
+
budgetTokens: 0,
|
|
2576
|
+
baseBudgetTokens: 0,
|
|
2577
|
+
incrementalBudgetTokens: 0,
|
|
2578
|
+
topK: 0,
|
|
2579
|
+
reason
|
|
2580
|
+
};
|
|
2581
|
+
}
|
|
2582
|
+
function positiveFloor(value) {
|
|
2583
|
+
return Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
2584
|
+
}
|
|
2585
|
+
function countMatches(value, patterns) {
|
|
2586
|
+
return patterns.reduce((total, pattern) => total + [...value.matchAll(pattern)].length, 0);
|
|
2587
|
+
}
|
|
2588
|
+
function countExplicitPaths(value) {
|
|
2589
|
+
const matches = value.match(/(?:^|[\s'"`(])@?(?:\.{0,2}[/\\]|[A-Za-z]:[/\\]|[\w.-]+[/\\])[\w@%+.,()[\]{}=-]+(?:[/\\][\w@%+.,()[\]{}=-]+)*(?:\.\w+)?/gu);
|
|
2590
|
+
return matches?.length ?? 0;
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2487
2593
|
// src/context/local-index.ts
|
|
2488
2594
|
import { createHash as createHash5 } from "node:crypto";
|
|
2489
2595
|
import { lstat as lstat8, readFile as readFile3, stat as stat3 } from "node:fs/promises";
|
|
@@ -2604,6 +2710,58 @@ async function pathExists(path) {
|
|
|
2604
2710
|
}
|
|
2605
2711
|
}
|
|
2606
2712
|
|
|
2713
|
+
// src/utils/tokens.ts
|
|
2714
|
+
function estimateTokens(value) {
|
|
2715
|
+
let tokens2 = 0;
|
|
2716
|
+
for (const character of value) tokens2 += estimatedTokenCost(character);
|
|
2717
|
+
return Math.ceil(tokens2 - Number.EPSILON * Math.max(1, value.length) * 32);
|
|
2718
|
+
}
|
|
2719
|
+
function estimatedTokenCost(character) {
|
|
2720
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
2721
|
+
if (codePoint <= 127) return 0.25;
|
|
2722
|
+
if (isEmojiOrSupplementarySymbol(character, codePoint)) return 2;
|
|
2723
|
+
if (/^[\p{P}\p{S}]$/u.test(character)) return 0.5;
|
|
2724
|
+
if (isCjkCharacter(codePoint)) return 1;
|
|
2725
|
+
return 2;
|
|
2726
|
+
}
|
|
2727
|
+
function sliceStartByTokens(value, budget) {
|
|
2728
|
+
if (budget <= 0) return "";
|
|
2729
|
+
let used = 0;
|
|
2730
|
+
let end = 0;
|
|
2731
|
+
for (const character of value) {
|
|
2732
|
+
const cost = estimatedTokenCost(character);
|
|
2733
|
+
if (used + cost > budget) break;
|
|
2734
|
+
used += cost;
|
|
2735
|
+
end += character.length;
|
|
2736
|
+
}
|
|
2737
|
+
return value.slice(0, end);
|
|
2738
|
+
}
|
|
2739
|
+
function sliceEndByTokens(value, budget) {
|
|
2740
|
+
if (budget <= 0) return "";
|
|
2741
|
+
let used = 0;
|
|
2742
|
+
let start = value.length;
|
|
2743
|
+
while (start > 0) {
|
|
2744
|
+
let next = start - 1;
|
|
2745
|
+
const code = value.charCodeAt(next);
|
|
2746
|
+
if (code >= 56320 && code <= 57343 && next > 0) {
|
|
2747
|
+
const previous = value.charCodeAt(next - 1);
|
|
2748
|
+
if (previous >= 55296 && previous <= 56319) next -= 1;
|
|
2749
|
+
}
|
|
2750
|
+
const character = value.slice(next, start);
|
|
2751
|
+
const cost = estimatedTokenCost(character);
|
|
2752
|
+
if (used + cost > budget) break;
|
|
2753
|
+
used += cost;
|
|
2754
|
+
start = next;
|
|
2755
|
+
}
|
|
2756
|
+
return value.slice(start);
|
|
2757
|
+
}
|
|
2758
|
+
function isCjkCharacter(codePoint) {
|
|
2759
|
+
return codePoint >= 11904 && codePoint <= 40959 || codePoint >= 44032 && codePoint <= 55215 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 131072 && codePoint <= 195103 || codePoint >= 12352 && codePoint <= 12543;
|
|
2760
|
+
}
|
|
2761
|
+
function isEmojiOrSupplementarySymbol(character, codePoint) {
|
|
2762
|
+
return codePoint > 65535 || new RegExp("\\p{Extended_Pictographic}", "u").test(character);
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2607
2765
|
// src/context/local-index.ts
|
|
2608
2766
|
var contentHashSchema = z3.string().regex(/^[a-f\d]{64}$/u);
|
|
2609
2767
|
var indexedChunkSchema = z3.object({
|
|
@@ -3045,17 +3203,31 @@ function packContextHits(hits, roots, maxTokens, engine) {
|
|
|
3045
3203
|
const uniquePaths = new Set(hits.map((hit) => hit.path)).size;
|
|
3046
3204
|
let estimatedTokens = 0;
|
|
3047
3205
|
let truncated = false;
|
|
3206
|
+
let duplicateHits = 0;
|
|
3207
|
+
let perFileLimitHits = 0;
|
|
3048
3208
|
for (const hit of hits) {
|
|
3049
3209
|
const count = perFile.get(hit.path) ?? 0;
|
|
3050
|
-
if (uniquePaths > 1 && count >= 2)
|
|
3051
|
-
|
|
3210
|
+
if (uniquePaths > 1 && count >= 2) {
|
|
3211
|
+
perFileLimitHits += 1;
|
|
3212
|
+
continue;
|
|
3213
|
+
}
|
|
3214
|
+
if (selected.some((candidate) => hasSubstantialOverlap(candidate, hit))) {
|
|
3215
|
+
duplicateHits += 1;
|
|
3216
|
+
continue;
|
|
3217
|
+
}
|
|
3052
3218
|
const tokens2 = estimateTokens(hit.content);
|
|
3053
3219
|
if (estimatedTokens + tokens2 > maxTokens) {
|
|
3054
|
-
const
|
|
3055
|
-
|
|
3056
|
-
|
|
3220
|
+
const remainingTokens = Math.max(0, maxTokens - estimatedTokens);
|
|
3221
|
+
const content = sliceStartByTokens(hit.content, remainingTokens);
|
|
3222
|
+
if (content.length >= 32) {
|
|
3223
|
+
const returnedLines = Math.max(1, content.split("\n").length);
|
|
3224
|
+
selected.push({
|
|
3225
|
+
...hit,
|
|
3226
|
+
content,
|
|
3227
|
+
endLine: Math.min(hit.endLine, hit.startLine + returnedLines - 1)
|
|
3228
|
+
});
|
|
3057
3229
|
perFile.set(hit.path, count + 1);
|
|
3058
|
-
estimatedTokens
|
|
3230
|
+
estimatedTokens += estimateTokens(content);
|
|
3059
3231
|
}
|
|
3060
3232
|
truncated = true;
|
|
3061
3233
|
break;
|
|
@@ -3071,7 +3243,16 @@ function packContextHits(hits, roots, maxTokens, engine) {
|
|
|
3071
3243
|
${hit.content}
|
|
3072
3244
|
</code>`;
|
|
3073
3245
|
}).join("\n\n");
|
|
3074
|
-
return {
|
|
3246
|
+
return {
|
|
3247
|
+
text,
|
|
3248
|
+
hits: selected,
|
|
3249
|
+
estimatedTokens,
|
|
3250
|
+
engine,
|
|
3251
|
+
truncated,
|
|
3252
|
+
candidateHits: hits.length,
|
|
3253
|
+
selectedHits: selected.length,
|
|
3254
|
+
duplicateHits: duplicateHits + perFileLimitHits
|
|
3255
|
+
};
|
|
3075
3256
|
}
|
|
3076
3257
|
function cloneHits(hits) {
|
|
3077
3258
|
return hits.map((hit) => ({ ...hit }));
|
|
@@ -3082,9 +3263,6 @@ function hashContent(content) {
|
|
|
3082
3263
|
function createGeneration(files) {
|
|
3083
3264
|
return createHash5("sha256").update(files.slice().sort((left, right) => left.absolutePath.localeCompare(right.absolutePath)).map((file) => `${file.absolutePath}\0${file.contentHash}`).join("\n"), "utf8").digest("hex").slice(0, 16);
|
|
3084
3265
|
}
|
|
3085
|
-
function estimateTokens(content) {
|
|
3086
|
-
return Math.ceil(content.length / 4);
|
|
3087
|
-
}
|
|
3088
3266
|
function hasSubstantialOverlap(left, right) {
|
|
3089
3267
|
if (left.path !== right.path) return false;
|
|
3090
3268
|
const overlap = Math.max(0, Math.min(left.endLine, right.endLine) - Math.max(left.startLine, right.startLine) + 1);
|
|
@@ -3205,15 +3383,28 @@ var ContextEngine = class {
|
|
|
3205
3383
|
config;
|
|
3206
3384
|
local;
|
|
3207
3385
|
degradation;
|
|
3208
|
-
async pack(query) {
|
|
3386
|
+
async pack(query, options = {}) {
|
|
3387
|
+
const decision = selectContextBudget(query, this.config, options);
|
|
3388
|
+
if (decision.budgetTokens === 0 || decision.topK === 0) {
|
|
3389
|
+
this.degradation = void 0;
|
|
3390
|
+
return emptyPackedContext(decision);
|
|
3391
|
+
}
|
|
3209
3392
|
try {
|
|
3210
3393
|
const packed = await this.local.pack(
|
|
3211
3394
|
query,
|
|
3212
|
-
|
|
3213
|
-
|
|
3395
|
+
decision.topK,
|
|
3396
|
+
decision.budgetTokens
|
|
3214
3397
|
);
|
|
3215
3398
|
this.degradation = void 0;
|
|
3216
|
-
return
|
|
3399
|
+
return {
|
|
3400
|
+
...packed,
|
|
3401
|
+
budgetTier: decision.tier,
|
|
3402
|
+
budgetTokens: decision.budgetTokens,
|
|
3403
|
+
baseBudgetTokens: decision.baseBudgetTokens,
|
|
3404
|
+
incrementalBudgetTokens: decision.incrementalBudgetTokens,
|
|
3405
|
+
budgetReason: decision.reason,
|
|
3406
|
+
incrementalEvidenceTokens: Math.max(0, packed.estimatedTokens - decision.baseBudgetTokens)
|
|
3407
|
+
};
|
|
3217
3408
|
} catch (error) {
|
|
3218
3409
|
const detail = error instanceof Error ? error.message : String(error);
|
|
3219
3410
|
this.degradation = {
|
|
@@ -3223,11 +3414,7 @@ var ContextEngine = class {
|
|
|
3223
3414
|
};
|
|
3224
3415
|
const degradation = this.lastDegradation();
|
|
3225
3416
|
return {
|
|
3226
|
-
|
|
3227
|
-
hits: [],
|
|
3228
|
-
estimatedTokens: 0,
|
|
3229
|
-
engine: "local",
|
|
3230
|
-
truncated: false,
|
|
3417
|
+
...emptyPackedContext(decision),
|
|
3231
3418
|
...degradation ? { degradation } : {}
|
|
3232
3419
|
};
|
|
3233
3420
|
}
|
|
@@ -3324,7 +3511,7 @@ var ContextManager = class {
|
|
|
3324
3511
|
status(session, modelContextTokens) {
|
|
3325
3512
|
const active = activeMessages(session);
|
|
3326
3513
|
const activeTokens = estimateMessages(active);
|
|
3327
|
-
const summaryTokens =
|
|
3514
|
+
const summaryTokens = estimateTokens(session.contextSummary ?? "");
|
|
3328
3515
|
const toolTokenCount = toolTokens(active);
|
|
3329
3516
|
const contextLimit = Math.max(
|
|
3330
3517
|
8e3,
|
|
@@ -3351,11 +3538,11 @@ var ContextManager = class {
|
|
|
3351
3538
|
const active = activeMessages(session);
|
|
3352
3539
|
const cut = compactionCut(active);
|
|
3353
3540
|
if (cut === 0) {
|
|
3354
|
-
return { omittedMessages: 0, summaryTokens:
|
|
3541
|
+
return { omittedMessages: 0, summaryTokens: estimateTokens(session.contextSummary ?? "") };
|
|
3355
3542
|
}
|
|
3356
3543
|
const older = active.slice(0, cut);
|
|
3357
3544
|
if (!older.length) {
|
|
3358
|
-
return { omittedMessages: 0, summaryTokens:
|
|
3545
|
+
return { omittedMessages: 0, summaryTokens: estimateTokens(session.contextSummary ?? "") };
|
|
3359
3546
|
}
|
|
3360
3547
|
const transcript = older.map(formatMessageForSummary).join("\n\n").slice(-14e4);
|
|
3361
3548
|
const response = await provider.complete([
|
|
@@ -3376,7 +3563,7 @@ ${transcript}`)
|
|
|
3376
3563
|
session.contextCompactions = (session.contextCompactions ?? 0) + 1;
|
|
3377
3564
|
return {
|
|
3378
3565
|
omittedMessages: older.length,
|
|
3379
|
-
summaryTokens:
|
|
3566
|
+
summaryTokens: estimateTokens(session.contextSummary)
|
|
3380
3567
|
};
|
|
3381
3568
|
}
|
|
3382
3569
|
buildShortTermPrompt(session) {
|
|
@@ -3505,13 +3692,10 @@ function concise(value, max) {
|
|
|
3505
3692
|
return normalized.length <= max ? normalized : `${normalized.slice(0, max - 1)}\u2026`;
|
|
3506
3693
|
}
|
|
3507
3694
|
function estimateMessages(messages) {
|
|
3508
|
-
return messages.reduce((sum, message2) => sum +
|
|
3695
|
+
return messages.reduce((sum, message2) => sum + estimateTokens(message2.content) + estimateTokens(JSON.stringify(message2.toolCalls ?? [])), 0);
|
|
3509
3696
|
}
|
|
3510
3697
|
function toolTokens(messages) {
|
|
3511
|
-
return messages.filter((message2) => message2.role === "tool").reduce((sum, message2) => sum +
|
|
3512
|
-
}
|
|
3513
|
-
function estimateTokens2(value) {
|
|
3514
|
-
return Math.ceil(value.length / 4);
|
|
3698
|
+
return messages.filter((message2) => message2.role === "tool").reduce((sum, message2) => sum + estimateTokens(message2.content), 0);
|
|
3515
3699
|
}
|
|
3516
3700
|
|
|
3517
3701
|
// src/context/mentions.ts
|
|
@@ -5058,8 +5242,54 @@ var sessionSchema = z5.object({
|
|
|
5058
5242
|
lastRun: lastRunSchema.optional(),
|
|
5059
5243
|
usage: z5.object({
|
|
5060
5244
|
inputTokens: z5.number().nonnegative(),
|
|
5061
|
-
outputTokens: z5.number().nonnegative()
|
|
5062
|
-
|
|
5245
|
+
outputTokens: z5.number().nonnegative(),
|
|
5246
|
+
source: z5.enum(["actual", "estimated", "mixed", "unknown"]).optional(),
|
|
5247
|
+
inputSource: z5.enum(["actual", "estimated", "mixed", "unknown"]).optional(),
|
|
5248
|
+
outputSource: z5.enum(["actual", "estimated", "mixed", "unknown"]).optional(),
|
|
5249
|
+
actualInputTokens: z5.number().nonnegative().optional(),
|
|
5250
|
+
actualOutputTokens: z5.number().nonnegative().optional(),
|
|
5251
|
+
estimatedInputTokens: z5.number().nonnegative().optional(),
|
|
5252
|
+
estimatedOutputTokens: z5.number().nonnegative().optional()
|
|
5253
|
+
}).strict(),
|
|
5254
|
+
tokenLedger: z5.array(z5.object({
|
|
5255
|
+
requestId: z5.string().uuid(),
|
|
5256
|
+
turn: z5.number().int().positive(),
|
|
5257
|
+
recordedAt: z5.string().datetime(),
|
|
5258
|
+
estimated: z5.object({
|
|
5259
|
+
stableTokens: z5.number().nonnegative(),
|
|
5260
|
+
dynamicTokens: z5.number().nonnegative(),
|
|
5261
|
+
conversationTokens: z5.number().nonnegative(),
|
|
5262
|
+
toolResultTokens: z5.number().nonnegative(),
|
|
5263
|
+
retrievedTokens: z5.number().nonnegative(),
|
|
5264
|
+
toolSchemaTokens: z5.number().nonnegative(),
|
|
5265
|
+
estimatedInputTokens: z5.number().nonnegative(),
|
|
5266
|
+
outputAllowanceTokens: z5.number().nonnegative(),
|
|
5267
|
+
outputTokens: z5.number().nonnegative()
|
|
5268
|
+
}).strict(),
|
|
5269
|
+
actual: z5.object({
|
|
5270
|
+
inputTokens: z5.number().nonnegative().optional(),
|
|
5271
|
+
outputTokens: z5.number().nonnegative().optional()
|
|
5272
|
+
}).strict(),
|
|
5273
|
+
inputSource: z5.enum(["actual", "estimated"]),
|
|
5274
|
+
outputSource: z5.enum(["actual", "estimated"]),
|
|
5275
|
+
tools: z5.object({
|
|
5276
|
+
loaded: z5.array(z5.string()),
|
|
5277
|
+
deferredCount: z5.number().int().nonnegative()
|
|
5278
|
+
}).strict(),
|
|
5279
|
+
retrieval: z5.object({
|
|
5280
|
+
engine: z5.string(),
|
|
5281
|
+
budgetTier: z5.enum(["none", "focused", "standard", "broad", "maximum"]).optional(),
|
|
5282
|
+
budgetTokens: z5.number().nonnegative().optional(),
|
|
5283
|
+
candidateHits: z5.number().int().nonnegative().optional(),
|
|
5284
|
+
selectedHits: z5.number().int().nonnegative().optional(),
|
|
5285
|
+
duplicateHits: z5.number().int().nonnegative().optional(),
|
|
5286
|
+
incrementalEvidenceTokens: z5.number().nonnegative().optional(),
|
|
5287
|
+
discarded: z5.array(z5.object({
|
|
5288
|
+
reason: z5.enum(["overlapping-span", "budget-cap"]),
|
|
5289
|
+
count: z5.number().int().positive()
|
|
5290
|
+
}).strict())
|
|
5291
|
+
}).strict()
|
|
5292
|
+
}).strict()).max(256).optional()
|
|
5063
5293
|
}).strict();
|
|
5064
5294
|
var SessionStore = class {
|
|
5065
5295
|
workspace;
|
|
@@ -8331,11 +8561,7 @@ function dynamicToolOutputBudget(contextWindowTokens, activeContextTokens, remai
|
|
|
8331
8561
|
return clamp2(budget, MIN_TOOL_OUTPUT_TOKENS, MAX_TOOL_OUTPUT_TOKENS);
|
|
8332
8562
|
}
|
|
8333
8563
|
function estimateToolOutputTokens(value) {
|
|
8334
|
-
|
|
8335
|
-
for (const character of value) {
|
|
8336
|
-
tokens2 += tokenCost(character);
|
|
8337
|
-
}
|
|
8338
|
-
return Math.max(1, Math.ceil(tokens2));
|
|
8564
|
+
return Math.max(1, estimateTokens(value));
|
|
8339
8565
|
}
|
|
8340
8566
|
function formatReceipt(options, metadata, preview) {
|
|
8341
8567
|
const lines = [
|
|
@@ -8399,35 +8625,6 @@ function sanitizeToolOutput(value) {
|
|
|
8399
8625
|
const text = stripAnsi(value).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f]/gu, "");
|
|
8400
8626
|
return { text, sanitized: text !== value };
|
|
8401
8627
|
}
|
|
8402
|
-
function sliceStartByTokens(value, budget) {
|
|
8403
|
-
let used = 0;
|
|
8404
|
-
let end = 0;
|
|
8405
|
-
for (const character of value) {
|
|
8406
|
-
const cost = tokenCost(character);
|
|
8407
|
-
if (used + cost > budget) break;
|
|
8408
|
-
used += cost;
|
|
8409
|
-
end += character.length;
|
|
8410
|
-
}
|
|
8411
|
-
return value.slice(0, end);
|
|
8412
|
-
}
|
|
8413
|
-
function sliceEndByTokens(value, budget) {
|
|
8414
|
-
let used = 0;
|
|
8415
|
-
let start = value.length;
|
|
8416
|
-
while (start > 0) {
|
|
8417
|
-
let next = start - 1;
|
|
8418
|
-
const code = value.charCodeAt(next);
|
|
8419
|
-
if (code >= 56320 && code <= 57343 && next > 0) {
|
|
8420
|
-
const previous = value.charCodeAt(next - 1);
|
|
8421
|
-
if (previous >= 55296 && previous <= 56319) next -= 1;
|
|
8422
|
-
}
|
|
8423
|
-
const character = value.slice(next, start);
|
|
8424
|
-
const cost = tokenCost(character);
|
|
8425
|
-
if (used + cost > budget) break;
|
|
8426
|
-
used += cost;
|
|
8427
|
-
start = next;
|
|
8428
|
-
}
|
|
8429
|
-
return value.slice(start);
|
|
8430
|
-
}
|
|
8431
8628
|
function boundedPreview(value, budget) {
|
|
8432
8629
|
if (budget <= 0) return "";
|
|
8433
8630
|
if (estimateToolOutputTokens(value) <= budget) return value;
|
|
@@ -8452,15 +8649,6 @@ ${tail}`;
|
|
|
8452
8649
|
}
|
|
8453
8650
|
return best || sliceStartByTokens(value, budget);
|
|
8454
8651
|
}
|
|
8455
|
-
function tokenCost(character) {
|
|
8456
|
-
const code = character.codePointAt(0) ?? 0;
|
|
8457
|
-
if (isCjkCharacter(character) || code > 127) return 2;
|
|
8458
|
-
return /[A-Z0-9\p{P}\p{S}]/u.test(character) ? 0.5 : 0.25;
|
|
8459
|
-
}
|
|
8460
|
-
function isCjkCharacter(character) {
|
|
8461
|
-
const code = character.codePointAt(0) ?? 0;
|
|
8462
|
-
return code >= 11904 && code <= 40959 || code >= 44032 && code <= 55215 || code >= 63744 && code <= 64255 || code >= 131072 && code <= 195103;
|
|
8463
|
-
}
|
|
8464
8652
|
function clamp2(value, minimum, maximum) {
|
|
8465
8653
|
return Math.max(minimum, Math.min(maximum, value));
|
|
8466
8654
|
}
|
|
@@ -8468,7 +8656,7 @@ function boundedInlineByTokens(value, maxTokens) {
|
|
|
8468
8656
|
const normalized = value.replace(/[\r\n\t]+/gu, " ").trim();
|
|
8469
8657
|
if (estimateToolOutputTokens(normalized) <= maxTokens) return normalized;
|
|
8470
8658
|
const suffix = "\u2026";
|
|
8471
|
-
return `${sliceStartByTokens(normalized, Math.max(0, maxTokens -
|
|
8659
|
+
return `${sliceStartByTokens(normalized, Math.max(0, maxTokens - estimatedTokenCost(suffix)))}${suffix}`;
|
|
8472
8660
|
}
|
|
8473
8661
|
|
|
8474
8662
|
// src/agent/rules.ts
|
|
@@ -8543,9 +8731,6 @@ import { readFile as readFile13, stat as stat9 } from "node:fs/promises";
|
|
|
8543
8731
|
var MAX_SOURCE_CHARS = 6e4;
|
|
8544
8732
|
var MAX_PINNED_CHARS = 16e4;
|
|
8545
8733
|
var MAX_CONTEXT_SOURCES = 32;
|
|
8546
|
-
function estimateTokens3(value) {
|
|
8547
|
-
return Math.ceil(value.length / 4);
|
|
8548
|
-
}
|
|
8549
8734
|
function sources(session) {
|
|
8550
8735
|
return session.contextSources ?? (session.contextSources = []);
|
|
8551
8736
|
}
|
|
@@ -8553,7 +8738,7 @@ async function pinContextSource(session, workspace, requested) {
|
|
|
8553
8738
|
const resolved = await workspace.resolvePath(requested, { expect: "file" });
|
|
8554
8739
|
const info = await stat9(resolved);
|
|
8555
8740
|
const alias = workspace.display(resolved);
|
|
8556
|
-
const tokens2 =
|
|
8741
|
+
const tokens2 = estimateTokens((await readFile13(resolved, "utf8")).slice(0, MAX_SOURCE_CHARS));
|
|
8557
8742
|
const list2 = sources(session);
|
|
8558
8743
|
const existing = list2.find((source2) => source2.path === alias);
|
|
8559
8744
|
if (existing) {
|
|
@@ -8605,7 +8790,7 @@ async function resolvePinnedContent(session, workspace) {
|
|
|
8605
8790
|
const safe = await workspace.resolvePath(source.path, { expect: "file" });
|
|
8606
8791
|
const raw = await readFile13(safe, "utf8");
|
|
8607
8792
|
const capped = raw.slice(0, Math.min(MAX_SOURCE_CHARS, remaining));
|
|
8608
|
-
source.tokens =
|
|
8793
|
+
source.tokens = estimateTokens(capped);
|
|
8609
8794
|
resolved.push({
|
|
8610
8795
|
path: source.path,
|
|
8611
8796
|
content: capped,
|
|
@@ -8771,13 +8956,20 @@ var AgentRunner = class {
|
|
|
8771
8956
|
this.session.title = titleFromInput(request);
|
|
8772
8957
|
}
|
|
8773
8958
|
this.contextManager.startTurn(this.session, request);
|
|
8774
|
-
|
|
8959
|
+
const userMessage = message("user", request);
|
|
8960
|
+
this.session.messages.push(userMessage);
|
|
8775
8961
|
await this.persist();
|
|
8776
8962
|
const trivialTurn = isTrivialTurn(request);
|
|
8777
|
-
const
|
|
8963
|
+
const turnDirective = buildTurnDirective(request, {
|
|
8964
|
+
agents: Boolean(this.config.agents?.enabled)
|
|
8965
|
+
});
|
|
8966
|
+
const packed = trivialTurn ? emptyPackedContext(selectContextBudget(request, this.config, {
|
|
8967
|
+
intent: turnDirective.intent,
|
|
8968
|
+
trivial: true
|
|
8969
|
+
})) : await this.packContext(request, { intent: turnDirective.intent });
|
|
8778
8970
|
if (!trivialTurn) await emit({ type: "context", packed });
|
|
8779
8971
|
const mentions = await this.packMentions(request);
|
|
8780
|
-
const retrievedContext = buildRetrievedContext(
|
|
8972
|
+
const retrievedContext = trivialTurn && !mentions.length ? "" : buildRetrievedContext(
|
|
8781
8973
|
packed,
|
|
8782
8974
|
mentions,
|
|
8783
8975
|
this.workspace.primaryRoot,
|
|
@@ -8803,9 +8995,6 @@ var AgentRunner = class {
|
|
|
8803
8995
|
scope: augmentation.memoryScope ?? "session"
|
|
8804
8996
|
});
|
|
8805
8997
|
}
|
|
8806
|
-
const turnDirective = buildTurnDirective(request, {
|
|
8807
|
-
agents: Boolean(this.config.agents?.enabled)
|
|
8808
|
-
});
|
|
8809
8998
|
const contractEnabled = shouldUseTaskContract(
|
|
8810
8999
|
request,
|
|
8811
9000
|
turnDirective.intent,
|
|
@@ -8829,21 +9018,6 @@ var AgentRunner = class {
|
|
|
8829
9018
|
...augmentation.skills?.length ? [`skills:${augmentation.skills.length}`] : [],
|
|
8830
9019
|
...augmentation.memoryCount ? [`memory:${augmentation.memoryCount}`] : []
|
|
8831
9020
|
];
|
|
8832
|
-
if (!trivialTurn) await emit({
|
|
8833
|
-
type: "prompt",
|
|
8834
|
-
intent: turnDirective.intent,
|
|
8835
|
-
sections: promptSections,
|
|
8836
|
-
estimatedTokens: Math.ceil([
|
|
8837
|
-
turnDirective.text,
|
|
8838
|
-
buildSessionStatePrompt(this.session),
|
|
8839
|
-
this.contextManager.buildShortTermPrompt(this.session),
|
|
8840
|
-
options.turnInstructions ?? "",
|
|
8841
|
-
augmentation.text,
|
|
8842
|
-
retrievedContext,
|
|
8843
|
-
pinnedContext,
|
|
8844
|
-
workspaceRules
|
|
8845
|
-
].join("\n").length / 4)
|
|
8846
|
-
});
|
|
8847
9021
|
let verificationAttempted = false;
|
|
8848
9022
|
const maxTurns = options.maxTurns ?? this.config.agent.maxTurns;
|
|
8849
9023
|
const contextBudget = Math.max(24e3, Math.min(1e5, this.config.context.maxTokens * 3));
|
|
@@ -8858,16 +9032,17 @@ var AgentRunner = class {
|
|
|
8858
9032
|
}
|
|
8859
9033
|
this.applySteering();
|
|
8860
9034
|
await emit({ type: "thinking", turn });
|
|
9035
|
+
const dynamicPrompt = [
|
|
9036
|
+
buildSessionStatePrompt(this.session),
|
|
9037
|
+
turnDirective.text,
|
|
9038
|
+
this.contextManager.buildShortTermPrompt(this.session),
|
|
9039
|
+
pinnedContext,
|
|
9040
|
+
options.turnInstructions ?? "",
|
|
9041
|
+
augmentation.text
|
|
9042
|
+
].filter(Boolean).join("\n\n");
|
|
8861
9043
|
const messages = packConversation(
|
|
8862
9044
|
stableSystemPrompt,
|
|
8863
|
-
|
|
8864
|
-
buildSessionStatePrompt(this.session),
|
|
8865
|
-
turnDirective.text,
|
|
8866
|
-
this.contextManager.buildShortTermPrompt(this.session),
|
|
8867
|
-
pinnedContext,
|
|
8868
|
-
options.turnInstructions ?? "",
|
|
8869
|
-
augmentation.text
|
|
8870
|
-
].filter(Boolean).join("\n\n"),
|
|
9045
|
+
dynamicPrompt,
|
|
8871
9046
|
retrievedContext,
|
|
8872
9047
|
activeMessages(this.session),
|
|
8873
9048
|
contextBudget
|
|
@@ -8887,6 +9062,23 @@ var AgentRunner = class {
|
|
|
8887
9062
|
this.config.model.maxTokens ?? 8192,
|
|
8888
9063
|
availableTokens - estimatedInputTokens
|
|
8889
9064
|
));
|
|
9065
|
+
const breakdown = promptTokenBreakdown(
|
|
9066
|
+
messages,
|
|
9067
|
+
stableSystemPrompt,
|
|
9068
|
+
dynamicPrompt,
|
|
9069
|
+
retrievedContext,
|
|
9070
|
+
visibleTools,
|
|
9071
|
+
maxOutputTokens
|
|
9072
|
+
);
|
|
9073
|
+
if (!trivialTurn) {
|
|
9074
|
+
await emit({
|
|
9075
|
+
type: "prompt",
|
|
9076
|
+
intent: turnDirective.intent,
|
|
9077
|
+
sections: promptSections,
|
|
9078
|
+
estimatedTokens: breakdown.estimatedInputTokens,
|
|
9079
|
+
breakdown
|
|
9080
|
+
});
|
|
9081
|
+
}
|
|
8890
9082
|
const assistantId = randomUUID12();
|
|
8891
9083
|
const response = await this.completeModel(
|
|
8892
9084
|
messages,
|
|
@@ -8902,17 +9094,49 @@ var AgentRunner = class {
|
|
|
8902
9094
|
assistantMessage.id = assistantId;
|
|
8903
9095
|
this.session.messages.push(assistantMessage);
|
|
8904
9096
|
if (response.content) await emit({ type: "assistant", id: assistantId, content: response.content });
|
|
8905
|
-
const
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
9097
|
+
const turnUsage = recordTokenUsage(
|
|
9098
|
+
this.session,
|
|
9099
|
+
response.usage,
|
|
9100
|
+
estimatedInputTokens,
|
|
9101
|
+
estimateResponseTokens(response)
|
|
9102
|
+
);
|
|
9103
|
+
const { inputTokens, outputTokens } = turnUsage;
|
|
9104
|
+
const actualInputTokens = validTokenCount(response.usage?.inputTokens);
|
|
9105
|
+
const actualOutputTokens = validTokenCount(response.usage?.outputTokens);
|
|
9106
|
+
const receipt = recordTokenLedger(this.session, {
|
|
9107
|
+
requestId: userMessage.id,
|
|
9108
|
+
turn,
|
|
9109
|
+
recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9110
|
+
estimated: {
|
|
9111
|
+
...breakdown,
|
|
9112
|
+
outputTokens: estimateResponseTokens(response)
|
|
9113
|
+
},
|
|
9114
|
+
actual: {
|
|
9115
|
+
...actualInputTokens === void 0 ? {} : { inputTokens: actualInputTokens },
|
|
9116
|
+
...actualOutputTokens === void 0 ? {} : { outputTokens: actualOutputTokens }
|
|
9117
|
+
},
|
|
9118
|
+
inputSource: actualInputTokens === void 0 ? "estimated" : "actual",
|
|
9119
|
+
outputSource: actualOutputTokens === void 0 ? "estimated" : "actual",
|
|
9120
|
+
tools: { loaded: visibleTools.map((tool) => tool.name), deferredCount: 0 },
|
|
9121
|
+
retrieval: tokenRetrievalReceipt(packed)
|
|
9122
|
+
});
|
|
9123
|
+
await emit({
|
|
9124
|
+
type: "usage",
|
|
9125
|
+
inputTokens: this.session.usage.inputTokens,
|
|
9126
|
+
outputTokens: this.session.usage.outputTokens,
|
|
9127
|
+
source: this.session.usage.source ?? "unknown",
|
|
9128
|
+
inputSource: this.session.usage.inputSource ?? "unknown",
|
|
9129
|
+
outputSource: this.session.usage.outputSource ?? "unknown",
|
|
9130
|
+
actual: {
|
|
9131
|
+
inputTokens: this.session.usage.actualInputTokens ?? 0,
|
|
9132
|
+
outputTokens: this.session.usage.actualOutputTokens ?? 0
|
|
9133
|
+
},
|
|
9134
|
+
estimated: {
|
|
9135
|
+
inputTokens: this.session.usage.estimatedInputTokens ?? 0,
|
|
9136
|
+
outputTokens: this.session.usage.estimatedOutputTokens ?? 0
|
|
9137
|
+
},
|
|
9138
|
+
receipt
|
|
9139
|
+
});
|
|
8916
9140
|
await this.persist();
|
|
8917
9141
|
if (this.session.usage.inputTokens + this.session.usage.outputTokens >= this.config.agent.maxSessionTokens) {
|
|
8918
9142
|
for (const call of response.toolCalls) {
|
|
@@ -9291,8 +9515,8 @@ ${completeContent}`;
|
|
|
9291
9515
|
toolCalls: toolCalls.map((call) => ({ id: call.id, name: call.name }))
|
|
9292
9516
|
}, signal);
|
|
9293
9517
|
}
|
|
9294
|
-
async packContext(input2) {
|
|
9295
|
-
return this.contextEngine.pack(input2);
|
|
9518
|
+
async packContext(input2, options) {
|
|
9519
|
+
return this.contextEngine.pack(input2, options);
|
|
9296
9520
|
}
|
|
9297
9521
|
async packMentions(input2) {
|
|
9298
9522
|
try {
|
|
@@ -9410,14 +9634,14 @@ function packConversation(systemPrompt, dynamicPrompt, retrievedContext, history
|
|
|
9410
9634
|
const system = message("system", systemPrompt);
|
|
9411
9635
|
const dynamic = dynamicPrompt ? message("system", dynamicPrompt) : void 0;
|
|
9412
9636
|
const context = retrievedContext ? message("system", retrievedContext) : void 0;
|
|
9413
|
-
const reserved =
|
|
9637
|
+
const reserved = estimateTokens(system.content) + estimateTokens(dynamic?.content ?? "") + estimateTokens(context?.content ?? "");
|
|
9414
9638
|
const budget = Math.max(4e3, tokenBudget - reserved);
|
|
9415
9639
|
const groups = groupMessages(clearOldToolResults(history));
|
|
9416
9640
|
const selected = [];
|
|
9417
9641
|
let used = 0;
|
|
9418
9642
|
for (let index = groups.length - 1; index >= 0; index -= 1) {
|
|
9419
9643
|
const group = groups[index] ?? [];
|
|
9420
|
-
const cost = group.reduce((sum, item) => sum +
|
|
9644
|
+
const cost = group.reduce((sum, item) => sum + estimateTokens(item.content) + estimateTokens(JSON.stringify(item.toolCalls ?? [])), 0);
|
|
9421
9645
|
if (selected.length && used + cost > budget) break;
|
|
9422
9646
|
selected.unshift(group);
|
|
9423
9647
|
used += cost;
|
|
@@ -9455,17 +9679,114 @@ function groupMessages(messages) {
|
|
|
9455
9679
|
}
|
|
9456
9680
|
return groups;
|
|
9457
9681
|
}
|
|
9458
|
-
function estimateTokens4(input2) {
|
|
9459
|
-
return Math.ceil(input2.length / 4);
|
|
9460
|
-
}
|
|
9461
9682
|
function estimateMessages2(messages) {
|
|
9462
|
-
return messages.reduce((total, item) => total +
|
|
9683
|
+
return messages.reduce((total, item) => total + estimateTokens(item.content) + estimateTokens(JSON.stringify(item.toolCalls ?? [])), 0);
|
|
9463
9684
|
}
|
|
9464
9685
|
function estimateToolDefinitions(tools) {
|
|
9465
|
-
return
|
|
9686
|
+
return estimateTokens(JSON.stringify(tools));
|
|
9466
9687
|
}
|
|
9467
9688
|
function estimateResponseTokens(response) {
|
|
9468
|
-
return
|
|
9689
|
+
return estimateTokens(response.content) + estimateTokens(JSON.stringify(response.toolCalls));
|
|
9690
|
+
}
|
|
9691
|
+
function promptTokenBreakdown(messages, stablePrompt, dynamicPrompt, retrievedContext, tools, outputAllowanceTokens) {
|
|
9692
|
+
const stableTokens = estimateTokens(stablePrompt);
|
|
9693
|
+
const dynamicTokens = estimateTokens(dynamicPrompt);
|
|
9694
|
+
const retrievedTokens = estimateTokens(retrievedContext);
|
|
9695
|
+
const messageTokens = estimateMessages2(messages);
|
|
9696
|
+
const toolSchemaTokens = estimateToolDefinitions(tools);
|
|
9697
|
+
const toolResultTokens = messages.filter((message2) => message2.role === "tool").reduce((total, message2) => total + estimateTokens(message2.content), 0);
|
|
9698
|
+
return {
|
|
9699
|
+
stableTokens,
|
|
9700
|
+
dynamicTokens,
|
|
9701
|
+
conversationTokens: Math.max(0, messageTokens - stableTokens - dynamicTokens - retrievedTokens - toolResultTokens),
|
|
9702
|
+
toolResultTokens,
|
|
9703
|
+
retrievedTokens,
|
|
9704
|
+
toolSchemaTokens,
|
|
9705
|
+
estimatedInputTokens: messageTokens + toolSchemaTokens,
|
|
9706
|
+
outputAllowanceTokens
|
|
9707
|
+
};
|
|
9708
|
+
}
|
|
9709
|
+
function tokenRetrievalReceipt(packed) {
|
|
9710
|
+
const discarded = [];
|
|
9711
|
+
if ((packed.duplicateHits ?? 0) > 0) {
|
|
9712
|
+
discarded.push({ reason: "overlapping-span", count: packed.duplicateHits ?? 0 });
|
|
9713
|
+
}
|
|
9714
|
+
if (packed.truncated) discarded.push({ reason: "budget-cap", count: 1 });
|
|
9715
|
+
return {
|
|
9716
|
+
engine: packed.engine,
|
|
9717
|
+
...packed.budgetTier ? { budgetTier: packed.budgetTier } : {},
|
|
9718
|
+
...packed.budgetTokens === void 0 ? {} : { budgetTokens: packed.budgetTokens },
|
|
9719
|
+
...packed.candidateHits === void 0 ? {} : { candidateHits: packed.candidateHits },
|
|
9720
|
+
...packed.selectedHits === void 0 ? {} : { selectedHits: packed.selectedHits },
|
|
9721
|
+
...packed.duplicateHits === void 0 ? {} : { duplicateHits: packed.duplicateHits },
|
|
9722
|
+
...packed.incrementalEvidenceTokens === void 0 ? {} : { incrementalEvidenceTokens: packed.incrementalEvidenceTokens },
|
|
9723
|
+
discarded
|
|
9724
|
+
};
|
|
9725
|
+
}
|
|
9726
|
+
function recordTokenLedger(session, entry) {
|
|
9727
|
+
const ledger = session.tokenLedger ?? (session.tokenLedger = []);
|
|
9728
|
+
ledger.push(entry);
|
|
9729
|
+
if (ledger.length > 256) ledger.splice(0, ledger.length - 256);
|
|
9730
|
+
return entry;
|
|
9731
|
+
}
|
|
9732
|
+
function recordTokenUsage(session, providerUsage, estimatedInputTokens, estimatedOutputTokens) {
|
|
9733
|
+
const inputActual = validTokenCount(providerUsage?.inputTokens);
|
|
9734
|
+
const outputActual = validTokenCount(providerUsage?.outputTokens);
|
|
9735
|
+
const priorInputSource = existingMeasurementSource(session, "input");
|
|
9736
|
+
const priorOutputSource = existingMeasurementSource(session, "output");
|
|
9737
|
+
const inputTokens = inputActual ?? estimatedInputTokens;
|
|
9738
|
+
const outputTokens = outputActual ?? estimatedOutputTokens;
|
|
9739
|
+
session.usage.inputTokens += inputTokens;
|
|
9740
|
+
session.usage.outputTokens += outputTokens;
|
|
9741
|
+
if (inputActual !== void 0) {
|
|
9742
|
+
session.usage.actualInputTokens = (session.usage.actualInputTokens ?? 0) + inputActual;
|
|
9743
|
+
} else {
|
|
9744
|
+
session.usage.estimatedInputTokens = (session.usage.estimatedInputTokens ?? 0) + inputTokens;
|
|
9745
|
+
}
|
|
9746
|
+
if (outputActual !== void 0) {
|
|
9747
|
+
session.usage.actualOutputTokens = (session.usage.actualOutputTokens ?? 0) + outputActual;
|
|
9748
|
+
} else {
|
|
9749
|
+
session.usage.estimatedOutputTokens = (session.usage.estimatedOutputTokens ?? 0) + outputTokens;
|
|
9750
|
+
}
|
|
9751
|
+
session.usage.inputSource = mergeMeasurementSource(
|
|
9752
|
+
priorInputSource,
|
|
9753
|
+
inputActual === void 0 ? "estimated" : "actual",
|
|
9754
|
+
session.usage.inputTokens
|
|
9755
|
+
);
|
|
9756
|
+
session.usage.outputSource = mergeMeasurementSource(
|
|
9757
|
+
priorOutputSource,
|
|
9758
|
+
outputActual === void 0 ? "estimated" : "actual",
|
|
9759
|
+
session.usage.outputTokens
|
|
9760
|
+
);
|
|
9761
|
+
session.usage.source = combineMeasurementSources(
|
|
9762
|
+
session.usage.inputSource,
|
|
9763
|
+
session.usage.outputSource,
|
|
9764
|
+
session.usage.inputTokens,
|
|
9765
|
+
session.usage.outputTokens
|
|
9766
|
+
);
|
|
9767
|
+
return { inputTokens, outputTokens };
|
|
9768
|
+
}
|
|
9769
|
+
function validTokenCount(value) {
|
|
9770
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : void 0;
|
|
9771
|
+
}
|
|
9772
|
+
function existingMeasurementSource(session, channel) {
|
|
9773
|
+
const source = channel === "input" ? session.usage.inputSource : session.usage.outputSource;
|
|
9774
|
+
if (source) return source;
|
|
9775
|
+
const total = channel === "input" ? session.usage.inputTokens : session.usage.outputTokens;
|
|
9776
|
+
return total > 0 ? "unknown" : void 0;
|
|
9777
|
+
}
|
|
9778
|
+
function mergeMeasurementSource(previous, current, total) {
|
|
9779
|
+
if (total === 0 && !previous) return current;
|
|
9780
|
+
if (!previous) return current;
|
|
9781
|
+
return previous === current ? current : "mixed";
|
|
9782
|
+
}
|
|
9783
|
+
function combineMeasurementSources(input2, output2, inputTokens, outputTokens) {
|
|
9784
|
+
const active = [
|
|
9785
|
+
...inputTokens > 0 ? [input2] : [],
|
|
9786
|
+
...outputTokens > 0 ? [output2] : []
|
|
9787
|
+
];
|
|
9788
|
+
if (!active.length) return "unknown";
|
|
9789
|
+
return active.every((source) => source === active[0]) ? active[0] ?? "unknown" : "mixed";
|
|
9469
9790
|
}
|
|
9470
9791
|
function uniqueCategories(categories) {
|
|
9471
9792
|
return [...new Set(categories)];
|
|
@@ -12136,9 +12457,10 @@ var HeadlessReporter = class {
|
|
|
12136
12457
|
}
|
|
12137
12458
|
if (!this.options.quiet && !this.options.compact) {
|
|
12138
12459
|
const usage = session.usage.inputTokens + session.usage.outputTokens;
|
|
12460
|
+
const usageLabel = tokenUsageLabel(session.usage);
|
|
12139
12461
|
process.stderr.write(this.paint.dim(
|
|
12140
12462
|
`
|
|
12141
|
-
${this.glyphs.meta} ${session.changedFiles.length} changed files ${this.glyphs.separator} ${usage.toLocaleString()} tokens ${this.glyphs.separator} session ${session.id.slice(0, 8)}
|
|
12463
|
+
${this.glyphs.meta} ${session.changedFiles.length} changed files ${this.glyphs.separator} ${usage.toLocaleString()} tokens (${usageLabel}) ${this.glyphs.separator} session ${session.id.slice(0, 8)}
|
|
12142
12464
|
`
|
|
12143
12465
|
));
|
|
12144
12466
|
}
|
|
@@ -12162,16 +12484,19 @@ ${this.glyphs.meta} ${session.changedFiles.length} changed files ${this.glyphs.s
|
|
|
12162
12484
|
if (!compact2) process.stderr.write(this.paint.dim(`${this.glyphs.meta} reasoning ${this.glyphs.separator} turn ${event.turn}
|
|
12163
12485
|
`));
|
|
12164
12486
|
break;
|
|
12165
|
-
case "context":
|
|
12487
|
+
case "context": {
|
|
12488
|
+
const budget = event.packed.budgetTokens === void 0 ? "" : ` ${this.glyphs.separator} ${event.packed.budgetTier ?? "adaptive"} ${event.packed.budgetTokens} budget`;
|
|
12166
12489
|
process.stderr.write(this.paint.cyan(
|
|
12167
|
-
`${this.glyphs.meta} context ${this.glyphs.separator} ${event.packed.engine} ${this.glyphs.separator} ${event.packed.hits.length} spans ${this.glyphs.separator} ~${event.packed.estimatedTokens} tokens${event.packed.degradation ? ` ${this.glyphs.separator} ${event.packed.degradation.summary}` : ""}
|
|
12490
|
+
`${this.glyphs.meta} context ${this.glyphs.separator} ${event.packed.engine} ${this.glyphs.separator} ${event.packed.hits.length} spans ${this.glyphs.separator} ~${event.packed.estimatedTokens} tokens${budget}${event.packed.degradation ? ` ${this.glyphs.separator} ${event.packed.degradation.summary}` : ""}
|
|
12168
12491
|
`
|
|
12169
12492
|
));
|
|
12170
12493
|
break;
|
|
12494
|
+
}
|
|
12171
12495
|
case "prompt":
|
|
12172
12496
|
if (!compact2) {
|
|
12497
|
+
const partition = event.breakdown ? ` ${this.glyphs.separator} stable ${event.breakdown.stableTokens} ${this.glyphs.separator} dynamic ${event.breakdown.dynamicTokens} ${this.glyphs.separator} history ${event.breakdown.conversationTokens} ${this.glyphs.separator} tool results ${event.breakdown.toolResultTokens} ${this.glyphs.separator} retrieved ${event.breakdown.retrievedTokens} ${this.glyphs.separator} tools ${event.breakdown.toolSchemaTokens} ${this.glyphs.separator} output cap ${event.breakdown.outputAllowanceTokens}` : "";
|
|
12173
12498
|
process.stderr.write(this.paint.dim(
|
|
12174
|
-
`${this.glyphs.meta} prompt ${this.glyphs.separator} ${event.intent} ${this.glyphs.separator}
|
|
12499
|
+
`${this.glyphs.meta} prompt ${this.glyphs.separator} ${event.intent} ${this.glyphs.separator} ~${event.estimatedTokens} estimated tokens${partition}
|
|
12175
12500
|
`
|
|
12176
12501
|
));
|
|
12177
12502
|
}
|
|
@@ -12303,9 +12628,14 @@ function sessionSummary(session) {
|
|
|
12303
12628
|
...session.taskContract ? { taskContract: session.taskContract } : {},
|
|
12304
12629
|
changedFiles: session.changedFiles,
|
|
12305
12630
|
...session.lastRun ? { lastRun: session.lastRun } : {},
|
|
12631
|
+
...session.tokenLedger?.length ? { tokenLedger: session.tokenLedger } : {},
|
|
12306
12632
|
usage: session.usage
|
|
12307
12633
|
};
|
|
12308
12634
|
}
|
|
12635
|
+
function tokenUsageLabel(usage) {
|
|
12636
|
+
if (usage.source) return usage.source;
|
|
12637
|
+
return usage.inputTokens + usage.outputTokens > 0 ? "unknown source" : "no usage";
|
|
12638
|
+
}
|
|
12309
12639
|
function formatToolDetail(call, paint = chalk2, glyphs = resolveCliGlyphs()) {
|
|
12310
12640
|
const environment = call.name === "shell" && typeof call.arguments.env === "object" && call.arguments.env !== null && !Array.isArray(call.arguments.env) ? Object.keys(call.arguments.env).sort() : [];
|
|
12311
12641
|
const environmentDetail = environment.length ? ` ${glyphs.separator} env ${environment.join(", ")}` : "";
|
|
@@ -13030,10 +13360,11 @@ function Timeline({ items, width = 80, glyphMode = "auto", showToolOutput = fals
|
|
|
13030
13360
|
width,
|
|
13031
13361
|
glyph: glyphs.context,
|
|
13032
13362
|
label: "context",
|
|
13033
|
-
detail: `${sanitizeInlineTerminalText(item.engine)} ${glyphs.separator} ${item.hits} spans ${glyphs.separator} ~${formatTokens(item.tokens)}${item.truncated ? ` ${glyphs.separator} truncated` : ""}`,
|
|
13363
|
+
detail: `${sanitizeInlineTerminalText(item.engine)} ${glyphs.separator} ${item.hits} spans ${glyphs.separator} ~${formatTokens(item.tokens)} estimated${item.budgetTokens === void 0 ? "" : ` ${glyphs.separator} ${item.budgetTier ?? "adaptive"} ${formatTokens(item.budgetTokens)} budget`}${item.truncated ? ` ${glyphs.separator} truncated` : ""}`,
|
|
13034
13364
|
labelColor: theme.accent
|
|
13035
13365
|
}
|
|
13036
13366
|
),
|
|
13367
|
+
!compact2 && item.budgetReason ? /* @__PURE__ */ jsx(Text, { color: theme.dim, children: truncateDisplay(`${glyphs.branchLast} ${sanitizeInlineTerminalText(item.budgetReason)}`, innerWidth) }) : null,
|
|
13037
13368
|
spans.slice(0, spanLimit).map((span, spanIndex) => {
|
|
13038
13369
|
const lines = span.startLine === span.endLine ? `${span.startLine}` : `${span.startLine}-${span.endLine}`;
|
|
13039
13370
|
const location = `${compactDisplayPath(sanitizeInlineTerminalText(span.path), 44)}:${lines}`;
|
|
@@ -13055,13 +13386,14 @@ function Timeline({ items, width = 80, glyphMode = "auto", showToolOutput = fals
|
|
|
13055
13386
|
] }, item.id);
|
|
13056
13387
|
}
|
|
13057
13388
|
if (item.kind === "prompt") {
|
|
13389
|
+
const detail = item.breakdown ? `~${formatTokens(item.tokens)} estimated ${glyphs.separator} stable ${formatTokens(item.breakdown.stableTokens)} ${glyphs.separator} dynamic ${formatTokens(item.breakdown.dynamicTokens)} ${glyphs.separator} history ${formatTokens(item.breakdown.conversationTokens)} ${glyphs.separator} tool results ${formatTokens(item.breakdown.toolResultTokens)} ${glyphs.separator} retrieved ${formatTokens(item.breakdown.retrievedTokens)} ${glyphs.separator} tools ${formatTokens(item.breakdown.toolSchemaTokens)} ${glyphs.separator} output cap ${formatTokens(item.breakdown.outputAllowanceTokens)}` : `${item.sections.map(sanitizeInlineTerminalText).join(` ${glyphs.separator} `)} ${glyphs.separator} ~${formatTokens(item.tokens)} estimated`;
|
|
13058
13390
|
return /* @__PURE__ */ jsx(
|
|
13059
13391
|
MetaRow,
|
|
13060
13392
|
{
|
|
13061
13393
|
width,
|
|
13062
13394
|
glyph: glyphs.pending,
|
|
13063
13395
|
label: `prompt/${sanitizeInlineTerminalText(item.intent)}`,
|
|
13064
|
-
detail
|
|
13396
|
+
detail
|
|
13065
13397
|
},
|
|
13066
13398
|
item.id
|
|
13067
13399
|
);
|
|
@@ -15176,6 +15508,9 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
15176
15508
|
engine: event.packed.engine,
|
|
15177
15509
|
hits: event.packed.hits.length,
|
|
15178
15510
|
tokens: event.packed.estimatedTokens,
|
|
15511
|
+
...event.packed.budgetTier ? { budgetTier: event.packed.budgetTier } : {},
|
|
15512
|
+
...event.packed.budgetTokens !== void 0 ? { budgetTokens: event.packed.budgetTokens } : {},
|
|
15513
|
+
...event.packed.budgetReason ? { budgetReason: event.packed.budgetReason } : {},
|
|
15179
15514
|
truncated: event.packed.truncated,
|
|
15180
15515
|
spans: event.packed.hits.slice(0, 5).map((hit) => ({
|
|
15181
15516
|
path: relative8(runner.workspace.primaryRoot, hit.path) || hit.path,
|
|
@@ -15189,7 +15524,14 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
15189
15524
|
setActivity({ label: "Assembling relevant context", startedAt: Date.now() });
|
|
15190
15525
|
break;
|
|
15191
15526
|
case "prompt":
|
|
15192
|
-
append({
|
|
15527
|
+
append({
|
|
15528
|
+
id: nextId(),
|
|
15529
|
+
kind: "prompt",
|
|
15530
|
+
intent: event.intent,
|
|
15531
|
+
sections: event.sections,
|
|
15532
|
+
tokens: event.estimatedTokens,
|
|
15533
|
+
...event.breakdown ? { breakdown: event.breakdown } : {}
|
|
15534
|
+
});
|
|
15193
15535
|
setActivity({ label: "Preparing the model prompt", startedAt: Date.now() });
|
|
15194
15536
|
break;
|
|
15195
15537
|
case "assistant_delta":
|
|
@@ -15736,7 +16078,7 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
15736
16078
|
{ label: config.agents?.enabled ? `${config.agents.maxConcurrent} concurrent` : "disabled", detail: "expert delegation" },
|
|
15737
16079
|
{
|
|
15738
16080
|
label: `${usage.inputTokens.toLocaleString()} in ${separator} ${usage.outputTokens.toLocaleString()} out`,
|
|
15739
|
-
detail: `session tokens${separator}${(usage.inputTokens + usage.outputTokens).toLocaleString()} total`
|
|
16081
|
+
detail: `session tokens${separator}${(usage.inputTokens + usage.outputTokens).toLocaleString()} total${separator}${usage.source ?? "unknown source"}`
|
|
15740
16082
|
},
|
|
15741
16083
|
{
|
|
15742
16084
|
label: `${Math.round(status.pressure * 100)}% context pressure`,
|
|
@@ -18871,7 +19213,7 @@ program.command("context").description("Pack task-oriented context under a token
|
|
|
18871
19213
|
|
|
18872
19214
|
`);
|
|
18873
19215
|
process.stderr.write(chalk4.dim(
|
|
18874
|
-
`${cliGlyphs.meta} ${packed.engine} ${cliGlyphs.separator} ${packed.hits.length} spans ${cliGlyphs.separator} ~${packed.estimatedTokens} tokens${packed.truncated ? ` ${cliGlyphs.separator} capped` : ""}${packed.degradation ? ` ${cliGlyphs.separator} ${packed.degradation.summary}` : ""}
|
|
19216
|
+
`${cliGlyphs.meta} ${packed.engine} ${cliGlyphs.separator} ${packed.hits.length} spans ${cliGlyphs.separator} ~${packed.estimatedTokens} estimated tokens${packed.budgetTokens === void 0 ? "" : ` ${cliGlyphs.separator} ${packed.budgetTier ?? "adaptive"} ${packed.budgetTokens} budget`}${packed.truncated ? ` ${cliGlyphs.separator} capped` : ""}${packed.degradation ? ` ${cliGlyphs.separator} ${packed.degradation.summary}` : ""}
|
|
18875
19217
|
`
|
|
18876
19218
|
));
|
|
18877
19219
|
});
|