@skein-code/cli 0.3.10 → 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 +33 -3
- package/dist/cli.js +1622 -524
- package/dist/cli.js.map +1 -1
- package/docs/ARCHITECTURE.md +53 -6
- package/docs/NEXT_STEPS.md +10 -9
- package/docs/PRODUCT.md +7 -1
- package/docs/PRODUCT_BENCHMARK.md +2 -1
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
5
5
|
import { stdin as input, stdout as output } from "node:process";
|
|
6
6
|
import { writeFile as writeFile3 } from "node:fs/promises";
|
|
7
|
-
import { basename as basename12, resolve as
|
|
7
|
+
import { basename as basename12, resolve as resolve23 } from "node:path";
|
|
8
8
|
import { Command, Option } from "commander";
|
|
9
9
|
import chalk4 from "chalk";
|
|
10
10
|
|
|
@@ -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: {
|
|
@@ -990,16 +990,16 @@ async function hashRegularFile(path) {
|
|
|
990
990
|
try {
|
|
991
991
|
const info = await handle.stat();
|
|
992
992
|
if (!info.isFile()) throw new Error(`Namespace entry is not a regular file: ${path}`);
|
|
993
|
-
const
|
|
993
|
+
const hash2 = createHash2("sha256");
|
|
994
994
|
const buffer = Buffer.allocUnsafe(64 * 1024);
|
|
995
995
|
let position = 0;
|
|
996
996
|
while (true) {
|
|
997
997
|
const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);
|
|
998
998
|
if (!bytesRead) break;
|
|
999
|
-
|
|
999
|
+
hash2.update(buffer.subarray(0, bytesRead));
|
|
1000
1000
|
position += bytesRead;
|
|
1001
1001
|
}
|
|
1002
|
-
return { sha256:
|
|
1002
|
+
return { sha256: hash2.digest("hex"), size: position };
|
|
1003
1003
|
} finally {
|
|
1004
1004
|
await handle.close();
|
|
1005
1005
|
}
|
|
@@ -1200,18 +1200,18 @@ var MemoryStore = class {
|
|
|
1200
1200
|
input2.lastVerifiedAt ?? (explicit ? now : void 0),
|
|
1201
1201
|
"Memory verification time"
|
|
1202
1202
|
);
|
|
1203
|
-
const
|
|
1203
|
+
const hash2 = createHash3("sha256").update(`${input2.scope}\0${scopeKey2}\0${content.toLocaleLowerCase()}`).digest("hex");
|
|
1204
1204
|
database.exec("BEGIN IMMEDIATE");
|
|
1205
1205
|
try {
|
|
1206
1206
|
const existing = database.prepare(
|
|
1207
1207
|
"SELECT * FROM memories WHERE content_hash = ?"
|
|
1208
|
-
).get(
|
|
1208
|
+
).get(hash2);
|
|
1209
1209
|
const conflicting = conflictKey ? database.prepare(`
|
|
1210
1210
|
SELECT * FROM memories
|
|
1211
1211
|
WHERE scope = ? AND scope_key = ? AND conflict_key = ?
|
|
1212
1212
|
AND status = 'active' AND content_hash <> ?
|
|
1213
1213
|
ORDER BY updated_at DESC
|
|
1214
|
-
`).all(input2.scope, scopeKey2, conflictKey,
|
|
1214
|
+
`).all(input2.scope, scopeKey2, conflictKey, hash2) : [];
|
|
1215
1215
|
const supersedesId = normalizeOptional(input2.supersedesId, 80) ?? conflicting[0]?.id;
|
|
1216
1216
|
let id;
|
|
1217
1217
|
if (existing) {
|
|
@@ -1256,7 +1256,7 @@ var MemoryStore = class {
|
|
|
1256
1256
|
importance,
|
|
1257
1257
|
confidence,
|
|
1258
1258
|
source,
|
|
1259
|
-
|
|
1259
|
+
hash2,
|
|
1260
1260
|
now,
|
|
1261
1261
|
now,
|
|
1262
1262
|
now,
|
|
@@ -1359,10 +1359,10 @@ var MemoryStore = class {
|
|
|
1359
1359
|
const conflictKey = normalizeOptional(input2.conflictKey, 240);
|
|
1360
1360
|
const candidateDefaultExpiry = !input2.expiresAt ? new Date(Date.now() + MEMORY_CANDIDATE_TTL_MS).toISOString() : void 0;
|
|
1361
1361
|
const expiresAt = normalizeTimestamp(input2.expiresAt ?? candidateDefaultExpiry, "Memory expiration");
|
|
1362
|
-
const
|
|
1362
|
+
const hash2 = createHash3("sha256").update(`${input2.scope}\0${scopeKey2}\0${content.toLocaleLowerCase()}`).digest("hex");
|
|
1363
1363
|
const existing = database.prepare(
|
|
1364
1364
|
"SELECT * FROM memory_candidates WHERE content_hash = ?"
|
|
1365
|
-
).get(
|
|
1365
|
+
).get(hash2);
|
|
1366
1366
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1367
1367
|
if (existing) {
|
|
1368
1368
|
if (existing.status === "approved" && existing.approved_memory_id) {
|
|
@@ -1409,7 +1409,7 @@ var MemoryStore = class {
|
|
|
1409
1409
|
confidence,
|
|
1410
1410
|
source,
|
|
1411
1411
|
rationale,
|
|
1412
|
-
|
|
1412
|
+
hash2,
|
|
1413
1413
|
now,
|
|
1414
1414
|
now,
|
|
1415
1415
|
revision ?? null,
|
|
@@ -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
|
|
@@ -4755,7 +4939,7 @@ function executableNames(command2) {
|
|
|
4755
4939
|
return [command2, ...extensions.map((extension) => `${command2}${extension}`)];
|
|
4756
4940
|
}
|
|
4757
4941
|
function runProcess(command2, args, options) {
|
|
4758
|
-
return new Promise((
|
|
4942
|
+
return new Promise((resolve24, reject) => {
|
|
4759
4943
|
const started = Date.now();
|
|
4760
4944
|
const environment = options.inheritEnv === false ? {} : { ...process.env };
|
|
4761
4945
|
for (const name of options.unsetEnv ?? []) delete environment[name];
|
|
@@ -4776,6 +4960,8 @@ function runProcess(command2, args, options) {
|
|
|
4776
4960
|
let stderr = "";
|
|
4777
4961
|
let stdoutBytes = 0;
|
|
4778
4962
|
let stderrBytes = 0;
|
|
4963
|
+
let stdoutObservedBytes = 0;
|
|
4964
|
+
let stderrObservedBytes = 0;
|
|
4779
4965
|
let timedOut = false;
|
|
4780
4966
|
let callbackError;
|
|
4781
4967
|
const stdoutDecoder = new StringDecoder("utf8");
|
|
@@ -4798,12 +4984,14 @@ function runProcess(command2, args, options) {
|
|
|
4798
4984
|
}
|
|
4799
4985
|
};
|
|
4800
4986
|
child.stdout.on("data", (chunk) => {
|
|
4987
|
+
stdoutObservedBytes += chunk.length;
|
|
4801
4988
|
const appended = append(stdoutDecoder, chunk, stdoutBytes);
|
|
4802
4989
|
stdout += appended.text;
|
|
4803
4990
|
stdoutBytes = appended.usedBytes;
|
|
4804
4991
|
notify(options.onStdout, stdoutCallbackDecoder, chunk);
|
|
4805
4992
|
});
|
|
4806
4993
|
child.stderr.on("data", (chunk) => {
|
|
4994
|
+
stderrObservedBytes += chunk.length;
|
|
4807
4995
|
const appended = append(stderrDecoder, chunk, stderrBytes);
|
|
4808
4996
|
stderr += appended.text;
|
|
4809
4997
|
stderrBytes = appended.usedBytes;
|
|
@@ -4834,13 +5022,17 @@ function runProcess(command2, args, options) {
|
|
|
4834
5022
|
reject(callbackError);
|
|
4835
5023
|
return;
|
|
4836
5024
|
}
|
|
4837
|
-
|
|
5025
|
+
resolve24({
|
|
4838
5026
|
command: [command2, ...args].join(" "),
|
|
4839
5027
|
exitCode: code ?? (timedOut ? 124 : 1),
|
|
4840
5028
|
stdout,
|
|
4841
5029
|
stderr,
|
|
4842
5030
|
timedOut,
|
|
4843
|
-
durationMs: Date.now() - started
|
|
5031
|
+
durationMs: Date.now() - started,
|
|
5032
|
+
stdoutBytes: stdoutObservedBytes,
|
|
5033
|
+
stderrBytes: stderrObservedBytes,
|
|
5034
|
+
stdoutTruncated: stdoutObservedBytes > stdoutBytes,
|
|
5035
|
+
stderrTruncated: stderrObservedBytes > stderrBytes
|
|
4844
5036
|
});
|
|
4845
5037
|
});
|
|
4846
5038
|
if (options.stdin) child.stdin.end(options.stdin);
|
|
@@ -4961,6 +5153,14 @@ var contextSourceSchema = z5.object({
|
|
|
4961
5153
|
tokens: z5.number().int().nonnegative(),
|
|
4962
5154
|
addedAt: z5.string()
|
|
4963
5155
|
}).strict();
|
|
5156
|
+
var toolArtifactSchema = z5.object({
|
|
5157
|
+
toolCallId: z5.string().min(1).max(512).refine((value) => !/[\u0000-\u001f\u007f-\u009f]/u.test(value)),
|
|
5158
|
+
sha256: z5.string().regex(/^[a-f0-9]{64}$/u),
|
|
5159
|
+
bytes: z5.number().int().nonnegative().max(5 * 1024 * 1024),
|
|
5160
|
+
createdAt: z5.string().datetime(),
|
|
5161
|
+
expiresAt: z5.string().datetime(),
|
|
5162
|
+
redacted: z5.boolean()
|
|
5163
|
+
}).strict();
|
|
4964
5164
|
var verificationEvidenceSchema = z5.object({
|
|
4965
5165
|
toolCallId: z5.string(),
|
|
4966
5166
|
tool: z5.enum(["shell", "git"]),
|
|
@@ -5037,12 +5237,59 @@ var sessionSchema = z5.object({
|
|
|
5037
5237
|
compactedThroughMessageId: z5.string().optional(),
|
|
5038
5238
|
workingMemory: workingMemorySchema.optional(),
|
|
5039
5239
|
contextSources: z5.array(contextSourceSchema).max(64).optional(),
|
|
5240
|
+
toolArtifacts: z5.array(toolArtifactSchema).max(200).optional(),
|
|
5040
5241
|
taskContract: taskContractSchema.optional(),
|
|
5041
5242
|
lastRun: lastRunSchema.optional(),
|
|
5042
5243
|
usage: z5.object({
|
|
5043
5244
|
inputTokens: z5.number().nonnegative(),
|
|
5044
|
-
outputTokens: z5.number().nonnegative()
|
|
5045
|
-
|
|
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()
|
|
5046
5293
|
}).strict();
|
|
5047
5294
|
var SessionStore = class {
|
|
5048
5295
|
workspace;
|
|
@@ -5298,13 +5545,390 @@ async function exists2(path) {
|
|
|
5298
5545
|
}
|
|
5299
5546
|
}
|
|
5300
5547
|
|
|
5548
|
+
// src/session/tool-artifacts.ts
|
|
5549
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
5550
|
+
import { lstat as lstat12, readFile as readFile7, readdir as readdir4, rm as rm2 } from "node:fs/promises";
|
|
5551
|
+
import { join as join11, resolve as resolve13 } from "node:path";
|
|
5552
|
+
import { z as z6 } from "zod";
|
|
5553
|
+
var MAX_ARTIFACT_BYTES = 5 * 1024 * 1024;
|
|
5554
|
+
var MAX_TOTAL_BYTES = 20 * 1024 * 1024;
|
|
5555
|
+
var RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
5556
|
+
var sessionIdSchema2 = z6.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/);
|
|
5557
|
+
var toolCallIdSchema = z6.string().min(1).max(512).refine((value) => !/[\u0000-\u001f\u007f-\u009f]/u.test(value), {
|
|
5558
|
+
message: "Tool call id cannot contain control characters."
|
|
5559
|
+
});
|
|
5560
|
+
var artifactSchema = z6.object({
|
|
5561
|
+
version: z6.literal(1),
|
|
5562
|
+
sessionId: sessionIdSchema2,
|
|
5563
|
+
toolCallId: toolCallIdSchema,
|
|
5564
|
+
sha256: z6.string().regex(/^[a-f0-9]{64}$/u),
|
|
5565
|
+
bytes: z6.number().int().nonnegative().max(MAX_ARTIFACT_BYTES),
|
|
5566
|
+
createdAt: z6.string().datetime(),
|
|
5567
|
+
expiresAt: z6.string().datetime(),
|
|
5568
|
+
redacted: z6.boolean(),
|
|
5569
|
+
content: z6.string()
|
|
5570
|
+
}).strict();
|
|
5571
|
+
var ToolArtifactStore = class {
|
|
5572
|
+
workspace;
|
|
5573
|
+
directory;
|
|
5574
|
+
managedDirectory;
|
|
5575
|
+
now;
|
|
5576
|
+
maxArtifactBytes;
|
|
5577
|
+
maxTotalBytes;
|
|
5578
|
+
retentionMs;
|
|
5579
|
+
writes = Promise.resolve();
|
|
5580
|
+
constructor(workspace, options = {}) {
|
|
5581
|
+
this.workspace = resolve13(workspace);
|
|
5582
|
+
this.managedDirectory = options.directory === void 0;
|
|
5583
|
+
this.directory = options.directory ? resolve13(options.directory) : join11(resolveProjectNamespaceSync(this.workspace).active, "tool-artifacts");
|
|
5584
|
+
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
5585
|
+
this.maxArtifactBytes = options.maxArtifactBytes ?? MAX_ARTIFACT_BYTES;
|
|
5586
|
+
this.maxTotalBytes = options.maxTotalBytes ?? MAX_TOTAL_BYTES;
|
|
5587
|
+
this.retentionMs = options.retentionMs ?? RETENTION_MS;
|
|
5588
|
+
if (!Number.isSafeInteger(this.maxArtifactBytes) || this.maxArtifactBytes < 1 || this.maxArtifactBytes > MAX_ARTIFACT_BYTES) {
|
|
5589
|
+
throw new Error(`maxArtifactBytes must be between 1 and ${MAX_ARTIFACT_BYTES}.`);
|
|
5590
|
+
}
|
|
5591
|
+
if (!Number.isSafeInteger(this.maxTotalBytes) || this.maxTotalBytes < this.maxArtifactBytes) {
|
|
5592
|
+
throw new Error("maxTotalBytes must be at least maxArtifactBytes.");
|
|
5593
|
+
}
|
|
5594
|
+
if (!Number.isSafeInteger(this.retentionMs) || this.retentionMs < 1) {
|
|
5595
|
+
throw new Error("retentionMs must be a positive integer.");
|
|
5596
|
+
}
|
|
5597
|
+
}
|
|
5598
|
+
async archive(sessionId, toolCallId, content, options) {
|
|
5599
|
+
sessionIdSchema2.parse(sessionId);
|
|
5600
|
+
toolCallIdSchema.parse(toolCallId);
|
|
5601
|
+
const bytes = Buffer.byteLength(content);
|
|
5602
|
+
if (bytes > this.maxArtifactBytes) return { stored: false, reason: "too_large" };
|
|
5603
|
+
return this.enqueue(() => this.withManagedLease(async () => {
|
|
5604
|
+
await this.ensureDirectory();
|
|
5605
|
+
const now = this.now();
|
|
5606
|
+
const artifacts = await this.loadArtifacts();
|
|
5607
|
+
await this.removeExpired(artifacts, now);
|
|
5608
|
+
const active = artifacts.filter((artifact2) => artifact2.expiresAt > now.toISOString());
|
|
5609
|
+
const sha256 = hash(content);
|
|
5610
|
+
const existing = active.find((artifact2) => artifact2.sessionId === sessionId && artifact2.toolCallId === toolCallId);
|
|
5611
|
+
if (existing) {
|
|
5612
|
+
if (existing.sha256 === sha256 && existing.bytes === bytes && existing.redacted === options.redacted) {
|
|
5613
|
+
return { stored: true, artifact: reference(existing) };
|
|
5614
|
+
}
|
|
5615
|
+
return { stored: false, reason: "conflict" };
|
|
5616
|
+
}
|
|
5617
|
+
const artifact = {
|
|
5618
|
+
version: 1,
|
|
5619
|
+
sessionId,
|
|
5620
|
+
toolCallId,
|
|
5621
|
+
sha256,
|
|
5622
|
+
bytes,
|
|
5623
|
+
createdAt: now.toISOString(),
|
|
5624
|
+
expiresAt: new Date(now.getTime() + this.retentionMs).toISOString(),
|
|
5625
|
+
redacted: options.redacted,
|
|
5626
|
+
content
|
|
5627
|
+
};
|
|
5628
|
+
const storageBytes = storedBytes(artifact);
|
|
5629
|
+
let retainedBytes = active.reduce((total, retained) => total + storedBytes(retained), 0);
|
|
5630
|
+
const evictable = active.slice().sort(
|
|
5631
|
+
(left, right) => left.createdAt.localeCompare(right.createdAt)
|
|
5632
|
+
);
|
|
5633
|
+
while (retainedBytes + storageBytes > this.maxTotalBytes && evictable.length) {
|
|
5634
|
+
const oldest = evictable.shift();
|
|
5635
|
+
await this.removeArtifact(oldest);
|
|
5636
|
+
retainedBytes -= storedBytes(oldest);
|
|
5637
|
+
}
|
|
5638
|
+
if (retainedBytes + storageBytes > this.maxTotalBytes) return { stored: false, reason: "total_limit" };
|
|
5639
|
+
const path = this.pathFor(sessionId, toolCallId);
|
|
5640
|
+
await ensureWorkspaceStorageDirectory(this.workspace, resolve13(path, ".."), {
|
|
5641
|
+
requireActiveNamespace: this.managedDirectory
|
|
5642
|
+
});
|
|
5643
|
+
await atomicWrite(path, `${JSON.stringify(artifact)}
|
|
5644
|
+
`, 384);
|
|
5645
|
+
return { stored: true, artifact: reference(artifact) };
|
|
5646
|
+
}));
|
|
5647
|
+
}
|
|
5648
|
+
async read(sessionId, toolCallId, options = {}) {
|
|
5649
|
+
sessionIdSchema2.parse(sessionId);
|
|
5650
|
+
toolCallIdSchema.parse(toolCallId);
|
|
5651
|
+
const startLine = options.startLine ?? 1;
|
|
5652
|
+
const startByte = options.startByte;
|
|
5653
|
+
const maxLines = options.maxLines ?? 200;
|
|
5654
|
+
const maxBytes = options.maxBytes ?? 3e3;
|
|
5655
|
+
if (startByte !== void 0 && options.startLine !== void 0) {
|
|
5656
|
+
throw new Error("Use either startLine or startByte, not both.");
|
|
5657
|
+
}
|
|
5658
|
+
if (!Number.isInteger(startLine) || startLine < 1) throw new Error("startLine must be a positive integer.");
|
|
5659
|
+
if (startByte !== void 0 && (!Number.isInteger(startByte) || startByte < 0)) {
|
|
5660
|
+
throw new Error("startByte must be a non-negative integer.");
|
|
5661
|
+
}
|
|
5662
|
+
if (!Number.isInteger(maxLines) || maxLines < 1 || maxLines > 1e3) {
|
|
5663
|
+
throw new Error("maxLines must be between 1 and 1000.");
|
|
5664
|
+
}
|
|
5665
|
+
if (!Number.isInteger(maxBytes) || maxBytes < 256 || maxBytes > 32e3) {
|
|
5666
|
+
throw new Error("maxBytes must be between 256 and 32000.");
|
|
5667
|
+
}
|
|
5668
|
+
await this.writes;
|
|
5669
|
+
return this.withManagedLease(async () => {
|
|
5670
|
+
const artifact = await this.readArtifact(sessionId, toolCallId);
|
|
5671
|
+
const now = this.now();
|
|
5672
|
+
if (artifact.expiresAt <= now.toISOString()) {
|
|
5673
|
+
await this.removeArtifact(artifact);
|
|
5674
|
+
throw new Error("The retained tool output has expired. Re-run the tool if it is still needed.");
|
|
5675
|
+
}
|
|
5676
|
+
const page = startByte === void 0 ? readLinePage(artifact.content, startLine, maxLines, maxBytes) : readBytePage(artifact.content, startByte, maxBytes);
|
|
5677
|
+
return {
|
|
5678
|
+
...reference(artifact),
|
|
5679
|
+
...page
|
|
5680
|
+
};
|
|
5681
|
+
});
|
|
5682
|
+
}
|
|
5683
|
+
/** Remove expired output and return the still-valid receipts for one session. */
|
|
5684
|
+
async prune(sessionId) {
|
|
5685
|
+
sessionIdSchema2.parse(sessionId);
|
|
5686
|
+
return this.enqueue(() => this.withManagedLease(async () => {
|
|
5687
|
+
const artifacts = await this.loadArtifacts();
|
|
5688
|
+
const now = this.now();
|
|
5689
|
+
await this.removeExpired(artifacts, now);
|
|
5690
|
+
return artifacts.filter((artifact) => artifact.sessionId === sessionId && artifact.expiresAt > now.toISOString()).sort((left, right) => left.createdAt.localeCompare(right.createdAt)).map(reference);
|
|
5691
|
+
}));
|
|
5692
|
+
}
|
|
5693
|
+
/** Delete every retained result owned by a removed session. */
|
|
5694
|
+
async removeSession(sessionId) {
|
|
5695
|
+
sessionIdSchema2.parse(sessionId);
|
|
5696
|
+
await this.enqueue(() => this.withManagedLease(async () => {
|
|
5697
|
+
if (!await this.directoryAvailable()) return;
|
|
5698
|
+
const directory = this.sessionDirectoryFor(sessionId);
|
|
5699
|
+
try {
|
|
5700
|
+
const info = await lstat12(directory);
|
|
5701
|
+
if (info.isSymbolicLink() || !info.isDirectory()) {
|
|
5702
|
+
throw new Error(`Tool artifact session storage is not a regular directory: ${directory}`);
|
|
5703
|
+
}
|
|
5704
|
+
await assertNoSymlinkPath(this.workspace, directory);
|
|
5705
|
+
} catch (error) {
|
|
5706
|
+
if (error.code === "ENOENT") return;
|
|
5707
|
+
throw error;
|
|
5708
|
+
}
|
|
5709
|
+
await rm2(directory, { recursive: true, force: true });
|
|
5710
|
+
}));
|
|
5711
|
+
}
|
|
5712
|
+
async readArtifact(sessionId, toolCallId) {
|
|
5713
|
+
const path = this.pathFor(sessionId, toolCallId);
|
|
5714
|
+
await this.assertRegularFile(path);
|
|
5715
|
+
let artifact;
|
|
5716
|
+
try {
|
|
5717
|
+
artifact = artifactSchema.parse(JSON.parse(await readFile7(path, "utf8")));
|
|
5718
|
+
} catch {
|
|
5719
|
+
throw new Error("Retained tool output is unreadable or corrupt.");
|
|
5720
|
+
}
|
|
5721
|
+
if (artifact.sessionId !== sessionId || artifact.toolCallId !== toolCallId) {
|
|
5722
|
+
throw new Error("Retained tool output does not belong to this session.");
|
|
5723
|
+
}
|
|
5724
|
+
if (artifact.bytes !== Buffer.byteLength(artifact.content) || artifact.sha256 !== hash(artifact.content)) {
|
|
5725
|
+
throw new Error("Retained tool output failed its integrity check.");
|
|
5726
|
+
}
|
|
5727
|
+
return artifact;
|
|
5728
|
+
}
|
|
5729
|
+
async loadArtifacts() {
|
|
5730
|
+
if (!await this.directoryAvailable()) return [];
|
|
5731
|
+
const artifacts = [];
|
|
5732
|
+
for (const entry of await readdir4(this.directory, { withFileTypes: true })) {
|
|
5733
|
+
const sessionDirectory = join11(this.directory, entry.name);
|
|
5734
|
+
if (entry.isSymbolicLink()) throw new Error(`Tool artifact storage contains a symbolic link: ${sessionDirectory}`);
|
|
5735
|
+
if (!entry.isDirectory() || !/^[a-f0-9]{64}$/u.test(entry.name)) continue;
|
|
5736
|
+
await assertNoSymlinkPath(this.workspace, sessionDirectory);
|
|
5737
|
+
for (const file of await readdir4(sessionDirectory, { withFileTypes: true })) {
|
|
5738
|
+
const path = join11(sessionDirectory, file.name);
|
|
5739
|
+
if (file.isSymbolicLink()) throw new Error(`Tool artifact storage contains a symbolic link: ${path}`);
|
|
5740
|
+
if (!file.isFile() || !/^[a-f0-9]{64}\.json$/u.test(file.name)) continue;
|
|
5741
|
+
await this.assertRegularFile(path);
|
|
5742
|
+
try {
|
|
5743
|
+
const artifact = artifactSchema.parse(JSON.parse(await readFile7(path, "utf8")));
|
|
5744
|
+
if (artifact.bytes !== Buffer.byteLength(artifact.content) || artifact.sha256 !== hash(artifact.content)) {
|
|
5745
|
+
throw new Error("integrity");
|
|
5746
|
+
}
|
|
5747
|
+
if (sessionDirectory !== this.sessionDirectoryFor(artifact.sessionId) || path !== this.pathFor(artifact.sessionId, artifact.toolCallId)) {
|
|
5748
|
+
throw new Error("identity");
|
|
5749
|
+
}
|
|
5750
|
+
artifacts.push(artifact);
|
|
5751
|
+
} catch {
|
|
5752
|
+
throw new Error(`Tool artifact is unreadable or corrupt: ${path}`);
|
|
5753
|
+
}
|
|
5754
|
+
}
|
|
5755
|
+
}
|
|
5756
|
+
return artifacts;
|
|
5757
|
+
}
|
|
5758
|
+
async removeExpired(artifacts, now) {
|
|
5759
|
+
for (const artifact of artifacts) {
|
|
5760
|
+
if (artifact.expiresAt <= now.toISOString()) await this.removeArtifact(artifact);
|
|
5761
|
+
}
|
|
5762
|
+
}
|
|
5763
|
+
async removeArtifact(artifact) {
|
|
5764
|
+
await rm2(this.pathFor(artifact.sessionId, artifact.toolCallId), { force: true });
|
|
5765
|
+
}
|
|
5766
|
+
pathFor(sessionId, toolCallId) {
|
|
5767
|
+
return join11(this.sessionDirectoryFor(sessionId), `${hash(`call\0${toolCallId}`)}.json`);
|
|
5768
|
+
}
|
|
5769
|
+
sessionDirectoryFor(sessionId) {
|
|
5770
|
+
return join11(this.directory, hash(`session\0${sessionId}`));
|
|
5771
|
+
}
|
|
5772
|
+
async ensureDirectory() {
|
|
5773
|
+
await ensureWorkspaceStorageDirectory(this.workspace, this.directory, {
|
|
5774
|
+
requireActiveNamespace: this.managedDirectory
|
|
5775
|
+
});
|
|
5776
|
+
}
|
|
5777
|
+
async directoryAvailable() {
|
|
5778
|
+
if (this.managedDirectory) await assertNoSymlinkPath(this.workspace, this.directory);
|
|
5779
|
+
try {
|
|
5780
|
+
const info = await lstat12(this.directory);
|
|
5781
|
+
if (info.isSymbolicLink() || !info.isDirectory()) {
|
|
5782
|
+
throw new Error(`Tool artifact storage is not a regular directory: ${this.directory}`);
|
|
5783
|
+
}
|
|
5784
|
+
return true;
|
|
5785
|
+
} catch (error) {
|
|
5786
|
+
if (error.code === "ENOENT") return false;
|
|
5787
|
+
throw error;
|
|
5788
|
+
}
|
|
5789
|
+
}
|
|
5790
|
+
async assertRegularFile(path) {
|
|
5791
|
+
await assertNoSymlinkPath(this.workspace, resolve13(path, ".."));
|
|
5792
|
+
try {
|
|
5793
|
+
const info = await lstat12(path);
|
|
5794
|
+
if (info.isSymbolicLink() || !info.isFile()) {
|
|
5795
|
+
throw new Error(`Tool artifact cannot be a symbolic link or non-regular file: ${path}`);
|
|
5796
|
+
}
|
|
5797
|
+
} catch (error) {
|
|
5798
|
+
if (error.code === "ENOENT") {
|
|
5799
|
+
throw new Error("No retained tool output matches this tool call in the current session.");
|
|
5800
|
+
}
|
|
5801
|
+
throw error;
|
|
5802
|
+
}
|
|
5803
|
+
}
|
|
5804
|
+
async withManagedLease(operation) {
|
|
5805
|
+
if (!this.managedDirectory) return operation();
|
|
5806
|
+
return withNamespaceLease(projectNamespacePaths(this.workspace).canonical, "shared", async () => {
|
|
5807
|
+
assertActiveProjectNamespacePath(this.workspace, this.directory);
|
|
5808
|
+
return operation();
|
|
5809
|
+
});
|
|
5810
|
+
}
|
|
5811
|
+
async enqueue(operation) {
|
|
5812
|
+
const next = this.writes.then(operation);
|
|
5813
|
+
this.writes = next.then(() => void 0, () => void 0);
|
|
5814
|
+
return next;
|
|
5815
|
+
}
|
|
5816
|
+
};
|
|
5817
|
+
function reference(artifact) {
|
|
5818
|
+
return {
|
|
5819
|
+
toolCallId: artifact.toolCallId,
|
|
5820
|
+
sha256: artifact.sha256,
|
|
5821
|
+
bytes: artifact.bytes,
|
|
5822
|
+
createdAt: artifact.createdAt,
|
|
5823
|
+
expiresAt: artifact.expiresAt,
|
|
5824
|
+
redacted: artifact.redacted
|
|
5825
|
+
};
|
|
5826
|
+
}
|
|
5827
|
+
function hash(value) {
|
|
5828
|
+
return createHash7("sha256").update(value).digest("hex");
|
|
5829
|
+
}
|
|
5830
|
+
function storedBytes(artifact) {
|
|
5831
|
+
return Buffer.byteLength(JSON.stringify(artifact)) + 1;
|
|
5832
|
+
}
|
|
5833
|
+
function readLinePage(content, startLine, maxLines, maxBytes) {
|
|
5834
|
+
const ranges = lineRanges(content);
|
|
5835
|
+
const startIndex = startLine - 1;
|
|
5836
|
+
if (startIndex >= ranges.length) {
|
|
5837
|
+
const totalBytes = Buffer.byteLength(content);
|
|
5838
|
+
return {
|
|
5839
|
+
content: "",
|
|
5840
|
+
startLine,
|
|
5841
|
+
endLine: startLine - 1,
|
|
5842
|
+
totalLines: ranges.length,
|
|
5843
|
+
startByte: totalBytes,
|
|
5844
|
+
endByte: totalBytes,
|
|
5845
|
+
hasMore: false
|
|
5846
|
+
};
|
|
5847
|
+
}
|
|
5848
|
+
const endIndex = Math.min(ranges.length - 1, startIndex + maxLines - 1);
|
|
5849
|
+
const startCharacter = ranges[startIndex]?.start ?? content.length;
|
|
5850
|
+
const endCharacter = ranges[endIndex]?.end ?? content.length;
|
|
5851
|
+
const startByte = Buffer.byteLength(content.slice(0, startCharacter));
|
|
5852
|
+
const selected = Buffer.from(content.slice(startCharacter, endCharacter));
|
|
5853
|
+
if (selected.length > maxBytes) {
|
|
5854
|
+
const page = sliceUtf8Buffer(selected, 0, maxBytes);
|
|
5855
|
+
const pageContent = page.content;
|
|
5856
|
+
return {
|
|
5857
|
+
content: pageContent,
|
|
5858
|
+
startLine,
|
|
5859
|
+
endLine: startLine + countNewlines(pageContent),
|
|
5860
|
+
totalLines: ranges.length,
|
|
5861
|
+
startByte,
|
|
5862
|
+
endByte: startByte + page.bytes,
|
|
5863
|
+
hasMore: true,
|
|
5864
|
+
nextStartByte: startByte + page.bytes
|
|
5865
|
+
};
|
|
5866
|
+
}
|
|
5867
|
+
const endLine = endIndex + 1;
|
|
5868
|
+
const hasMore = endLine < ranges.length;
|
|
5869
|
+
return {
|
|
5870
|
+
content: selected.toString("utf8"),
|
|
5871
|
+
startLine,
|
|
5872
|
+
endLine,
|
|
5873
|
+
totalLines: ranges.length,
|
|
5874
|
+
startByte,
|
|
5875
|
+
endByte: startByte + selected.length,
|
|
5876
|
+
hasMore,
|
|
5877
|
+
...hasMore ? { nextStartLine: endLine + 1 } : {}
|
|
5878
|
+
};
|
|
5879
|
+
}
|
|
5880
|
+
function readBytePage(content, requestedStart, maxBytes) {
|
|
5881
|
+
const buffer = Buffer.from(content);
|
|
5882
|
+
const startByte = Math.min(requestedStart, buffer.length);
|
|
5883
|
+
if (startByte < buffer.length && isUtf8Continuation(buffer[startByte] ?? 0)) {
|
|
5884
|
+
throw new Error("startByte must be an exact UTF-8 boundary shown by a previous page.");
|
|
5885
|
+
}
|
|
5886
|
+
const page = sliceUtf8Buffer(buffer, startByte, maxBytes);
|
|
5887
|
+
const prefix = buffer.subarray(0, startByte).toString("utf8");
|
|
5888
|
+
const startLine = countNewlines(prefix) + 1;
|
|
5889
|
+
const endByte = startByte + page.bytes;
|
|
5890
|
+
const hasMore = endByte < buffer.length;
|
|
5891
|
+
return {
|
|
5892
|
+
content: page.content,
|
|
5893
|
+
startLine,
|
|
5894
|
+
endLine: startLine + countNewlines(page.content),
|
|
5895
|
+
totalLines: countNewlines(content) + 1,
|
|
5896
|
+
startByte,
|
|
5897
|
+
endByte,
|
|
5898
|
+
hasMore,
|
|
5899
|
+
...hasMore ? { nextStartByte: endByte } : {}
|
|
5900
|
+
};
|
|
5901
|
+
}
|
|
5902
|
+
function sliceUtf8Buffer(buffer, start, maxBytes) {
|
|
5903
|
+
let end = Math.min(buffer.length, start + maxBytes);
|
|
5904
|
+
while (end > start && isUtf8Continuation(buffer[end] ?? 0)) end -= 1;
|
|
5905
|
+
return { content: buffer.subarray(start, end).toString("utf8"), bytes: end - start };
|
|
5906
|
+
}
|
|
5907
|
+
function lineRanges(content) {
|
|
5908
|
+
const ranges = [];
|
|
5909
|
+
const expression = /\r?\n/gu;
|
|
5910
|
+
let start = 0;
|
|
5911
|
+
for (const match of content.matchAll(expression)) {
|
|
5912
|
+
ranges.push({ start, end: match.index });
|
|
5913
|
+
start = match.index + match[0].length;
|
|
5914
|
+
}
|
|
5915
|
+
ranges.push({ start, end: content.length });
|
|
5916
|
+
return ranges;
|
|
5917
|
+
}
|
|
5918
|
+
function countNewlines(value) {
|
|
5919
|
+
return value.match(/\n/gu)?.length ?? 0;
|
|
5920
|
+
}
|
|
5921
|
+
function isUtf8Continuation(byte) {
|
|
5922
|
+
return byte >= 128 && byte < 192;
|
|
5923
|
+
}
|
|
5924
|
+
|
|
5301
5925
|
// src/tools/apply-patch.ts
|
|
5302
|
-
import { lstat as
|
|
5926
|
+
import { lstat as lstat13, readFile as readFile8, unlink as unlink4 } from "node:fs/promises";
|
|
5303
5927
|
import { applyPatch as applyUnifiedPatch, parsePatch } from "diff";
|
|
5304
|
-
import { z as
|
|
5305
|
-
var inputSchema2 =
|
|
5306
|
-
patch:
|
|
5307
|
-
dry_run:
|
|
5928
|
+
import { z as z7 } from "zod";
|
|
5929
|
+
var inputSchema2 = z7.object({
|
|
5930
|
+
patch: z7.string().min(1).max(1e7),
|
|
5931
|
+
dry_run: z7.boolean().optional()
|
|
5308
5932
|
}).strict();
|
|
5309
5933
|
var applyPatchTool = {
|
|
5310
5934
|
definition: {
|
|
@@ -5560,10 +6184,10 @@ function findSequence(haystack, needle, hint, minimum) {
|
|
|
5560
6184
|
}
|
|
5561
6185
|
async function readSnapshot2(path) {
|
|
5562
6186
|
try {
|
|
5563
|
-
const info = await
|
|
6187
|
+
const info = await lstat13(path);
|
|
5564
6188
|
if (info.isSymbolicLink()) throw new Error(`Refusing to patch a symbolic link: ${path}`);
|
|
5565
6189
|
if (!info.isFile()) throw new Error(`Patch target is not a regular file: ${path}`);
|
|
5566
|
-
return { before: await
|
|
6190
|
+
return { before: await readFile8(path), mode: info.mode };
|
|
5567
6191
|
} catch (error) {
|
|
5568
6192
|
if (error.code === "ENOENT") return { before: null };
|
|
5569
6193
|
throw error;
|
|
@@ -5612,13 +6236,13 @@ function buffersEqual(left, right) {
|
|
|
5612
6236
|
}
|
|
5613
6237
|
|
|
5614
6238
|
// src/tools/git.ts
|
|
5615
|
-
import { join as
|
|
5616
|
-
import { z as
|
|
5617
|
-
var inputSchema3 =
|
|
5618
|
-
args:
|
|
5619
|
-
cwd:
|
|
5620
|
-
timeout_ms:
|
|
5621
|
-
stdin:
|
|
6239
|
+
import { join as join12 } from "node:path";
|
|
6240
|
+
import { z as z8 } from "zod";
|
|
6241
|
+
var inputSchema3 = z8.object({
|
|
6242
|
+
args: z8.array(z8.string().max(1e4)).min(1).max(200),
|
|
6243
|
+
cwd: z8.string().min(1).optional(),
|
|
6244
|
+
timeout_ms: z8.number().int().min(100).max(6e5).optional(),
|
|
6245
|
+
stdin: z8.string().max(5e6).optional()
|
|
5622
6246
|
}).strict();
|
|
5623
6247
|
var networkCommands = /* @__PURE__ */ new Set([
|
|
5624
6248
|
"clone",
|
|
@@ -5831,7 +6455,7 @@ var gitTool = {
|
|
|
5831
6455
|
for (const candidate of explicit) {
|
|
5832
6456
|
if (!candidate || candidate.startsWith("-")) continue;
|
|
5833
6457
|
try {
|
|
5834
|
-
paths.add(await context.workspace.resolvePath(
|
|
6458
|
+
paths.add(await context.workspace.resolvePath(join12(cwd, candidate), { allowMissing: true }));
|
|
5835
6459
|
} catch {
|
|
5836
6460
|
}
|
|
5837
6461
|
}
|
|
@@ -5852,7 +6476,7 @@ var gitTool = {
|
|
|
5852
6476
|
const candidates = candidate.includes(" -> ") ? candidate.split(" -> ") : [candidate];
|
|
5853
6477
|
for (const value of candidates) {
|
|
5854
6478
|
try {
|
|
5855
|
-
paths.add(await context.workspace.resolvePath(
|
|
6479
|
+
paths.add(await context.workspace.resolvePath(join12(cwd, value), { allowMissing: true }));
|
|
5856
6480
|
} catch {
|
|
5857
6481
|
}
|
|
5858
6482
|
}
|
|
@@ -5933,7 +6557,7 @@ async function collectGitChanges(before, after, runtime, cwd, context) {
|
|
|
5933
6557
|
const paths = [];
|
|
5934
6558
|
for (const candidate of [...candidates].slice(0, 2e3)) {
|
|
5935
6559
|
try {
|
|
5936
|
-
paths.push(await context.workspace.resolvePath(
|
|
6560
|
+
paths.push(await context.workspace.resolvePath(join12(cwd, candidate), { allowMissing: true }));
|
|
5937
6561
|
} catch {
|
|
5938
6562
|
}
|
|
5939
6563
|
}
|
|
@@ -6049,7 +6673,7 @@ async function validateGitWorkspaceArguments(args, command2, cwd, context) {
|
|
|
6049
6673
|
if (/^(?:[a-z]+:\/\/|[a-z]+@[^:]+:)/i.test(candidate)) continue;
|
|
6050
6674
|
if (command2 === "clone" && index === 0 && !candidate) continue;
|
|
6051
6675
|
await context.workspace.resolvePath(
|
|
6052
|
-
candidate.startsWith("/") ? candidate :
|
|
6676
|
+
candidate.startsWith("/") ? candidate : join12(cwd, candidate),
|
|
6053
6677
|
{ allowMissing: true }
|
|
6054
6678
|
);
|
|
6055
6679
|
}
|
|
@@ -6102,17 +6726,17 @@ function positionalArguments(args, command2) {
|
|
|
6102
6726
|
}
|
|
6103
6727
|
|
|
6104
6728
|
// src/tools/list.ts
|
|
6105
|
-
import { lstat as
|
|
6106
|
-
import { relative as relative6, resolve as
|
|
6729
|
+
import { lstat as lstat14 } from "node:fs/promises";
|
|
6730
|
+
import { relative as relative6, resolve as resolve14 } from "node:path";
|
|
6107
6731
|
import fg3 from "fast-glob";
|
|
6108
|
-
import { z as
|
|
6109
|
-
var inputSchema4 =
|
|
6110
|
-
path:
|
|
6111
|
-
pattern:
|
|
6112
|
-
depth:
|
|
6113
|
-
include_hidden:
|
|
6114
|
-
include_directories:
|
|
6115
|
-
limit:
|
|
6732
|
+
import { z as z9 } from "zod";
|
|
6733
|
+
var inputSchema4 = z9.object({
|
|
6734
|
+
path: z9.string().min(1).optional(),
|
|
6735
|
+
pattern: z9.string().min(1).optional(),
|
|
6736
|
+
depth: z9.number().int().min(1).max(20).optional(),
|
|
6737
|
+
include_hidden: z9.boolean().optional(),
|
|
6738
|
+
include_directories: z9.boolean().optional(),
|
|
6739
|
+
limit: z9.number().int().min(1).max(5e3).optional()
|
|
6116
6740
|
}).strict();
|
|
6117
6741
|
var ignored = [
|
|
6118
6742
|
"**/.git/**",
|
|
@@ -6161,11 +6785,11 @@ var listFilesTool = {
|
|
|
6161
6785
|
for (const path of selected) {
|
|
6162
6786
|
let safePath;
|
|
6163
6787
|
try {
|
|
6164
|
-
safePath = await context.workspace.resolvePath(
|
|
6788
|
+
safePath = await context.workspace.resolvePath(resolve14(directory, path), { expect: "any" });
|
|
6165
6789
|
} catch {
|
|
6166
6790
|
continue;
|
|
6167
6791
|
}
|
|
6168
|
-
const info = await
|
|
6792
|
+
const info = await lstat14(safePath);
|
|
6169
6793
|
rendered.push(`${info.isDirectory() ? "d" : "f"} ${relative6(directory, safePath)}${info.isDirectory() ? "/" : ""}`);
|
|
6170
6794
|
}
|
|
6171
6795
|
const base = workspaceAliasPath(directory, context.workspace.roots);
|
|
@@ -6182,14 +6806,14 @@ var listFilesTool = {
|
|
|
6182
6806
|
};
|
|
6183
6807
|
|
|
6184
6808
|
// src/tools/read.ts
|
|
6185
|
-
import { readFile as
|
|
6186
|
-
import { z as
|
|
6187
|
-
var inputSchema5 =
|
|
6188
|
-
path:
|
|
6189
|
-
start_line:
|
|
6190
|
-
end_line:
|
|
6191
|
-
line_numbers:
|
|
6192
|
-
max_bytes:
|
|
6809
|
+
import { readFile as readFile9, stat as stat7 } from "node:fs/promises";
|
|
6810
|
+
import { z as z10 } from "zod";
|
|
6811
|
+
var inputSchema5 = z10.object({
|
|
6812
|
+
path: z10.string().min(1),
|
|
6813
|
+
start_line: z10.number().int().positive().optional(),
|
|
6814
|
+
end_line: z10.number().int().positive().optional(),
|
|
6815
|
+
line_numbers: z10.boolean().optional(),
|
|
6816
|
+
max_bytes: z10.number().int().positive().max(1e6).optional()
|
|
6193
6817
|
}).strict();
|
|
6194
6818
|
var readFileTool = {
|
|
6195
6819
|
definition: {
|
|
@@ -6212,7 +6836,7 @@ var readFileTool = {
|
|
|
6212
6836
|
if (info.size > 1e7) {
|
|
6213
6837
|
throw new Error(`File is too large to read safely (${info.size} bytes).`);
|
|
6214
6838
|
}
|
|
6215
|
-
const buffer = await
|
|
6839
|
+
const buffer = await readFile9(path);
|
|
6216
6840
|
if (looksBinary(buffer)) throw new Error("Binary files cannot be read with read_file.");
|
|
6217
6841
|
const raw = buffer.toString("utf8");
|
|
6218
6842
|
const lines = raw.split("\n");
|
|
@@ -6256,6 +6880,73 @@ function truncateUtf8(input2, maxBytes) {
|
|
|
6256
6880
|
return buffer.subarray(0, end).toString("utf8");
|
|
6257
6881
|
}
|
|
6258
6882
|
|
|
6883
|
+
// src/tools/read-artifact.ts
|
|
6884
|
+
import { z as z11 } from "zod";
|
|
6885
|
+
var inputSchema6 = z11.object({
|
|
6886
|
+
sha256: z11.string().regex(/^[a-f0-9]{64}$/u),
|
|
6887
|
+
start_line: z11.number().int().positive().optional(),
|
|
6888
|
+
start_byte: z11.number().int().nonnegative().optional(),
|
|
6889
|
+
max_lines: z11.number().int().min(1).max(1e3).optional(),
|
|
6890
|
+
max_bytes: z11.number().int().min(256).max(768).optional()
|
|
6891
|
+
}).strict().refine((input2) => input2.start_line === void 0 || input2.start_byte === void 0, {
|
|
6892
|
+
message: "Use either start_line or start_byte, not both."
|
|
6893
|
+
});
|
|
6894
|
+
var readToolArtifactTool = {
|
|
6895
|
+
definition: {
|
|
6896
|
+
name: "read_tool_artifact",
|
|
6897
|
+
description: "Read a bounded page of oversized tool output retained for this session. Use only the SHA-256 shown in a tool-output receipt.",
|
|
6898
|
+
category: "read",
|
|
6899
|
+
inputSchema: jsonSchema({
|
|
6900
|
+
sha256: { type: "string", description: "The exact 64-character SHA-256 from a retained-output receipt." },
|
|
6901
|
+
start_line: { type: "integer", minimum: 1, default: 1 },
|
|
6902
|
+
start_byte: { type: "integer", minimum: 0, description: "Exact UTF-8 continuation byte shown by a previous page." },
|
|
6903
|
+
max_lines: { type: "integer", minimum: 1, maximum: 1e3, default: 200 },
|
|
6904
|
+
max_bytes: { type: "integer", minimum: 256, maximum: 768, default: 768 }
|
|
6905
|
+
}, ["sha256"])
|
|
6906
|
+
},
|
|
6907
|
+
async execute(arguments_, context) {
|
|
6908
|
+
const input2 = inputSchema6.parse(arguments_);
|
|
6909
|
+
const artifact = context.session.toolArtifacts?.find(
|
|
6910
|
+
(candidate) => candidate.sha256 === input2.sha256 && Date.parse(candidate.expiresAt) > Date.now()
|
|
6911
|
+
);
|
|
6912
|
+
if (!artifact) {
|
|
6913
|
+
throw new Error("No retained tool output matches this tool call in the current session.");
|
|
6914
|
+
}
|
|
6915
|
+
const store = context.toolArtifactStore ?? new ToolArtifactStore(context.workspace.primaryRoot);
|
|
6916
|
+
const page = await store.read(context.session.id, artifact.toolCallId, {
|
|
6917
|
+
...input2.start_line !== void 0 ? { startLine: input2.start_line } : {},
|
|
6918
|
+
...input2.start_byte !== void 0 ? { startByte: input2.start_byte } : {},
|
|
6919
|
+
...input2.max_lines !== void 0 ? { maxLines: input2.max_lines } : {},
|
|
6920
|
+
maxBytes: input2.max_bytes ?? 768
|
|
6921
|
+
});
|
|
6922
|
+
if (page.sha256 !== artifact.sha256 || page.bytes !== artifact.bytes) {
|
|
6923
|
+
throw new Error("Retained tool output no longer matches the session receipt.");
|
|
6924
|
+
}
|
|
6925
|
+
const heading = `Retained output page: lines ${page.startLine}-${page.endLine} of ${page.totalLines}; bytes ${page.startByte}-${page.endByte} of ${page.bytes}`;
|
|
6926
|
+
const continuation = page.nextStartByte !== void 0 ? `continue with start_byte=${page.nextStartByte}` : page.nextStartLine !== void 0 ? `continue with start_line=${page.nextStartLine}` : "";
|
|
6927
|
+
return {
|
|
6928
|
+
content: `${heading}
|
|
6929
|
+
${page.content}${page.hasMore ? `
|
|
6930
|
+
\u2026 more retained output; ${continuation}` : ""}`,
|
|
6931
|
+
metadata: {
|
|
6932
|
+
artifact: {
|
|
6933
|
+
toolCallId: page.toolCallId,
|
|
6934
|
+
sha256: page.sha256,
|
|
6935
|
+
bytes: page.bytes,
|
|
6936
|
+
expiresAt: page.expiresAt,
|
|
6937
|
+
redacted: page.redacted
|
|
6938
|
+
},
|
|
6939
|
+
startLine: page.startLine,
|
|
6940
|
+
endLine: page.endLine,
|
|
6941
|
+
totalLines: page.totalLines,
|
|
6942
|
+
startByte: page.startByte,
|
|
6943
|
+
endByte: page.endByte,
|
|
6944
|
+
truncated: page.hasMore
|
|
6945
|
+
}
|
|
6946
|
+
};
|
|
6947
|
+
}
|
|
6948
|
+
};
|
|
6949
|
+
|
|
6259
6950
|
// src/tools/registry.ts
|
|
6260
6951
|
var ToolRegistry = class {
|
|
6261
6952
|
tools = /* @__PURE__ */ new Map();
|
|
@@ -6303,19 +6994,19 @@ function assertToolName(name) {
|
|
|
6303
6994
|
}
|
|
6304
6995
|
|
|
6305
6996
|
// src/tools/search.ts
|
|
6306
|
-
import { readFile as
|
|
6307
|
-
import { resolve as
|
|
6997
|
+
import { readFile as readFile10, stat as stat8 } from "node:fs/promises";
|
|
6998
|
+
import { resolve as resolve15 } from "node:path";
|
|
6308
6999
|
import fg4 from "fast-glob";
|
|
6309
|
-
import { z as
|
|
6310
|
-
var
|
|
6311
|
-
query:
|
|
6312
|
-
path:
|
|
6313
|
-
pattern:
|
|
6314
|
-
mode:
|
|
6315
|
-
literal:
|
|
6316
|
-
case_sensitive:
|
|
6317
|
-
context_lines:
|
|
6318
|
-
max_results:
|
|
7000
|
+
import { z as z12 } from "zod";
|
|
7001
|
+
var inputSchema7 = z12.object({
|
|
7002
|
+
query: z12.string().min(1).max(1e4),
|
|
7003
|
+
path: z12.string().min(1).optional(),
|
|
7004
|
+
pattern: z12.string().min(1).optional(),
|
|
7005
|
+
mode: z12.enum(["text", "ranked"]).optional(),
|
|
7006
|
+
literal: z12.boolean().optional(),
|
|
7007
|
+
case_sensitive: z12.boolean().optional(),
|
|
7008
|
+
context_lines: z12.number().int().min(0).max(20).optional(),
|
|
7009
|
+
max_results: z12.number().int().min(1).max(500).optional()
|
|
6319
7010
|
}).strict();
|
|
6320
7011
|
var ignore = [
|
|
6321
7012
|
"**/.git/**",
|
|
@@ -6350,7 +7041,7 @@ var searchCodeTool = {
|
|
|
6350
7041
|
}, ["query"])
|
|
6351
7042
|
},
|
|
6352
7043
|
async execute(arguments_, context) {
|
|
6353
|
-
const input2 =
|
|
7044
|
+
const input2 = inputSchema7.parse(arguments_);
|
|
6354
7045
|
const directory = await context.workspace.resolveDirectory(input2.path ?? ".");
|
|
6355
7046
|
if ((input2.mode ?? "text") === "ranked") {
|
|
6356
7047
|
if (!context.contextEngine) throw new Error("Local ranked retrieval is unavailable.");
|
|
@@ -6374,7 +7065,7 @@ var searchCodeTool = {
|
|
|
6374
7065
|
if (results.length >= maxResults) break;
|
|
6375
7066
|
let path;
|
|
6376
7067
|
try {
|
|
6377
|
-
path = await context.workspace.resolvePath(
|
|
7068
|
+
path = await context.workspace.resolvePath(resolve15(directory, file), { expect: "file" });
|
|
6378
7069
|
} catch {
|
|
6379
7070
|
skipped += 1;
|
|
6380
7071
|
continue;
|
|
@@ -6384,7 +7075,7 @@ var searchCodeTool = {
|
|
|
6384
7075
|
skipped += 1;
|
|
6385
7076
|
continue;
|
|
6386
7077
|
}
|
|
6387
|
-
const buffer = await
|
|
7078
|
+
const buffer = await readFile10(path);
|
|
6388
7079
|
if (buffer.subarray(0, 8192).includes(0)) {
|
|
6389
7080
|
skipped += 1;
|
|
6390
7081
|
continue;
|
|
@@ -6456,17 +7147,17 @@ ${hit.content}`
|
|
|
6456
7147
|
}
|
|
6457
7148
|
|
|
6458
7149
|
// src/tools/shell.ts
|
|
6459
|
-
import { createHash as
|
|
6460
|
-
import { lstat as
|
|
6461
|
-
import { join as
|
|
6462
|
-
import { z as
|
|
6463
|
-
var
|
|
6464
|
-
command:
|
|
6465
|
-
cwd:
|
|
6466
|
-
timeout_ms:
|
|
6467
|
-
max_output_bytes:
|
|
6468
|
-
env:
|
|
6469
|
-
stdin:
|
|
7150
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
7151
|
+
import { lstat as lstat15, readFile as readFile11, readdir as readdir5 } from "node:fs/promises";
|
|
7152
|
+
import { join as join13 } from "node:path";
|
|
7153
|
+
import { z as z13 } from "zod";
|
|
7154
|
+
var inputSchema8 = z13.object({
|
|
7155
|
+
command: z13.string().min(1).max(1e5),
|
|
7156
|
+
cwd: z13.string().min(1).optional(),
|
|
7157
|
+
timeout_ms: z13.number().int().min(100).max(6e5).optional(),
|
|
7158
|
+
max_output_bytes: z13.number().int().min(1e3).max(5e6).optional(),
|
|
7159
|
+
env: z13.record(z13.string(), z13.string()).optional(),
|
|
7160
|
+
stdin: z13.string().max(5e6).optional()
|
|
6470
7161
|
}).strict();
|
|
6471
7162
|
var shellTool = {
|
|
6472
7163
|
definition: {
|
|
@@ -6483,7 +7174,7 @@ var shellTool = {
|
|
|
6483
7174
|
}, ["command"])
|
|
6484
7175
|
},
|
|
6485
7176
|
permissionCategories(arguments_) {
|
|
6486
|
-
const input2 =
|
|
7177
|
+
const input2 = inputSchema8.parse(arguments_);
|
|
6487
7178
|
validateEnvironment(input2.env);
|
|
6488
7179
|
return [
|
|
6489
7180
|
"shell",
|
|
@@ -6493,12 +7184,12 @@ var shellTool = {
|
|
|
6493
7184
|
];
|
|
6494
7185
|
},
|
|
6495
7186
|
async affectedPaths(arguments_, context) {
|
|
6496
|
-
const input2 =
|
|
7187
|
+
const input2 = inputSchema8.parse(arguments_);
|
|
6497
7188
|
if (!appearsToModifyWorkspace(input2.command)) return [];
|
|
6498
7189
|
return collectAffectedPaths(input2.command, input2.cwd ?? ".", context);
|
|
6499
7190
|
},
|
|
6500
7191
|
async execute(arguments_, context) {
|
|
6501
|
-
const input2 =
|
|
7192
|
+
const input2 = inputSchema8.parse(arguments_);
|
|
6502
7193
|
validateEnvironment(input2.env);
|
|
6503
7194
|
const cwd = await context.workspace.resolveDirectory(input2.cwd ?? ".");
|
|
6504
7195
|
const candidates = appearsToModifyWorkspace(input2.command) ? await collectAffectedPaths(input2.command, input2.cwd ?? ".", context) : [];
|
|
@@ -6538,7 +7229,8 @@ Execution interrupted: ${error instanceof Error ? error.message : String(error)}
|
|
|
6538
7229
|
result.stdout ? `stdout:
|
|
6539
7230
|
${result.stdout}` : "",
|
|
6540
7231
|
result.stderr ? `stderr:
|
|
6541
|
-
${result.stderr}` : ""
|
|
7232
|
+
${result.stderr}` : "",
|
|
7233
|
+
result.stdoutTruncated || result.stderrTruncated ? `Source output exceeded the command capture limit (${result.stdoutBytes} stdout bytes, ${result.stderrBytes} stderr bytes observed). Rerun with max_output_bytes up to 5000000 or narrow the command; omitted source bytes are not recoverable from this result.` : ""
|
|
6542
7234
|
].filter(Boolean);
|
|
6543
7235
|
return {
|
|
6544
7236
|
ok: result.exitCode === 0 && !result.timedOut,
|
|
@@ -6548,6 +7240,9 @@ ${result.stderr}` : ""
|
|
|
6548
7240
|
exitCode: result.exitCode,
|
|
6549
7241
|
timedOut: result.timedOut,
|
|
6550
7242
|
durationMs: result.durationMs,
|
|
7243
|
+
stdoutBytes: result.stdoutBytes,
|
|
7244
|
+
stderrBytes: result.stderrBytes,
|
|
7245
|
+
sourceTruncated: result.stdoutTruncated || result.stderrTruncated,
|
|
6551
7246
|
changeTracking: candidates.length ? "targeted" : beforeWorkspace && afterWorkspace && beforeWorkspace.complete && afterWorkspace.complete ? "workspace-snapshot" : beforeWorkspace ? "unresolved" : "read-only"
|
|
6552
7247
|
},
|
|
6553
7248
|
...changedFiles.length ? { changedFiles } : {}
|
|
@@ -6611,7 +7306,7 @@ async function collectAffectedPaths(command2, cwdInput, context) {
|
|
|
6611
7306
|
allowMissing: true
|
|
6612
7307
|
});
|
|
6613
7308
|
try {
|
|
6614
|
-
if ((await
|
|
7309
|
+
if ((await lstat15(path)).isDirectory()) continue;
|
|
6615
7310
|
} catch (error) {
|
|
6616
7311
|
if (error.code !== "ENOENT") throw error;
|
|
6617
7312
|
}
|
|
@@ -6639,7 +7334,7 @@ async function changedPaths(paths, before) {
|
|
|
6639
7334
|
}
|
|
6640
7335
|
async function snapshotPath(path) {
|
|
6641
7336
|
try {
|
|
6642
|
-
const info = await
|
|
7337
|
+
const info = await lstat15(path);
|
|
6643
7338
|
return { exists: true, size: info.size, mtimeMs: info.mtimeMs };
|
|
6644
7339
|
} catch (error) {
|
|
6645
7340
|
if (error.code === "ENOENT") return { exists: false };
|
|
@@ -6655,18 +7350,18 @@ async function captureWorkspaceSnapshot(roots) {
|
|
|
6655
7350
|
if (!discovered.complete) complete = false;
|
|
6656
7351
|
for (const path of discovered.files) {
|
|
6657
7352
|
try {
|
|
6658
|
-
const info = await
|
|
7353
|
+
const info = await lstat15(path);
|
|
6659
7354
|
if (!info.isFile() || info.isSymbolicLink()) continue;
|
|
6660
7355
|
if (files.size >= MAX_SNAPSHOT_FILES || info.size > MAX_SNAPSHOT_FILE_BYTES || totalBytes + info.size > MAX_SNAPSHOT_TOTAL_BYTES) {
|
|
6661
7356
|
complete = false;
|
|
6662
7357
|
continue;
|
|
6663
7358
|
}
|
|
6664
|
-
const content = await
|
|
7359
|
+
const content = await readFile11(path);
|
|
6665
7360
|
totalBytes += content.length;
|
|
6666
7361
|
files.set(path, {
|
|
6667
7362
|
size: info.size,
|
|
6668
7363
|
mtimeMs: info.mtimeMs,
|
|
6669
|
-
hash:
|
|
7364
|
+
hash: createHash8("sha256").update(content).digest("hex")
|
|
6670
7365
|
});
|
|
6671
7366
|
} catch (error) {
|
|
6672
7367
|
if (error.code !== "ENOENT") complete = false;
|
|
@@ -6687,14 +7382,14 @@ async function discoverSnapshotFiles(root) {
|
|
|
6687
7382
|
const directory = pending.pop();
|
|
6688
7383
|
if (!directory) break;
|
|
6689
7384
|
try {
|
|
6690
|
-
const entries = await
|
|
7385
|
+
const entries = await readdir5(directory, { withFileTypes: true, encoding: "utf8" });
|
|
6691
7386
|
for (const entry of entries) {
|
|
6692
7387
|
if (ignored2.has(entry.name) || /^\.skein\.(?:migrating|rollback)-/u.test(entry.name)) continue;
|
|
6693
7388
|
if (entry.isSymbolicLink()) {
|
|
6694
7389
|
complete = false;
|
|
6695
7390
|
continue;
|
|
6696
7391
|
}
|
|
6697
|
-
const path =
|
|
7392
|
+
const path = join13(directory, entry.name);
|
|
6698
7393
|
if (entry.isDirectory()) pending.push(path);
|
|
6699
7394
|
else if (entry.isFile()) files.push(path);
|
|
6700
7395
|
}
|
|
@@ -6708,18 +7403,18 @@ async function discoverSnapshotFiles(root) {
|
|
|
6708
7403
|
|
|
6709
7404
|
// src/tools/task.ts
|
|
6710
7405
|
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
6711
|
-
import { z as
|
|
6712
|
-
var taskSchema2 =
|
|
6713
|
-
id:
|
|
6714
|
-
title:
|
|
6715
|
-
status:
|
|
7406
|
+
import { z as z14 } from "zod";
|
|
7407
|
+
var taskSchema2 = z14.object({
|
|
7408
|
+
id: z14.string().min(1).optional(),
|
|
7409
|
+
title: z14.string().min(1).max(500),
|
|
7410
|
+
status: z14.enum(["pending", "in_progress", "completed"])
|
|
6716
7411
|
}).strict();
|
|
6717
|
-
var
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
7412
|
+
var inputSchema9 = z14.discriminatedUnion("action", [
|
|
7413
|
+
z14.object({ action: z14.literal("list") }).strict(),
|
|
7414
|
+
z14.object({ action: z14.literal("add"), title: z14.string().min(1).max(500), status: z14.enum(["pending", "in_progress", "completed"]).optional() }).strict(),
|
|
7415
|
+
z14.object({ action: z14.literal("update"), id: z14.string().min(1), title: z14.string().min(1).max(500).optional(), status: z14.enum(["pending", "in_progress", "completed"]).optional() }).strict(),
|
|
7416
|
+
z14.object({ action: z14.literal("remove"), id: z14.string().min(1) }).strict(),
|
|
7417
|
+
z14.object({ action: z14.literal("replace"), tasks: z14.array(taskSchema2).max(100) }).strict()
|
|
6723
7418
|
]);
|
|
6724
7419
|
var taskTool = {
|
|
6725
7420
|
definition: {
|
|
@@ -6750,7 +7445,7 @@ var taskTool = {
|
|
|
6750
7445
|
}
|
|
6751
7446
|
},
|
|
6752
7447
|
async execute(arguments_, context) {
|
|
6753
|
-
const input2 =
|
|
7448
|
+
const input2 = inputSchema9.parse(arguments_);
|
|
6754
7449
|
const tasks = context.session.tasks;
|
|
6755
7450
|
switch (input2.action) {
|
|
6756
7451
|
case "list":
|
|
@@ -6788,7 +7483,7 @@ var taskTool = {
|
|
|
6788
7483
|
|
|
6789
7484
|
// src/tools/task-contract.ts
|
|
6790
7485
|
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
6791
|
-
import { z as
|
|
7486
|
+
import { z as z15 } from "zod";
|
|
6792
7487
|
|
|
6793
7488
|
// src/agent/task-contract.ts
|
|
6794
7489
|
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
@@ -6869,14 +7564,14 @@ function compact(value, limit) {
|
|
|
6869
7564
|
}
|
|
6870
7565
|
|
|
6871
7566
|
// src/agent/completion-gate.ts
|
|
6872
|
-
import { createHash as
|
|
7567
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
6873
7568
|
|
|
6874
7569
|
// src/tools/permissions.ts
|
|
6875
|
-
import { createHash as
|
|
7570
|
+
import { createHash as createHash9, createHmac, randomBytes } from "node:crypto";
|
|
6876
7571
|
var permissionScopeSecret = randomBytes(32);
|
|
6877
7572
|
function permissionKey(call, category) {
|
|
6878
7573
|
const categoryScope = category === "network" ? `\0network-request:${scopeFingerprint(normalizeRequestState(call.arguments))}` : "";
|
|
6879
|
-
const digest =
|
|
7574
|
+
const digest = createHash9("sha256").update(`${category}\0${call.name}\0${permissionTarget(call)}${categoryScope}`).digest("hex").slice(0, 24);
|
|
6880
7575
|
return `${category}:${call.name}:${digest}`;
|
|
6881
7576
|
}
|
|
6882
7577
|
function permissionTarget(call) {
|
|
@@ -7070,7 +7765,7 @@ function captureVerification(call, result, changeSequence, configuredCommands) {
|
|
|
7070
7765
|
kind,
|
|
7071
7766
|
ok: result.ok,
|
|
7072
7767
|
changeSequence,
|
|
7073
|
-
commandKey:
|
|
7768
|
+
commandKey: createHash10("sha256").update(normalized).digest("hex")
|
|
7074
7769
|
};
|
|
7075
7770
|
}
|
|
7076
7771
|
function buildRunCompletion(changedFiles, evidence, currentChangeSequence, mutationTracking = "complete", taskContract, audit = []) {
|
|
@@ -7230,7 +7925,7 @@ function verificationRequirementMet(requirement, checks) {
|
|
|
7230
7925
|
const normalized = normalizeCommand2(requirement);
|
|
7231
7926
|
const broad = normalized.match(/^Record at least one successful (.+) after the final mutation\.?$/iu);
|
|
7232
7927
|
if (broad) return checks.length > 0;
|
|
7233
|
-
const commandKey =
|
|
7928
|
+
const commandKey = createHash10("sha256").update(normalized).digest("hex");
|
|
7234
7929
|
return checks.some((item) => item.commandKey === commandKey);
|
|
7235
7930
|
}
|
|
7236
7931
|
function acceptanceUnresolved(acceptance) {
|
|
@@ -7282,29 +7977,29 @@ function fileCount(count) {
|
|
|
7282
7977
|
}
|
|
7283
7978
|
|
|
7284
7979
|
// src/tools/task-contract.ts
|
|
7285
|
-
var criterionSchema =
|
|
7286
|
-
id:
|
|
7287
|
-
description:
|
|
7288
|
-
required:
|
|
7980
|
+
var criterionSchema = z15.object({
|
|
7981
|
+
id: z15.string().min(1).max(128).optional(),
|
|
7982
|
+
description: z15.string().min(1).max(2e3),
|
|
7983
|
+
required: z15.boolean().optional()
|
|
7289
7984
|
}).strict();
|
|
7290
|
-
var
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
action:
|
|
7294
|
-
objective:
|
|
7295
|
-
scope:
|
|
7296
|
-
constraints:
|
|
7297
|
-
non_goals:
|
|
7298
|
-
acceptance_criteria:
|
|
7299
|
-
verification_requirements:
|
|
7985
|
+
var inputSchema10 = z15.discriminatedUnion("action", [
|
|
7986
|
+
z15.object({ action: z15.literal("show") }).strict(),
|
|
7987
|
+
z15.object({
|
|
7988
|
+
action: z15.literal("replace"),
|
|
7989
|
+
objective: z15.string().min(1).max(2e4),
|
|
7990
|
+
scope: z15.array(z15.string().min(1).max(2e3)).max(64),
|
|
7991
|
+
constraints: z15.array(z15.string().min(1).max(2e3)).max(64),
|
|
7992
|
+
non_goals: z15.array(z15.string().min(1).max(2e3)).max(64),
|
|
7993
|
+
acceptance_criteria: z15.array(criterionSchema).min(1).max(64),
|
|
7994
|
+
verification_requirements: z15.array(z15.string().min(1).max(2e3)).min(1).max(64)
|
|
7300
7995
|
}).strict(),
|
|
7301
|
-
|
|
7302
|
-
|
|
7303
|
-
action:
|
|
7304
|
-
id:
|
|
7305
|
-
status:
|
|
7306
|
-
evidence_refs:
|
|
7307
|
-
note:
|
|
7996
|
+
z15.object({ action: z15.literal("activate") }).strict(),
|
|
7997
|
+
z15.object({
|
|
7998
|
+
action: z15.literal("update_criterion"),
|
|
7999
|
+
id: z15.string().min(1).max(128),
|
|
8000
|
+
status: z15.enum(["pending", "satisfied", "blocked"]),
|
|
8001
|
+
evidence_refs: z15.array(z15.string().min(1).max(256)).max(64).optional(),
|
|
8002
|
+
note: z15.string().max(2e3).optional()
|
|
7308
8003
|
}).strict()
|
|
7309
8004
|
]);
|
|
7310
8005
|
var taskContractTool = {
|
|
@@ -7346,7 +8041,7 @@ var taskContractTool = {
|
|
|
7346
8041
|
}
|
|
7347
8042
|
},
|
|
7348
8043
|
async execute(arguments_, context) {
|
|
7349
|
-
const input2 =
|
|
8044
|
+
const input2 = inputSchema10.parse(arguments_);
|
|
7350
8045
|
let contract = context.session.taskContract;
|
|
7351
8046
|
if (!contract) throw new Error("No Task Contract is active for this session.");
|
|
7352
8047
|
if (input2.action === "replace") {
|
|
@@ -7443,17 +8138,17 @@ function validateVerificationRequirement(requirement) {
|
|
|
7443
8138
|
}
|
|
7444
8139
|
|
|
7445
8140
|
// src/tools/working-memory.ts
|
|
7446
|
-
import { z as
|
|
7447
|
-
var
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
8141
|
+
import { z as z16 } from "zod";
|
|
8142
|
+
var inputSchema11 = z16.discriminatedUnion("action", [
|
|
8143
|
+
z16.object({ action: z16.literal("show") }).strict(),
|
|
8144
|
+
z16.object({ action: z16.literal("set_goal"), value: z16.string().min(1).max(1e3) }).strict(),
|
|
8145
|
+
z16.object({ action: z16.literal("set_focus"), value: z16.string().min(1).max(1e3) }).strict(),
|
|
8146
|
+
z16.object({ action: z16.literal("add_constraint"), value: z16.string().min(1).max(1e3) }).strict(),
|
|
8147
|
+
z16.object({ action: z16.literal("add_decision"), value: z16.string().min(1).max(1e3) }).strict(),
|
|
8148
|
+
z16.object({ action: z16.literal("add_question"), value: z16.string().min(1).max(1e3) }).strict(),
|
|
8149
|
+
z16.object({ action: z16.literal("resolve_question"), value: z16.string().min(1).max(1e3) }).strict(),
|
|
8150
|
+
z16.object({ action: z16.literal("add_file"), path: z16.string().min(1).max(4e3) }).strict(),
|
|
8151
|
+
z16.object({ action: z16.literal("clear"), field: z16.enum(["constraints", "decisions", "openQuestions", "relevantFiles"]) }).strict()
|
|
7457
8152
|
]);
|
|
7458
8153
|
var workingMemoryTool = {
|
|
7459
8154
|
definition: {
|
|
@@ -7478,7 +8173,7 @@ var workingMemoryTool = {
|
|
|
7478
8173
|
}, ["action"])
|
|
7479
8174
|
},
|
|
7480
8175
|
async execute(arguments_, context) {
|
|
7481
|
-
const input2 =
|
|
8176
|
+
const input2 = inputSchema11.parse(arguments_);
|
|
7482
8177
|
const memory = context.session.workingMemory ?? emptyWorkingMemory2();
|
|
7483
8178
|
switch (input2.action) {
|
|
7484
8179
|
case "show":
|
|
@@ -7560,6 +8255,7 @@ function render(memory) {
|
|
|
7560
8255
|
function createDefaultToolRegistry(_options = {}) {
|
|
7561
8256
|
return new ToolRegistry([
|
|
7562
8257
|
readFileTool,
|
|
8258
|
+
readToolArtifactTool,
|
|
7563
8259
|
listFilesTool,
|
|
7564
8260
|
searchCodeTool,
|
|
7565
8261
|
writeFileTool,
|
|
@@ -7688,7 +8384,7 @@ function escapeAttribute3(value) {
|
|
|
7688
8384
|
}
|
|
7689
8385
|
|
|
7690
8386
|
// src/agent/tool-recovery.ts
|
|
7691
|
-
import { createHash as
|
|
8387
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
7692
8388
|
var RETRY_BUDGET = {
|
|
7693
8389
|
schema_input: 3,
|
|
7694
8390
|
unknown_tool: 2,
|
|
@@ -7783,10 +8479,10 @@ function isRetryable(failureClass) {
|
|
|
7783
8479
|
return RETRY_BUDGET[failureClass] > 0;
|
|
7784
8480
|
}
|
|
7785
8481
|
function callSignature(call) {
|
|
7786
|
-
return
|
|
8482
|
+
return createHash11("sha256").update(`${call.name}\0${stableJson(redact(call.arguments))}`).digest("hex");
|
|
7787
8483
|
}
|
|
7788
8484
|
function failureSignature(call, failureClass) {
|
|
7789
|
-
return
|
|
8485
|
+
return createHash11("sha256").update(`${call.name}\0${stableJson(redact(call.arguments))}\0${failureClass}`).digest("hex");
|
|
7790
8486
|
}
|
|
7791
8487
|
function redact(value, key = "") {
|
|
7792
8488
|
if (/api[_-]?key|authorization|cookie|password|secret|token/i.test(key)) return "[redacted]";
|
|
@@ -7803,10 +8499,170 @@ function isFailureClass(value) {
|
|
|
7803
8499
|
return typeof value === "string" && Object.hasOwn(RETRY_BUDGET, value);
|
|
7804
8500
|
}
|
|
7805
8501
|
|
|
8502
|
+
// src/agent/tool-output.ts
|
|
8503
|
+
import stripAnsi from "strip-ansi";
|
|
8504
|
+
var MIN_TOOL_OUTPUT_TOKENS = 1024;
|
|
8505
|
+
var MAX_TOOL_OUTPUT_TOKENS = 8192;
|
|
8506
|
+
async function protectToolOutput(options) {
|
|
8507
|
+
const safe = sanitizeToolOutput(options.content);
|
|
8508
|
+
const sanitized = redactToolOutput(safe.text);
|
|
8509
|
+
const estimatedTokens = estimateToolOutputTokens(sanitized.text);
|
|
8510
|
+
const budgetTokens = clamp2(options.budgetTokens, MIN_TOOL_OUTPUT_TOKENS, MAX_TOOL_OUTPUT_TOKENS);
|
|
8511
|
+
const base = {
|
|
8512
|
+
originalChars: options.content.length,
|
|
8513
|
+
originalBytes: Buffer.byteLength(options.content),
|
|
8514
|
+
estimatedTokens,
|
|
8515
|
+
budgetTokens,
|
|
8516
|
+
truncated: estimatedTokens > budgetTokens,
|
|
8517
|
+
redacted: sanitized.redacted,
|
|
8518
|
+
sanitized: safe.sanitized
|
|
8519
|
+
};
|
|
8520
|
+
if (estimatedTokens <= budgetTokens) return { content: sanitized.text, metadata: base };
|
|
8521
|
+
let archived;
|
|
8522
|
+
try {
|
|
8523
|
+
archived = await options.artifacts.archive(
|
|
8524
|
+
options.sessionId,
|
|
8525
|
+
options.toolCallId,
|
|
8526
|
+
sanitized.text,
|
|
8527
|
+
{ redacted: sanitized.redacted }
|
|
8528
|
+
);
|
|
8529
|
+
} catch {
|
|
8530
|
+
archived = { stored: false, reason: "storage_error" };
|
|
8531
|
+
}
|
|
8532
|
+
const artifact = archived.stored ? archived.artifact : void 0;
|
|
8533
|
+
const artifactUnavailable = archived.stored ? void 0 : archived.reason;
|
|
8534
|
+
const metadata = {
|
|
8535
|
+
...base,
|
|
8536
|
+
...artifact ? {
|
|
8537
|
+
artifact: {
|
|
8538
|
+
toolCallId: artifact.toolCallId,
|
|
8539
|
+
sha256: artifact.sha256,
|
|
8540
|
+
bytes: artifact.bytes,
|
|
8541
|
+
createdAt: artifact.createdAt,
|
|
8542
|
+
expiresAt: artifact.expiresAt
|
|
8543
|
+
}
|
|
8544
|
+
} : artifactUnavailable ? { artifactUnavailable } : {}
|
|
8545
|
+
};
|
|
8546
|
+
const receipt = formatReceipt(options, metadata, "");
|
|
8547
|
+
const previewBudget = Math.max(0, budgetTokens - estimateToolOutputTokens(receipt));
|
|
8548
|
+
const preview = boundedPreview(sanitized.text, previewBudget);
|
|
8549
|
+
return {
|
|
8550
|
+
content: formatReceipt(options, metadata, preview),
|
|
8551
|
+
metadata
|
|
8552
|
+
};
|
|
8553
|
+
}
|
|
8554
|
+
function dynamicToolOutputBudget(contextWindowTokens, activeContextTokens, remainingSessionTokens) {
|
|
8555
|
+
const contextHeadroom = Math.max(0, contextWindowTokens - activeContextTokens);
|
|
8556
|
+
const sessionHeadroom = Math.max(0, remainingSessionTokens);
|
|
8557
|
+
const budget = Math.min(
|
|
8558
|
+
Math.floor(contextHeadroom * 0.35),
|
|
8559
|
+
Math.floor(sessionHeadroom * 0.12)
|
|
8560
|
+
);
|
|
8561
|
+
return clamp2(budget, MIN_TOOL_OUTPUT_TOKENS, MAX_TOOL_OUTPUT_TOKENS);
|
|
8562
|
+
}
|
|
8563
|
+
function estimateToolOutputTokens(value) {
|
|
8564
|
+
return Math.max(1, estimateTokens(value));
|
|
8565
|
+
}
|
|
8566
|
+
function formatReceipt(options, metadata, preview) {
|
|
8567
|
+
const lines = [
|
|
8568
|
+
"[Oversized tool output bounded for the model transcript]",
|
|
8569
|
+
`tool: ${boundedInlineByTokens(options.tool, 64)}`,
|
|
8570
|
+
`tool-call-id: ${boundedInlineByTokens(options.toolCallId, 96)}`,
|
|
8571
|
+
`status: ${options.ok ? "success" : "failure"}`,
|
|
8572
|
+
`original-output: ${metadata.originalChars} chars, ${metadata.originalBytes} bytes, ~${metadata.estimatedTokens} tokens`,
|
|
8573
|
+
`model-budget: ${metadata.budgetTokens} tokens (head + tail preview)`,
|
|
8574
|
+
...metadata.redacted ? ["sensitive-values: redacted before display and retention"] : [],
|
|
8575
|
+
...preservedSignals(options.metadata),
|
|
8576
|
+
...metadata.artifact ? [
|
|
8577
|
+
`artifact: session-scoped; sha256 ${metadata.artifact.sha256}; ${metadata.artifact.bytes} bytes`,
|
|
8578
|
+
`read-back: read_tool_artifact({"sha256":"${metadata.artifact.sha256}","start_line":1,"max_lines":200}) before ${metadata.artifact.expiresAt}`
|
|
8579
|
+
] : [`artifact: unavailable (${metadata.artifactUnavailable ?? "unknown"}); rerun the tool with a narrower result if more detail is needed`],
|
|
8580
|
+
"preview:",
|
|
8581
|
+
preview
|
|
8582
|
+
];
|
|
8583
|
+
return lines.join("\n");
|
|
8584
|
+
}
|
|
8585
|
+
function preservedSignals(metadata) {
|
|
8586
|
+
const lines = [];
|
|
8587
|
+
if (typeof metadata.exitCode === "number") lines.push(`exit-code: ${metadata.exitCode}`);
|
|
8588
|
+
if (metadata.timedOut === true) lines.push("timed-out: true");
|
|
8589
|
+
if (metadata.sourceTruncated === true) {
|
|
8590
|
+
lines.push("source-truncated: true; the producing tool reached its capture limit before this firewall");
|
|
8591
|
+
}
|
|
8592
|
+
const changedFiles = metadata.changedFiles;
|
|
8593
|
+
if (Array.isArray(changedFiles)) {
|
|
8594
|
+
const allPaths = changedFiles.filter((path) => typeof path === "string");
|
|
8595
|
+
const paths = allPaths.slice(0, 3).map((path) => boundedInlineByTokens(path, 24));
|
|
8596
|
+
if (paths.length) {
|
|
8597
|
+
lines.push(`changed-files: ${paths.join(", ")}${allPaths.length > paths.length ? ` (+${allPaths.length - paths.length} more in metadata)` : ""}`);
|
|
8598
|
+
}
|
|
8599
|
+
}
|
|
8600
|
+
const failure = metadata.failure;
|
|
8601
|
+
if (isFailureReceipt(failure)) {
|
|
8602
|
+
lines.push(`failure-class: ${failure.class}; attempt ${failure.attempt}; ${failure.remaining} retries remain`);
|
|
8603
|
+
}
|
|
8604
|
+
return lines;
|
|
8605
|
+
}
|
|
8606
|
+
function isFailureReceipt(value) {
|
|
8607
|
+
return typeof value === "object" && value !== null && typeof value.class === "string" && typeof value.attempt === "number" && typeof value.remaining === "number";
|
|
8608
|
+
}
|
|
8609
|
+
function redactToolOutput(value) {
|
|
8610
|
+
let redacted = false;
|
|
8611
|
+
const replace = (expression, replacement) => {
|
|
8612
|
+
expression.lastIndex = 0;
|
|
8613
|
+
if (!expression.test(value)) return;
|
|
8614
|
+
redacted = true;
|
|
8615
|
+
expression.lastIndex = 0;
|
|
8616
|
+
value = value.replace(expression, replacement);
|
|
8617
|
+
};
|
|
8618
|
+
replace(/\b(sk-[A-Za-z0-9_-]{12,}|AIza[0-9A-Za-z_-]{20,})\b/gu, "[redacted-secret]");
|
|
8619
|
+
replace(/\b(authorization\s*:\s*(?:bearer\s+|basic\s+)?)\S+/giu, "$1[redacted]");
|
|
8620
|
+
replace(/\b((?:api[_-]?key|access[_-]?token|authorization|cookie|password|client[_-]?secret)\s*[:=]\s*)(?:"[A-Za-z0-9+/_=-]{8,}"|'[A-Za-z0-9+/_=-]{8,}'|[A-Za-z0-9+/_=-]{8,})/giu, "$1[redacted]");
|
|
8621
|
+
replace(/(--(?:api[_-]?key|access[_-]?token|password|client[_-]?secret)(?:=|\s+))[A-Za-z0-9+/_=-]{8,}/giu, "$1[redacted]");
|
|
8622
|
+
return { text: value, redacted };
|
|
8623
|
+
}
|
|
8624
|
+
function sanitizeToolOutput(value) {
|
|
8625
|
+
const text = stripAnsi(value).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f]/gu, "");
|
|
8626
|
+
return { text, sanitized: text !== value };
|
|
8627
|
+
}
|
|
8628
|
+
function boundedPreview(value, budget) {
|
|
8629
|
+
if (budget <= 0) return "";
|
|
8630
|
+
if (estimateToolOutputTokens(value) <= budget) return value;
|
|
8631
|
+
let low = 0;
|
|
8632
|
+
let high = budget;
|
|
8633
|
+
let best = "";
|
|
8634
|
+
while (low <= high) {
|
|
8635
|
+
const contentBudget = Math.floor((low + high) / 2);
|
|
8636
|
+
const headBudget = Math.ceil(contentBudget * 0.6);
|
|
8637
|
+
const tailBudget = Math.max(0, contentBudget - headBudget);
|
|
8638
|
+
const head = sliceStartByTokens(value, headBudget);
|
|
8639
|
+
const tail = sliceEndByTokens(value, tailBudget);
|
|
8640
|
+
const preview = `${head}
|
|
8641
|
+
\u2026 ${Math.max(0, value.length - head.length - tail.length)} chars omitted \u2026
|
|
8642
|
+
${tail}`;
|
|
8643
|
+
if (estimateToolOutputTokens(preview) <= budget) {
|
|
8644
|
+
best = preview;
|
|
8645
|
+
low = contentBudget + 1;
|
|
8646
|
+
} else {
|
|
8647
|
+
high = contentBudget - 1;
|
|
8648
|
+
}
|
|
8649
|
+
}
|
|
8650
|
+
return best || sliceStartByTokens(value, budget);
|
|
8651
|
+
}
|
|
8652
|
+
function clamp2(value, minimum, maximum) {
|
|
8653
|
+
return Math.max(minimum, Math.min(maximum, value));
|
|
8654
|
+
}
|
|
8655
|
+
function boundedInlineByTokens(value, maxTokens) {
|
|
8656
|
+
const normalized = value.replace(/[\r\n\t]+/gu, " ").trim();
|
|
8657
|
+
if (estimateToolOutputTokens(normalized) <= maxTokens) return normalized;
|
|
8658
|
+
const suffix = "\u2026";
|
|
8659
|
+
return `${sliceStartByTokens(normalized, Math.max(0, maxTokens - estimatedTokenCost(suffix)))}${suffix}`;
|
|
8660
|
+
}
|
|
8661
|
+
|
|
7806
8662
|
// src/agent/rules.ts
|
|
7807
8663
|
import { existsSync as existsSync2 } from "node:fs";
|
|
7808
|
-
import { join as
|
|
7809
|
-
import { lstat as
|
|
8664
|
+
import { join as join14 } from "node:path";
|
|
8665
|
+
import { lstat as lstat16, readFile as readFile12 } from "node:fs/promises";
|
|
7810
8666
|
var workspaceRulePaths = [
|
|
7811
8667
|
"AGENTS.md",
|
|
7812
8668
|
"CLAUDE.md",
|
|
@@ -7815,14 +8671,14 @@ var workspaceRulePaths = [
|
|
|
7815
8671
|
];
|
|
7816
8672
|
async function discoverWorkspaceRules(workspace, maxChars = 12e4) {
|
|
7817
8673
|
const workspaceAccess = new WorkspaceAccess([workspace]);
|
|
7818
|
-
const activeProjectRules =
|
|
8674
|
+
const activeProjectRules = join14(resolveProjectNamespaceSync(workspace).active, "rules.md");
|
|
7819
8675
|
const projectCandidates = [
|
|
7820
|
-
...workspaceRulePaths.slice(0, 3).map((path) =>
|
|
8676
|
+
...workspaceRulePaths.slice(0, 3).map((path) => join14(workspace, path)),
|
|
7821
8677
|
activeProjectRules,
|
|
7822
|
-
...workspaceRulePaths.slice(3).map((path) =>
|
|
8678
|
+
...workspaceRulePaths.slice(3).map((path) => join14(workspace, path))
|
|
7823
8679
|
];
|
|
7824
8680
|
const candidates = [
|
|
7825
|
-
{ path:
|
|
8681
|
+
{ path: join14(resolveHomeNamespace(), "rules.md"), scope: "user" },
|
|
7826
8682
|
...projectCandidates.map((path) => ({
|
|
7827
8683
|
path,
|
|
7828
8684
|
scope: "workspace"
|
|
@@ -7833,13 +8689,13 @@ async function discoverWorkspaceRules(workspace, maxChars = 12e4) {
|
|
|
7833
8689
|
for (const candidate of candidates) {
|
|
7834
8690
|
if (remaining <= 0 || !existsSync2(candidate.path)) continue;
|
|
7835
8691
|
try {
|
|
7836
|
-
const info = await
|
|
8692
|
+
const info = await lstat16(candidate.path);
|
|
7837
8693
|
if (!info.isFile() || info.size > 1e6) continue;
|
|
7838
8694
|
if (candidate.scope === "workspace") {
|
|
7839
8695
|
const safePath = await workspaceAccess.resolvePath(candidate.path, { expect: "file" });
|
|
7840
8696
|
if (safePath !== candidate.path) continue;
|
|
7841
8697
|
}
|
|
7842
|
-
const raw = await
|
|
8698
|
+
const raw = await readFile12(candidate.path, "utf8");
|
|
7843
8699
|
if (raw.includes("\0")) continue;
|
|
7844
8700
|
const content = raw.slice(0, remaining);
|
|
7845
8701
|
rules.push({
|
|
@@ -7871,13 +8727,10 @@ function escapeAttribute4(value) {
|
|
|
7871
8727
|
}
|
|
7872
8728
|
|
|
7873
8729
|
// src/context/context-sources.ts
|
|
7874
|
-
import { readFile as
|
|
8730
|
+
import { readFile as readFile13, stat as stat9 } from "node:fs/promises";
|
|
7875
8731
|
var MAX_SOURCE_CHARS = 6e4;
|
|
7876
8732
|
var MAX_PINNED_CHARS = 16e4;
|
|
7877
8733
|
var MAX_CONTEXT_SOURCES = 32;
|
|
7878
|
-
function estimateTokens3(value) {
|
|
7879
|
-
return Math.ceil(value.length / 4);
|
|
7880
|
-
}
|
|
7881
8734
|
function sources(session) {
|
|
7882
8735
|
return session.contextSources ?? (session.contextSources = []);
|
|
7883
8736
|
}
|
|
@@ -7885,7 +8738,7 @@ async function pinContextSource(session, workspace, requested) {
|
|
|
7885
8738
|
const resolved = await workspace.resolvePath(requested, { expect: "file" });
|
|
7886
8739
|
const info = await stat9(resolved);
|
|
7887
8740
|
const alias = workspace.display(resolved);
|
|
7888
|
-
const tokens2 =
|
|
8741
|
+
const tokens2 = estimateTokens((await readFile13(resolved, "utf8")).slice(0, MAX_SOURCE_CHARS));
|
|
7889
8742
|
const list2 = sources(session);
|
|
7890
8743
|
const existing = list2.find((source2) => source2.path === alias);
|
|
7891
8744
|
if (existing) {
|
|
@@ -7935,9 +8788,9 @@ async function resolvePinnedContent(session, workspace) {
|
|
|
7935
8788
|
if (remaining <= 0) break;
|
|
7936
8789
|
try {
|
|
7937
8790
|
const safe = await workspace.resolvePath(source.path, { expect: "file" });
|
|
7938
|
-
const raw = await
|
|
8791
|
+
const raw = await readFile13(safe, "utf8");
|
|
7939
8792
|
const capped = raw.slice(0, Math.min(MAX_SOURCE_CHARS, remaining));
|
|
7940
|
-
source.tokens =
|
|
8793
|
+
source.tokens = estimateTokens(capped);
|
|
7941
8794
|
resolved.push({
|
|
7942
8795
|
path: source.path,
|
|
7943
8796
|
content: capped,
|
|
@@ -7983,6 +8836,7 @@ var AgentRunner = class {
|
|
|
7983
8836
|
contextEngine;
|
|
7984
8837
|
tools;
|
|
7985
8838
|
sessionStore;
|
|
8839
|
+
toolArtifactStore;
|
|
7986
8840
|
checkpointStore;
|
|
7987
8841
|
workspace;
|
|
7988
8842
|
hooks;
|
|
@@ -8004,6 +8858,7 @@ var AgentRunner = class {
|
|
|
8004
8858
|
contextEngine: this.contextEngine
|
|
8005
8859
|
});
|
|
8006
8860
|
this.sessionStore = options.sessionStore ?? new SessionStore(this.workspace.primaryRoot);
|
|
8861
|
+
this.toolArtifactStore = options.toolArtifactStore ?? new ToolArtifactStore(this.workspace.primaryRoot);
|
|
8007
8862
|
this.checkpointStore = options.checkpointStore ?? new CheckpointStore(this.workspace);
|
|
8008
8863
|
this.hooks = new HookRunner(options.config.hooks, this.workspace);
|
|
8009
8864
|
this.contextManager = options.contextManager ?? new ContextManager(options.config);
|
|
@@ -8096,17 +8951,25 @@ var AgentRunner = class {
|
|
|
8096
8951
|
};
|
|
8097
8952
|
try {
|
|
8098
8953
|
throwIfAborted(options.signal);
|
|
8954
|
+
await this.reconcileToolArtifacts();
|
|
8099
8955
|
if (this.session.messages.length === 0 && this.session.title === "New session") {
|
|
8100
8956
|
this.session.title = titleFromInput(request);
|
|
8101
8957
|
}
|
|
8102
8958
|
this.contextManager.startTurn(this.session, request);
|
|
8103
|
-
|
|
8959
|
+
const userMessage = message("user", request);
|
|
8960
|
+
this.session.messages.push(userMessage);
|
|
8104
8961
|
await this.persist();
|
|
8105
8962
|
const trivialTurn = isTrivialTurn(request);
|
|
8106
|
-
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 });
|
|
8107
8970
|
if (!trivialTurn) await emit({ type: "context", packed });
|
|
8108
8971
|
const mentions = await this.packMentions(request);
|
|
8109
|
-
const retrievedContext = buildRetrievedContext(
|
|
8972
|
+
const retrievedContext = trivialTurn && !mentions.length ? "" : buildRetrievedContext(
|
|
8110
8973
|
packed,
|
|
8111
8974
|
mentions,
|
|
8112
8975
|
this.workspace.primaryRoot,
|
|
@@ -8132,9 +8995,6 @@ var AgentRunner = class {
|
|
|
8132
8995
|
scope: augmentation.memoryScope ?? "session"
|
|
8133
8996
|
});
|
|
8134
8997
|
}
|
|
8135
|
-
const turnDirective = buildTurnDirective(request, {
|
|
8136
|
-
agents: Boolean(this.config.agents?.enabled)
|
|
8137
|
-
});
|
|
8138
8998
|
const contractEnabled = shouldUseTaskContract(
|
|
8139
8999
|
request,
|
|
8140
9000
|
turnDirective.intent,
|
|
@@ -8158,21 +9018,6 @@ var AgentRunner = class {
|
|
|
8158
9018
|
...augmentation.skills?.length ? [`skills:${augmentation.skills.length}`] : [],
|
|
8159
9019
|
...augmentation.memoryCount ? [`memory:${augmentation.memoryCount}`] : []
|
|
8160
9020
|
];
|
|
8161
|
-
if (!trivialTurn) await emit({
|
|
8162
|
-
type: "prompt",
|
|
8163
|
-
intent: turnDirective.intent,
|
|
8164
|
-
sections: promptSections,
|
|
8165
|
-
estimatedTokens: Math.ceil([
|
|
8166
|
-
turnDirective.text,
|
|
8167
|
-
buildSessionStatePrompt(this.session),
|
|
8168
|
-
this.contextManager.buildShortTermPrompt(this.session),
|
|
8169
|
-
options.turnInstructions ?? "",
|
|
8170
|
-
augmentation.text,
|
|
8171
|
-
retrievedContext,
|
|
8172
|
-
pinnedContext,
|
|
8173
|
-
workspaceRules
|
|
8174
|
-
].join("\n").length / 4)
|
|
8175
|
-
});
|
|
8176
9021
|
let verificationAttempted = false;
|
|
8177
9022
|
const maxTurns = options.maxTurns ?? this.config.agent.maxTurns;
|
|
8178
9023
|
const contextBudget = Math.max(24e3, Math.min(1e5, this.config.context.maxTokens * 3));
|
|
@@ -8187,24 +9032,29 @@ var AgentRunner = class {
|
|
|
8187
9032
|
}
|
|
8188
9033
|
this.applySteering();
|
|
8189
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");
|
|
8190
9043
|
const messages = packConversation(
|
|
8191
9044
|
stableSystemPrompt,
|
|
8192
|
-
|
|
8193
|
-
buildSessionStatePrompt(this.session),
|
|
8194
|
-
turnDirective.text,
|
|
8195
|
-
this.contextManager.buildShortTermPrompt(this.session),
|
|
8196
|
-
pinnedContext,
|
|
8197
|
-
options.turnInstructions ?? "",
|
|
8198
|
-
augmentation.text
|
|
8199
|
-
].filter(Boolean).join("\n\n"),
|
|
9045
|
+
dynamicPrompt,
|
|
8200
9046
|
retrievedContext,
|
|
8201
9047
|
activeMessages(this.session),
|
|
8202
9048
|
contextBudget
|
|
8203
9049
|
);
|
|
8204
9050
|
const availableTokens = this.config.agent.maxSessionTokens - (this.session.usage.inputTokens + this.session.usage.outputTokens);
|
|
8205
|
-
const
|
|
8206
|
-
|
|
9051
|
+
const visibleTools = visibleToolDefinitions(
|
|
9052
|
+
this.tools,
|
|
9053
|
+
options.askMode === true,
|
|
9054
|
+
contractEnabled,
|
|
9055
|
+
this.hasReadableToolArtifact()
|
|
8207
9056
|
);
|
|
9057
|
+
const estimatedInputTokens = estimateMessages2(messages) + estimateToolDefinitions(visibleTools);
|
|
8208
9058
|
if (availableTokens <= 0 || estimatedInputTokens >= availableTokens) {
|
|
8209
9059
|
return finishRun("token_budget");
|
|
8210
9060
|
}
|
|
@@ -8212,7 +9062,23 @@ var AgentRunner = class {
|
|
|
8212
9062
|
this.config.model.maxTokens ?? 8192,
|
|
8213
9063
|
availableTokens - estimatedInputTokens
|
|
8214
9064
|
));
|
|
8215
|
-
const
|
|
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
|
+
}
|
|
8216
9082
|
const assistantId = randomUUID12();
|
|
8217
9083
|
const response = await this.completeModel(
|
|
8218
9084
|
messages,
|
|
@@ -8228,17 +9094,49 @@ var AgentRunner = class {
|
|
|
8228
9094
|
assistantMessage.id = assistantId;
|
|
8229
9095
|
this.session.messages.push(assistantMessage);
|
|
8230
9096
|
if (response.content) await emit({ type: "assistant", id: assistantId, content: response.content });
|
|
8231
|
-
const
|
|
8232
|
-
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
|
|
8236
|
-
|
|
8237
|
-
|
|
8238
|
-
|
|
8239
|
-
|
|
8240
|
-
|
|
8241
|
-
|
|
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
|
+
});
|
|
8242
9140
|
await this.persist();
|
|
8243
9141
|
if (this.session.usage.inputTokens + this.session.usage.outputTokens >= this.config.agent.maxSessionTokens) {
|
|
8244
9142
|
for (const call of response.toolCalls) {
|
|
@@ -8370,17 +9268,17 @@ ${input2}`
|
|
|
8370
9268
|
async executeTool(call, options, emit, recovery = new ToolRecoveryController(), visibleToolNames) {
|
|
8371
9269
|
const preflight = recovery.preflight(call);
|
|
8372
9270
|
if (preflight) {
|
|
8373
|
-
const result =
|
|
9271
|
+
const result = await this.protectToolResult(
|
|
9272
|
+
failedResult(call, "Tool call rejected by the recovery circuit.", preflight)
|
|
9273
|
+
);
|
|
8374
9274
|
this.recordToolResult(result);
|
|
8375
9275
|
await emit({ type: "tool_result", result });
|
|
8376
9276
|
return result;
|
|
8377
9277
|
}
|
|
8378
9278
|
if (visibleToolNames && !visibleToolNames.has(call.name)) {
|
|
8379
9279
|
const receipt = recovery.recordFailure(call, "unknown_tool");
|
|
8380
|
-
const result =
|
|
8381
|
-
call,
|
|
8382
|
-
`Tool is not exposed for this turn: ${call.name}`,
|
|
8383
|
-
receipt
|
|
9280
|
+
const result = await this.protectToolResult(
|
|
9281
|
+
failedResult(call, `Tool is not exposed for this turn: ${call.name}`, receipt)
|
|
8384
9282
|
);
|
|
8385
9283
|
this.recordToolResult(result);
|
|
8386
9284
|
await emit({ type: "tool_result", result });
|
|
@@ -8389,7 +9287,7 @@ ${input2}`
|
|
|
8389
9287
|
const tool = this.tools.get(call.name);
|
|
8390
9288
|
if (!tool) {
|
|
8391
9289
|
const receipt = recovery.recordFailure(call, "unknown_tool");
|
|
8392
|
-
const result = failedResult(call, `Unknown tool: ${call.name}`, receipt);
|
|
9290
|
+
const result = await this.protectToolResult(failedResult(call, `Unknown tool: ${call.name}`, receipt));
|
|
8393
9291
|
this.recordToolResult(result);
|
|
8394
9292
|
await emit({ type: "tool_result", result });
|
|
8395
9293
|
return result;
|
|
@@ -8402,17 +9300,15 @@ ${input2}`
|
|
|
8402
9300
|
} catch (error) {
|
|
8403
9301
|
const failureClass = classifyThrownToolFailure(error, options.signal);
|
|
8404
9302
|
const receipt = recovery.recordFailure(call, failureClass);
|
|
8405
|
-
const result = failedResult(call, formatToolError(error), receipt);
|
|
9303
|
+
const result = await this.protectToolResult(failedResult(call, formatToolError(error), receipt));
|
|
8406
9304
|
this.recordToolResult(result, tool.definition.category);
|
|
8407
9305
|
await emit({ type: "tool_result", result });
|
|
8408
9306
|
return result;
|
|
8409
9307
|
}
|
|
8410
9308
|
if (categories.some((category) => category !== "read") && this.session.taskContract?.state === "draft") {
|
|
8411
9309
|
const receipt = recovery.recordFailure(call, "contract_required");
|
|
8412
|
-
const result =
|
|
8413
|
-
call,
|
|
8414
|
-
"Potentially mutating work is paused until the draft Task Contract is activated.",
|
|
8415
|
-
receipt
|
|
9310
|
+
const result = await this.protectToolResult(
|
|
9311
|
+
failedResult(call, "Potentially mutating work is paused until the draft Task Contract is activated.", receipt)
|
|
8416
9312
|
);
|
|
8417
9313
|
this.recordToolResult(result, tool.definition.category);
|
|
8418
9314
|
await emit({ type: "tool_result", result });
|
|
@@ -8422,7 +9318,9 @@ ${input2}`
|
|
|
8422
9318
|
const allowed = await this.authorize(call, category, options, emit);
|
|
8423
9319
|
if (!allowed) {
|
|
8424
9320
|
const receipt = recovery.recordFailure(call, "permission_denied");
|
|
8425
|
-
const result =
|
|
9321
|
+
const result = await this.protectToolResult(
|
|
9322
|
+
failedResult(call, `Permission denied for ${category} operation.`, receipt)
|
|
9323
|
+
);
|
|
8426
9324
|
this.recordToolResult(result, category);
|
|
8427
9325
|
await emit({ type: "tool_result", result });
|
|
8428
9326
|
return result;
|
|
@@ -8436,6 +9334,7 @@ ${input2}`
|
|
|
8436
9334
|
workspace: this.workspace,
|
|
8437
9335
|
session: this.session,
|
|
8438
9336
|
contextEngine: this.contextEngine,
|
|
9337
|
+
toolArtifactStore: this.toolArtifactStore,
|
|
8439
9338
|
emit,
|
|
8440
9339
|
...options.signal ? { signal: options.signal } : {}
|
|
8441
9340
|
};
|
|
@@ -8469,30 +9368,33 @@ ${input2}`
|
|
|
8469
9368
|
} catch (error) {
|
|
8470
9369
|
afterHookError = toError(error);
|
|
8471
9370
|
}
|
|
8472
|
-
|
|
8473
|
-
toolCallId: call.id,
|
|
8474
|
-
name: call.name,
|
|
8475
|
-
ok: execution.ok !== false && !afterHookError,
|
|
8476
|
-
content: truncateToolOutput(afterHookError ? `${execution.content}
|
|
9371
|
+
let completeContent = afterHookError ? `${execution.content}
|
|
8477
9372
|
|
|
8478
|
-
Tool succeeded, but afterTool hook failed: ${afterHookError.message}` : execution.content
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
}
|
|
9373
|
+
Tool succeeded, but afterTool hook failed: ${afterHookError.message}` : execution.content;
|
|
9374
|
+
const metadata = {
|
|
9375
|
+
...execution.metadata ?? {},
|
|
9376
|
+
...changedFiles.length ? { changedFiles } : {},
|
|
9377
|
+
...checkpointId ? { checkpointId } : {},
|
|
9378
|
+
...beforeHooks.length || afterHooks.length ? { hooks: { before: beforeHooks.length, after: afterHooks.length } } : {},
|
|
9379
|
+
...afterHookError ? { toolSucceeded: true, hookError: afterHookError.message } : {}
|
|
8486
9380
|
};
|
|
8487
|
-
|
|
8488
|
-
|
|
9381
|
+
const ok = execution.ok !== false && !afterHookError;
|
|
9382
|
+
if (!ok) {
|
|
9383
|
+
const failureClass = options.signal?.aborted ? "cancelled" : classifyToolFailure({ toolCallId: call.id, name: call.name, ok, content: completeContent, metadata });
|
|
8489
9384
|
const receipt = recovery.recordFailure(call, failureClass);
|
|
8490
|
-
|
|
8491
|
-
${
|
|
8492
|
-
|
|
9385
|
+
completeContent = `${formatFailureReceipt(receipt)}
|
|
9386
|
+
${completeContent}`;
|
|
9387
|
+
metadata.failure = receipt;
|
|
8493
9388
|
} else {
|
|
8494
9389
|
recovery.recordSuccess(call);
|
|
8495
9390
|
}
|
|
9391
|
+
const result = await this.protectToolResult({
|
|
9392
|
+
toolCallId: call.id,
|
|
9393
|
+
name: call.name,
|
|
9394
|
+
ok,
|
|
9395
|
+
content: completeContent,
|
|
9396
|
+
metadata
|
|
9397
|
+
});
|
|
8496
9398
|
this.contextManager.recordTool(this.session, call, result);
|
|
8497
9399
|
if (JSON.stringify(this.session.tasks) !== tasksBefore || call.name === "task") {
|
|
8498
9400
|
await emit({ type: "tasks", tasks: this.session.tasks.map((task) => ({ ...task })) });
|
|
@@ -8507,7 +9409,7 @@ ${result.content}`);
|
|
|
8507
9409
|
const normalized = toError(error);
|
|
8508
9410
|
const failureClass = classifyThrownToolFailure(normalized, options.signal);
|
|
8509
9411
|
const receipt = recovery.recordFailure(call, failureClass);
|
|
8510
|
-
const result = failedResult(call, formatToolError(error), receipt);
|
|
9412
|
+
const result = await this.protectToolResult(failedResult(call, formatToolError(error), receipt));
|
|
8511
9413
|
this.recordToolResult(result, tool.definition.category);
|
|
8512
9414
|
await emit({ type: "tool_result", result });
|
|
8513
9415
|
return result;
|
|
@@ -8613,8 +9515,8 @@ ${result.content}`);
|
|
|
8613
9515
|
toolCalls: toolCalls.map((call) => ({ id: call.id, name: call.name }))
|
|
8614
9516
|
}, signal);
|
|
8615
9517
|
}
|
|
8616
|
-
async packContext(input2) {
|
|
8617
|
-
return this.contextEngine.pack(input2);
|
|
9518
|
+
async packContext(input2, options) {
|
|
9519
|
+
return this.contextEngine.pack(input2, options);
|
|
8618
9520
|
}
|
|
8619
9521
|
async packMentions(input2) {
|
|
8620
9522
|
try {
|
|
@@ -8661,6 +9563,63 @@ ${result.content}`);
|
|
|
8661
9563
|
persist() {
|
|
8662
9564
|
return this.persistSession ? this.sessionStore.save(this.session) : Promise.resolve();
|
|
8663
9565
|
}
|
|
9566
|
+
toolOutputBudget() {
|
|
9567
|
+
const contextWindowTokens = Math.max(24e3, Math.min(1e5, this.config.context.maxTokens * 3));
|
|
9568
|
+
const activeContextTokens = this.contextManager.status(this.session, contextWindowTokens).activeTokens;
|
|
9569
|
+
const remainingSessionTokens = this.config.agent.maxSessionTokens - (this.session.usage.inputTokens + this.session.usage.outputTokens);
|
|
9570
|
+
return dynamicToolOutputBudget(contextWindowTokens, activeContextTokens, remainingSessionTokens);
|
|
9571
|
+
}
|
|
9572
|
+
async protectToolResult(result) {
|
|
9573
|
+
const metadata = { ...result.metadata ?? {} };
|
|
9574
|
+
const output2 = await protectToolOutput({
|
|
9575
|
+
content: result.content,
|
|
9576
|
+
sessionId: this.session.id,
|
|
9577
|
+
toolCallId: result.toolCallId,
|
|
9578
|
+
tool: result.name,
|
|
9579
|
+
ok: result.ok,
|
|
9580
|
+
budgetTokens: this.toolOutputBudget(),
|
|
9581
|
+
metadata,
|
|
9582
|
+
artifacts: this.toolArtifactStore
|
|
9583
|
+
});
|
|
9584
|
+
if (output2.metadata.artifact) this.registerToolArtifact({
|
|
9585
|
+
...output2.metadata.artifact,
|
|
9586
|
+
redacted: output2.metadata.redacted
|
|
9587
|
+
});
|
|
9588
|
+
return {
|
|
9589
|
+
...result,
|
|
9590
|
+
content: output2.content,
|
|
9591
|
+
...output2.metadata.truncated || output2.metadata.redacted || output2.metadata.sanitized || output2.metadata.artifact || output2.metadata.artifactUnavailable ? { metadata: { ...metadata, toolOutput: output2.metadata } } : Object.keys(metadata).length ? { metadata } : {}
|
|
9592
|
+
};
|
|
9593
|
+
}
|
|
9594
|
+
hasReadableToolArtifact() {
|
|
9595
|
+
const now = Date.now();
|
|
9596
|
+
return (this.session.toolArtifacts ?? []).some(
|
|
9597
|
+
(artifact) => Date.parse(artifact.expiresAt) > now
|
|
9598
|
+
);
|
|
9599
|
+
}
|
|
9600
|
+
registerToolArtifact(artifact) {
|
|
9601
|
+
const now = Date.now();
|
|
9602
|
+
const artifacts = (this.session.toolArtifacts ?? []).filter((candidate) => Date.parse(candidate.expiresAt) > now && candidate.toolCallId !== artifact.toolCallId);
|
|
9603
|
+
artifacts.push(artifact);
|
|
9604
|
+
artifacts.sort((left, right) => left.createdAt.localeCompare(right.createdAt));
|
|
9605
|
+
if (artifacts.length > 200) artifacts.splice(0, artifacts.length - 200);
|
|
9606
|
+
this.session.toolArtifacts = artifacts;
|
|
9607
|
+
}
|
|
9608
|
+
async reconcileToolArtifacts() {
|
|
9609
|
+
let stored;
|
|
9610
|
+
try {
|
|
9611
|
+
stored = await this.toolArtifactStore.prune(this.session.id);
|
|
9612
|
+
} catch {
|
|
9613
|
+
this.session.toolArtifacts = [];
|
|
9614
|
+
return;
|
|
9615
|
+
}
|
|
9616
|
+
const valid = new Map(stored.map((artifact) => [artifact.toolCallId, artifact]));
|
|
9617
|
+
const reconciled = (this.session.toolArtifacts ?? []).filter((receipt) => {
|
|
9618
|
+
const artifact = valid.get(receipt.toolCallId);
|
|
9619
|
+
return artifact !== void 0 && artifact.sha256 === receipt.sha256 && artifact.bytes === receipt.bytes && artifact.createdAt === receipt.createdAt && artifact.expiresAt === receipt.expiresAt && artifact.redacted === receipt.redacted;
|
|
9620
|
+
});
|
|
9621
|
+
this.session.toolArtifacts = reconciled;
|
|
9622
|
+
}
|
|
8664
9623
|
};
|
|
8665
9624
|
function message(role, content, extra = {}) {
|
|
8666
9625
|
return {
|
|
@@ -8675,14 +9634,14 @@ function packConversation(systemPrompt, dynamicPrompt, retrievedContext, history
|
|
|
8675
9634
|
const system = message("system", systemPrompt);
|
|
8676
9635
|
const dynamic = dynamicPrompt ? message("system", dynamicPrompt) : void 0;
|
|
8677
9636
|
const context = retrievedContext ? message("system", retrievedContext) : void 0;
|
|
8678
|
-
const reserved =
|
|
9637
|
+
const reserved = estimateTokens(system.content) + estimateTokens(dynamic?.content ?? "") + estimateTokens(context?.content ?? "");
|
|
8679
9638
|
const budget = Math.max(4e3, tokenBudget - reserved);
|
|
8680
9639
|
const groups = groupMessages(clearOldToolResults(history));
|
|
8681
9640
|
const selected = [];
|
|
8682
9641
|
let used = 0;
|
|
8683
9642
|
for (let index = groups.length - 1; index >= 0; index -= 1) {
|
|
8684
9643
|
const group = groups[index] ?? [];
|
|
8685
|
-
const cost = group.reduce((sum, item) => sum +
|
|
9644
|
+
const cost = group.reduce((sum, item) => sum + estimateTokens(item.content) + estimateTokens(JSON.stringify(item.toolCalls ?? [])), 0);
|
|
8686
9645
|
if (selected.length && used + cost > budget) break;
|
|
8687
9646
|
selected.unshift(group);
|
|
8688
9647
|
used += cost;
|
|
@@ -8720,17 +9679,114 @@ function groupMessages(messages) {
|
|
|
8720
9679
|
}
|
|
8721
9680
|
return groups;
|
|
8722
9681
|
}
|
|
8723
|
-
function estimateTokens4(input2) {
|
|
8724
|
-
return Math.ceil(input2.length / 4);
|
|
8725
|
-
}
|
|
8726
9682
|
function estimateMessages2(messages) {
|
|
8727
|
-
return messages.reduce((total, item) => total +
|
|
9683
|
+
return messages.reduce((total, item) => total + estimateTokens(item.content) + estimateTokens(JSON.stringify(item.toolCalls ?? [])), 0);
|
|
8728
9684
|
}
|
|
8729
9685
|
function estimateToolDefinitions(tools) {
|
|
8730
|
-
return
|
|
9686
|
+
return estimateTokens(JSON.stringify(tools));
|
|
8731
9687
|
}
|
|
8732
9688
|
function estimateResponseTokens(response) {
|
|
8733
|
-
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";
|
|
8734
9790
|
}
|
|
8735
9791
|
function uniqueCategories(categories) {
|
|
8736
9792
|
return [...new Set(categories)];
|
|
@@ -8740,21 +9796,16 @@ function failedResult(call, content, failure) {
|
|
|
8740
9796
|
toolCallId: call.id,
|
|
8741
9797
|
name: call.name,
|
|
8742
9798
|
ok: false,
|
|
8743
|
-
content:
|
|
8744
|
-
${content}` : content
|
|
9799
|
+
content: failure ? `${formatFailureReceipt(failure)}
|
|
9800
|
+
${content}` : content,
|
|
8745
9801
|
...failure ? { metadata: { failure } } : {}
|
|
8746
9802
|
};
|
|
8747
9803
|
}
|
|
8748
|
-
function visibleToolDefinitions(tools, askMode, contractEnabled) {
|
|
9804
|
+
function visibleToolDefinitions(tools, askMode, contractEnabled, artifactReadAvailable) {
|
|
8749
9805
|
return tools.definitions().filter(
|
|
8750
|
-
(tool) => (!askMode || tool.category === "read") && (contractEnabled || tool.name !== "task_contract")
|
|
9806
|
+
(tool) => (!askMode || tool.category === "read") && (contractEnabled || tool.name !== "task_contract") && (artifactReadAvailable || tool.name !== "read_tool_artifact")
|
|
8751
9807
|
);
|
|
8752
9808
|
}
|
|
8753
|
-
function truncateToolOutput(content) {
|
|
8754
|
-
const max = 8e4;
|
|
8755
|
-
return content.length <= max ? content : `${content.slice(0, max)}
|
|
8756
|
-
\u2026 tool output truncated`;
|
|
8757
|
-
}
|
|
8758
9809
|
function formatToolError(error) {
|
|
8759
9810
|
const normalized = toError(error);
|
|
8760
9811
|
if (normalized.name === "ZodError") return `Invalid tool arguments: ${normalized.message}`;
|
|
@@ -8787,9 +9838,9 @@ async function safeEmit(emit, event) {
|
|
|
8787
9838
|
}
|
|
8788
9839
|
|
|
8789
9840
|
// src/agent/profiles.ts
|
|
8790
|
-
import { lstat as
|
|
9841
|
+
import { lstat as lstat17, readFile as readFile14, readdir as readdir6 } from "node:fs/promises";
|
|
8791
9842
|
import { homedir as homedir3 } from "node:os";
|
|
8792
|
-
import { basename as basename8, join as
|
|
9843
|
+
import { basename as basename8, join as join15 } from "node:path";
|
|
8793
9844
|
import { parse as parseYaml2 } from "yaml";
|
|
8794
9845
|
var builtInProfiles = [
|
|
8795
9846
|
{
|
|
@@ -8881,14 +9932,14 @@ var AgentProfileCatalog = class {
|
|
|
8881
9932
|
profiles = new Map(builtInProfiles.map((profile) => [profile.name, profile]));
|
|
8882
9933
|
async discover() {
|
|
8883
9934
|
const locations = [
|
|
8884
|
-
{ path:
|
|
8885
|
-
{ path:
|
|
8886
|
-
{ path:
|
|
8887
|
-
{ path:
|
|
9935
|
+
{ path: join15(resolveHomeNamespace(), "agents"), source: "user" },
|
|
9936
|
+
{ path: join15(homedir3(), ".claude", "agents"), source: "user" },
|
|
9937
|
+
{ path: join15(resolveProjectNamespaceSync(this.workspace).active, "agents"), source: "workspace" },
|
|
9938
|
+
{ path: join15(this.workspace, ".claude", "agents"), source: "workspace" }
|
|
8888
9939
|
];
|
|
8889
9940
|
for (const location of locations) {
|
|
8890
9941
|
for (const file of await markdownFiles(location.path)) {
|
|
8891
|
-
const profile = await readProfile(
|
|
9942
|
+
const profile = await readProfile(join15(location.path, file), location.source);
|
|
8892
9943
|
if (profile) this.profiles.set(profile.name, profile);
|
|
8893
9944
|
}
|
|
8894
9945
|
}
|
|
@@ -8904,18 +9955,18 @@ var AgentProfileCatalog = class {
|
|
|
8904
9955
|
};
|
|
8905
9956
|
async function markdownFiles(directory) {
|
|
8906
9957
|
try {
|
|
8907
|
-
const info = await
|
|
9958
|
+
const info = await lstat17(directory);
|
|
8908
9959
|
if (!info.isDirectory() || info.isSymbolicLink()) return [];
|
|
8909
|
-
return (await
|
|
9960
|
+
return (await readdir6(directory, { withFileTypes: true })).filter((entry) => entry.isFile() && !entry.isSymbolicLink() && entry.name.endsWith(".md")).map((entry) => entry.name).slice(0, 200);
|
|
8910
9961
|
} catch {
|
|
8911
9962
|
return [];
|
|
8912
9963
|
}
|
|
8913
9964
|
}
|
|
8914
9965
|
async function readProfile(path, source) {
|
|
8915
9966
|
try {
|
|
8916
|
-
const info = await
|
|
9967
|
+
const info = await lstat17(path);
|
|
8917
9968
|
if (!info.isFile() || info.isSymbolicLink() || info.size > 1e5) return void 0;
|
|
8918
|
-
const raw = await
|
|
9969
|
+
const raw = await readFile14(path, "utf8");
|
|
8919
9970
|
const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
8920
9971
|
const frontmatter = match ? parseYaml2(match[1] ?? "") : {};
|
|
8921
9972
|
const prompt = (match ? raw.slice(match[0].length) : raw).trim();
|
|
@@ -8943,8 +9994,8 @@ function integer(value, fallback, min, max) {
|
|
|
8943
9994
|
|
|
8944
9995
|
// src/agent/delegation.ts
|
|
8945
9996
|
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
8946
|
-
import { join as
|
|
8947
|
-
import { z as
|
|
9997
|
+
import { join as join18 } from "node:path";
|
|
9998
|
+
import { z as z18 } from "zod";
|
|
8948
9999
|
|
|
8949
10000
|
// src/agent/external-runtime.ts
|
|
8950
10001
|
async function runExternalAgent(request) {
|
|
@@ -9071,95 +10122,95 @@ function numeric(...values) {
|
|
|
9071
10122
|
}
|
|
9072
10123
|
|
|
9073
10124
|
// src/agent/team-store.ts
|
|
9074
|
-
import { createHash as
|
|
9075
|
-
import { lstat as
|
|
9076
|
-
import { join as
|
|
9077
|
-
import { z as
|
|
9078
|
-
var runIdSchema =
|
|
9079
|
-
var hashSchema =
|
|
9080
|
-
var
|
|
10125
|
+
import { createHash as createHash12, randomUUID as randomUUID13 } from "node:crypto";
|
|
10126
|
+
import { lstat as lstat18, readFile as readFile15, readdir as readdir7, rm as rm3 } from "node:fs/promises";
|
|
10127
|
+
import { join as join16, resolve as resolve16 } from "node:path";
|
|
10128
|
+
import { z as z17 } from "zod";
|
|
10129
|
+
var runIdSchema = z17.string().uuid();
|
|
10130
|
+
var hashSchema = z17.string().regex(/^[a-f0-9]{64}$/u);
|
|
10131
|
+
var artifactSchema2 = z17.object({
|
|
9081
10132
|
sha256: hashSchema,
|
|
9082
|
-
bytes:
|
|
10133
|
+
bytes: z17.number().int().nonnegative().max(5e5)
|
|
9083
10134
|
}).strict();
|
|
9084
|
-
var phaseSchema =
|
|
9085
|
-
var agentRecordSchema =
|
|
9086
|
-
id:
|
|
9087
|
-
profile:
|
|
9088
|
-
provider:
|
|
9089
|
-
model:
|
|
10135
|
+
var phaseSchema = z17.enum(["work", "review", "revision", "write"]);
|
|
10136
|
+
var agentRecordSchema = z17.object({
|
|
10137
|
+
id: z17.string().uuid(),
|
|
10138
|
+
profile: z17.string(),
|
|
10139
|
+
provider: z17.string(),
|
|
10140
|
+
model: z17.string(),
|
|
9090
10141
|
phase: phaseSchema,
|
|
9091
|
-
ok:
|
|
9092
|
-
createdAt:
|
|
9093
|
-
startedAt:
|
|
9094
|
-
endedAt:
|
|
9095
|
-
durationMs:
|
|
9096
|
-
toolCalls:
|
|
9097
|
-
usage:
|
|
9098
|
-
inputTokens:
|
|
9099
|
-
outputTokens:
|
|
10142
|
+
ok: z17.boolean(),
|
|
10143
|
+
createdAt: z17.string(),
|
|
10144
|
+
startedAt: z17.string().optional(),
|
|
10145
|
+
endedAt: z17.string().optional(),
|
|
10146
|
+
durationMs: z17.number().int().nonnegative().optional(),
|
|
10147
|
+
toolCalls: z17.number().int().nonnegative().optional(),
|
|
10148
|
+
usage: z17.object({
|
|
10149
|
+
inputTokens: z17.number().int().nonnegative(),
|
|
10150
|
+
outputTokens: z17.number().int().nonnegative()
|
|
9100
10151
|
}).strict().optional(),
|
|
9101
|
-
report:
|
|
10152
|
+
report: artifactSchema2
|
|
9102
10153
|
}).strict();
|
|
9103
|
-
var messageRecordSchema =
|
|
9104
|
-
id:
|
|
9105
|
-
from:
|
|
9106
|
-
to:
|
|
9107
|
-
createdAt:
|
|
9108
|
-
content:
|
|
10154
|
+
var messageRecordSchema = z17.object({
|
|
10155
|
+
id: z17.string().uuid(),
|
|
10156
|
+
from: z17.string(),
|
|
10157
|
+
to: z17.string(),
|
|
10158
|
+
createdAt: z17.string(),
|
|
10159
|
+
content: artifactSchema2
|
|
9109
10160
|
}).strict();
|
|
9110
|
-
var writerIntegrationSchema =
|
|
9111
|
-
status:
|
|
9112
|
-
checkedAt:
|
|
9113
|
-
detail:
|
|
9114
|
-
checkpoint:
|
|
9115
|
-
sessionId:
|
|
9116
|
-
checkpointId:
|
|
10161
|
+
var writerIntegrationSchema = z17.object({
|
|
10162
|
+
status: z17.enum(["ready", "conflict", "integrated"]),
|
|
10163
|
+
checkedAt: z17.string(),
|
|
10164
|
+
detail: z17.string().max(2e4),
|
|
10165
|
+
checkpoint: z17.object({
|
|
10166
|
+
sessionId: z17.string(),
|
|
10167
|
+
checkpointId: z17.string()
|
|
9117
10168
|
}).strict().optional(),
|
|
9118
|
-
integratedAt:
|
|
10169
|
+
integratedAt: z17.string().optional()
|
|
9119
10170
|
}).strict();
|
|
9120
|
-
var writerLaneSchema =
|
|
9121
|
-
profile:
|
|
9122
|
-
reviewer:
|
|
9123
|
-
baseCommit:
|
|
9124
|
-
outcome:
|
|
9125
|
-
patch:
|
|
9126
|
-
files:
|
|
9127
|
-
worktreeCleaned:
|
|
9128
|
-
review:
|
|
10171
|
+
var writerLaneSchema = z17.object({
|
|
10172
|
+
profile: z17.string(),
|
|
10173
|
+
reviewer: z17.string(),
|
|
10174
|
+
baseCommit: z17.string().regex(/^[a-f0-9]{40,64}$/u),
|
|
10175
|
+
outcome: z17.enum(["accepted", "rejected", "failed", "cancelled"]),
|
|
10176
|
+
patch: artifactSchema2,
|
|
10177
|
+
files: z17.array(z17.string().min(1).max(4e3)).max(2e3),
|
|
10178
|
+
worktreeCleaned: z17.boolean(),
|
|
10179
|
+
review: artifactSchema2.optional(),
|
|
9129
10180
|
integration: writerIntegrationSchema.optional()
|
|
9130
10181
|
}).strict();
|
|
9131
10182
|
var manifestFields = {
|
|
9132
10183
|
id: runIdSchema,
|
|
9133
|
-
workspace:
|
|
9134
|
-
objective:
|
|
9135
|
-
reviewer:
|
|
9136
|
-
createdAt:
|
|
9137
|
-
updatedAt:
|
|
9138
|
-
status:
|
|
9139
|
-
maxReviewRounds:
|
|
9140
|
-
reviewRounds:
|
|
9141
|
-
agents:
|
|
9142
|
-
messages:
|
|
10184
|
+
workspace: z17.string(),
|
|
10185
|
+
objective: z17.string().max(3e4),
|
|
10186
|
+
reviewer: z17.string(),
|
|
10187
|
+
createdAt: z17.string(),
|
|
10188
|
+
updatedAt: z17.string(),
|
|
10189
|
+
status: z17.enum(["running", "accepted", "rejected", "failed"]),
|
|
10190
|
+
maxReviewRounds: z17.number().int().min(0).max(3),
|
|
10191
|
+
reviewRounds: z17.number().int().min(0).max(3),
|
|
10192
|
+
agents: z17.array(agentRecordSchema).max(256),
|
|
10193
|
+
messages: z17.array(messageRecordSchema).max(512)
|
|
9143
10194
|
};
|
|
9144
|
-
var manifestV1Schema =
|
|
9145
|
-
version:
|
|
10195
|
+
var manifestV1Schema = z17.object({
|
|
10196
|
+
version: z17.literal(1),
|
|
9146
10197
|
...manifestFields
|
|
9147
10198
|
}).strict();
|
|
9148
|
-
var manifestV2Schema =
|
|
9149
|
-
version:
|
|
10199
|
+
var manifestV2Schema = z17.object({
|
|
10200
|
+
version: z17.literal(2),
|
|
9150
10201
|
...manifestFields,
|
|
9151
10202
|
writer: writerLaneSchema.optional()
|
|
9152
10203
|
}).strict();
|
|
9153
|
-
var manifestSchema2 =
|
|
10204
|
+
var manifestSchema2 = z17.discriminatedUnion("version", [manifestV1Schema, manifestV2Schema]);
|
|
9154
10205
|
var TeamRunStore = class {
|
|
9155
10206
|
workspace;
|
|
9156
10207
|
directory;
|
|
9157
10208
|
managedDirectory;
|
|
9158
10209
|
writes = Promise.resolve();
|
|
9159
10210
|
constructor(workspace, directory) {
|
|
9160
|
-
this.workspace =
|
|
10211
|
+
this.workspace = resolve16(workspace);
|
|
9161
10212
|
this.managedDirectory = directory === void 0;
|
|
9162
|
-
this.directory = directory ?
|
|
10213
|
+
this.directory = directory ? resolve16(directory) : join16(resolveProjectNamespaceSync(this.workspace).active, "team-runs");
|
|
9163
10214
|
}
|
|
9164
10215
|
async create(input2) {
|
|
9165
10216
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -9243,10 +10294,10 @@ var TeamRunStore = class {
|
|
|
9243
10294
|
runIdSchema.parse(runId);
|
|
9244
10295
|
await this.writes;
|
|
9245
10296
|
await this.assertRunDirectory(runId);
|
|
9246
|
-
const path =
|
|
10297
|
+
const path = join16(this.runDirectory(runId), "manifest.json");
|
|
9247
10298
|
await this.assertRegularFile(path);
|
|
9248
|
-
const manifest = manifestSchema2.parse(JSON.parse(await
|
|
9249
|
-
if (manifest.id !== runId ||
|
|
10299
|
+
const manifest = manifestSchema2.parse(JSON.parse(await readFile15(path, "utf8")));
|
|
10300
|
+
if (manifest.id !== runId || resolve16(manifest.workspace) !== this.workspace) {
|
|
9250
10301
|
throw new Error("Team run manifest identity does not match its location.");
|
|
9251
10302
|
}
|
|
9252
10303
|
if (verify) {
|
|
@@ -9260,13 +10311,13 @@ var TeamRunStore = class {
|
|
|
9260
10311
|
}
|
|
9261
10312
|
async readArtifact(runId, artifact) {
|
|
9262
10313
|
await this.verifyArtifact(runId, artifact);
|
|
9263
|
-
return
|
|
10314
|
+
return readFile15(this.artifactPath(runId, artifact.sha256), "utf8");
|
|
9264
10315
|
}
|
|
9265
10316
|
async list() {
|
|
9266
10317
|
await this.writes;
|
|
9267
10318
|
try {
|
|
9268
10319
|
await assertNoSymlinkPath(this.workspace, this.directory);
|
|
9269
|
-
const entries = await
|
|
10320
|
+
const entries = await readdir7(this.directory, { withFileTypes: true });
|
|
9270
10321
|
const summaries = [];
|
|
9271
10322
|
for (const entry of entries) {
|
|
9272
10323
|
if (!entry.isDirectory() || entry.isSymbolicLink() || !runIdSchema.safeParse(entry.name).success) continue;
|
|
@@ -9289,9 +10340,9 @@ var TeamRunStore = class {
|
|
|
9289
10340
|
const directory = this.runDirectory(runId);
|
|
9290
10341
|
await assertNoSymlinkPath(this.workspace, directory);
|
|
9291
10342
|
try {
|
|
9292
|
-
const info = await
|
|
10343
|
+
const info = await lstat18(directory);
|
|
9293
10344
|
if (!info.isDirectory() || info.isSymbolicLink()) throw new Error("Team run storage is not a regular directory.");
|
|
9294
|
-
await
|
|
10345
|
+
await rm3(directory, { recursive: true });
|
|
9295
10346
|
return true;
|
|
9296
10347
|
} catch (error) {
|
|
9297
10348
|
if (error.code === "ENOENT") return false;
|
|
@@ -9314,31 +10365,31 @@ var TeamRunStore = class {
|
|
|
9314
10365
|
}
|
|
9315
10366
|
async loadUnlocked(runId) {
|
|
9316
10367
|
await this.assertRunDirectory(runId);
|
|
9317
|
-
const path =
|
|
10368
|
+
const path = join16(this.runDirectory(runId), "manifest.json");
|
|
9318
10369
|
await this.assertRegularFile(path);
|
|
9319
|
-
return manifestSchema2.parse(JSON.parse(await
|
|
10370
|
+
return manifestSchema2.parse(JSON.parse(await readFile15(path, "utf8")));
|
|
9320
10371
|
}
|
|
9321
10372
|
async writeManifest(manifest) {
|
|
9322
10373
|
const directory = this.runDirectory(manifest.id);
|
|
9323
10374
|
await ensureWorkspaceStorageDirectory(this.workspace, directory, {
|
|
9324
10375
|
requireActiveNamespace: this.managedDirectory
|
|
9325
10376
|
});
|
|
9326
|
-
await atomicWrite(
|
|
10377
|
+
await atomicWrite(join16(directory, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
9327
10378
|
`, 384);
|
|
9328
10379
|
}
|
|
9329
10380
|
async writeArtifact(runId, content, truncate = true) {
|
|
9330
10381
|
const data = boundedArtifactText(content, 5e5, truncate);
|
|
9331
10382
|
const bytes = Buffer.byteLength(data);
|
|
9332
|
-
const sha256 =
|
|
9333
|
-
const directory =
|
|
10383
|
+
const sha256 = createHash12("sha256").update(data).digest("hex");
|
|
10384
|
+
const directory = join16(this.runDirectory(runId), "blobs");
|
|
9334
10385
|
await ensureWorkspaceStorageDirectory(this.workspace, directory, {
|
|
9335
10386
|
requireActiveNamespace: this.managedDirectory
|
|
9336
10387
|
});
|
|
9337
|
-
const path =
|
|
10388
|
+
const path = join16(directory, `${sha256}.txt`);
|
|
9338
10389
|
try {
|
|
9339
10390
|
await this.assertRegularFile(path);
|
|
9340
|
-
const existing = await
|
|
9341
|
-
if (
|
|
10391
|
+
const existing = await readFile15(path);
|
|
10392
|
+
if (createHash12("sha256").update(existing).digest("hex") !== sha256) {
|
|
9342
10393
|
throw new Error(`Team artifact hash collision or corruption: ${sha256}`);
|
|
9343
10394
|
}
|
|
9344
10395
|
} catch (error) {
|
|
@@ -9358,27 +10409,27 @@ var TeamRunStore = class {
|
|
|
9358
10409
|
hashSchema.parse(artifact.sha256);
|
|
9359
10410
|
const path = this.artifactPath(runId, artifact.sha256);
|
|
9360
10411
|
await this.assertRegularFile(path);
|
|
9361
|
-
const data = await
|
|
9362
|
-
const
|
|
9363
|
-
if (
|
|
10412
|
+
const data = await readFile15(path);
|
|
10413
|
+
const hash2 = createHash12("sha256").update(data).digest("hex");
|
|
10414
|
+
if (hash2 !== artifact.sha256 || data.byteLength !== artifact.bytes) {
|
|
9364
10415
|
throw new Error(`Team artifact integrity check failed: ${artifact.sha256}`);
|
|
9365
10416
|
}
|
|
9366
10417
|
}
|
|
9367
10418
|
artifactPath(runId, sha256) {
|
|
9368
|
-
return
|
|
10419
|
+
return join16(this.runDirectory(runId), "blobs", `${sha256}.txt`);
|
|
9369
10420
|
}
|
|
9370
10421
|
runDirectory(runId) {
|
|
9371
|
-
return
|
|
10422
|
+
return join16(this.directory, runId);
|
|
9372
10423
|
}
|
|
9373
10424
|
async assertRunDirectory(runId) {
|
|
9374
10425
|
const directory = this.runDirectory(runId);
|
|
9375
10426
|
await assertNoSymlinkPath(this.workspace, directory);
|
|
9376
|
-
const info = await
|
|
10427
|
+
const info = await lstat18(directory);
|
|
9377
10428
|
if (!info.isDirectory() || info.isSymbolicLink()) throw new Error("Team run storage is not a regular directory.");
|
|
9378
10429
|
}
|
|
9379
10430
|
async assertRegularFile(path) {
|
|
9380
|
-
await assertNoSymlinkPath(this.workspace,
|
|
9381
|
-
const info = await
|
|
10431
|
+
await assertNoSymlinkPath(this.workspace, resolve16(path, ".."));
|
|
10432
|
+
const info = await lstat18(path);
|
|
9382
10433
|
if (!info.isFile() || info.isSymbolicLink()) throw new Error(`Team run file is not a regular file: ${path}`);
|
|
9383
10434
|
}
|
|
9384
10435
|
};
|
|
@@ -9422,10 +10473,10 @@ function resolveAgentModelRoute(team, parent, profile) {
|
|
|
9422
10473
|
}
|
|
9423
10474
|
|
|
9424
10475
|
// src/agent/writer-lane.ts
|
|
9425
|
-
import { createHash as
|
|
9426
|
-
import { lstat as
|
|
10476
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
10477
|
+
import { lstat as lstat19, mkdtemp, realpath as realpath7, rm as rm4 } from "node:fs/promises";
|
|
9427
10478
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
9428
|
-
import { isAbsolute as isAbsolute5, join as
|
|
10479
|
+
import { isAbsolute as isAbsolute5, join as join17, resolve as resolve17 } from "node:path";
|
|
9429
10480
|
var WriterLaneApplyError = class extends Error {
|
|
9430
10481
|
constructor(message2, attempted, cause) {
|
|
9431
10482
|
super(message2, cause === void 0 ? void 0 : { cause });
|
|
@@ -9438,18 +10489,18 @@ var WriterLane = class {
|
|
|
9438
10489
|
workspace;
|
|
9439
10490
|
constructor(workspace, workspaceRoots) {
|
|
9440
10491
|
this.workspace = new WorkspaceAccess([
|
|
9441
|
-
|
|
9442
|
-
...workspaceRoots.map((root) =>
|
|
10492
|
+
resolve17(workspace),
|
|
10493
|
+
...workspaceRoots.map((root) => resolve17(root))
|
|
9443
10494
|
]);
|
|
9444
10495
|
}
|
|
9445
10496
|
async createDraft(maxPatchBytes, operation, signal) {
|
|
9446
10497
|
const repository = await this.repository();
|
|
9447
10498
|
const baseCommit = await this.head(repository);
|
|
9448
10499
|
const lease = await acquireNamespaceLease(
|
|
9449
|
-
|
|
10500
|
+
join17(repository.commonDirectory, "skein-writer-lane"),
|
|
9450
10501
|
"exclusive"
|
|
9451
10502
|
);
|
|
9452
|
-
const worktree = await mkdtemp(
|
|
10503
|
+
const worktree = await mkdtemp(join17(tmpdir2(), "skein-writer-"));
|
|
9453
10504
|
let added = false;
|
|
9454
10505
|
let value;
|
|
9455
10506
|
let patch = "";
|
|
@@ -9520,7 +10571,7 @@ var WriterLane = class {
|
|
|
9520
10571
|
return {
|
|
9521
10572
|
baseCommit,
|
|
9522
10573
|
patch,
|
|
9523
|
-
patchSha256:
|
|
10574
|
+
patchSha256: createHash13("sha256").update(patch).digest("hex"),
|
|
9524
10575
|
files,
|
|
9525
10576
|
worktreeCleaned,
|
|
9526
10577
|
value
|
|
@@ -9539,7 +10590,7 @@ var WriterLane = class {
|
|
|
9539
10590
|
try {
|
|
9540
10591
|
const repository = await this.repository();
|
|
9541
10592
|
const lease = await acquireNamespaceLease(
|
|
9542
|
-
|
|
10593
|
+
join17(repository.commonDirectory, "skein-writer-lane"),
|
|
9543
10594
|
"exclusive"
|
|
9544
10595
|
);
|
|
9545
10596
|
try {
|
|
@@ -9647,7 +10698,7 @@ var WriterLane = class {
|
|
|
9647
10698
|
if (isAbsolute5(file) || file === ".git" || file.startsWith(".git/") || file.includes("\0")) {
|
|
9648
10699
|
throw new Error(`Writer patch contains an unsafe path: ${file}`);
|
|
9649
10700
|
}
|
|
9650
|
-
await this.workspace.resolvePath(
|
|
10701
|
+
await this.workspace.resolvePath(join17(repository.root, file), { allowMissing: true });
|
|
9651
10702
|
}
|
|
9652
10703
|
if (expectedFiles && !sameSet(files, expectedFiles)) {
|
|
9653
10704
|
throw new Error("Writer patch paths do not match the persisted file manifest.");
|
|
@@ -9662,7 +10713,7 @@ var WriterLane = class {
|
|
|
9662
10713
|
if (topLevel.exitCode !== 0 || !topLevel.stdout.trim()) {
|
|
9663
10714
|
throw new Error("The primary workspace must be a Git repository root for writer lanes.");
|
|
9664
10715
|
}
|
|
9665
|
-
const discoveredRoot =
|
|
10716
|
+
const discoveredRoot = resolve17(topLevel.stdout.trim());
|
|
9666
10717
|
if (await realpath7(discoveredRoot) !== await realpath7(root)) {
|
|
9667
10718
|
throw new Error("The primary workspace must be the Git repository root for writer lanes.");
|
|
9668
10719
|
}
|
|
@@ -9671,7 +10722,7 @@ var WriterLane = class {
|
|
|
9671
10722
|
if (common.exitCode !== 0 || !common.stdout.trim()) {
|
|
9672
10723
|
throw new Error(`Unable to resolve the Git common directory: ${processDetail(common)}`);
|
|
9673
10724
|
}
|
|
9674
|
-
const commonDirectory = await realpath7(isAbsolute5(common.stdout.trim()) ? common.stdout.trim() :
|
|
10725
|
+
const commonDirectory = await realpath7(isAbsolute5(common.stdout.trim()) ? common.stdout.trim() : resolve17(repositoryRoot, common.stdout.trim()));
|
|
9675
10726
|
return { root: repositoryRoot, commonDirectory, runtime };
|
|
9676
10727
|
}
|
|
9677
10728
|
async head(repository) {
|
|
@@ -9683,7 +10734,7 @@ var WriterLane = class {
|
|
|
9683
10734
|
return value;
|
|
9684
10735
|
}
|
|
9685
10736
|
async cleanupWorktree(repository, worktree, added) {
|
|
9686
|
-
const physicalWorktree = await realpath7(worktree).catch(() =>
|
|
10737
|
+
const physicalWorktree = await realpath7(worktree).catch(() => resolve17(worktree));
|
|
9687
10738
|
if (added) {
|
|
9688
10739
|
await runIsolatedGit(repository.runtime, [
|
|
9689
10740
|
"worktree",
|
|
@@ -9692,7 +10743,7 @@ var WriterLane = class {
|
|
|
9692
10743
|
worktree
|
|
9693
10744
|
], repository.root, { timeoutMs: 6e4 }).catch(() => void 0);
|
|
9694
10745
|
}
|
|
9695
|
-
await
|
|
10746
|
+
await rm4(worktree, { recursive: true, force: true }).catch(() => void 0);
|
|
9696
10747
|
await runIsolatedGit(repository.runtime, [
|
|
9697
10748
|
"worktree",
|
|
9698
10749
|
"prune",
|
|
@@ -9741,7 +10792,7 @@ function processDetail(result) {
|
|
|
9741
10792
|
}
|
|
9742
10793
|
async function pathExists2(path) {
|
|
9743
10794
|
try {
|
|
9744
|
-
await
|
|
10795
|
+
await lstat19(path);
|
|
9745
10796
|
return true;
|
|
9746
10797
|
} catch (error) {
|
|
9747
10798
|
if (error.code === "ENOENT") return false;
|
|
@@ -9750,14 +10801,14 @@ async function pathExists2(path) {
|
|
|
9750
10801
|
}
|
|
9751
10802
|
|
|
9752
10803
|
// src/agent/delegation.ts
|
|
9753
|
-
var writerRunInputSchema =
|
|
9754
|
-
task:
|
|
9755
|
-
profile:
|
|
9756
|
-
reviewer:
|
|
10804
|
+
var writerRunInputSchema = z18.object({
|
|
10805
|
+
task: z18.string().min(1).max(2e4),
|
|
10806
|
+
profile: z18.string().max(64).optional(),
|
|
10807
|
+
reviewer: z18.string().max(64).optional()
|
|
9757
10808
|
}).strict();
|
|
9758
|
-
var writerIntegrateInputSchema =
|
|
9759
|
-
run_id:
|
|
9760
|
-
patch_sha256:
|
|
10809
|
+
var writerIntegrateInputSchema = z18.object({
|
|
10810
|
+
run_id: z18.string().uuid(),
|
|
10811
|
+
patch_sha256: z18.string().regex(/^[a-f0-9]{64}$/u)
|
|
9761
10812
|
}).strict();
|
|
9762
10813
|
var DelegationManager = class {
|
|
9763
10814
|
constructor(options) {
|
|
@@ -9818,10 +10869,10 @@ var DelegationManager = class {
|
|
|
9818
10869
|
},
|
|
9819
10870
|
async execute(arguments_, context) {
|
|
9820
10871
|
if (!manager.team.enabled) return { ok: false, content: "Multi-agent delegation is disabled." };
|
|
9821
|
-
const input2 =
|
|
9822
|
-
tasks:
|
|
9823
|
-
profile:
|
|
9824
|
-
task:
|
|
10872
|
+
const input2 = z18.object({
|
|
10873
|
+
tasks: z18.array(z18.object({
|
|
10874
|
+
profile: z18.string().max(64).optional(),
|
|
10875
|
+
task: z18.string().min(1).max(2e4)
|
|
9825
10876
|
})).min(1).max(manager.team.maxDelegations)
|
|
9826
10877
|
}).parse(arguments_);
|
|
9827
10878
|
const tasks = input2.tasks.map((task) => ({
|
|
@@ -9867,13 +10918,13 @@ var DelegationManager = class {
|
|
|
9867
10918
|
},
|
|
9868
10919
|
async execute(arguments_, context) {
|
|
9869
10920
|
if (!manager.team.enabled) return { ok: false, content: "Multi-agent teams are disabled." };
|
|
9870
|
-
const input2 =
|
|
9871
|
-
objective:
|
|
9872
|
-
tasks:
|
|
9873
|
-
profile:
|
|
9874
|
-
task:
|
|
10921
|
+
const input2 = z18.object({
|
|
10922
|
+
objective: z18.string().min(1).max(3e4),
|
|
10923
|
+
tasks: z18.array(z18.object({
|
|
10924
|
+
profile: z18.string().max(64).optional(),
|
|
10925
|
+
task: z18.string().min(1).max(2e4)
|
|
9875
10926
|
})).min(1).max(manager.team.maxDelegations),
|
|
9876
|
-
reviewer:
|
|
10927
|
+
reviewer: z18.string().max(64).optional()
|
|
9877
10928
|
}).parse(arguments_);
|
|
9878
10929
|
const tasks = input2.tasks.map((task) => ({
|
|
9879
10930
|
profile: task.profile ?? manager.team.defaultProfile,
|
|
@@ -9936,7 +10987,7 @@ var DelegationManager = class {
|
|
|
9936
10987
|
const plan = await manager.loadWriterPlan(input2.run_id, input2.patch_sha256);
|
|
9937
10988
|
const files = await manager.writerLane.inspectPatch(plan.patch, plan.writer.files);
|
|
9938
10989
|
return Promise.all(files.map(
|
|
9939
|
-
(file) => context.workspace.resolvePath(
|
|
10990
|
+
(file) => context.workspace.resolvePath(join18(context.workspace.primaryRoot, file), { allowMissing: true })
|
|
9940
10991
|
));
|
|
9941
10992
|
},
|
|
9942
10993
|
async execute(arguments_, context) {
|
|
@@ -10123,7 +11174,7 @@ ${review.summary}`,
|
|
|
10123
11174
|
const plan = await this.loadWriterPlan(runId, patchSha256);
|
|
10124
11175
|
const files = await this.writerLane.inspectPatch(plan.patch, plan.writer.files);
|
|
10125
11176
|
const paths = await Promise.all(files.map(
|
|
10126
|
-
(file) => context.workspace.resolvePath(
|
|
11177
|
+
(file) => context.workspace.resolvePath(join18(context.workspace.primaryRoot, file), { allowMissing: true })
|
|
10127
11178
|
));
|
|
10128
11179
|
const checkpointStore = new CheckpointStore(context.workspace);
|
|
10129
11180
|
let checkpointId = context.checkpointId;
|
|
@@ -11406,9 +12457,10 @@ var HeadlessReporter = class {
|
|
|
11406
12457
|
}
|
|
11407
12458
|
if (!this.options.quiet && !this.options.compact) {
|
|
11408
12459
|
const usage = session.usage.inputTokens + session.usage.outputTokens;
|
|
12460
|
+
const usageLabel = tokenUsageLabel(session.usage);
|
|
11409
12461
|
process.stderr.write(this.paint.dim(
|
|
11410
12462
|
`
|
|
11411
|
-
${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)}
|
|
11412
12464
|
`
|
|
11413
12465
|
));
|
|
11414
12466
|
}
|
|
@@ -11432,16 +12484,19 @@ ${this.glyphs.meta} ${session.changedFiles.length} changed files ${this.glyphs.s
|
|
|
11432
12484
|
if (!compact2) process.stderr.write(this.paint.dim(`${this.glyphs.meta} reasoning ${this.glyphs.separator} turn ${event.turn}
|
|
11433
12485
|
`));
|
|
11434
12486
|
break;
|
|
11435
|
-
case "context":
|
|
12487
|
+
case "context": {
|
|
12488
|
+
const budget = event.packed.budgetTokens === void 0 ? "" : ` ${this.glyphs.separator} ${event.packed.budgetTier ?? "adaptive"} ${event.packed.budgetTokens} budget`;
|
|
11436
12489
|
process.stderr.write(this.paint.cyan(
|
|
11437
|
-
`${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}` : ""}
|
|
11438
12491
|
`
|
|
11439
12492
|
));
|
|
11440
12493
|
break;
|
|
12494
|
+
}
|
|
11441
12495
|
case "prompt":
|
|
11442
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}` : "";
|
|
11443
12498
|
process.stderr.write(this.paint.dim(
|
|
11444
|
-
`${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}
|
|
11445
12500
|
`
|
|
11446
12501
|
));
|
|
11447
12502
|
}
|
|
@@ -11573,9 +12628,14 @@ function sessionSummary(session) {
|
|
|
11573
12628
|
...session.taskContract ? { taskContract: session.taskContract } : {},
|
|
11574
12629
|
changedFiles: session.changedFiles,
|
|
11575
12630
|
...session.lastRun ? { lastRun: session.lastRun } : {},
|
|
12631
|
+
...session.tokenLedger?.length ? { tokenLedger: session.tokenLedger } : {},
|
|
11576
12632
|
usage: session.usage
|
|
11577
12633
|
};
|
|
11578
12634
|
}
|
|
12635
|
+
function tokenUsageLabel(usage) {
|
|
12636
|
+
if (usage.source) return usage.source;
|
|
12637
|
+
return usage.inputTokens + usage.outputTokens > 0 ? "unknown source" : "no usage";
|
|
12638
|
+
}
|
|
11579
12639
|
function formatToolDetail(call, paint = chalk2, glyphs = resolveCliGlyphs()) {
|
|
11580
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() : [];
|
|
11581
12641
|
const environmentDetail = environment.length ? ` ${glyphs.separator} env ${environment.join(", ")}` : "";
|
|
@@ -11598,7 +12658,7 @@ function formatResultDetail(result, paint = chalk2, glyphs = resolveCliGlyphs())
|
|
|
11598
12658
|
}
|
|
11599
12659
|
|
|
11600
12660
|
// src/cli/namespace-leases.ts
|
|
11601
|
-
import { resolve as
|
|
12661
|
+
import { resolve as resolve18 } from "node:path";
|
|
11602
12662
|
function cliNamespaceLeaseScopes(actionCommand) {
|
|
11603
12663
|
const names = commandNames(actionCommand);
|
|
11604
12664
|
const topLevel = names[1];
|
|
@@ -11620,7 +12680,7 @@ async function acquireCliNamespaceLeases(actionCommand) {
|
|
|
11620
12680
|
const scopes = cliNamespaceLeaseScopes(actionCommand);
|
|
11621
12681
|
const localOptions = actionCommand.opts();
|
|
11622
12682
|
const globalOptions = actionCommand.optsWithGlobals();
|
|
11623
|
-
const workspace =
|
|
12683
|
+
const workspace = resolve18(localOptions.workspace ?? globalOptions.workspace ?? process.cwd());
|
|
11624
12684
|
const targets = scopes.map((scope) => scope === "project" ? projectNamespacePaths(workspace).canonical : homeNamespacePaths().canonical);
|
|
11625
12685
|
const leases = [];
|
|
11626
12686
|
try {
|
|
@@ -11774,11 +12834,11 @@ function command(name, description, usage, aliases) {
|
|
|
11774
12834
|
|
|
11775
12835
|
// src/ui/text.ts
|
|
11776
12836
|
import stringWidth from "string-width";
|
|
11777
|
-
import
|
|
12837
|
+
import stripAnsi2 from "strip-ansi";
|
|
11778
12838
|
var controlCharacters = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f]/gu;
|
|
11779
12839
|
var escapeSequences = /[[\]][0-9;?=>!]*[ -/]*[@-~]|[[\]][?=>][0-9;?=>!]*[a-zA-Z~]/gu;
|
|
11780
12840
|
function sanitizeTerminalText(value) {
|
|
11781
|
-
return
|
|
12841
|
+
return stripAnsi2(value).replace(escapeSequences, "").replace(/\r\n?/g, "\n").replace(controlCharacters, "");
|
|
11782
12842
|
}
|
|
11783
12843
|
function terminalEllipsis() {
|
|
11784
12844
|
return process.env.SKEIN_GLYPHS === "ascii" || process.env.MOSAIC_GLYPHS === "ascii" ? "..." : "\u2026";
|
|
@@ -11871,8 +12931,8 @@ function graphemes(value) {
|
|
|
11871
12931
|
}
|
|
11872
12932
|
|
|
11873
12933
|
// src/ui/theme.ts
|
|
11874
|
-
import { lstat as
|
|
11875
|
-
import { basename as basename9, join as
|
|
12934
|
+
import { lstat as lstat20, readdir as readdir8, readFile as readFile16 } from "node:fs/promises";
|
|
12935
|
+
import { basename as basename9, join as join19, resolve as resolve19 } from "node:path";
|
|
11876
12936
|
import React, { createContext, useContext } from "react";
|
|
11877
12937
|
function defineTheme(seed) {
|
|
11878
12938
|
return {
|
|
@@ -11994,10 +13054,10 @@ async function reloadUserThemes(directory = userThemeDirectory()) {
|
|
|
11994
13054
|
userThemeNames.clear();
|
|
11995
13055
|
const loaded = [];
|
|
11996
13056
|
const errors = [];
|
|
11997
|
-
const resolvedDirectory =
|
|
13057
|
+
const resolvedDirectory = resolve19(directory);
|
|
11998
13058
|
let entries;
|
|
11999
13059
|
try {
|
|
12000
|
-
entries = await
|
|
13060
|
+
entries = await readdir8(resolvedDirectory, { encoding: "utf8" });
|
|
12001
13061
|
} catch (error) {
|
|
12002
13062
|
if (error.code === "ENOENT") {
|
|
12003
13063
|
return { directory: resolvedDirectory, loaded, errors };
|
|
@@ -12006,13 +13066,13 @@ async function reloadUserThemes(directory = userThemeDirectory()) {
|
|
|
12006
13066
|
}
|
|
12007
13067
|
for (const entry of entries) {
|
|
12008
13068
|
if (!entry.endsWith(".json")) continue;
|
|
12009
|
-
const path =
|
|
13069
|
+
const path = join19(resolvedDirectory, entry);
|
|
12010
13070
|
try {
|
|
12011
|
-
const info = await
|
|
13071
|
+
const info = await lstat20(path);
|
|
12012
13072
|
if (!info.isFile() || info.isSymbolicLink() || info.size > 64e3) {
|
|
12013
13073
|
throw new Error("must be a regular JSON file smaller than 64 KB");
|
|
12014
13074
|
}
|
|
12015
|
-
const parsed = JSON.parse(await
|
|
13075
|
+
const parsed = JSON.parse(await readFile16(path, "utf8"));
|
|
12016
13076
|
const name = themeName(parsed, basename9(entry, ".json"));
|
|
12017
13077
|
if (builtInThemeNames.has(name)) throw new Error(`cannot replace built-in theme \`${name}\``);
|
|
12018
13078
|
themes[name] = defineTheme(themeSeed(parsed, name));
|
|
@@ -12025,7 +13085,7 @@ async function reloadUserThemes(directory = userThemeDirectory()) {
|
|
|
12025
13085
|
return { directory: resolvedDirectory, loaded, errors };
|
|
12026
13086
|
}
|
|
12027
13087
|
function userThemeDirectory(environment = process.env) {
|
|
12028
|
-
return environment.SKEIN_THEME_DIR ?? environment.MOSAIC_THEME_DIR ??
|
|
13088
|
+
return environment.SKEIN_THEME_DIR ?? environment.MOSAIC_THEME_DIR ?? join19(resolveHomeNamespace(environment), "themes");
|
|
12029
13089
|
}
|
|
12030
13090
|
var defaultTheme = themes.graphite;
|
|
12031
13091
|
var palette = {
|
|
@@ -12300,10 +13360,11 @@ function Timeline({ items, width = 80, glyphMode = "auto", showToolOutput = fals
|
|
|
12300
13360
|
width,
|
|
12301
13361
|
glyph: glyphs.context,
|
|
12302
13362
|
label: "context",
|
|
12303
|
-
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` : ""}`,
|
|
12304
13364
|
labelColor: theme.accent
|
|
12305
13365
|
}
|
|
12306
13366
|
),
|
|
13367
|
+
!compact2 && item.budgetReason ? /* @__PURE__ */ jsx(Text, { color: theme.dim, children: truncateDisplay(`${glyphs.branchLast} ${sanitizeInlineTerminalText(item.budgetReason)}`, innerWidth) }) : null,
|
|
12307
13368
|
spans.slice(0, spanLimit).map((span, spanIndex) => {
|
|
12308
13369
|
const lines = span.startLine === span.endLine ? `${span.startLine}` : `${span.startLine}-${span.endLine}`;
|
|
12309
13370
|
const location = `${compactDisplayPath(sanitizeInlineTerminalText(span.path), 44)}:${lines}`;
|
|
@@ -12325,13 +13386,14 @@ function Timeline({ items, width = 80, glyphMode = "auto", showToolOutput = fals
|
|
|
12325
13386
|
] }, item.id);
|
|
12326
13387
|
}
|
|
12327
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`;
|
|
12328
13390
|
return /* @__PURE__ */ jsx(
|
|
12329
13391
|
MetaRow,
|
|
12330
13392
|
{
|
|
12331
13393
|
width,
|
|
12332
13394
|
glyph: glyphs.pending,
|
|
12333
13395
|
label: `prompt/${sanitizeInlineTerminalText(item.intent)}`,
|
|
12334
|
-
detail
|
|
13396
|
+
detail
|
|
12335
13397
|
},
|
|
12336
13398
|
item.id
|
|
12337
13399
|
);
|
|
@@ -13252,9 +14314,9 @@ function safeWidth(width) {
|
|
|
13252
14314
|
}
|
|
13253
14315
|
|
|
13254
14316
|
// src/utils/update-check.ts
|
|
13255
|
-
import { mkdir as mkdir9, readFile as
|
|
13256
|
-
import { join as
|
|
13257
|
-
import
|
|
14317
|
+
import { mkdir as mkdir9, readFile as readFile17, writeFile as writeFile2 } from "node:fs/promises";
|
|
14318
|
+
import { join as join20 } from "node:path";
|
|
14319
|
+
import stripAnsi3 from "strip-ansi";
|
|
13258
14320
|
var PACKAGE_NAME = "@skein-code/cli";
|
|
13259
14321
|
var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
13260
14322
|
var CACHE_FILE = "update-check.json";
|
|
@@ -13269,7 +14331,7 @@ function sanitizeHighlights(value) {
|
|
|
13269
14331
|
const cleaned = [];
|
|
13270
14332
|
for (const entry of value) {
|
|
13271
14333
|
if (typeof entry !== "string") continue;
|
|
13272
|
-
const flattened =
|
|
14334
|
+
const flattened = stripAnsi3(entry).replace(BIDI_CONTROLS, "").replace(/[\u0000-\u001f\u007f-\u009f]/gu, " ").replace(/\s+/gu, " ").trim();
|
|
13273
14335
|
if (!flattened || flattened.length > MAX_HIGHLIGHT_LENGTH) continue;
|
|
13274
14336
|
cleaned.push(flattened);
|
|
13275
14337
|
if (cleaned.length >= MAX_HIGHLIGHTS) break;
|
|
@@ -13333,7 +14395,7 @@ function compareVersions(a, b) {
|
|
|
13333
14395
|
return 0;
|
|
13334
14396
|
}
|
|
13335
14397
|
function updateCachePath(env = process.env) {
|
|
13336
|
-
return
|
|
14398
|
+
return join20(resolveHomeNamespace(env), CACHE_FILE);
|
|
13337
14399
|
}
|
|
13338
14400
|
function upgradeCommand() {
|
|
13339
14401
|
return `npm i -g ${PACKAGE_NAME}`;
|
|
@@ -13350,7 +14412,7 @@ function noticeIfNewer(latest, current, highlights) {
|
|
|
13350
14412
|
}
|
|
13351
14413
|
async function readUpdateCache(env = process.env) {
|
|
13352
14414
|
try {
|
|
13353
|
-
const raw = await
|
|
14415
|
+
const raw = await readFile17(updateCachePath(env), "utf8");
|
|
13354
14416
|
const parsed = JSON.parse(raw);
|
|
13355
14417
|
if (typeof parsed?.checkedAt === "number" && Number.isFinite(parsed.checkedAt) && parsed.checkedAt >= 0 && (parsed.latest === null || typeof parsed.latest === "string" && parseVersion(parsed.latest))) {
|
|
13356
14418
|
const latest = typeof parsed.latest === "string" ? parsed.latest : null;
|
|
@@ -14431,7 +15493,7 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
14431
15493
|
return () => clearInterval(timer);
|
|
14432
15494
|
}, [busy]);
|
|
14433
15495
|
const requestPermission = useCallback((call, category) => {
|
|
14434
|
-
return new Promise((
|
|
15496
|
+
return new Promise((resolve24) => setPermission({ call, category, resolve: resolve24 }));
|
|
14435
15497
|
}, []);
|
|
14436
15498
|
const onEvent = useCallback((event) => {
|
|
14437
15499
|
switch (event.type) {
|
|
@@ -14446,6 +15508,9 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
14446
15508
|
engine: event.packed.engine,
|
|
14447
15509
|
hits: event.packed.hits.length,
|
|
14448
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 } : {},
|
|
14449
15514
|
truncated: event.packed.truncated,
|
|
14450
15515
|
spans: event.packed.hits.slice(0, 5).map((hit) => ({
|
|
14451
15516
|
path: relative8(runner.workspace.primaryRoot, hit.path) || hit.path,
|
|
@@ -14459,7 +15524,14 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
14459
15524
|
setActivity({ label: "Assembling relevant context", startedAt: Date.now() });
|
|
14460
15525
|
break;
|
|
14461
15526
|
case "prompt":
|
|
14462
|
-
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
|
+
});
|
|
14463
15535
|
setActivity({ label: "Preparing the model prompt", startedAt: Date.now() });
|
|
14464
15536
|
break;
|
|
14465
15537
|
case "assistant_delta":
|
|
@@ -15006,7 +16078,7 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
15006
16078
|
{ label: config.agents?.enabled ? `${config.agents.maxConcurrent} concurrent` : "disabled", detail: "expert delegation" },
|
|
15007
16079
|
{
|
|
15008
16080
|
label: `${usage.inputTokens.toLocaleString()} in ${separator} ${usage.outputTokens.toLocaleString()} out`,
|
|
15009
|
-
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"}`
|
|
15010
16082
|
},
|
|
15011
16083
|
{
|
|
15012
16084
|
label: `${Math.round(status.pressure * 100)}% context pressure`,
|
|
@@ -15255,8 +16327,8 @@ function SkeinApp({ runner, config, extensions, initialPrompt, askMode = false,
|
|
|
15255
16327
|
}, [append]);
|
|
15256
16328
|
function settlePermission(grant, stop = false) {
|
|
15257
16329
|
if (!permission) return;
|
|
15258
|
-
const { call, category, resolve:
|
|
15259
|
-
|
|
16330
|
+
const { call, category, resolve: resolve24 } = permission;
|
|
16331
|
+
resolve24(grant);
|
|
15260
16332
|
setPermission(void 0);
|
|
15261
16333
|
if (grant === "session") {
|
|
15262
16334
|
append({
|
|
@@ -16591,24 +17663,24 @@ async function runFirstRunOnboarding(initialConfig, options = {}) {
|
|
|
16591
17663
|
}
|
|
16592
17664
|
|
|
16593
17665
|
// src/runtime/extensions.ts
|
|
16594
|
-
import { resolve as
|
|
17666
|
+
import { resolve as resolve22 } from "node:path";
|
|
16595
17667
|
|
|
16596
17668
|
// src/mcp/manager.ts
|
|
16597
17669
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
16598
17670
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
16599
17671
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
16600
|
-
import
|
|
17672
|
+
import stripAnsi5 from "strip-ansi";
|
|
16601
17673
|
|
|
16602
17674
|
// src/mcp/tool.ts
|
|
16603
|
-
import { createHash as
|
|
16604
|
-
import
|
|
17675
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
17676
|
+
import stripAnsi4 from "strip-ansi";
|
|
16605
17677
|
var MAX_ARGUMENT_BYTES = 256e3;
|
|
16606
17678
|
var MAX_DESCRIPTION_LENGTH = 4e3;
|
|
16607
|
-
var
|
|
17679
|
+
var MAX_RESULT_BYTES = 5 * 1024 * 1024;
|
|
16608
17680
|
var MAX_SCHEMA_BYTES = 1e5;
|
|
16609
17681
|
function createMcpToolAdapter(options) {
|
|
16610
17682
|
const { remoteTool } = options;
|
|
16611
|
-
const
|
|
17683
|
+
const inputSchema12 = copyInputSchema(remoteTool.inputSchema);
|
|
16612
17684
|
return {
|
|
16613
17685
|
definition: {
|
|
16614
17686
|
name: options.exposedName,
|
|
@@ -16616,7 +17688,7 @@ function createMcpToolAdapter(options) {
|
|
|
16616
17688
|
// MCP servers are an external trust boundary. Read-only annotations are
|
|
16617
17689
|
// hints from that server and must not lower the local permission level.
|
|
16618
17690
|
category: "network",
|
|
16619
|
-
inputSchema:
|
|
17691
|
+
inputSchema: inputSchema12
|
|
16620
17692
|
},
|
|
16621
17693
|
permissionCategories: () => ["network"],
|
|
16622
17694
|
async execute(arguments_, context) {
|
|
@@ -16636,6 +17708,8 @@ function createMcpToolAdapter(options) {
|
|
|
16636
17708
|
metadata: {
|
|
16637
17709
|
mcpServer: options.serverName,
|
|
16638
17710
|
mcpTool: remoteTool.name,
|
|
17711
|
+
sourceBytes: normalized.sourceBytes,
|
|
17712
|
+
sourceTruncated: normalized.sourceTruncated,
|
|
16639
17713
|
...normalized.isError ? { mcpError: true } : {}
|
|
16640
17714
|
}
|
|
16641
17715
|
};
|
|
@@ -16665,8 +17739,8 @@ function isUsableRemoteTool(tool) {
|
|
|
16665
17739
|
}
|
|
16666
17740
|
}
|
|
16667
17741
|
function describeTool(serverName, tool) {
|
|
16668
|
-
const label =
|
|
16669
|
-
const description = tool.description ?
|
|
17742
|
+
const label = stripAnsi4(tool.title?.trim() || tool.name).replace(/[\u0000-\u001f\u007f]/g, " ").slice(0, 200);
|
|
17743
|
+
const description = tool.description ? stripAnsi4(tool.description).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim().slice(0, MAX_DESCRIPTION_LENGTH) : void 0;
|
|
16670
17744
|
return description ? `[MCP ${serverName}/${label}] ${description}` : `Call the ${label} tool provided by the ${serverName} MCP server.`;
|
|
16671
17745
|
}
|
|
16672
17746
|
function copyInputSchema(schema) {
|
|
@@ -16689,7 +17763,7 @@ function assertArguments(arguments_) {
|
|
|
16689
17763
|
}
|
|
16690
17764
|
function normalizeCallResult(result) {
|
|
16691
17765
|
if (!isRecord2(result)) {
|
|
16692
|
-
return
|
|
17766
|
+
return boundResult(safeJson(result), false);
|
|
16693
17767
|
}
|
|
16694
17768
|
const isError = result.isError === true;
|
|
16695
17769
|
const sections = [];
|
|
@@ -16705,7 +17779,22 @@ ${safeJson(result.structuredContent)}`);
|
|
|
16705
17779
|
${safeJson(result.toolResult)}`);
|
|
16706
17780
|
}
|
|
16707
17781
|
const content = sections.filter(Boolean).join("\n\n") || (isError ? "The MCP tool reported an error." : "The MCP tool completed without output.");
|
|
16708
|
-
return
|
|
17782
|
+
return boundResult(content, isError);
|
|
17783
|
+
}
|
|
17784
|
+
function boundResult(content, isError) {
|
|
17785
|
+
const sourceBytes = Buffer.byteLength(content);
|
|
17786
|
+
if (sourceBytes <= MAX_RESULT_BYTES) {
|
|
17787
|
+
return { content, isError, sourceBytes, sourceTruncated: false };
|
|
17788
|
+
}
|
|
17789
|
+
const marker = `
|
|
17790
|
+
... ${sourceBytes - MAX_RESULT_BYTES} or more source bytes omitted by the MCP safety limit ...
|
|
17791
|
+
`;
|
|
17792
|
+
const markerBytes = Buffer.byteLength(marker);
|
|
17793
|
+
const available = MAX_RESULT_BYTES - markerBytes;
|
|
17794
|
+
const headBytes = Math.floor(available * 0.6);
|
|
17795
|
+
const tailBytes = available - headBytes;
|
|
17796
|
+
const bounded = `${sliceUtf8Start(content, headBytes)}${marker}${sliceUtf8End(content, tailBytes)}`;
|
|
17797
|
+
return { content: bounded, isError, sourceBytes, sourceTruncated: true };
|
|
16709
17798
|
}
|
|
16710
17799
|
function formatContentBlock(block) {
|
|
16711
17800
|
if (!isRecord2(block) || typeof block.type !== "string") return safeJson(block);
|
|
@@ -16753,15 +17842,10 @@ function fitToolName(name, identity) {
|
|
|
16753
17842
|
return `${name.slice(0, 64 - suffix.length).replace(/_+$/g, "")}${suffix}`;
|
|
16754
17843
|
}
|
|
16755
17844
|
function shortHash(value) {
|
|
16756
|
-
return
|
|
16757
|
-
}
|
|
16758
|
-
function truncateResult(content) {
|
|
16759
|
-
if (content.length <= MAX_RESULT_LENGTH) return content;
|
|
16760
|
-
return `${content.slice(0, MAX_RESULT_LENGTH)}
|
|
16761
|
-
... MCP result truncated`;
|
|
17845
|
+
return createHash14("sha256").update(value).digest("hex").slice(0, 8);
|
|
16762
17846
|
}
|
|
16763
17847
|
function sanitizeOutputText(value) {
|
|
16764
|
-
return
|
|
17848
|
+
return stripAnsi4(value).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "");
|
|
16765
17849
|
}
|
|
16766
17850
|
function sanitizeInlineText(value) {
|
|
16767
17851
|
return sanitizeOutputText(value).replace(/[\r\n\t]+/g, " ").trim().slice(0, 2e3);
|
|
@@ -16773,13 +17857,25 @@ function safeJson(value) {
|
|
|
16773
17857
|
return String(value);
|
|
16774
17858
|
}
|
|
16775
17859
|
}
|
|
17860
|
+
function sliceUtf8Start(value, maxBytes) {
|
|
17861
|
+
const buffer = Buffer.from(value);
|
|
17862
|
+
let end = Math.min(buffer.length, maxBytes);
|
|
17863
|
+
while (end > 0 && (buffer[end] ?? 0) >= 128 && (buffer[end] ?? 0) < 192) end -= 1;
|
|
17864
|
+
return buffer.subarray(0, end).toString("utf8");
|
|
17865
|
+
}
|
|
17866
|
+
function sliceUtf8End(value, maxBytes) {
|
|
17867
|
+
const buffer = Buffer.from(value);
|
|
17868
|
+
let start = Math.max(0, buffer.length - maxBytes);
|
|
17869
|
+
while (start < buffer.length && (buffer[start] ?? 0) >= 128 && (buffer[start] ?? 0) < 192) start += 1;
|
|
17870
|
+
return buffer.subarray(start).toString("utf8");
|
|
17871
|
+
}
|
|
16776
17872
|
function isRecord2(value) {
|
|
16777
17873
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16778
17874
|
}
|
|
16779
17875
|
|
|
16780
17876
|
// src/mcp/validation.ts
|
|
16781
17877
|
import { stat as stat10, realpath as realpath8 } from "node:fs/promises";
|
|
16782
|
-
import { isAbsolute as isAbsolute6, resolve as
|
|
17878
|
+
import { isAbsolute as isAbsolute6, resolve as resolve20 } from "node:path";
|
|
16783
17879
|
var SERVER_NAME = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
16784
17880
|
var ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
16785
17881
|
var HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
@@ -16851,9 +17947,9 @@ async function validateCwd(configured, options) {
|
|
|
16851
17947
|
if (configured !== void 0 && (configured.length > 4e3 || CONTROL_CHARACTERS.test(configured))) {
|
|
16852
17948
|
throw new Error("MCP working directory is invalid or too long");
|
|
16853
17949
|
}
|
|
16854
|
-
const defaultCwd =
|
|
16855
|
-
const candidate = configured ? isAbsolute6(configured) ?
|
|
16856
|
-
const roots = options.workspaceRoots?.length ? options.workspaceRoots.map((root) =>
|
|
17950
|
+
const defaultCwd = resolve20(options.cwd ?? process.cwd());
|
|
17951
|
+
const candidate = configured ? isAbsolute6(configured) ? resolve20(configured) : resolve20(defaultCwd, configured) : defaultCwd;
|
|
17952
|
+
const roots = options.workspaceRoots?.length ? options.workspaceRoots.map((root) => resolve20(root)) : [defaultCwd];
|
|
16857
17953
|
const resolvedCandidate = await realpath8(candidate).catch(() => {
|
|
16858
17954
|
throw new Error(`MCP working directory does not exist: ${candidate}`);
|
|
16859
17955
|
});
|
|
@@ -17191,7 +18287,7 @@ var McpManager = class {
|
|
|
17191
18287
|
stderr: "pipe"
|
|
17192
18288
|
});
|
|
17193
18289
|
transport.stderr?.on("data", (chunk) => {
|
|
17194
|
-
const text =
|
|
18290
|
+
const text = stripAnsi5(chunk.toString()).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "").trim();
|
|
17195
18291
|
if (text) this.options.logger?.(`MCP ${name} stderr`, { text: text.slice(0, 2e3) });
|
|
17196
18292
|
});
|
|
17197
18293
|
return transport;
|
|
@@ -17350,7 +18446,7 @@ function errorMessage2(error) {
|
|
|
17350
18446
|
return sanitizeStatusText(message2) || "Unknown MCP error";
|
|
17351
18447
|
}
|
|
17352
18448
|
function sanitizeStatusText(value) {
|
|
17353
|
-
return
|
|
18449
|
+
return stripAnsi5(value).replace(/[\u0000-\u001f\u007f]/g, " ").trim().slice(0, 1e3);
|
|
17354
18450
|
}
|
|
17355
18451
|
function timeoutError(timeoutMs) {
|
|
17356
18452
|
const error = new Error(`MCP request timed out after ${timeoutMs} ms`);
|
|
@@ -17376,9 +18472,9 @@ async function closeTransportQuietly(transport) {
|
|
|
17376
18472
|
}
|
|
17377
18473
|
|
|
17378
18474
|
// src/memory/tools.ts
|
|
17379
|
-
import { z as
|
|
17380
|
-
var scopeSchema =
|
|
17381
|
-
var kindSchema =
|
|
18475
|
+
import { z as z19 } from "zod";
|
|
18476
|
+
var scopeSchema = z19.enum(["user", "workspace", "session", "agent"]);
|
|
18477
|
+
var kindSchema = z19.enum(["semantic", "episodic", "procedural"]);
|
|
17382
18478
|
function createMemoryTools(store) {
|
|
17383
18479
|
return [
|
|
17384
18480
|
{
|
|
@@ -17393,10 +18489,10 @@ function createMemoryTools(store) {
|
|
|
17393
18489
|
}, ["query"])
|
|
17394
18490
|
},
|
|
17395
18491
|
async execute(arguments_, context) {
|
|
17396
|
-
const input2 =
|
|
17397
|
-
query:
|
|
18492
|
+
const input2 = z19.object({
|
|
18493
|
+
query: z19.string().max(4e3),
|
|
17398
18494
|
scope: scopeSchema.optional(),
|
|
17399
|
-
limit:
|
|
18495
|
+
limit: z19.number().int().min(1).max(20).optional()
|
|
17400
18496
|
}).parse(arguments_);
|
|
17401
18497
|
const scopes = input2.scope ? [{ scope: input2.scope, scopeKey: scopeKey(input2.scope, context) }] : availableScopes(context);
|
|
17402
18498
|
const records = store.search(input2.query, { scopes, limit: input2.limit ?? 8 });
|
|
@@ -17428,17 +18524,17 @@ ${record.content}`
|
|
|
17428
18524
|
}, ["content", "rationale"])
|
|
17429
18525
|
},
|
|
17430
18526
|
async execute(arguments_, context) {
|
|
17431
|
-
const input2 =
|
|
17432
|
-
content:
|
|
17433
|
-
rationale:
|
|
18527
|
+
const input2 = z19.object({
|
|
18528
|
+
content: z19.string().min(1).max(12e3),
|
|
18529
|
+
rationale: z19.string().min(1).max(1e3),
|
|
17434
18530
|
scope: scopeSchema.optional(),
|
|
17435
18531
|
kind: kindSchema.optional(),
|
|
17436
|
-
tags:
|
|
17437
|
-
importance:
|
|
17438
|
-
confidence:
|
|
17439
|
-
agent:
|
|
17440
|
-
revision:
|
|
17441
|
-
conflictKey:
|
|
18532
|
+
tags: z19.array(z19.string()).max(24).optional(),
|
|
18533
|
+
importance: z19.number().min(0).max(1).optional(),
|
|
18534
|
+
confidence: z19.number().min(0).max(1).optional(),
|
|
18535
|
+
agent: z19.string().max(64).optional(),
|
|
18536
|
+
revision: z19.string().max(240).optional(),
|
|
18537
|
+
conflictKey: z19.string().max(240).optional()
|
|
17442
18538
|
}).strict().parse(arguments_);
|
|
17443
18539
|
const scope = input2.scope ?? "workspace";
|
|
17444
18540
|
const candidate = store.propose({
|
|
@@ -17476,9 +18572,9 @@ ${record.content}`
|
|
|
17476
18572
|
}, ["id"])
|
|
17477
18573
|
},
|
|
17478
18574
|
async execute(arguments_) {
|
|
17479
|
-
const input2 =
|
|
17480
|
-
id:
|
|
17481
|
-
permanent:
|
|
18575
|
+
const input2 = z19.object({
|
|
18576
|
+
id: z19.string().uuid(),
|
|
18577
|
+
permanent: z19.boolean().optional()
|
|
17482
18578
|
}).parse(arguments_);
|
|
17483
18579
|
const changed = input2.permanent ? store.remove(input2.id) : store.archive(input2.id);
|
|
17484
18580
|
return {
|
|
@@ -17505,9 +18601,9 @@ function scopeKey(scope, context) {
|
|
|
17505
18601
|
}
|
|
17506
18602
|
|
|
17507
18603
|
// src/skills/catalog.ts
|
|
17508
|
-
import { lstat as
|
|
18604
|
+
import { lstat as lstat21, readFile as readFile18, readdir as readdir9, realpath as realpath9 } from "node:fs/promises";
|
|
17509
18605
|
import { homedir as homedir4 } from "node:os";
|
|
17510
|
-
import { basename as basename11, join as
|
|
18606
|
+
import { basename as basename11, join as join21, resolve as resolve21 } from "node:path";
|
|
17511
18607
|
import { parse as parseYaml3 } from "yaml";
|
|
17512
18608
|
var SkillCatalog = class {
|
|
17513
18609
|
constructor(workspace, config) {
|
|
@@ -17527,7 +18623,7 @@ var SkillCatalog = class {
|
|
|
17527
18623
|
for (const location of locations) {
|
|
17528
18624
|
const entries = await safeDirectories(location.path);
|
|
17529
18625
|
for (const entry of entries) {
|
|
17530
|
-
const skillPath =
|
|
18626
|
+
const skillPath = join21(location.path, entry, "SKILL.md");
|
|
17531
18627
|
const metadata = await readMetadata(skillPath);
|
|
17532
18628
|
if (!metadata) continue;
|
|
17533
18629
|
const descriptor = {
|
|
@@ -17576,31 +18672,31 @@ ${skill.content}
|
|
|
17576
18672
|
}
|
|
17577
18673
|
function discoveryLocations(workspace, configured) {
|
|
17578
18674
|
const home = homedir4();
|
|
17579
|
-
const workspaceRoot =
|
|
18675
|
+
const workspaceRoot = resolve21(workspace);
|
|
17580
18676
|
return [
|
|
17581
|
-
{ path:
|
|
17582
|
-
{ path:
|
|
17583
|
-
{ path:
|
|
17584
|
-
{ path:
|
|
18677
|
+
{ path: join21(home, ".agents", "skills"), scope: "user", trusted: true },
|
|
18678
|
+
{ path: join21(home, ".claude", "skills"), scope: "user", trusted: true },
|
|
18679
|
+
{ path: join21(home, ".augment", "skills"), scope: "user", trusted: true },
|
|
18680
|
+
{ path: join21(resolveHomeNamespace(), "skills"), scope: "user", trusted: true },
|
|
17585
18681
|
...configured.map((path) => {
|
|
17586
|
-
const resolved =
|
|
18682
|
+
const resolved = resolve21(workspaceRoot, path);
|
|
17587
18683
|
return {
|
|
17588
18684
|
path: resolved,
|
|
17589
18685
|
scope: "configured",
|
|
17590
18686
|
trusted: !isInside(workspaceRoot, resolved)
|
|
17591
18687
|
};
|
|
17592
18688
|
}),
|
|
17593
|
-
{ path:
|
|
17594
|
-
{ path:
|
|
17595
|
-
{ path:
|
|
17596
|
-
{ path:
|
|
18689
|
+
{ path: join21(workspace, ".agents", "skills"), scope: "workspace", trusted: false },
|
|
18690
|
+
{ path: join21(workspace, ".claude", "skills"), scope: "workspace", trusted: false },
|
|
18691
|
+
{ path: join21(workspace, ".augment", "skills"), scope: "workspace", trusted: false },
|
|
18692
|
+
{ path: join21(resolveProjectNamespaceSync(workspaceRoot).active, "skills"), scope: "workspace", trusted: false }
|
|
17597
18693
|
];
|
|
17598
18694
|
}
|
|
17599
18695
|
async function safeDirectories(path) {
|
|
17600
18696
|
try {
|
|
17601
|
-
const info = await
|
|
18697
|
+
const info = await lstat21(path);
|
|
17602
18698
|
if (!info.isDirectory() || info.isSymbolicLink()) return [];
|
|
17603
|
-
const entries = await
|
|
18699
|
+
const entries = await readdir9(path, { withFileTypes: true });
|
|
17604
18700
|
return entries.filter((entry) => entry.isDirectory() && !entry.isSymbolicLink()).map((entry) => entry.name).slice(0, 500);
|
|
17605
18701
|
} catch {
|
|
17606
18702
|
return [];
|
|
@@ -17612,7 +18708,7 @@ async function readMetadata(path) {
|
|
|
17612
18708
|
const { frontmatter } = splitFrontmatter(raw);
|
|
17613
18709
|
if (!frontmatter) return void 0;
|
|
17614
18710
|
const parsed = parseYaml3(frontmatter);
|
|
17615
|
-
const name = typeof parsed?.name === "string" ? parsed.name.trim() : basename11(
|
|
18711
|
+
const name = typeof parsed?.name === "string" ? parsed.name.trim() : basename11(resolve21(path, ".."));
|
|
17616
18712
|
const description = typeof parsed?.description === "string" ? parsed.description.trim() : "";
|
|
17617
18713
|
if (!/^[a-z][a-z0-9_-]{0,63}$/.test(name) || !description || description.length > 1e3) {
|
|
17618
18714
|
return void 0;
|
|
@@ -17631,13 +18727,13 @@ async function readSkill(path, maxChars) {
|
|
|
17631
18727
|
}
|
|
17632
18728
|
async function safeRead(path, maxBytes) {
|
|
17633
18729
|
try {
|
|
17634
|
-
const info = await
|
|
18730
|
+
const info = await lstat21(path);
|
|
17635
18731
|
if (!info.isFile() || info.isSymbolicLink() || info.size > maxBytes) return void 0;
|
|
17636
|
-
const parent =
|
|
18732
|
+
const parent = resolve21(path, "..");
|
|
17637
18733
|
const resolvedParent = await realpath9(parent);
|
|
17638
18734
|
const resolvedPath = await realpath9(path);
|
|
17639
18735
|
if (!resolvedPath.startsWith(`${resolvedParent}/`)) return void 0;
|
|
17640
|
-
return await
|
|
18736
|
+
return await readFile18(path, "utf8");
|
|
17641
18737
|
} catch {
|
|
17642
18738
|
return void 0;
|
|
17643
18739
|
}
|
|
@@ -17682,7 +18778,7 @@ function escapeAttribute6(value) {
|
|
|
17682
18778
|
}
|
|
17683
18779
|
|
|
17684
18780
|
// src/workflows/catalog.ts
|
|
17685
|
-
import { z as
|
|
18781
|
+
import { z as z20 } from "zod";
|
|
17686
18782
|
var builtInWorkflows = [
|
|
17687
18783
|
{
|
|
17688
18784
|
name: "implement",
|
|
@@ -17773,7 +18869,7 @@ function createWorkflowTool(catalog) {
|
|
|
17773
18869
|
}, ["name", "task"])
|
|
17774
18870
|
},
|
|
17775
18871
|
async execute(arguments_) {
|
|
17776
|
-
const input2 =
|
|
18872
|
+
const input2 = z20.object({ name: z20.string(), task: z20.string().min(1).max(2e4) }).parse(arguments_);
|
|
17777
18873
|
const workflow = catalog.get(input2.name);
|
|
17778
18874
|
if (!workflow) return { ok: false, content: `Unknown workflow: ${input2.name}` };
|
|
17779
18875
|
return {
|
|
@@ -17818,7 +18914,7 @@ var ExtensionRuntime = class _ExtensionRuntime {
|
|
|
17818
18914
|
delegation;
|
|
17819
18915
|
initialized = false;
|
|
17820
18916
|
static async create(config, registry, options = {}) {
|
|
17821
|
-
const runtime = new _ExtensionRuntime(config,
|
|
18917
|
+
const runtime = new _ExtensionRuntime(config, resolve22(config.workspaceRoots[0] ?? process.cwd()), options);
|
|
17822
18918
|
try {
|
|
17823
18919
|
await runtime.initialize(registry, options.signal);
|
|
17824
18920
|
return runtime;
|
|
@@ -18023,15 +19119,15 @@ function upgradeCommandOverride(env = process.env) {
|
|
|
18023
19119
|
return trimmed ? trimmed : void 0;
|
|
18024
19120
|
}
|
|
18025
19121
|
function runUpgrade(plan) {
|
|
18026
|
-
return new Promise((
|
|
19122
|
+
return new Promise((resolve24) => {
|
|
18027
19123
|
const child = spawn2(plan.command, plan.args, {
|
|
18028
19124
|
stdio: "inherit",
|
|
18029
19125
|
shell: plan.shell ?? false
|
|
18030
19126
|
});
|
|
18031
|
-
child.on("error", () =>
|
|
19127
|
+
child.on("error", () => resolve24({ ok: false, exitCode: 127, display: plan.display }));
|
|
18032
19128
|
child.on("close", (code) => {
|
|
18033
19129
|
const exitCode = code ?? 1;
|
|
18034
|
-
|
|
19130
|
+
resolve24({ ok: exitCode === 0, exitCode, display: plan.display });
|
|
18035
19131
|
});
|
|
18036
19132
|
});
|
|
18037
19133
|
}
|
|
@@ -18117,7 +19213,7 @@ program.command("context").description("Pack task-oriented context under a token
|
|
|
18117
19213
|
|
|
18118
19214
|
`);
|
|
18119
19215
|
process.stderr.write(chalk4.dim(
|
|
18120
|
-
`${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}` : ""}
|
|
18121
19217
|
`
|
|
18122
19218
|
));
|
|
18123
19219
|
});
|
|
@@ -18280,9 +19376,11 @@ sessionCommand.command("show <id>").description("Show a saved session transcript
|
|
|
18280
19376
|
else process.stdout.write(sessionMarkdown(session));
|
|
18281
19377
|
});
|
|
18282
19378
|
sessionCommand.command("delete <id>").description("Delete a saved session").option("-w, --workspace <path>", "workspace root").option("--yes", "skip confirmation").action(async (id, options) => {
|
|
18283
|
-
const
|
|
19379
|
+
const workspace = workspaceOption(options.workspace);
|
|
19380
|
+
const store = new SessionStore(workspace);
|
|
18284
19381
|
const session = await requireSessionSelector(store, id);
|
|
18285
19382
|
if (!options.yes && !await confirm(`Delete session ${session.id.slice(0, 8)}?`)) return;
|
|
19383
|
+
await new ToolArtifactStore(workspace).removeSession(session.id);
|
|
18286
19384
|
await store.remove(session.id);
|
|
18287
19385
|
process.stdout.write(`Deleted ${session.id}
|
|
18288
19386
|
`);
|
|
@@ -18291,7 +19389,7 @@ sessionCommand.command("export <id>").description("Export a session as Markdown"
|
|
|
18291
19389
|
const store = new SessionStore(workspaceOption(options.workspace));
|
|
18292
19390
|
const session = await requireSessionSelector(store, id);
|
|
18293
19391
|
const markdown = sessionMarkdown(session);
|
|
18294
|
-
if (options.output) await writeFile3(
|
|
19392
|
+
if (options.output) await writeFile3(resolve23(options.output), markdown, "utf8");
|
|
18295
19393
|
else process.stdout.write(markdown);
|
|
18296
19394
|
});
|
|
18297
19395
|
var checkpointCommand = program.command("checkpoint").description("Inspect and restore pre-mutation snapshots");
|
|
@@ -18668,7 +19766,7 @@ async function runChat(prompts, options) {
|
|
|
18668
19766
|
const stdinPrompt = !process.stdin.isTTY ? await readStdin() : "";
|
|
18669
19767
|
const firstPrompt = [...prompts, stdinPrompt].filter(Boolean).join("\n\n").trim();
|
|
18670
19768
|
if (shouldPrint && !firstPrompt) throw new Error("Provide a prompt argument or pipe input on stdin.");
|
|
18671
|
-
const workspace =
|
|
19769
|
+
const workspace = resolve23(options.workspace);
|
|
18672
19770
|
let config = await runtimeConfig(workspace, options);
|
|
18673
19771
|
let completedOnboarding = false;
|
|
18674
19772
|
if (!shouldPrint && needsFirstRunOnboarding(config)) {
|
|
@@ -18893,14 +19991,14 @@ async function runAgentSetup(options) {
|
|
|
18893
19991
|
`);
|
|
18894
19992
|
}
|
|
18895
19993
|
async function runtimeConfig(workspaceInput, options) {
|
|
18896
|
-
const workspace =
|
|
19994
|
+
const workspace = resolve23(workspaceInput);
|
|
18897
19995
|
const loaded = await loadConfig(workspace, options.config, {
|
|
18898
19996
|
trustProjectConfig: options.trustProjectConfig === true
|
|
18899
19997
|
});
|
|
18900
19998
|
const roots = [
|
|
18901
19999
|
workspace,
|
|
18902
20000
|
...loaded.workspaceRoots,
|
|
18903
|
-
...(options.addWorkspace ?? []).map((root) =>
|
|
20001
|
+
...(options.addWorkspace ?? []).map((root) => resolve23(workspace, root))
|
|
18904
20002
|
];
|
|
18905
20003
|
const provider = options.provider ? validateProvider(options.provider) : loaded.model.provider;
|
|
18906
20004
|
return {
|
|
@@ -19056,7 +20154,7 @@ function collect(value, previous) {
|
|
|
19056
20154
|
}
|
|
19057
20155
|
function workspaceOption(value) {
|
|
19058
20156
|
const rootOptions = program.opts();
|
|
19059
|
-
return
|
|
20157
|
+
return resolve23(value ?? rootOptions.workspace ?? process.cwd());
|
|
19060
20158
|
}
|
|
19061
20159
|
function runtimeOptions(options) {
|
|
19062
20160
|
const root = program.opts();
|