@wrongstack/core 0.275.0 → 0.276.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-bridge-D9JkPvJ0.d.ts → agent-bridge-D7A-eu3C.d.ts} +1 -1
- package/dist/{agent-subagent-runner-CArSFKFl.d.ts → agent-subagent-runner-CEuw4ATz.d.ts} +16 -10
- package/dist/{brain-DCkB5_e7.d.ts → brain-BLOyN5ZP.d.ts} +127 -1
- package/dist/{compactor-CzSvxM1g.d.ts → compactor-DcBpaJsI.d.ts} +1 -1
- package/dist/{config-BzFRKkg7.d.ts → config-Bf5mj-ad.d.ts} +20 -2
- package/dist/{context-BrLe8pJy.d.ts → context-CLnUMW5g.d.ts} +40 -2
- package/dist/coordination/index.d.ts +43 -24
- package/dist/coordination/index.js +849 -648
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +28 -28
- package/dist/defaults/index.js +1636 -845
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +16 -16
- package/dist/execution/index.js +218 -49
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +1 -1
- package/dist/extension/index.d.ts +7 -7
- package/dist/extension/index.js.map +1 -1
- package/dist/{global-mailbox-CXkugtNQ.d.ts → global-mailbox-Iqfkgmwu.d.ts} +3 -3
- package/dist/{goal-store-DUwdbdoY.d.ts → goal-store-DGb6b5Ed.d.ts} +1 -1
- package/dist/hq/index.d.ts +6 -6
- package/dist/hq/index.js +178 -75
- package/dist/hq/index.js.map +1 -1
- package/dist/{index-CtlizLTK.d.ts → index-Cn0NOshr.d.ts} +10 -5
- package/dist/{index-neOCEy6q.d.ts → index-L4RZN9jJ.d.ts} +2 -2
- package/dist/index.d.ts +56 -48
- package/dist/index.js +2789 -1546
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +26 -7
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +20 -12
- package/dist/kernel/index.js +55 -9
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mailbox-types-_7gaY0Rl.d.ts → mailbox-types-DTl7bRH3.d.ts} +3 -1
- package/dist/{mcp-servers-MLL6bMlv.d.ts → mcp-servers-CuZGf9fI.d.ts} +4 -4
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +223 -139
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-CrkcxQ-g.d.ts → models-registry-8XOdxWQu.d.ts} +16 -1
- package/dist/{multi-agent-coordinator-Dc_HuG9p.d.ts → multi-agent-coordinator-CiRtKVTk.d.ts} +8 -1
- package/dist/{null-fleet-bus-BMZwMin7.d.ts → null-fleet-bus-d9G-bVy9.d.ts} +26 -22
- package/dist/observability/index.d.ts +2 -2
- package/dist/{path-resolver-uVK4BatM.d.ts → path-resolver-BhIb6mtd.d.ts} +8 -3
- package/dist/{permission-CJR1qfOi.d.ts → permission-BCbQDR2s.d.ts} +1 -1
- package/dist/{permission-policy-DLVKKk4w.d.ts → permission-policy-C0ikndX_.d.ts} +2 -18
- package/dist/{pipeline-BYR-Vdau.d.ts → pipeline-Dl6XbfE7.d.ts} +10 -6
- package/dist/{provider-model-resolve-iREK_1lG.d.ts → provider-model-resolve-B70epO19.d.ts} +3 -3
- package/dist/{provider-runner-i7SQXZuC.d.ts → provider-runner-DZ808MSM.d.ts} +3 -3
- package/dist/{retry-policy-BmY5ooh3.d.ts → retry-policy-Dt3_z8Aj.d.ts} +1 -1
- package/dist/sdd/index.d.ts +19 -10
- package/dist/sdd/index.js +411 -240
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-C9leEMzr.d.ts → secret-vault-BUJ2d1gB.d.ts} +1 -1
- package/dist/security/index.d.ts +5 -5
- package/dist/security/index.js +30 -6
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-qjpee9BF.d.ts → selector-BCkWgdwy.d.ts} +1 -1
- package/dist/{session-event-bridge-m7y--I-H.d.ts → session-event-bridge-CMvIO59_.d.ts} +1 -1
- package/dist/{session-reader-BjLH4V9n.d.ts → session-reader-C8aiChUu.d.ts} +1 -1
- package/dist/skills/index.js +1 -0
- package/dist/skills/index.js.map +1 -1
- package/dist/storage/index.d.ts +68 -30
- package/dist/storage/index.js +839 -528
- package/dist/storage/index.js.map +1 -1
- package/dist/{strategy-compactor-C2bmlWYg.d.ts → strategy-compactor-DI1OHVbB.d.ts} +10 -10
- package/dist/{todos-checkpoint-oDS9IBNS.d.ts → todos-checkpoint-Ddd2CGr0.d.ts} +56 -9
- package/dist/{tool-executor-D4YdaJ-M.d.ts → tool-executor-Bmd5Ygoo.d.ts} +45 -10
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/types/index.d.ts +20 -20
- package/dist/types/index.js +331 -98
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +16 -3
- package/dist/utils/index.js +159 -83
- package/dist/utils/index.js.map +1 -1
- package/dist/{worktree-manager-A1Efnvs0.d.ts → worktree-manager-DBdl_5rs.d.ts} +4 -1
- package/instructions/agents/shadow-agent.md +3 -3
- package/instructions/coordination/director-preamble.md +3 -3
- package/instructions/modes/research-web.md +4 -4
- package/package.json +1 -1
- package/skills/research-web/SKILL.md +26 -26
- package/skills/research-web/SKILL.save.md +1 -1
package/dist/types/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomBytes, createCipheriv, createDecipheriv, randomUUID, scryptSync } from 'crypto';
|
|
2
2
|
import * as fs from 'fs/promises';
|
|
3
|
-
import * as
|
|
3
|
+
import * as path7 from 'path';
|
|
4
4
|
import * as os from 'os';
|
|
5
5
|
import * as fs2 from 'fs';
|
|
6
6
|
import { readFileSync, statSync } from 'fs';
|
|
@@ -46,9 +46,9 @@ var ToolErrorCategory = /* @__PURE__ */ ((ToolErrorCategory2) => {
|
|
|
46
46
|
// src/types/tool-markers.ts
|
|
47
47
|
var MALFORMED_ARG_MARKERS = ["__raw", "__raw_arguments", "_raw"];
|
|
48
48
|
async function atomicWrite(targetPath, content, opts = {}) {
|
|
49
|
-
const dir =
|
|
49
|
+
const dir = path7.dirname(targetPath);
|
|
50
50
|
await fs.mkdir(dir, { recursive: true });
|
|
51
|
-
const tmp =
|
|
51
|
+
const tmp = path7.join(dir, `.${path7.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
52
52
|
try {
|
|
53
53
|
if (typeof content === "string") {
|
|
54
54
|
await fs.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
@@ -107,7 +107,7 @@ async function renameWithRetry(from, to) {
|
|
|
107
107
|
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
108
108
|
throw err;
|
|
109
109
|
}
|
|
110
|
-
await new Promise((
|
|
110
|
+
await new Promise((resolve8) => setTimeout(resolve8, delays[i]));
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
throw lastErr;
|
|
@@ -599,6 +599,25 @@ function compileUserRegex(pattern, flags) {
|
|
|
599
599
|
};
|
|
600
600
|
}
|
|
601
601
|
}
|
|
602
|
+
function sessionScopedPath(dir, sessionId, suffix) {
|
|
603
|
+
if (!sessionId || sessionId.includes("\\") || sessionId.includes("..")) {
|
|
604
|
+
throw invalid(sessionId);
|
|
605
|
+
}
|
|
606
|
+
const resolved = path7.resolve(dir, `${sessionId}${suffix}`);
|
|
607
|
+
const rel = path7.relative(path7.resolve(dir), resolved);
|
|
608
|
+
if (rel.startsWith("..") || path7.isAbsolute(rel)) {
|
|
609
|
+
throw invalid(sessionId);
|
|
610
|
+
}
|
|
611
|
+
return resolved;
|
|
612
|
+
}
|
|
613
|
+
function invalid(sessionId) {
|
|
614
|
+
return new FsError({
|
|
615
|
+
message: `Invalid sessionId: ${sessionId}`,
|
|
616
|
+
code: ERROR_CODES.FS_DELETE_FAILED,
|
|
617
|
+
path: sessionId,
|
|
618
|
+
context: { reason: "path_traversal" }
|
|
619
|
+
});
|
|
620
|
+
}
|
|
602
621
|
|
|
603
622
|
// src/utils/string.ts
|
|
604
623
|
function truncate(s, max) {
|
|
@@ -1506,8 +1525,8 @@ function resolveToolResultRenderMode(modes, toolName) {
|
|
|
1506
1525
|
}
|
|
1507
1526
|
function wstackGlobalRoot() {
|
|
1508
1527
|
const fromEnv = process.env["WRONGSTACK_HOME"];
|
|
1509
|
-
if (fromEnv && fromEnv.trim().length > 0) return
|
|
1510
|
-
return
|
|
1528
|
+
if (fromEnv && fromEnv.trim().length > 0) return path7.resolve(fromEnv);
|
|
1529
|
+
return path7.join(os.homedir(), ".wrongstack");
|
|
1511
1530
|
}
|
|
1512
1531
|
|
|
1513
1532
|
// src/types/errors.ts
|
|
@@ -1564,6 +1583,7 @@ var ERROR_CODES = {
|
|
|
1564
1583
|
SDD_NOT_READY: "SDD_NOT_READY",
|
|
1565
1584
|
// General
|
|
1566
1585
|
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
1586
|
+
PARSE_FAILED: "PARSE_FAILED",
|
|
1567
1587
|
UNKNOWN: "UNKNOWN"
|
|
1568
1588
|
};
|
|
1569
1589
|
var WrongStackError = class extends Error {
|
|
@@ -1738,6 +1758,22 @@ var ToolValidationError = class extends WrongStackError {
|
|
|
1738
1758
|
this.name = "ToolValidationError";
|
|
1739
1759
|
}
|
|
1740
1760
|
};
|
|
1761
|
+
var ParseError = class extends WrongStackError {
|
|
1762
|
+
source;
|
|
1763
|
+
constructor(opts) {
|
|
1764
|
+
super({
|
|
1765
|
+
message: opts.message,
|
|
1766
|
+
code: ERROR_CODES.PARSE_FAILED,
|
|
1767
|
+
subsystem: "general",
|
|
1768
|
+
severity: "error",
|
|
1769
|
+
recoverable: false,
|
|
1770
|
+
context: { source: opts.source, ...opts.context },
|
|
1771
|
+
cause: opts.cause
|
|
1772
|
+
});
|
|
1773
|
+
this.name = "ParseError";
|
|
1774
|
+
this.source = opts.source;
|
|
1775
|
+
}
|
|
1776
|
+
};
|
|
1741
1777
|
function isWrongStackError(err) {
|
|
1742
1778
|
return err instanceof WrongStackError;
|
|
1743
1779
|
}
|
|
@@ -1765,6 +1801,9 @@ function isToolValidationError(err) {
|
|
|
1765
1801
|
function isFetchError(err) {
|
|
1766
1802
|
return err instanceof FetchError;
|
|
1767
1803
|
}
|
|
1804
|
+
function isParseError(err) {
|
|
1805
|
+
return err instanceof ParseError;
|
|
1806
|
+
}
|
|
1768
1807
|
function isSddError(err) {
|
|
1769
1808
|
return err instanceof SddError;
|
|
1770
1809
|
}
|
|
@@ -2112,7 +2151,7 @@ var DefaultSecretVault = class {
|
|
|
2112
2151
|
const oldVersion = this._keyVersion;
|
|
2113
2152
|
const newKey = randomBytes(KEY_BYTES);
|
|
2114
2153
|
const newVersion = oldVersion + 1;
|
|
2115
|
-
fs2.mkdirSync(
|
|
2154
|
+
fs2.mkdirSync(path7.dirname(this.keyFile), { recursive: true });
|
|
2116
2155
|
const passphrase = getVaultPassphrase();
|
|
2117
2156
|
if (passphrase) {
|
|
2118
2157
|
fs2.writeFileSync(this.keyFile, wrapDataKey(newKey, newVersion, passphrase), { mode: 384 });
|
|
@@ -2196,7 +2235,7 @@ var DefaultSecretVault = class {
|
|
|
2196
2235
|
} catch (err) {
|
|
2197
2236
|
if (err.code !== "ENOENT") throw err;
|
|
2198
2237
|
}
|
|
2199
|
-
fs2.mkdirSync(
|
|
2238
|
+
fs2.mkdirSync(path7.dirname(this.keyFile), { recursive: true });
|
|
2200
2239
|
const key = randomBytes(KEY_BYTES);
|
|
2201
2240
|
const passphrase = getVaultPassphrase();
|
|
2202
2241
|
const initialBytes = passphrase ? wrapDataKey(key, 1, passphrase) : key;
|
|
@@ -2295,7 +2334,7 @@ async function rewriteConfigEncrypted(configPath, vault, patch) {
|
|
|
2295
2334
|
}
|
|
2296
2335
|
const merged = deepMerge(current, patch ?? {});
|
|
2297
2336
|
const encrypted = encryptConfigSecrets(merged, vault);
|
|
2298
|
-
await fs.mkdir(
|
|
2337
|
+
await fs.mkdir(path7.dirname(configPath), { recursive: true });
|
|
2299
2338
|
await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
2300
2339
|
await restrictFilePermissions(configPath);
|
|
2301
2340
|
}
|
|
@@ -2521,7 +2560,7 @@ var DefaultLogger = class _DefaultLogger {
|
|
|
2521
2560
|
this.stderr = opts.stderr !== false;
|
|
2522
2561
|
this.maxFileBytes = opts.maxFileBytes ?? 10 * 1024 * 1024;
|
|
2523
2562
|
if (this.file) {
|
|
2524
|
-
const dir =
|
|
2563
|
+
const dir = path7.dirname(this.file);
|
|
2525
2564
|
this._tail = this._tail.then(async () => {
|
|
2526
2565
|
await fs.mkdir(dir, { recursive: true });
|
|
2527
2566
|
}).catch(() => void 0);
|
|
@@ -2668,6 +2707,7 @@ var DefaultTokenCounter = class {
|
|
|
2668
2707
|
registry;
|
|
2669
2708
|
providerId;
|
|
2670
2709
|
events;
|
|
2710
|
+
sessionId;
|
|
2671
2711
|
priceCache = /* @__PURE__ */ new Map();
|
|
2672
2712
|
/** Most recently accounted request's tokens. Used for per-request context pressure. */
|
|
2673
2713
|
lastInput = 0;
|
|
@@ -2676,8 +2716,13 @@ var DefaultTokenCounter = class {
|
|
|
2676
2716
|
this.registry = opts.registry;
|
|
2677
2717
|
this.providerId = opts.providerId;
|
|
2678
2718
|
this.events = opts.events;
|
|
2719
|
+
this.sessionId = opts.sessionId;
|
|
2720
|
+
}
|
|
2721
|
+
setSessionId(sessionId) {
|
|
2722
|
+
this.sessionId = sessionId;
|
|
2679
2723
|
}
|
|
2680
2724
|
account(usage, model) {
|
|
2725
|
+
const eventSessionId = this.currentSessionId();
|
|
2681
2726
|
this.input += usage.input;
|
|
2682
2727
|
this.output += usage.output;
|
|
2683
2728
|
this.cacheRead += usage.cacheRead ?? 0;
|
|
@@ -2687,7 +2732,7 @@ var DefaultTokenCounter = class {
|
|
|
2687
2732
|
const price = model ? this.priceCache.get(model) : void 0;
|
|
2688
2733
|
if (price) {
|
|
2689
2734
|
this.applyPrice(usage, price);
|
|
2690
|
-
this.emitAccounted();
|
|
2735
|
+
this.emitAccounted(eventSessionId);
|
|
2691
2736
|
return;
|
|
2692
2737
|
}
|
|
2693
2738
|
if (this.registry && this.providerId && model) {
|
|
@@ -2701,18 +2746,22 @@ var DefaultTokenCounter = class {
|
|
|
2701
2746
|
this.priceCache.set(model, p);
|
|
2702
2747
|
this.applyPrice(usage, p);
|
|
2703
2748
|
}
|
|
2704
|
-
this.emitAccounted();
|
|
2749
|
+
this.emitAccounted(eventSessionId);
|
|
2705
2750
|
}).catch(() => {
|
|
2706
|
-
this.events?.emit("token.cost_estimate_unavailable", {
|
|
2707
|
-
|
|
2751
|
+
this.events?.emit("token.cost_estimate_unavailable", {
|
|
2752
|
+
...eventSessionId ? { sessionId: eventSessionId } : {},
|
|
2753
|
+
model: model ?? "<unknown>"
|
|
2754
|
+
});
|
|
2755
|
+
this.emitAccounted(eventSessionId);
|
|
2708
2756
|
return void 0;
|
|
2709
2757
|
});
|
|
2710
2758
|
return;
|
|
2711
2759
|
}
|
|
2712
|
-
this.emitAccounted();
|
|
2760
|
+
this.emitAccounted(eventSessionId);
|
|
2713
2761
|
}
|
|
2714
2762
|
/** Synchronous variant for code paths that have already resolved the model. */
|
|
2715
2763
|
accountWithModel(usage, resolved) {
|
|
2764
|
+
const eventSessionId = this.currentSessionId();
|
|
2716
2765
|
this.input += usage.input;
|
|
2717
2766
|
this.output += usage.output;
|
|
2718
2767
|
this.cacheRead += usage.cacheRead ?? 0;
|
|
@@ -2726,7 +2775,7 @@ var DefaultTokenCounter = class {
|
|
|
2726
2775
|
}
|
|
2727
2776
|
this.priceCache.set(resolved.modelId, price);
|
|
2728
2777
|
this.applyPrice(usage, price);
|
|
2729
|
-
this.emitAccounted();
|
|
2778
|
+
this.emitAccounted(eventSessionId);
|
|
2730
2779
|
}
|
|
2731
2780
|
total() {
|
|
2732
2781
|
return {
|
|
@@ -2739,6 +2788,10 @@ var DefaultTokenCounter = class {
|
|
|
2739
2788
|
currentRequestTokens() {
|
|
2740
2789
|
return { input: this.lastInput, cacheRead: this.lastCacheRead };
|
|
2741
2790
|
}
|
|
2791
|
+
setCurrentRequestTokens(input, cacheRead) {
|
|
2792
|
+
this.lastInput = input;
|
|
2793
|
+
this.lastCacheRead = cacheRead ?? 0;
|
|
2794
|
+
}
|
|
2742
2795
|
estimateCost() {
|
|
2743
2796
|
return {
|
|
2744
2797
|
input: round4(this.costInput),
|
|
@@ -2759,12 +2812,17 @@ var DefaultTokenCounter = class {
|
|
|
2759
2812
|
invalidateCache() {
|
|
2760
2813
|
this.priceCache.clear();
|
|
2761
2814
|
}
|
|
2762
|
-
emitAccounted() {
|
|
2815
|
+
emitAccounted(sessionId = this.currentSessionId()) {
|
|
2763
2816
|
this.events?.emit("token.accounted", {
|
|
2817
|
+
...sessionId ? { sessionId } : {},
|
|
2764
2818
|
usage: this.total(),
|
|
2765
2819
|
cost: { input: this.costInput, output: this.costOutput, total: this.costInput + this.costOutput }
|
|
2766
2820
|
});
|
|
2767
2821
|
}
|
|
2822
|
+
currentSessionId() {
|
|
2823
|
+
const value = typeof this.sessionId === "function" ? this.sessionId() : this.sessionId;
|
|
2824
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
2825
|
+
}
|
|
2768
2826
|
reset() {
|
|
2769
2827
|
this.input = 0;
|
|
2770
2828
|
this.output = 0;
|
|
@@ -2818,6 +2876,18 @@ var MEMORY_TYPE_LABELS = {
|
|
|
2818
2876
|
};
|
|
2819
2877
|
|
|
2820
2878
|
// src/execution/compaction-core.ts
|
|
2879
|
+
var FAILURE_PATTERN = /(error|fail|exception|timeout|enonet|eacces|eperm|enoent|abort)/i;
|
|
2880
|
+
var CORRECTION_PATTERN = /\b(wrong|no\b|stop\b|don'?t\b|actually|fix that|undo|revert|forget|ignore|skip)\b/i;
|
|
2881
|
+
var ERROR_LANG_PATTERN = /\b(error|exception|fatal|critical|crash|panic|abort|segfault|core dump|undefined is not|null pointer|typeerror|referenceerror|syntaxerror)\b/i;
|
|
2882
|
+
var SECURITY_PATTERN = /\b(security|vulnerability|injection|xss|csrf|secret|apikey|api.key|hardcoded|leak|exploit|cve)\b/i;
|
|
2883
|
+
var ARCHITECTURE_PATTERN = /\b(architecture|design|approach|strategy|pattern|refactor|migrate|restructure|decision|trade.?off)\b/i;
|
|
2884
|
+
var BOILERPLATE_PATTERN = /\b(files_with_matches|count|found \d+ match|directory tree|\.\.\. and \d+ more)\b/i;
|
|
2885
|
+
var PATH_HINT_PATTERN = /(?:(?:[A-Za-z]:)?[./\\]?[\w@.-]+(?:[\\/][\w@(). -]+)+\.[A-Za-z0-9]{1,12})/g;
|
|
2886
|
+
var PATH_BACKSLASH_PATTERN = /\\/g;
|
|
2887
|
+
var PATH_TRIM_PATTERN = /^["'`]+|["'`),;:]+$/g;
|
|
2888
|
+
var ERROR_LINE_PATTERN = /\b(error|exception|failed|failure|fatal|panic|timeout|denied|enoent|eacces|eperm)\b/i;
|
|
2889
|
+
var NEWLINE_SPLIT_PATTERN = /\r?\n/;
|
|
2890
|
+
var WHITESPACE_COLLAPSE_PATTERN = /\s+/g;
|
|
2821
2891
|
function compactionDebugEnabled() {
|
|
2822
2892
|
return process.env["NODE_ENV"] === "development" || process.env["WRONGSTACK_DEBUG"] === "1";
|
|
2823
2893
|
}
|
|
@@ -3053,9 +3123,8 @@ function summarizeToolResultElision(block, tokens) {
|
|
|
3053
3123
|
function extractPathHints(content) {
|
|
3054
3124
|
const text = typeof content === "string" ? content : JSON.stringify(content);
|
|
3055
3125
|
const out = /* @__PURE__ */ new Set();
|
|
3056
|
-
const
|
|
3057
|
-
|
|
3058
|
-
const clean = match[0]?.replace(/\\/g, "/").replace(/^["'`]+|["'`),;:]+$/g, "");
|
|
3126
|
+
for (const match of text.matchAll(PATH_HINT_PATTERN)) {
|
|
3127
|
+
const clean = match[0]?.replace(PATH_BACKSLASH_PATTERN, "/").replace(PATH_TRIM_PATTERN, "");
|
|
3059
3128
|
if (clean && clean.length <= 220) out.add(clean);
|
|
3060
3129
|
if (out.size >= 5) break;
|
|
3061
3130
|
}
|
|
@@ -3063,12 +3132,9 @@ function extractPathHints(content) {
|
|
|
3063
3132
|
}
|
|
3064
3133
|
function firstErrorLine(content) {
|
|
3065
3134
|
const text = typeof content === "string" ? content : JSON.stringify(content);
|
|
3066
|
-
for (const line of text.split(
|
|
3067
|
-
if (
|
|
3068
|
-
|
|
3069
|
-
))
|
|
3070
|
-
continue;
|
|
3071
|
-
const trimmed = line.replace(/\s+/g, " ").trim();
|
|
3135
|
+
for (const line of text.split(NEWLINE_SPLIT_PATTERN)) {
|
|
3136
|
+
if (!ERROR_LINE_PATTERN.test(line)) continue;
|
|
3137
|
+
const trimmed = line.replace(WHITESPACE_COLLAPSE_PATTERN, " ").trim();
|
|
3072
3138
|
if (trimmed) return trimmed.slice(0, 180);
|
|
3073
3139
|
}
|
|
3074
3140
|
return void 0;
|
|
@@ -3115,9 +3181,9 @@ function scoreMessage(m, context) {
|
|
|
3115
3181
|
if (hasToolUse2(m) || hasResult) return 0;
|
|
3116
3182
|
}
|
|
3117
3183
|
if (context?.failureCounts && m.role === "user" && hasToolUse2(m) === false) {
|
|
3118
|
-
const
|
|
3119
|
-
if (
|
|
3120
|
-
const errKey =
|
|
3184
|
+
const failureMatch = FAILURE_PATTERN.exec(text);
|
|
3185
|
+
if (failureMatch) {
|
|
3186
|
+
const errKey = failureMatch[0]?.toLowerCase() ?? "error";
|
|
3121
3187
|
const count = (context.failureCounts.get(errKey) ?? 0) + 1;
|
|
3122
3188
|
context.failureCounts.set(errKey, count);
|
|
3123
3189
|
if (count >= 5) return 0;
|
|
@@ -3125,29 +3191,21 @@ function scoreMessage(m, context) {
|
|
|
3125
3191
|
}
|
|
3126
3192
|
}
|
|
3127
3193
|
if (m.role === "user") {
|
|
3128
|
-
if (
|
|
3129
|
-
text
|
|
3130
|
-
)) {
|
|
3194
|
+
if (CORRECTION_PATTERN.test(text)) {
|
|
3131
3195
|
return 5;
|
|
3132
3196
|
}
|
|
3133
3197
|
}
|
|
3134
|
-
if (
|
|
3135
|
-
text
|
|
3136
|
-
)) {
|
|
3198
|
+
if (ERROR_LANG_PATTERN.test(text)) {
|
|
3137
3199
|
return 5;
|
|
3138
3200
|
}
|
|
3139
|
-
if (
|
|
3140
|
-
text
|
|
3141
|
-
)) {
|
|
3201
|
+
if (SECURITY_PATTERN.test(text)) {
|
|
3142
3202
|
return 5;
|
|
3143
3203
|
}
|
|
3144
|
-
if (m.role === "assistant" &&
|
|
3145
|
-
text
|
|
3146
|
-
)) {
|
|
3204
|
+
if (m.role === "assistant" && ARCHITECTURE_PATTERN.test(text)) {
|
|
3147
3205
|
return 5;
|
|
3148
3206
|
}
|
|
3149
3207
|
if (hasLargeToolResult(m)) return 1;
|
|
3150
|
-
if (m.role === "user" && !hasToolUse2(m) &&
|
|
3208
|
+
if (m.role === "user" && !hasToolUse2(m) && BOILERPLATE_PATTERN.test(text)) {
|
|
3151
3209
|
return 1;
|
|
3152
3210
|
}
|
|
3153
3211
|
return 3;
|
|
@@ -3157,6 +3215,11 @@ function buildSmartDigest(messages) {
|
|
|
3157
3215
|
const failureCounts = /* @__PURE__ */ new Map();
|
|
3158
3216
|
let noiseCount = 0;
|
|
3159
3217
|
for (const m of messages) {
|
|
3218
|
+
const isPureToolIO = Array.isArray(m.content) && m.content.length > 0 && !hasToolUse2(m) && m.content.every((b) => b.type === "tool_result");
|
|
3219
|
+
if (isPureToolIO) {
|
|
3220
|
+
noiseCount++;
|
|
3221
|
+
continue;
|
|
3222
|
+
}
|
|
3160
3223
|
const score = scoreMessage(m, { failureCounts });
|
|
3161
3224
|
const text = extractText(m);
|
|
3162
3225
|
const toolCount = countToolBlocks(m);
|
|
@@ -3369,52 +3432,52 @@ var DefaultPathResolver = class {
|
|
|
3369
3432
|
projectRoot;
|
|
3370
3433
|
cwd;
|
|
3371
3434
|
constructor(cwd = process.cwd()) {
|
|
3372
|
-
this.cwd =
|
|
3435
|
+
this.cwd = path7.resolve(cwd);
|
|
3373
3436
|
this.projectRoot = this.detectProjectRoot(this.cwd);
|
|
3374
3437
|
}
|
|
3375
3438
|
detectProjectRoot(start) {
|
|
3376
|
-
let dir =
|
|
3377
|
-
const root =
|
|
3378
|
-
const home =
|
|
3379
|
-
const startPath =
|
|
3439
|
+
let dir = path7.resolve(start);
|
|
3440
|
+
const root = path7.parse(dir).root;
|
|
3441
|
+
const home = path7.resolve(os.homedir());
|
|
3442
|
+
const startPath = path7.resolve(start);
|
|
3380
3443
|
while (dir !== root) {
|
|
3381
3444
|
if (dir === home && dir !== startPath) {
|
|
3382
3445
|
break;
|
|
3383
3446
|
}
|
|
3384
3447
|
for (const marker of PROJECT_MARKERS) {
|
|
3385
3448
|
try {
|
|
3386
|
-
fs2.accessSync(
|
|
3449
|
+
fs2.accessSync(path7.join(dir, marker));
|
|
3387
3450
|
return dir;
|
|
3388
3451
|
} catch {
|
|
3389
3452
|
}
|
|
3390
3453
|
}
|
|
3391
|
-
const parent =
|
|
3454
|
+
const parent = path7.dirname(dir);
|
|
3392
3455
|
if (parent === dir) break;
|
|
3393
3456
|
dir = parent;
|
|
3394
3457
|
}
|
|
3395
3458
|
return startPath;
|
|
3396
3459
|
}
|
|
3397
3460
|
resolve(input) {
|
|
3398
|
-
const abs =
|
|
3461
|
+
const abs = path7.isAbsolute(input) ? input : path7.resolve(this.cwd, input);
|
|
3399
3462
|
let real;
|
|
3400
3463
|
try {
|
|
3401
3464
|
real = fs2.realpathSync(abs);
|
|
3402
3465
|
} catch {
|
|
3403
|
-
real =
|
|
3466
|
+
real = path7.normalize(abs);
|
|
3404
3467
|
}
|
|
3405
3468
|
return real;
|
|
3406
3469
|
}
|
|
3407
3470
|
isInsideRoot(absPath) {
|
|
3408
|
-
const normalized =
|
|
3409
|
-
const root =
|
|
3471
|
+
const normalized = path7.normalize(absPath);
|
|
3472
|
+
const root = path7.normalize(this.projectRoot);
|
|
3410
3473
|
if (normalized === root) return true;
|
|
3411
|
-
const rel =
|
|
3412
|
-
return !rel.startsWith("..") && !
|
|
3474
|
+
const rel = path7.relative(root, normalized);
|
|
3475
|
+
return !rel.startsWith("..") && !path7.isAbsolute(rel);
|
|
3413
3476
|
}
|
|
3414
3477
|
ensureInsideRoot(absPath) {
|
|
3415
3478
|
const resolved = this.resolve(absPath);
|
|
3416
3479
|
if (!this.isInsideRoot(resolved)) {
|
|
3417
|
-
const display =
|
|
3480
|
+
const display = path7.isAbsolute(absPath) ? path7.basename(absPath) : absPath;
|
|
3418
3481
|
const err = new Error(`Path "${display}" resolves outside the project root`);
|
|
3419
3482
|
err.fullPath = absPath;
|
|
3420
3483
|
err.projectRoot = this.projectRoot;
|
|
@@ -3463,7 +3526,7 @@ function buildRecoveryStrategies(opts) {
|
|
|
3463
3526
|
label: "downgrade_model",
|
|
3464
3527
|
async attempt(err, ctx) {
|
|
3465
3528
|
if (!(err instanceof ProviderError)) return null;
|
|
3466
|
-
if (err.status !==
|
|
3529
|
+
if (err.status !== 529 && err.status < 500) return null;
|
|
3467
3530
|
const registry = opts?.modelsRegistry;
|
|
3468
3531
|
if (!registry) return null;
|
|
3469
3532
|
try {
|
|
@@ -3486,7 +3549,7 @@ function buildRecoveryStrategies(opts) {
|
|
|
3486
3549
|
});
|
|
3487
3550
|
if (candidates.length === 0) return null;
|
|
3488
3551
|
const fallback = candidates.reduce(
|
|
3489
|
-
(prev, curr) => (curr.cost?.input ??
|
|
3552
|
+
(prev, curr) => (curr.cost?.input ?? Number.POSITIVE_INFINITY) < (prev.cost?.input ?? Number.POSITIVE_INFINITY) ? curr : prev
|
|
3490
3553
|
);
|
|
3491
3554
|
return {
|
|
3492
3555
|
action: "retry",
|
|
@@ -3769,13 +3832,27 @@ var PATTERNS = [
|
|
|
3769
3832
|
// Min 12 chars: some OAuth providers issue shorter-lived tokens (< 20
|
|
3770
3833
|
// chars). A 12-char base64 string has ~71 bits of entropy — above the
|
|
3771
3834
|
// threshold where random strings are unlikely to produce false matches.
|
|
3772
|
-
|
|
3835
|
+
// The trailing boundary is a NON-consuming lookahead: two adjacent bearer
|
|
3836
|
+
// tokens sharing a single delimiter (`Bearer a… Bearer b…`) must both be
|
|
3837
|
+
// redacted. A consuming trailing delimiter would eat the separator the
|
|
3838
|
+
// next match needs for its leading anchor, leaking the second token.
|
|
3839
|
+
regex: /(?:^|[^A-Za-z0-9_.~+/-])Bearer\s+[A-Za-z0-9._~+/-]{12,512}=*(?=$|[^A-Za-z0-9_.~+/-])/g
|
|
3773
3840
|
},
|
|
3774
3841
|
{
|
|
3775
3842
|
type: "high_entropy_env",
|
|
3776
3843
|
// Anchored with alternation instead of lookbehind to avoid backtracking.
|
|
3777
3844
|
// Value bounded at 512 chars.
|
|
3778
|
-
|
|
3845
|
+
// The trailing boundary is a NON-consuming lookahead so two secrets
|
|
3846
|
+
// separated by a single delimiter (one space OR one newline, e.g.
|
|
3847
|
+
// `printenv` / `.env` dumps: `API_KEY=… \n SESSION_TOKEN=…`) are BOTH
|
|
3848
|
+
// redacted. A consuming trailing `\s` would swallow the separator the
|
|
3849
|
+
// next match needs for its leading anchor, so every other secret would
|
|
3850
|
+
// leak in plaintext.
|
|
3851
|
+
// The leading delimiter is CAPTURED (group 1) and re-emitted by the
|
|
3852
|
+
// replacement so the separator between adjacent secrets is preserved
|
|
3853
|
+
// rather than collapsed. Capture groups are therefore: 1=leading
|
|
3854
|
+
// delimiter, 2=key name, 3=value.
|
|
3855
|
+
regex: /(^|\s)([A-Z_]{4,}(?:KEY|TOKEN|SECRET|PASSWORD|PWD))\s*[:=]\s*['"]?([A-Za-z0-9_/+=-]{20,512})['"]?(?=\s|$)/g
|
|
3779
3856
|
}
|
|
3780
3857
|
];
|
|
3781
3858
|
var SIMPLE_PATTERNS = PATTERNS.filter((p) => p.type !== "high_entropy_env");
|
|
@@ -3783,6 +3860,7 @@ var COMBINED_REGEX = new RegExp(SIMPLE_PATTERNS.map((p) => `(${p.regex.source})`
|
|
|
3783
3860
|
var HIGH_ENTROPY_REGEX = PATTERNS.find((p) => p.type === "high_entropy_env").regex;
|
|
3784
3861
|
var COMBINED_REPLACEMENTS = SIMPLE_PATTERNS.map((p) => `[REDACTED:${p.type}]`);
|
|
3785
3862
|
var SCRUB_CHUNK_BYTES = 64 * 1024;
|
|
3863
|
+
var SCRUB_OVERLAP_BYTES = 1024;
|
|
3786
3864
|
function hasCredentialAnchors(text) {
|
|
3787
3865
|
return text.includes("-----BEGIN") || // Private keys (most unique → cheap reject)
|
|
3788
3866
|
text.includes("sk-") || // Anthropic + OpenAI keys
|
|
@@ -3817,8 +3895,16 @@ var DefaultSecretScrubber = class {
|
|
|
3817
3895
|
while (i < text.length) {
|
|
3818
3896
|
let end = Math.min(i + SCRUB_CHUNK_BYTES, text.length);
|
|
3819
3897
|
if (end < text.length) {
|
|
3820
|
-
const
|
|
3821
|
-
|
|
3898
|
+
const limit = Math.min(end + SCRUB_OVERLAP_BYTES, text.length);
|
|
3899
|
+
let safe = -1;
|
|
3900
|
+
for (let j = end; j < limit; j++) {
|
|
3901
|
+
const ch = text.charCodeAt(j);
|
|
3902
|
+
if (ch === 32 || ch === 9 || ch === 10 || ch === 13) {
|
|
3903
|
+
safe = j;
|
|
3904
|
+
break;
|
|
3905
|
+
}
|
|
3906
|
+
}
|
|
3907
|
+
end = safe === -1 ? end : safe + 1;
|
|
3822
3908
|
}
|
|
3823
3909
|
out.push(this.scrubOne(text.slice(i, end)));
|
|
3824
3910
|
i = end;
|
|
@@ -3836,8 +3922,8 @@ var DefaultSecretScrubber = class {
|
|
|
3836
3922
|
return replacement !== void 0 ? replacement : match;
|
|
3837
3923
|
}
|
|
3838
3924
|
);
|
|
3839
|
-
out = out.replace(HIGH_ENTROPY_REGEX, (_match,
|
|
3840
|
-
return `${
|
|
3925
|
+
out = out.replace(HIGH_ENTROPY_REGEX, (_match, lead, key, _value) => {
|
|
3926
|
+
return `${lead}${key}=[REDACTED:high_entropy_env]`;
|
|
3841
3927
|
});
|
|
3842
3928
|
return out;
|
|
3843
3929
|
}
|
|
@@ -3898,6 +3984,14 @@ var DefaultModelsRegistry = class {
|
|
|
3898
3984
|
payload;
|
|
3899
3985
|
/** Memoised overlay payload (in-memory / fetched / file). */
|
|
3900
3986
|
overlayPayload;
|
|
3987
|
+
/**
|
|
3988
|
+
* Extra providers injected at runtime via `mergeOverlay()` — e.g. an
|
|
3989
|
+
* openai-compatible server (omniroute, LiteLLM, …) auto-discovered from its
|
|
3990
|
+
* `/v1/models` endpoint at boot. Applied LAST (on top of base + curated
|
|
3991
|
+
* overlay) and re-applied across `refresh()` so the discovered catalog
|
|
3992
|
+
* survives a models.dev refetch.
|
|
3993
|
+
*/
|
|
3994
|
+
extraOverlay;
|
|
3901
3995
|
fetchedAt;
|
|
3902
3996
|
cacheFile;
|
|
3903
3997
|
url;
|
|
@@ -3922,7 +4016,7 @@ var DefaultModelsRegistry = class {
|
|
|
3922
4016
|
this.overlay = opts.overlay;
|
|
3923
4017
|
this.overlayUrl = opts.overlayUrl;
|
|
3924
4018
|
this.overlayFile = opts.overlayFile;
|
|
3925
|
-
this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ?
|
|
4019
|
+
this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path7.join(path7.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
|
|
3926
4020
|
}
|
|
3927
4021
|
async load(opts = {}) {
|
|
3928
4022
|
if (this.payload && !opts.force) return this.payload;
|
|
@@ -3933,9 +4027,22 @@ var DefaultModelsRegistry = class {
|
|
|
3933
4027
|
}
|
|
3934
4028
|
const overlay = await this.loadOverlay(opts);
|
|
3935
4029
|
const base = await this.loadBase(opts, Object.keys(overlay).length > 0);
|
|
3936
|
-
this.payload = mergeModelsPayload(base, overlay);
|
|
4030
|
+
this.payload = this.withExtraOverlay(mergeModelsPayload(base, overlay));
|
|
3937
4031
|
return this.payload;
|
|
3938
4032
|
}
|
|
4033
|
+
/**
|
|
4034
|
+
* Merge an additional provider payload on top of the resolved catalog. Used
|
|
4035
|
+
* for runtime-discovered openai-compatible providers. Remembered so it is
|
|
4036
|
+
* re-applied across `refresh()`. A no-op for an empty payload.
|
|
4037
|
+
*/
|
|
4038
|
+
mergeOverlay(payload) {
|
|
4039
|
+
if (!hasEntries(payload)) return;
|
|
4040
|
+
this.extraOverlay = this.extraOverlay ? mergeModelsPayload(this.extraOverlay, payload) : payload;
|
|
4041
|
+
if (this.payload) this.payload = mergeModelsPayload(this.payload, this.extraOverlay);
|
|
4042
|
+
}
|
|
4043
|
+
withExtraOverlay(payload) {
|
|
4044
|
+
return this.extraOverlay ? mergeModelsPayload(payload, this.extraOverlay) : payload;
|
|
4045
|
+
}
|
|
3939
4046
|
/**
|
|
3940
4047
|
* Load the models.dev base payload: fresh cache → network → stale cache.
|
|
3941
4048
|
* On total failure, degrade to `{}` (so a non-empty overlay still drives
|
|
@@ -3984,7 +4091,11 @@ var DefaultModelsRegistry = class {
|
|
|
3984
4091
|
});
|
|
3985
4092
|
clearTimeout(timeout);
|
|
3986
4093
|
if (!res.ok) {
|
|
3987
|
-
throw new
|
|
4094
|
+
throw new FetchError({
|
|
4095
|
+
message: `ModelsRegistry: HTTP ${res.status} fetching ${this.url}`,
|
|
4096
|
+
status: res.status,
|
|
4097
|
+
context: { url: this.url, op: "refreshModels" }
|
|
4098
|
+
});
|
|
3988
4099
|
}
|
|
3989
4100
|
const json = await res.json();
|
|
3990
4101
|
this.fetchedAt = /* @__PURE__ */ new Date();
|
|
@@ -3998,7 +4109,11 @@ var DefaultModelsRegistry = class {
|
|
|
3998
4109
|
} catch (err) {
|
|
3999
4110
|
clearTimeout(timeout);
|
|
4000
4111
|
if (err instanceof Error && err.name === "AbortError") {
|
|
4001
|
-
throw new
|
|
4112
|
+
throw new FetchError({
|
|
4113
|
+
message: `ModelsRegistry: fetch timed out after ${this.refreshTimeoutMs}ms`,
|
|
4114
|
+
status: 408,
|
|
4115
|
+
context: { url: this.url, op: "refreshModels", timedOut: true }
|
|
4116
|
+
});
|
|
4002
4117
|
}
|
|
4003
4118
|
throw err;
|
|
4004
4119
|
}
|
|
@@ -4034,7 +4149,11 @@ var DefaultModelsRegistry = class {
|
|
|
4034
4149
|
method: "GET",
|
|
4035
4150
|
headers: { accept: "application/json" }
|
|
4036
4151
|
});
|
|
4037
|
-
if (!res.ok) throw new
|
|
4152
|
+
if (!res.ok) throw new FetchError({
|
|
4153
|
+
message: `HTTP ${res.status}`,
|
|
4154
|
+
status: res.status,
|
|
4155
|
+
context: { url: this.url, op: "refreshModels" }
|
|
4156
|
+
});
|
|
4038
4157
|
const json = await res.json();
|
|
4039
4158
|
const envelope = {
|
|
4040
4159
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -4068,7 +4187,7 @@ var DefaultModelsRegistry = class {
|
|
|
4068
4187
|
async refresh() {
|
|
4069
4188
|
const base = await this.refreshBase();
|
|
4070
4189
|
const overlay = await this.loadOverlay({ force: true });
|
|
4071
|
-
this.payload = mergeModelsPayload(base, overlay);
|
|
4190
|
+
this.payload = this.withExtraOverlay(mergeModelsPayload(base, overlay));
|
|
4072
4191
|
return this.payload;
|
|
4073
4192
|
}
|
|
4074
4193
|
async listProviders() {
|
|
@@ -4146,7 +4265,7 @@ var DefaultModelsRegistry = class {
|
|
|
4146
4265
|
}
|
|
4147
4266
|
/** Used by `wstack models refresh` to expose where the cache lives. */
|
|
4148
4267
|
cacheLocation() {
|
|
4149
|
-
return
|
|
4268
|
+
return path7.resolve(this.cacheFile);
|
|
4150
4269
|
}
|
|
4151
4270
|
};
|
|
4152
4271
|
function formatAge(seconds) {
|
|
@@ -4165,18 +4284,18 @@ function hasEntries(payload) {
|
|
|
4165
4284
|
function modePrompt(id) {
|
|
4166
4285
|
for (const dir of modePromptDirCandidates()) {
|
|
4167
4286
|
try {
|
|
4168
|
-
return readFileSync(
|
|
4287
|
+
return readFileSync(path7.join(dir, `${id}.md`), "utf8").trimEnd();
|
|
4169
4288
|
} catch {
|
|
4170
4289
|
}
|
|
4171
4290
|
}
|
|
4172
4291
|
return "";
|
|
4173
4292
|
}
|
|
4174
4293
|
function modePromptDirCandidates() {
|
|
4175
|
-
const here =
|
|
4294
|
+
const here = path7.dirname(fileURLToPath(import.meta.url));
|
|
4176
4295
|
const candidates = [
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4296
|
+
path7.resolve(here, "../../instructions/modes"),
|
|
4297
|
+
path7.resolve(here, "../instructions/modes"),
|
|
4298
|
+
path7.resolve(here, "instructions/modes")
|
|
4180
4299
|
];
|
|
4181
4300
|
return candidates.sort((a, b) => Number(!isDirectory(a)) - Number(!isDirectory(b)));
|
|
4182
4301
|
}
|
|
@@ -4293,7 +4412,7 @@ var DEFAULT_MODES = [
|
|
|
4293
4412
|
description: "Current-data research \u2014 search web, verify, inject findings into context",
|
|
4294
4413
|
prompt: modePrompt("research-web"),
|
|
4295
4414
|
tags: ["research", "web", "current-data", "up-to-date"],
|
|
4296
|
-
toolPreferences: ["
|
|
4415
|
+
toolPreferences: ["search/fetch", "search/fetch", "search", "fetch", "context_manager"],
|
|
4297
4416
|
suggestedSkills: ["research-web", "tech-stack", "node-modern", "security-scanner", "react-modern"]
|
|
4298
4417
|
}
|
|
4299
4418
|
];
|
|
@@ -4479,7 +4598,7 @@ var InMemoryAgentBridge = class {
|
|
|
4479
4598
|
});
|
|
4480
4599
|
}
|
|
4481
4600
|
this.inflightGuards.add(correlationId);
|
|
4482
|
-
return new Promise((
|
|
4601
|
+
return new Promise((resolve8, reject) => {
|
|
4483
4602
|
const timer = setTimeout(() => {
|
|
4484
4603
|
this.inflightGuards.delete(correlationId);
|
|
4485
4604
|
this.pendingRequests.delete(correlationId);
|
|
@@ -4498,7 +4617,7 @@ var InMemoryAgentBridge = class {
|
|
|
4498
4617
|
return;
|
|
4499
4618
|
}
|
|
4500
4619
|
this.pendingRequests.set(correlationId, {
|
|
4501
|
-
resolve:
|
|
4620
|
+
resolve: resolve8,
|
|
4502
4621
|
reject,
|
|
4503
4622
|
timer
|
|
4504
4623
|
});
|
|
@@ -4741,6 +4860,64 @@ var ToolExecutor = class _ToolExecutor {
|
|
|
4741
4860
|
const mode = resolveToolResultRenderMode(modes, toolName);
|
|
4742
4861
|
renderer.setResultRenderMode(toolName, mode);
|
|
4743
4862
|
}
|
|
4863
|
+
toolLogBase(ctx, use, toolName, durationMs) {
|
|
4864
|
+
return {
|
|
4865
|
+
event: "tool.execution",
|
|
4866
|
+
traceId: ctx.traceId,
|
|
4867
|
+
sessionId: ctx.session.id,
|
|
4868
|
+
agentId: ctx.agentId ?? "<unknown>",
|
|
4869
|
+
toolName,
|
|
4870
|
+
toolUseId: use.id,
|
|
4871
|
+
durationMs
|
|
4872
|
+
};
|
|
4873
|
+
}
|
|
4874
|
+
logToolSuccess(ctx, use, toolName, durationMs, outputChars) {
|
|
4875
|
+
this.opts.events?.emit("tool.completed", {
|
|
4876
|
+
name: toolName,
|
|
4877
|
+
id: use.id,
|
|
4878
|
+
sessionId: ctx.session.id,
|
|
4879
|
+
...ctx.traceId ? { traceId: ctx.traceId } : {},
|
|
4880
|
+
agentId: ctx.agentId ?? "<unknown>",
|
|
4881
|
+
durationMs,
|
|
4882
|
+
outputChars
|
|
4883
|
+
});
|
|
4884
|
+
this.opts.logger?.info("tool execution completed", {
|
|
4885
|
+
...this.toolLogBase(ctx, use, toolName, durationMs),
|
|
4886
|
+
outcome: "success",
|
|
4887
|
+
isError: false,
|
|
4888
|
+
outputChars
|
|
4889
|
+
});
|
|
4890
|
+
}
|
|
4891
|
+
logToolFailure(ctx, use, toolName, durationMs, err) {
|
|
4892
|
+
const { category, retryable, detail } = classifyToolError(err);
|
|
4893
|
+
const structured = isWrongStackError(err);
|
|
4894
|
+
const structuredError = structured ? {
|
|
4895
|
+
errorCode: err.code,
|
|
4896
|
+
errorSubsystem: err.subsystem,
|
|
4897
|
+
errorSeverity: err.severity
|
|
4898
|
+
} : {};
|
|
4899
|
+
this.opts.events?.emit("tool.failed", {
|
|
4900
|
+
name: toolName,
|
|
4901
|
+
id: use.id,
|
|
4902
|
+
sessionId: ctx.session.id,
|
|
4903
|
+
...ctx.traceId ? { traceId: ctx.traceId } : {},
|
|
4904
|
+
agentId: ctx.agentId ?? "<unknown>",
|
|
4905
|
+
durationMs,
|
|
4906
|
+
category,
|
|
4907
|
+
retryable,
|
|
4908
|
+
...detail ? { detail } : {},
|
|
4909
|
+
...structuredError
|
|
4910
|
+
});
|
|
4911
|
+
this.opts.logger?.warn("tool execution failed", {
|
|
4912
|
+
...this.toolLogBase(ctx, use, toolName, durationMs),
|
|
4913
|
+
outcome: "failure",
|
|
4914
|
+
isError: true,
|
|
4915
|
+
errorCategory: category,
|
|
4916
|
+
retryable,
|
|
4917
|
+
errorDetail: detail,
|
|
4918
|
+
...structuredError
|
|
4919
|
+
});
|
|
4920
|
+
}
|
|
4744
4921
|
/**
|
|
4745
4922
|
* Execute a batch of tool uses using the configured strategy.
|
|
4746
4923
|
* Returns the execution results and the remaining output budget.
|
|
@@ -4871,7 +5048,8 @@ ${errorDetails}`,
|
|
|
4871
5048
|
"tool.has_dangerous_capabilities": toolCapsForAudit.length > 0
|
|
4872
5049
|
});
|
|
4873
5050
|
try {
|
|
4874
|
-
|
|
5051
|
+
const producedText = await this.produceToolOutput(tool, use, ctx, budget);
|
|
5052
|
+
let { block: result, bytes } = this.settleToolOutput(tool, use, producedText, budget);
|
|
4875
5053
|
budget -= bytes;
|
|
4876
5054
|
if (this.opts.hookRunner?.has("PostToolUse")) {
|
|
4877
5055
|
const post = await this.opts.hookRunner.postToolUse(
|
|
@@ -4888,13 +5066,18 @@ ${post.additionalContext}`;
|
|
|
4888
5066
|
budget = Math.max(0, budget - Buffer.byteLength(appended, "utf8"));
|
|
4889
5067
|
}
|
|
4890
5068
|
}
|
|
5069
|
+
const outputChars = typeof result.content === "string" ? result.content.length : 0;
|
|
4891
5070
|
span?.setAttribute("tool.is_error", !!result.is_error);
|
|
4892
|
-
span?.setAttribute(
|
|
4893
|
-
|
|
4894
|
-
typeof result.content === "string" ? result.content.length : 0
|
|
4895
|
-
);
|
|
5071
|
+
span?.setAttribute("tool.output_bytes", outputChars);
|
|
5072
|
+
this.logToolSuccess(ctx, use, tool.name, Date.now() - start, outputChars);
|
|
4896
5073
|
return { result, tool, durationMs: Date.now() - start };
|
|
4897
5074
|
} catch (err) {
|
|
5075
|
+
if (isWrongStackError(err)) {
|
|
5076
|
+
if (err instanceof Error) span?.recordError(err);
|
|
5077
|
+
span?.setAttribute("tool.is_error", true);
|
|
5078
|
+
this.logToolFailure(ctx, use, tool.name, Date.now() - start, err);
|
|
5079
|
+
throw err;
|
|
5080
|
+
}
|
|
4898
5081
|
const msg = toErrorMessage(err);
|
|
4899
5082
|
const scrubbed = this.opts.secretScrubber.scrub(msg);
|
|
4900
5083
|
const { category, retryable, detail } = classifyToolError(err);
|
|
@@ -4912,6 +5095,7 @@ ${post.additionalContext}`;
|
|
|
4912
5095
|
span?.setAttribute("tool.error_category", category);
|
|
4913
5096
|
span?.setAttribute("tool.error_retryable", retryable);
|
|
4914
5097
|
if (detail) span?.setAttribute("tool.error_detail", detail);
|
|
5098
|
+
this.logToolFailure(ctx, use, tool.name, Date.now() - start, err);
|
|
4915
5099
|
return { result, tool, durationMs: Date.now() - start };
|
|
4916
5100
|
} finally {
|
|
4917
5101
|
span?.end();
|
|
@@ -4921,7 +5105,8 @@ ${post.additionalContext}`;
|
|
|
4921
5105
|
try {
|
|
4922
5106
|
return await runOne(use);
|
|
4923
5107
|
} catch (err) {
|
|
4924
|
-
const
|
|
5108
|
+
const isStructured = isWrongStackError(err);
|
|
5109
|
+
const msg = isStructured ? err.describe() : toErrorMessage(err);
|
|
4925
5110
|
const scrubbed = this.opts.secretScrubber.scrub(msg);
|
|
4926
5111
|
const { category, retryable, detail } = classifyToolError(err);
|
|
4927
5112
|
const tool = this.registry.get(use.name);
|
|
@@ -4931,7 +5116,7 @@ ${post.additionalContext}`;
|
|
|
4931
5116
|
const result = {
|
|
4932
5117
|
type: "tool_result",
|
|
4933
5118
|
tool_use_id: use.id,
|
|
4934
|
-
content: `Tool "${use.name}" execution failed: ${scrubbed}`,
|
|
5119
|
+
content: isStructured ? scrubbed : `Tool "${use.name}" execution failed: ${scrubbed}`,
|
|
4935
5120
|
is_error: true
|
|
4936
5121
|
};
|
|
4937
5122
|
budget = this.budgetForString(result.content, budget);
|
|
@@ -4974,7 +5159,23 @@ ${post.additionalContext}`;
|
|
|
4974
5159
|
* to call a tool" and "tool.executed".
|
|
4975
5160
|
*/
|
|
4976
5161
|
async executeTool(tool, use, ctx, budget) {
|
|
5162
|
+
const text = await this.produceToolOutput(tool, use, ctx, budget);
|
|
5163
|
+
return this.settleToolOutput(tool, use, text, budget);
|
|
5164
|
+
}
|
|
5165
|
+
/**
|
|
5166
|
+
* Async "produce" phase: run the tool, serialize, scrub, and spill an
|
|
5167
|
+
* oversized output to a disk artifact. Returns the pre-cap text. This is
|
|
5168
|
+
* the long-running, concurrency-safe part — it touches NO shared budget
|
|
5169
|
+
* state, so multiple invocations can run in parallel without racing.
|
|
5170
|
+
*
|
|
5171
|
+
* The `budgetHint` only gates the disk-spill threshold (a heuristic for
|
|
5172
|
+
* "is this output large enough to persist"); it is NOT the output cap.
|
|
5173
|
+
* The authoritative cap is applied synchronously in settleToolOutput()
|
|
5174
|
+
* against the live budget.
|
|
5175
|
+
*/
|
|
5176
|
+
async produceToolOutput(tool, use, ctx, budgetHint) {
|
|
4977
5177
|
this.opts.events?.emit("tool.started", {
|
|
5178
|
+
sessionId: ctx.session.id,
|
|
4978
5179
|
name: tool.name,
|
|
4979
5180
|
id: use.id,
|
|
4980
5181
|
input: use.input
|
|
@@ -4983,8 +5184,20 @@ ${post.additionalContext}`;
|
|
|
4983
5184
|
const output = await this.runWithTimeout(tool, use.input, ctx.signal, ctx, use.id);
|
|
4984
5185
|
const text = this.serializer.serialize(output, { toolName: tool.name, input: use.input, tool });
|
|
4985
5186
|
const scrubbed = this.opts.secretScrubber.scrub(text);
|
|
4986
|
-
|
|
4987
|
-
|
|
5187
|
+
return maybePersistLargeToolOutput(tool.name, scrubbed, budgetHint);
|
|
5188
|
+
}
|
|
5189
|
+
/**
|
|
5190
|
+
* Synchronous "settle" phase: enforce the output cap against the CURRENT
|
|
5191
|
+
* budget, render, and build the result block. This MUST stay synchronous
|
|
5192
|
+
* (no awaits) so that, in the parallel/smart strategies, two tools settling
|
|
5193
|
+
* one after another against the shared closure budget can't interleave a
|
|
5194
|
+
* stale read with a write. The first to settle consumes its bytes; the next
|
|
5195
|
+
* settles against the reduced budget; once the budget hits 0, enforceCap
|
|
5196
|
+
* truncates — making the per-iteration cap genuinely CUMULATIVE across
|
|
5197
|
+
* parallel tools instead of degrading into a per-tool cap.
|
|
5198
|
+
*/
|
|
5199
|
+
settleToolOutput(tool, use, text, budget) {
|
|
5200
|
+
const { text: capped, newBudget } = this.serializer.enforceCap(text, budget);
|
|
4988
5201
|
this.hintRenderMode(tool.name);
|
|
4989
5202
|
this.opts.renderer?.writeToolResult(tool.name, capped, false);
|
|
4990
5203
|
return {
|
|
@@ -5037,7 +5250,12 @@ ${post.additionalContext}`;
|
|
|
5037
5250
|
let finalOutput;
|
|
5038
5251
|
let sawFinal = false;
|
|
5039
5252
|
if (!tool.executeStream) {
|
|
5040
|
-
throw new
|
|
5253
|
+
throw new ToolError({
|
|
5254
|
+
message: `Tool "${tool.name}" does not support streaming execution`,
|
|
5255
|
+
code: "TOOL_EXECUTION_FAILED",
|
|
5256
|
+
toolName: tool.name,
|
|
5257
|
+
context: { reason: "streaming_not_supported" }
|
|
5258
|
+
});
|
|
5041
5259
|
}
|
|
5042
5260
|
const stream = tool.executeStream(input, ctx, { signal });
|
|
5043
5261
|
const iter = stream[Symbol.asyncIterator]();
|
|
@@ -5047,6 +5265,7 @@ ${post.additionalContext}`;
|
|
|
5047
5265
|
let lastProgressEmitAt = 0;
|
|
5048
5266
|
const emitProgress = (ev) => {
|
|
5049
5267
|
this.opts.events?.emit("tool.progress", {
|
|
5268
|
+
sessionId: ctx.session.id,
|
|
5050
5269
|
name: tool.name,
|
|
5051
5270
|
id: toolUseId ?? "<unknown>",
|
|
5052
5271
|
event: ev
|
|
@@ -5102,7 +5321,12 @@ ${progressTail}`;
|
|
|
5102
5321
|
await iter.return?.(void 0);
|
|
5103
5322
|
}
|
|
5104
5323
|
if (!sawFinal) {
|
|
5105
|
-
throw new
|
|
5324
|
+
throw new ToolError({
|
|
5325
|
+
message: `tool "${tool.name}" executeStream completed without a 'final' event`,
|
|
5326
|
+
code: "TOOL_EXECUTION_FAILED",
|
|
5327
|
+
toolName: tool.name,
|
|
5328
|
+
context: { reason: "missing_final_event" }
|
|
5329
|
+
});
|
|
5106
5330
|
}
|
|
5107
5331
|
return finalOutput;
|
|
5108
5332
|
}
|
|
@@ -5226,6 +5450,15 @@ function classifyToolError(err) {
|
|
|
5226
5450
|
if (err instanceof Error && err.message.includes("validation")) {
|
|
5227
5451
|
return { category: "validation" /* VALIDATION */, retryable: false, detail: "validation" };
|
|
5228
5452
|
}
|
|
5453
|
+
if (err instanceof WrongStackError) {
|
|
5454
|
+
const wse = err;
|
|
5455
|
+
const category = wse.severity === "warning" ? "transient" /* TRANSIENT */ : "fatal" /* FATAL */;
|
|
5456
|
+
return {
|
|
5457
|
+
category,
|
|
5458
|
+
retryable: wse.recoverable,
|
|
5459
|
+
detail: `${wse.code} [${wse.subsystem}]`
|
|
5460
|
+
};
|
|
5461
|
+
}
|
|
5229
5462
|
return {
|
|
5230
5463
|
category: "fatal" /* FATAL */,
|
|
5231
5464
|
retryable: false,
|
|
@@ -5253,11 +5486,11 @@ async function maybePersistLargeToolOutput(toolName, content, budget) {
|
|
|
5253
5486
|
return content;
|
|
5254
5487
|
}
|
|
5255
5488
|
try {
|
|
5256
|
-
const dir =
|
|
5489
|
+
const dir = path7.join(wstackGlobalRoot(), "tool-output");
|
|
5257
5490
|
await fs.mkdir(dir, { recursive: true });
|
|
5258
5491
|
const safeTool = toolName.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40) || "tool";
|
|
5259
5492
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
5260
|
-
const filePath =
|
|
5493
|
+
const filePath = path7.join(dir, `${stamp}-${safeTool}-${randomUUID()}.log`);
|
|
5261
5494
|
await fs.writeFile(filePath, content, "utf8");
|
|
5262
5495
|
return content + `
|
|
5263
5496
|
[full tool output: ${bytes} bytes at ${filePath}; read/grep that file selectively instead of re-running or requesting more output]`;
|
|
@@ -5617,11 +5850,11 @@ var Context = class {
|
|
|
5617
5850
|
* Returns the resolved absolute path.
|
|
5618
5851
|
*/
|
|
5619
5852
|
setWorkingDir(dir) {
|
|
5620
|
-
const resolved =
|
|
5853
|
+
const resolved = path7.isAbsolute(dir) ? path7.resolve(dir) : path7.resolve(this.projectRoot, dir);
|
|
5621
5854
|
if (!this.allowOutsideProjectRoot) {
|
|
5622
|
-
const root =
|
|
5623
|
-
const rel =
|
|
5624
|
-
if (rel.startsWith("..") ||
|
|
5855
|
+
const root = path7.resolve(this.projectRoot);
|
|
5856
|
+
const rel = path7.relative(root, resolved);
|
|
5857
|
+
if (rel.startsWith("..") || path7.isAbsolute(rel)) {
|
|
5625
5858
|
throw new Error(
|
|
5626
5859
|
`Working directory "${resolved}" is outside project root "${root}"`
|
|
5627
5860
|
);
|
|
@@ -5668,7 +5901,7 @@ var DefaultSessionReader = class _DefaultSessionReader {
|
|
|
5668
5901
|
if (!rootDir) {
|
|
5669
5902
|
return await this.store.load(sessionId);
|
|
5670
5903
|
}
|
|
5671
|
-
const sessionPath =
|
|
5904
|
+
const sessionPath = sessionScopedPath(rootDir, sessionId, ".jsonl");
|
|
5672
5905
|
let mtimeMs = null;
|
|
5673
5906
|
try {
|
|
5674
5907
|
const stat4 = await fs.stat(sessionPath);
|
|
@@ -6029,6 +6262,6 @@ function renderPlainText(meta, events) {
|
|
|
6029
6262
|
return lines.join("\n");
|
|
6030
6263
|
}
|
|
6031
6264
|
|
|
6032
|
-
export { AgentError, BUILTIN_PROMPT_CATEGORIES, CONTEXT_WINDOW_MODES, ConfigError, Context, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CIRCUIT_BREAKER_CONFIG, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SESSION_PRUNE_DAYS, DEFAULT_SPEC_TEMPLATE, DEFAULT_TOOLS_CONFIG, DEFAULT_TUI_THINKING_WORD, DESIGN_STACKS, DefaultErrorHandler, DefaultLogger, DefaultModelsRegistry, DefaultPathResolver, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultTokenCounter, ERROR_CODES, FetchError, FsError, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, MALFORMED_ARG_MARKERS, MAX_TUI_THINKING_WORD_LENGTH, MEMORY_TYPE_LABELS, PROMPT_CATEGORY_LABELS, PluginError, ProviderError, SddError, SessionError, StreamHangError, ToolError, ToolErrorCategory, ToolExecutor, ToolValidationError, WrongStackError, asBlocks, asText, buildRecoveryStrategies, classifyFamily, computeTaskProgress, createMessage, decryptConfigSecrets, diffRegistry, encryptConfigSecrets, findCriticalPath, formatContextWindowModeList, getContextWindowMode, isAgentError, isBuiltinCategory, isConfigError, isContextWindowModeId, isDesignStack, isFetchError, isFsError, isImageBlock, isPluginError, isSddError, isSecretField, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isToolValidationError, isWrongStackError, listContextWindowModes, migratePlaintextSecrets, noOpLogger, normalizeTokenSavingTier, normalizeTuiThinkingWord, resolveContextWindowPolicy, rewriteConfigEncrypted, rotateConfigKeys, toWrongStackError, topologicalSort, validateRegistryManifest };
|
|
6265
|
+
export { AgentError, BUILTIN_PROMPT_CATEGORIES, CONTEXT_WINDOW_MODES, ConfigError, Context, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CIRCUIT_BREAKER_CONFIG, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SESSION_PRUNE_DAYS, DEFAULT_SPEC_TEMPLATE, DEFAULT_TOOLS_CONFIG, DEFAULT_TUI_THINKING_WORD, DESIGN_STACKS, DefaultErrorHandler, DefaultLogger, DefaultModelsRegistry, DefaultPathResolver, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultTokenCounter, ERROR_CODES, FetchError, FsError, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, MALFORMED_ARG_MARKERS, MAX_TUI_THINKING_WORD_LENGTH, MEMORY_TYPE_LABELS, PROMPT_CATEGORY_LABELS, ParseError, PluginError, ProviderError, SddError, SessionError, StreamHangError, ToolError, ToolErrorCategory, ToolExecutor, ToolValidationError, WrongStackError, asBlocks, asText, buildRecoveryStrategies, classifyFamily, classifyToolError, computeTaskProgress, createMessage, decryptConfigSecrets, diffRegistry, encryptConfigSecrets, findCriticalPath, formatContextWindowModeList, getContextWindowMode, isAgentError, isBuiltinCategory, isConfigError, isContextWindowModeId, isDesignStack, isFetchError, isFsError, isImageBlock, isParseError, isPluginError, isSddError, isSecretField, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isToolValidationError, isWrongStackError, listContextWindowModes, migratePlaintextSecrets, noOpLogger, normalizeTokenSavingTier, normalizeTuiThinkingWord, resolveContextWindowPolicy, rewriteConfigEncrypted, rotateConfigKeys, toWrongStackError, topologicalSort, validateRegistryManifest };
|
|
6033
6266
|
//# sourceMappingURL=index.js.map
|
|
6034
6267
|
//# sourceMappingURL=index.js.map
|