@wrongstack/core 0.265.1 → 0.267.0
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-DrkBxszZ.d.ts → agent-bridge-STJ3JwwK.d.ts} +1 -1
- package/dist/{agent-subagent-runner-DM2pP-B6.d.ts → agent-subagent-runner-CzPGP3jA.d.ts} +25 -7
- package/dist/{brain-BXd_61kQ.d.ts → brain-Cdg77tVN.d.ts} +73 -1
- package/dist/{compactor-B8pOf45Y.d.ts → compactor-iMZ84CXq.d.ts} +19 -1
- package/dist/{config-BMCj_XDs.d.ts → config-Du3pYYln.d.ts} +54 -3
- package/dist/{context-MRk5PhNv.d.ts → context-dT5Ueund.d.ts} +65 -1
- package/dist/coordination/index.d.ts +17 -17
- package/dist/coordination/index.js +138 -114
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +25 -25
- package/dist/defaults/index.js +1729 -781
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +15 -15
- package/dist/execution/index.js +1119 -229
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/{goal-preamble-DvHDSKSe.d.ts → goal-preamble-SulMTowG.d.ts} +28 -11
- package/dist/{goal-store-DtLMySNb.d.ts → goal-store-CABDwdFE.d.ts} +1 -1
- package/dist/{index-CEDeNodM.d.ts → index-Bms0m4oy.d.ts} +5 -5
- package/dist/{index-B-ch8K9C.d.ts → index-DtCVWel4.d.ts} +8 -8
- package/dist/index-IEuxQd-E.d.ts +82 -0
- package/dist/index.d.ts +118 -45
- package/dist/index.js +3083 -1602
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +72 -1
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +9 -9
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mcp-servers-2x4w6Jn9.d.ts → mcp-servers-C2cBTxUR.d.ts} +3 -3
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +30 -1
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-DmJlKuNp.d.ts → models-registry-BqGZNJQ-.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-DyCkCZnU.d.ts → multi-agent-coordinator-B8R43uPz.d.ts} +1 -1
- package/dist/{null-fleet-bus-CG9QY2aP.d.ts → null-fleet-bus-CnXa5oTH.d.ts} +14 -9
- package/dist/observability/index.d.ts +2 -2
- package/dist/{parallel-eternal-engine-Jw9uhEoT.d.ts → parallel-eternal-engine-DdNnw9BQ.d.ts} +11 -9
- package/dist/{path-resolver-Dy2ej-gE.d.ts → path-resolver-COIMLCQL.d.ts} +3 -3
- package/dist/{permission-B9SB45lp.d.ts → permission-B75JAi3-.d.ts} +1 -1
- package/dist/{permission-policy-CkjSXabK.d.ts → permission-policy-DlR9eJAM.d.ts} +2 -2
- package/dist/{pipeline-DPDxH_7m.d.ts → pipeline-BfD2k1rT.d.ts} +2 -2
- package/dist/{plan-templates-CzD9GnAU.d.ts → plan-templates-DSIKCXZN.d.ts} +5 -5
- package/dist/{llm-selector-C0tfTCUe.d.ts → provider-model-resolve-BNRsNuJx.d.ts} +40 -3
- package/dist/{provider-runner-DMa70ODu.d.ts → provider-runner-CX7iIvox.d.ts} +3 -3
- package/dist/{retry-policy-CN0khdlj.d.ts → retry-policy-BilV1ujH.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -8
- package/dist/sdd/index.js +12 -12
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-B2yw84VT.d.ts → secret-vault-gkvEZZfE.d.ts} +2 -2
- package/dist/security/index.d.ts +5 -67
- package/dist/security/index.js +96 -76
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-CzHh_igB.d.ts → selector-Bc7eWtT3.d.ts} +1 -1
- package/dist/{session-event-bridge-BUI6Jf-4.d.ts → session-event-bridge-D-araDEz.d.ts} +1 -1
- package/dist/{session-reader-CMgdMSRP.d.ts → session-reader-D7Dapswh.d.ts} +1 -1
- package/dist/storage/index.d.ts +11 -11
- package/dist/storage/index.js +81 -84
- package/dist/storage/index.js.map +1 -1
- package/dist/tools/index.d.ts +4 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/types/index.d.ts +19 -19
- package/dist/types/index.js +1265 -400
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +454 -406
- package/dist/utils/index.js +2191 -1201
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
package/dist/utils/index.js
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import { randomBytes, createHash } from 'crypto';
|
|
2
2
|
import * as fs from 'fs/promises';
|
|
3
|
-
import * as
|
|
3
|
+
import * as path3 from 'path';
|
|
4
4
|
import { isAbsolute, resolve } from 'path';
|
|
5
|
-
import * as os from 'os';
|
|
6
5
|
import * as dns from 'dns/promises';
|
|
7
6
|
import * as net from 'net';
|
|
7
|
+
import * as os from 'os';
|
|
8
8
|
|
|
9
|
-
// src/utils/
|
|
9
|
+
// src/utils/assert-never.ts
|
|
10
|
+
function assertNever(x, message) {
|
|
11
|
+
const err = new Error(
|
|
12
|
+
message ?? `Unhandled case: ${JSON.stringify(x)}`
|
|
13
|
+
);
|
|
14
|
+
err.name = "AssertNeverError";
|
|
15
|
+
throw err;
|
|
16
|
+
}
|
|
10
17
|
async function atomicWrite(targetPath, content, opts = {}) {
|
|
11
|
-
const dir =
|
|
18
|
+
const dir = path3.dirname(targetPath);
|
|
12
19
|
await fs.mkdir(dir, { recursive: true });
|
|
13
|
-
const tmp =
|
|
20
|
+
const tmp = path3.join(dir, `.${path3.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
14
21
|
try {
|
|
15
22
|
if (typeof content === "string") {
|
|
16
23
|
await fs.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
@@ -49,9 +56,9 @@ async function ensureDir(dir) {
|
|
|
49
56
|
await fs.mkdir(dir, { recursive: true });
|
|
50
57
|
}
|
|
51
58
|
async function withFileLock(targetPath, fn, opts = {}) {
|
|
52
|
-
const dir =
|
|
59
|
+
const dir = path3.dirname(targetPath);
|
|
53
60
|
await fs.mkdir(dir, { recursive: true });
|
|
54
|
-
const lockPath =
|
|
61
|
+
const lockPath = path3.join(dir, `.${path3.basename(targetPath)}.lock`);
|
|
55
62
|
const timeoutMs = opts.timeoutMs ?? 5e3;
|
|
56
63
|
const staleMs = opts.staleMs ?? 3e4;
|
|
57
64
|
const started = Date.now();
|
|
@@ -80,7 +87,7 @@ async function withFileLock(targetPath, fn, opts = {}) {
|
|
|
80
87
|
if (Date.now() - started >= timeoutMs) {
|
|
81
88
|
throw new Error(`Timed out waiting for file lock: ${targetPath}`);
|
|
82
89
|
}
|
|
83
|
-
await new Promise((
|
|
90
|
+
await new Promise((resolve4) => setTimeout(resolve4, 25));
|
|
84
91
|
}
|
|
85
92
|
}
|
|
86
93
|
try {
|
|
@@ -114,150 +121,105 @@ async function renameWithRetry(from, to) {
|
|
|
114
121
|
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
115
122
|
throw err;
|
|
116
123
|
}
|
|
117
|
-
await new Promise((
|
|
124
|
+
await new Promise((resolve4) => setTimeout(resolve4, delays[i]));
|
|
118
125
|
}
|
|
119
126
|
}
|
|
120
127
|
throw lastErr;
|
|
121
128
|
}
|
|
122
129
|
|
|
123
|
-
// src/utils/
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
130
|
+
// src/utils/child-env.ts
|
|
131
|
+
var ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
132
|
+
"PATH",
|
|
133
|
+
"HOME",
|
|
134
|
+
"USER",
|
|
135
|
+
"USERNAME",
|
|
136
|
+
"LOGNAME",
|
|
137
|
+
"SHELL",
|
|
138
|
+
"LANG",
|
|
139
|
+
"LC_ALL",
|
|
140
|
+
"LC_CTYPE",
|
|
141
|
+
"TERM",
|
|
142
|
+
"TZ",
|
|
143
|
+
"TMPDIR",
|
|
144
|
+
"TEMP",
|
|
145
|
+
"TMP",
|
|
146
|
+
"PWD",
|
|
147
|
+
"OLDPWD",
|
|
148
|
+
"COMSPEC",
|
|
149
|
+
"SYSTEMROOT",
|
|
150
|
+
"SYSTEMDRIVE",
|
|
151
|
+
"WINDIR",
|
|
152
|
+
"PROGRAMFILES",
|
|
153
|
+
"PROGRAMFILES(X86)",
|
|
154
|
+
"PROGRAMDATA",
|
|
155
|
+
"APPDATA",
|
|
156
|
+
"LOCALAPPDATA",
|
|
157
|
+
"USERPROFILE",
|
|
158
|
+
"PUBLIC",
|
|
159
|
+
"PATHEXT"
|
|
160
|
+
]);
|
|
161
|
+
var SECRET_NAME_PARTS = [
|
|
162
|
+
"TOKEN",
|
|
163
|
+
"SECRET",
|
|
164
|
+
"PASSWORD",
|
|
165
|
+
"PASSWD",
|
|
166
|
+
"AUTH",
|
|
167
|
+
"CRED",
|
|
168
|
+
"BEARER",
|
|
169
|
+
"COOKIE",
|
|
170
|
+
"PRIVATE"
|
|
171
|
+
];
|
|
172
|
+
function looksSecret(name) {
|
|
173
|
+
const upper = name.toUpperCase();
|
|
174
|
+
for (const p of SECRET_NAME_PARTS) {
|
|
175
|
+
if (upper.includes(p)) return true;
|
|
140
176
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if (v instanceof Error) {
|
|
147
|
-
return { name: v.name, message: v.message, stack: v.stack };
|
|
148
|
-
}
|
|
149
|
-
if (typeof v === "object" && v !== null) {
|
|
150
|
-
if (seen.has(v)) return "[Circular]";
|
|
151
|
-
seen.add(v);
|
|
152
|
-
}
|
|
153
|
-
return v;
|
|
154
|
-
};
|
|
155
|
-
try {
|
|
156
|
-
return JSON.stringify(value, replacer, pretty ? 2 : void 0) ?? "null";
|
|
157
|
-
} catch (err) {
|
|
158
|
-
return JSON.stringify({
|
|
159
|
-
__serialization_error: toErrorMessage(err)
|
|
160
|
-
});
|
|
177
|
+
if (/(?:^|_)KEY(?:$|_|S$)/i.test(upper)) return true;
|
|
178
|
+
if (/API[_-]?KEY/i.test(upper)) return true;
|
|
179
|
+
if (/ACCESS[_-]?KEY/i.test(upper)) return true;
|
|
180
|
+
if (/SESSION[_-]?ID/i.test(upper) === false && /SESSION/i.test(upper)) {
|
|
181
|
+
return true;
|
|
161
182
|
}
|
|
183
|
+
return false;
|
|
162
184
|
}
|
|
163
|
-
function
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return null;
|
|
185
|
+
function buildChildEnv(optsOrSessionId) {
|
|
186
|
+
const opts = typeof optsOrSessionId === "string" ? { sessionId: optsOrSessionId } : optsOrSessionId ?? {};
|
|
187
|
+
const hasOwn = Object.hasOwn(process.env, "WRONGSTACK_CHILD_ENV_PASSTHROUGH");
|
|
188
|
+
const legacyHasOwn = Object.hasOwn(process.env, "WRONGSTACK_BASH_ENV_PASSTHROUGH");
|
|
189
|
+
const passthrough = hasOwn && process.env["WRONGSTACK_CHILD_ENV_PASSTHROUGH"] === "1" || legacyHasOwn && process.env["WRONGSTACK_BASH_ENV_PASSTHROUGH"] === "1";
|
|
190
|
+
if (passthrough && !process.env["CI"]) {
|
|
191
|
+
console.warn(
|
|
192
|
+
"[agent] WARNING: WRONGSTACK_*_ENV_PASSTHROUGH=1 is active \u2014\n all parent env vars (including API keys) forwarded to child processes.\n Do not use on shared or multi-tenant systems."
|
|
193
|
+
);
|
|
173
194
|
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
inString = !inString;
|
|
182
|
-
out += c;
|
|
195
|
+
const out = {};
|
|
196
|
+
const nodeEnvDefaulted = process.env["WRONGSTACK_NODE_ENV_DEFAULTED"] === "1";
|
|
197
|
+
for (const [k, v] of Object.entries(process.env)) {
|
|
198
|
+
if (v === void 0) continue;
|
|
199
|
+
if (nodeEnvDefaulted && (k === "NODE_ENV" || k === "WRONGSTACK_NODE_ENV_DEFAULTED")) continue;
|
|
200
|
+
if (passthrough) {
|
|
201
|
+
out[k] = v;
|
|
183
202
|
continue;
|
|
184
203
|
}
|
|
185
|
-
const
|
|
186
|
-
if (
|
|
187
|
-
|
|
188
|
-
case "\n":
|
|
189
|
-
out += "\\n";
|
|
190
|
-
break;
|
|
191
|
-
case "\r":
|
|
192
|
-
out += "\\r";
|
|
193
|
-
break;
|
|
194
|
-
case " ":
|
|
195
|
-
out += "\\t";
|
|
196
|
-
break;
|
|
197
|
-
case "\b":
|
|
198
|
-
out += "\\b";
|
|
199
|
-
break;
|
|
200
|
-
case "\f":
|
|
201
|
-
out += "\\f";
|
|
202
|
-
break;
|
|
203
|
-
default:
|
|
204
|
-
out += `\\u${code.toString(16).padStart(4, "0")}`;
|
|
205
|
-
}
|
|
204
|
+
const upper = k.toUpperCase();
|
|
205
|
+
if (ALLOWED_KEYS.has(upper)) {
|
|
206
|
+
out[k] = v;
|
|
206
207
|
continue;
|
|
207
208
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
let i = 0;
|
|
216
|
-
while (i < s.length) {
|
|
217
|
-
const c = s.charAt(i);
|
|
218
|
-
if (c === '"' && (i === 0 || s.charAt(i - 1) !== "\\")) {
|
|
219
|
-
inString = !inString;
|
|
220
|
-
chars.push(c);
|
|
221
|
-
} else if (c === "/" && s.charAt(i + 1) === "/" && !inString) {
|
|
222
|
-
while (i < s.length && s.charAt(i) !== "\n") i++;
|
|
223
|
-
} else {
|
|
224
|
-
chars.push(c);
|
|
209
|
+
if (looksSecret(upper)) continue;
|
|
210
|
+
if (upper.startsWith("NODE_") || upper.startsWith("NPM_") || upper.startsWith("PNPM_") || upper.startsWith("YARN_") || upper.startsWith("GIT_") || upper.startsWith("CI") || upper.startsWith("XDG_") || // Our own non-secret knobs (WRONGSTACK_HOME, WRONGSTACK_SESSION_ID, …).
|
|
211
|
+
// Secrets never live in WRONGSTACK_* env vars (they're in the encrypted
|
|
212
|
+
// vault). Forwarding keeps child wstack processes — e.g. ones spawned
|
|
213
|
+
// by the test suite — inside the same redirected global root.
|
|
214
|
+
upper.startsWith("WRONGSTACK_") || upper === "EDITOR" || upper === "VISUAL" || upper === "PAGER") {
|
|
215
|
+
out[k] = v;
|
|
225
216
|
}
|
|
226
|
-
i++;
|
|
227
217
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
// src/utils/newline-normalize.ts
|
|
232
|
-
function detectNewlineStyle(text) {
|
|
233
|
-
let lf = 0;
|
|
234
|
-
let crlf = 0;
|
|
235
|
-
let cr = 0;
|
|
236
|
-
for (let i = 0; i < text.length; i++) {
|
|
237
|
-
const c = text.charCodeAt(i);
|
|
238
|
-
if (c === 13) {
|
|
239
|
-
if (text.charCodeAt(i + 1) === 10) {
|
|
240
|
-
crlf++;
|
|
241
|
-
i++;
|
|
242
|
-
} else {
|
|
243
|
-
cr++;
|
|
244
|
-
}
|
|
245
|
-
} else if (c === 10) {
|
|
246
|
-
lf++;
|
|
247
|
-
}
|
|
218
|
+
if (opts.extra) {
|
|
219
|
+
Object.assign(out, opts.extra);
|
|
248
220
|
}
|
|
249
|
-
if (
|
|
250
|
-
|
|
251
|
-
return "lf";
|
|
252
|
-
}
|
|
253
|
-
function toStyle(text, style) {
|
|
254
|
-
const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
255
|
-
if (style === "lf") return normalized;
|
|
256
|
-
if (style === "crlf") return normalized.replace(/\n/g, "\r\n");
|
|
257
|
-
return normalized.replace(/\n/g, "\r");
|
|
258
|
-
}
|
|
259
|
-
function normalizeToLf(text) {
|
|
260
|
-
return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
221
|
+
if (opts.sessionId) out["WRONGSTACK_SESSION_ID"] = opts.sessionId;
|
|
222
|
+
return out;
|
|
261
223
|
}
|
|
262
224
|
|
|
263
225
|
// src/utils/term.ts
|
|
@@ -360,220 +322,388 @@ function stripAnsi(s) {
|
|
|
360
322
|
""
|
|
361
323
|
);
|
|
362
324
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
325
|
+
var MAX_TOOL_CALLS = 80;
|
|
326
|
+
var MAX_FACTS = 40;
|
|
327
|
+
var MAX_ERRORS = 20;
|
|
328
|
+
var MAX_DIGEST_CHARS = 4e3;
|
|
329
|
+
var WRITE_TOOLS = /* @__PURE__ */ new Set(["edit", "write", "replace", "patch"]);
|
|
330
|
+
var READ_TOOLS = /* @__PURE__ */ new Set(["read", "grep", "glob", "ls", "tree"]);
|
|
331
|
+
function createContextEvidenceState() {
|
|
332
|
+
return {
|
|
333
|
+
sessionGoals: [],
|
|
334
|
+
implicitFacts: [],
|
|
335
|
+
activeErrors: [],
|
|
336
|
+
toolCalls: [],
|
|
337
|
+
fileGraph: {},
|
|
338
|
+
repeatedReads: [],
|
|
339
|
+
updatedAt: Date.now()
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function recordUserIntentEvidence(ctx, text) {
|
|
343
|
+
const intent = normalizeWhitespace(text).slice(0, 700);
|
|
344
|
+
if (!intent) return;
|
|
345
|
+
const state = ensureEvidence(ctx);
|
|
346
|
+
state.currentIntent = { text: intent, updatedAt: Date.now() };
|
|
347
|
+
if (state.sessionGoals.length === 0 || isGoalish(intent)) {
|
|
348
|
+
pushUniqueBounded(state.sessionGoals, intent, 8);
|
|
349
|
+
}
|
|
350
|
+
state.updatedAt = Date.now();
|
|
351
|
+
}
|
|
352
|
+
function recordToolOutputEvidence(ctx, input) {
|
|
353
|
+
const state = ensureEvidence(ctx);
|
|
354
|
+
const files = extractFiles(ctx, input.toolName, input.input, input.content);
|
|
355
|
+
const symbols = extractSymbols(input.content, input.input);
|
|
356
|
+
const commands = extractCommands(input.toolName, input.input);
|
|
357
|
+
const errors = extractErrors(input.content);
|
|
358
|
+
const summary = summarizeToolOutput(input.toolName, input.input, input.content, {
|
|
359
|
+
files,
|
|
360
|
+
errors,
|
|
361
|
+
ok: input.ok
|
|
375
362
|
});
|
|
376
|
-
|
|
363
|
+
const metadata = {
|
|
364
|
+
toolUseId: input.toolUseId,
|
|
365
|
+
toolName: input.toolName,
|
|
366
|
+
ok: input.ok,
|
|
367
|
+
inputSummary: summarizeInput(input.input),
|
|
368
|
+
summary,
|
|
369
|
+
files,
|
|
370
|
+
symbols,
|
|
371
|
+
commands,
|
|
372
|
+
errors,
|
|
373
|
+
status: "seen",
|
|
374
|
+
referenceCount: 0,
|
|
375
|
+
seenAt: Date.now(),
|
|
376
|
+
outputBytes: input.outputBytes,
|
|
377
|
+
outputTokens: input.outputTokens,
|
|
378
|
+
outputLines: input.outputLines
|
|
379
|
+
};
|
|
380
|
+
state.toolCalls.push(metadata);
|
|
381
|
+
if (state.toolCalls.length > MAX_TOOL_CALLS) {
|
|
382
|
+
state.toolCalls.splice(0, state.toolCalls.length - MAX_TOOL_CALLS);
|
|
383
|
+
}
|
|
384
|
+
updateFileGraph(state, metadata);
|
|
385
|
+
updateRepeatedReadSignals(state, metadata);
|
|
386
|
+
if (errors.length > 0) {
|
|
387
|
+
for (const err of errors) pushUniqueBounded(state.activeErrors, err, MAX_ERRORS);
|
|
388
|
+
}
|
|
389
|
+
const fact = implicitFactFor(metadata);
|
|
390
|
+
if (fact) pushUniqueBounded(state.implicitFacts, fact, MAX_FACTS);
|
|
391
|
+
state.updatedAt = Date.now();
|
|
392
|
+
return metadata;
|
|
393
|
+
}
|
|
394
|
+
function markAssistantReferencedEvidence(ctx, text) {
|
|
395
|
+
const state = ensureEvidence(ctx);
|
|
396
|
+
const haystack = text.toLowerCase();
|
|
397
|
+
if (!haystack.trim()) return;
|
|
398
|
+
for (const tool of state.toolCalls) {
|
|
399
|
+
if (!metadataReferencedByText(tool, haystack)) continue;
|
|
400
|
+
tool.status = "referenced";
|
|
401
|
+
tool.referenceCount++;
|
|
402
|
+
tool.referencedAt = Date.now();
|
|
403
|
+
for (const file of tool.files) {
|
|
404
|
+
const node = state.fileGraph[file];
|
|
405
|
+
if (node) node.referenced = true;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
state.updatedAt = Date.now();
|
|
377
409
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
break;
|
|
406
|
-
case "review":
|
|
407
|
-
review++;
|
|
408
|
-
break;
|
|
410
|
+
function buildContextEvidenceDigest(ctx) {
|
|
411
|
+
const state = ensureEvidence(ctx);
|
|
412
|
+
const lines = [];
|
|
413
|
+
if (state.currentIntent?.text) {
|
|
414
|
+
lines.push(`intent: ${state.currentIntent.text}`);
|
|
415
|
+
}
|
|
416
|
+
const goals = state.sessionGoals.slice(-3);
|
|
417
|
+
if (goals.length > 0) {
|
|
418
|
+
lines.push("session_goals:");
|
|
419
|
+
for (const goal of goals) lines.push(`- ${goal}`);
|
|
420
|
+
}
|
|
421
|
+
const activeErrors = state.activeErrors.slice(-5);
|
|
422
|
+
if (activeErrors.length > 0) {
|
|
423
|
+
lines.push("active_errors:");
|
|
424
|
+
for (const err of activeErrors) lines.push(`- ${err}`);
|
|
425
|
+
}
|
|
426
|
+
const files = Object.values(state.fileGraph).sort((a, b) => b.writes - a.writes || b.reads - a.reads || a.path.localeCompare(b.path)).slice(0, 12);
|
|
427
|
+
if (files.length > 0) {
|
|
428
|
+
lines.push("dependency_graph:");
|
|
429
|
+
for (const file of files) {
|
|
430
|
+
const actions = [
|
|
431
|
+
file.reads > 0 ? `read ${file.reads}x` : "",
|
|
432
|
+
file.writes > 0 ? `write ${file.writes}x` : ""
|
|
433
|
+
].filter(Boolean).join(", ");
|
|
434
|
+
const refs = file.referenced ? "; referenced by assistant" : "";
|
|
435
|
+
const via = file.lastToolUseId ? `; last via ${file.lastToolUseId}` : "";
|
|
436
|
+
lines.push(`- ${file.path} (${actions || "seen"}${refs}${via})`);
|
|
409
437
|
}
|
|
410
|
-
estimatedHours += t.estimateHours ?? 0;
|
|
411
438
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
439
|
+
const referenced = state.toolCalls.filter((tool) => tool.status === "referenced").slice(-10);
|
|
440
|
+
const recentSeen = state.toolCalls.filter((tool) => tool.status === "seen").slice(-5);
|
|
441
|
+
const trail = [...referenced, ...recentSeen];
|
|
442
|
+
if (trail.length > 0) {
|
|
443
|
+
lines.push("tool_trail:");
|
|
444
|
+
for (const tool of trail) {
|
|
445
|
+
const size = tool.outputTokens ? `; ~${tool.outputTokens} tokens` : "";
|
|
446
|
+
const filesText = tool.files.length > 0 ? `; files=${tool.files.slice(0, 4).join(", ")}` : "";
|
|
447
|
+
const symbolsText = tool.symbols.length > 0 ? `; symbols=${tool.symbols.slice(0, 4).join(", ")}` : "";
|
|
448
|
+
lines.push(
|
|
449
|
+
`- ${tool.toolUseId} ${tool.toolName} ${tool.status}: ${tool.summary}${filesText}${symbolsText}${size}`
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
const facts = state.implicitFacts.slice(-8);
|
|
454
|
+
if (facts.length > 0) {
|
|
455
|
+
lines.push("implicit_facts:");
|
|
456
|
+
for (const fact of facts) lines.push(`- ${fact}`);
|
|
457
|
+
}
|
|
458
|
+
const digest = lines.join("\n");
|
|
459
|
+
if (digest.length <= MAX_DIGEST_CHARS) return digest;
|
|
460
|
+
return `${digest.slice(0, MAX_DIGEST_CHARS)}... [+${digest.length - MAX_DIGEST_CHARS} chars]`;
|
|
461
|
+
}
|
|
462
|
+
function repeatedReadPressure(ctx) {
|
|
463
|
+
return ensureEvidence(ctx).repeatedReads.reduce((max, item) => Math.max(max, item.count), 0);
|
|
464
|
+
}
|
|
465
|
+
function ensureEvidence(ctx) {
|
|
466
|
+
if (!ctx.contextEvidence) {
|
|
467
|
+
ctx.contextEvidence = createContextEvidenceState();
|
|
468
|
+
}
|
|
469
|
+
return ctx.contextEvidence;
|
|
470
|
+
}
|
|
471
|
+
function isGoalish(text) {
|
|
472
|
+
return /\b(goal|objective|task|need|want|implement|fix|improve|refactor|add|remove|hedef|amac|istiyorum|gerekiyor|iyilestir|duzelt|ekle|kaldir)\b/i.test(text);
|
|
473
|
+
}
|
|
474
|
+
function normalizeWhitespace(text) {
|
|
475
|
+
return text.replace(/\s+/g, " ").trim();
|
|
476
|
+
}
|
|
477
|
+
function pushUniqueBounded(list, value, max) {
|
|
478
|
+
const normalized = normalizeWhitespace(value);
|
|
479
|
+
if (!normalized) return;
|
|
480
|
+
const existing = list.findIndex((item) => item.toLowerCase() === normalized.toLowerCase());
|
|
481
|
+
if (existing >= 0) list.splice(existing, 1);
|
|
482
|
+
list.push(normalized);
|
|
483
|
+
if (list.length > max) list.splice(0, list.length - max);
|
|
484
|
+
}
|
|
485
|
+
function extractFiles(ctx, toolName, input, content) {
|
|
486
|
+
const out = /* @__PURE__ */ new Set();
|
|
487
|
+
for (const value of inputPathValues(input)) addPath(ctx, out, value);
|
|
488
|
+
if (toolName === "grep" || toolName === "glob" || toolName === "bash") {
|
|
489
|
+
const re = /(?:(?:[A-Za-z]:)?[./\\]?[\w@.-]+(?:[\\/][\w@(). -]+)+\.[A-Za-z0-9]{1,12})/g;
|
|
490
|
+
for (const match of content.matchAll(re)) addPath(ctx, out, match[0]);
|
|
491
|
+
}
|
|
492
|
+
return [...out].slice(0, 30);
|
|
493
|
+
}
|
|
494
|
+
function inputPathValues(input) {
|
|
495
|
+
const values = [];
|
|
496
|
+
const visit = (value, key) => {
|
|
497
|
+
if (typeof value === "string") {
|
|
498
|
+
if (key && /^(path|file|files|fromFile|toFile|dir|cwd)$/i.test(key)) values.push(value);
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
if (Array.isArray(value)) {
|
|
502
|
+
for (const item of value) visit(item, key);
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
if (!value || typeof value !== "object") return;
|
|
506
|
+
for (const [k, v] of Object.entries(value)) visit(v, k);
|
|
423
507
|
};
|
|
508
|
+
visit(input);
|
|
509
|
+
return values;
|
|
424
510
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
].filter(Boolean).join("\n");
|
|
511
|
+
function addPath(ctx, out, raw) {
|
|
512
|
+
const clean = raw.trim().replace(/^["'`]+|["'`),;:]+$/g, "");
|
|
513
|
+
if (!clean || clean.length > 260) return;
|
|
514
|
+
let normalized = clean.replace(/\\/g, "/");
|
|
515
|
+
try {
|
|
516
|
+
const abs = path3.isAbsolute(clean) ? path3.resolve(clean) : null;
|
|
517
|
+
if (abs) {
|
|
518
|
+
const rel = path3.relative(ctx.projectRoot, abs);
|
|
519
|
+
if (!rel.startsWith("..") && !path3.isAbsolute(rel)) {
|
|
520
|
+
normalized = rel.replace(/\\/g, "/");
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
} catch {
|
|
524
|
+
}
|
|
525
|
+
if (normalized.length > 0) out.add(normalized);
|
|
526
|
+
}
|
|
527
|
+
function extractSymbols(content, input) {
|
|
528
|
+
const out = /* @__PURE__ */ new Set();
|
|
529
|
+
const patterns = [
|
|
530
|
+
/\b(?:function|class|interface|type|enum|const|let|var|def|fn|struct)\s+([A-Za-z_$][\w$]*)/g,
|
|
531
|
+
/\b(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)/g
|
|
532
|
+
];
|
|
533
|
+
for (const re of patterns) {
|
|
534
|
+
for (const match of content.matchAll(re)) {
|
|
535
|
+
if (match[1]) out.add(match[1]);
|
|
536
|
+
if (out.size >= 30) break;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
const pattern = input && typeof input === "object" ? input["pattern"] : void 0;
|
|
540
|
+
if (typeof pattern === "string" && /^[A-Za-z_$][\w$]*$/.test(pattern)) {
|
|
541
|
+
out.add(pattern);
|
|
542
|
+
}
|
|
543
|
+
return [...out].slice(0, 30);
|
|
459
544
|
}
|
|
460
|
-
function
|
|
461
|
-
if (
|
|
462
|
-
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
545
|
+
function extractCommands(toolName, input) {
|
|
546
|
+
if (toolName !== "bash" && toolName !== "exec" && toolName !== "shell") return [];
|
|
547
|
+
if (!input || typeof input !== "object") return [];
|
|
548
|
+
const command = input["command"];
|
|
549
|
+
if (typeof command !== "string") return [];
|
|
550
|
+
return [command.slice(0, 220)];
|
|
551
|
+
}
|
|
552
|
+
function extractErrors(content) {
|
|
553
|
+
const lines = content.split(/\r?\n/);
|
|
554
|
+
const errors = [];
|
|
555
|
+
for (const line of lines) {
|
|
556
|
+
if (!/\b(error|exception|failed|failure|fatal|panic|timeout|denied|enoent|eacces|eperm|typeerror|syntaxerror)\b/i.test(line)) continue;
|
|
557
|
+
errors.push(normalizeWhitespace(line).slice(0, 260));
|
|
558
|
+
if (errors.length >= 5) break;
|
|
559
|
+
}
|
|
560
|
+
return errors;
|
|
561
|
+
}
|
|
562
|
+
function summarizeInput(input) {
|
|
563
|
+
if (!input || typeof input !== "object") return void 0;
|
|
564
|
+
const obj = input;
|
|
565
|
+
const parts = [];
|
|
566
|
+
for (const key of ["path", "file", "pattern", "glob", "command"]) {
|
|
567
|
+
const value = obj[key];
|
|
568
|
+
if (typeof value === "string") parts.push(`${key}=${value.slice(0, 160)}`);
|
|
569
|
+
}
|
|
570
|
+
return parts.length > 0 ? parts.join(", ") : void 0;
|
|
571
|
+
}
|
|
572
|
+
function summarizeToolOutput(toolName, input, content, opts) {
|
|
573
|
+
if (!opts.ok && opts.errors.length > 0) return opts.errors[0] ?? `${toolName} failed`;
|
|
574
|
+
if (toolName === "read" && opts.files[0]) return `read ${opts.files[0]}`;
|
|
575
|
+
if (toolName === "grep") {
|
|
576
|
+
const pattern = input && typeof input === "object" ? input["pattern"] : void 0;
|
|
577
|
+
return `searched ${typeof pattern === "string" ? pattern : "pattern"} (${opts.files.length} file hint(s))`;
|
|
578
|
+
}
|
|
579
|
+
if ((toolName === "edit" || toolName === "write") && opts.files[0]) {
|
|
580
|
+
return `${toolName === "write" ? "wrote" : "edited"} ${opts.files[0]}`;
|
|
581
|
+
}
|
|
582
|
+
const firstLine = normalizeWhitespace(content.split(/\r?\n/).find((line) => line.trim()) ?? "");
|
|
583
|
+
return firstLine ? firstLine.slice(0, 220) : `${toolName} returned no text`;
|
|
584
|
+
}
|
|
585
|
+
function updateFileGraph(state, metadata) {
|
|
586
|
+
const writes = WRITE_TOOLS.has(metadata.toolName) ? 1 : 0;
|
|
587
|
+
const reads = writes === 0 && (READ_TOOLS.has(metadata.toolName) || metadata.files.length > 0) ? 1 : 0;
|
|
588
|
+
for (const file of metadata.files) {
|
|
589
|
+
const existing = state.fileGraph[file] ?? {
|
|
590
|
+
path: file,
|
|
591
|
+
reads: 0,
|
|
592
|
+
writes: 0,
|
|
593
|
+
tools: [],
|
|
594
|
+
referenced: false
|
|
595
|
+
};
|
|
596
|
+
existing.reads += reads;
|
|
597
|
+
existing.writes += writes;
|
|
598
|
+
existing.lastToolUseId = metadata.toolUseId;
|
|
599
|
+
pushUniqueBounded(existing.tools, `${metadata.toolName}#${metadata.toolUseId}`, 8);
|
|
600
|
+
state.fileGraph[file] = existing;
|
|
468
601
|
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
602
|
+
}
|
|
603
|
+
function updateRepeatedReadSignals(state, metadata) {
|
|
604
|
+
if (metadata.toolName !== "read" || metadata.files.length === 0) {
|
|
605
|
+
state.lastReadPath = void 0;
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
const file = metadata.files[0];
|
|
609
|
+
if (state.lastReadPath === file) {
|
|
610
|
+
const existing = state.repeatedReads.find((item) => item.file === file);
|
|
611
|
+
if (existing) {
|
|
612
|
+
existing.count++;
|
|
613
|
+
existing.lastToolUseId = metadata.toolUseId;
|
|
614
|
+
} else {
|
|
615
|
+
state.repeatedReads.push({ file, count: 2, lastToolUseId: metadata.toolUseId });
|
|
483
616
|
}
|
|
617
|
+
if (state.repeatedReads.length > 10) state.repeatedReads.shift();
|
|
484
618
|
}
|
|
485
|
-
|
|
619
|
+
state.lastReadPath = file;
|
|
486
620
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
621
|
+
function implicitFactFor(metadata) {
|
|
622
|
+
if (metadata.errors.length > 0) return `${metadata.toolName}#${metadata.toolUseId} exposed error: ${metadata.errors[0]}`;
|
|
623
|
+
if (metadata.toolName === "read" && metadata.files[0]) {
|
|
624
|
+
const size = metadata.outputLines ? ` (${metadata.outputLines} line(s) returned)` : "";
|
|
625
|
+
return `read ${metadata.files[0]}${size}`;
|
|
626
|
+
}
|
|
627
|
+
if ((metadata.toolName === "edit" || metadata.toolName === "write") && metadata.files[0]) {
|
|
628
|
+
return `${metadata.toolName} changed ${metadata.files[0]}`;
|
|
629
|
+
}
|
|
630
|
+
if (metadata.status === "referenced") return `${metadata.toolName}#${metadata.toolUseId} was referenced`;
|
|
631
|
+
return void 0;
|
|
491
632
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
throw err;
|
|
633
|
+
function metadataReferencedByText(metadata, haystack) {
|
|
634
|
+
for (const file of metadata.files) {
|
|
635
|
+
const f = file.toLowerCase();
|
|
636
|
+
const base = path3.basename(file).toLowerCase();
|
|
637
|
+
if (f && haystack.includes(f)) return true;
|
|
638
|
+
if (base && haystack.includes(base)) return true;
|
|
499
639
|
}
|
|
500
|
-
|
|
640
|
+
for (const symbol of metadata.symbols) {
|
|
641
|
+
if (symbol.length >= 3 && haystack.includes(symbol.toLowerCase())) return true;
|
|
642
|
+
}
|
|
643
|
+
for (const err of metadata.errors) {
|
|
644
|
+
const head = err.slice(0, 80).toLowerCase();
|
|
645
|
+
if (head.length >= 12 && haystack.includes(head)) return true;
|
|
646
|
+
}
|
|
647
|
+
return false;
|
|
501
648
|
}
|
|
502
649
|
|
|
503
|
-
// src/utils/
|
|
504
|
-
|
|
505
|
-
|
|
650
|
+
// src/utils/deep-merge.ts
|
|
651
|
+
var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
|
|
652
|
+
"__proto__",
|
|
653
|
+
"constructor",
|
|
654
|
+
"prototype",
|
|
655
|
+
"__defineGetter__",
|
|
656
|
+
"__defineSetter__",
|
|
657
|
+
"__lookupGetter__",
|
|
658
|
+
"__lookupSetter__"
|
|
659
|
+
]);
|
|
660
|
+
function isPrimitiveArray(a) {
|
|
661
|
+
return a.every((v) => v === null || typeof v !== "object" && typeof v !== "function");
|
|
506
662
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
663
|
+
function deepMerge(base, patch, options = {}) {
|
|
664
|
+
const {
|
|
665
|
+
conflictResolution = "prefer-patch",
|
|
666
|
+
arrayMode = "replace",
|
|
667
|
+
protectProto = true,
|
|
668
|
+
onNonPrimitiveArrayReplace
|
|
669
|
+
} = options;
|
|
670
|
+
if (typeof base !== "object" || base === null) {
|
|
671
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
672
|
+
}
|
|
673
|
+
if (typeof patch !== "object" || patch === null) {
|
|
674
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
675
|
+
}
|
|
676
|
+
if (Array.isArray(base) && Array.isArray(patch)) {
|
|
677
|
+
if (arrayMode === "concat-primitives" && isPrimitiveArray(base) && isPrimitiveArray(patch)) {
|
|
678
|
+
return [.../* @__PURE__ */ new Set([...base, ...patch])];
|
|
516
679
|
}
|
|
680
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
517
681
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
return re;
|
|
521
|
-
}
|
|
522
|
-
var MAX_GLOB_PATTERN_LEN = 1024;
|
|
523
|
-
function compileGlob(pattern) {
|
|
524
|
-
if (pattern.length > MAX_GLOB_PATTERN_LEN) {
|
|
525
|
-
throw new Error(`Glob pattern exceeds ${MAX_GLOB_PATTERN_LEN} characters`);
|
|
682
|
+
if (Array.isArray(base) || Array.isArray(patch)) {
|
|
683
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
526
684
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
if (
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
i++;
|
|
539
|
-
}
|
|
540
|
-
} else if (c === "?") {
|
|
541
|
-
re += "[^/]";
|
|
542
|
-
i++;
|
|
543
|
-
} else if (c === "[") {
|
|
544
|
-
let cls = "[";
|
|
545
|
-
i++;
|
|
546
|
-
if (pattern[i] === "!" || pattern[i] === "^") {
|
|
547
|
-
cls += "^";
|
|
548
|
-
i++;
|
|
685
|
+
const baseObj = base;
|
|
686
|
+
const patchObj = patch;
|
|
687
|
+
const out = { ...baseObj };
|
|
688
|
+
for (const [k, v] of Object.entries(patchObj)) {
|
|
689
|
+
if (protectProto && FORBIDDEN_PROTO_KEYS.has(k)) continue;
|
|
690
|
+
const existing = out[k];
|
|
691
|
+
if (v !== null && typeof v === "object" && !Array.isArray(v) && existing !== null && typeof existing === "object" && !Array.isArray(existing)) {
|
|
692
|
+
out[k] = deepMerge(existing, v, options);
|
|
693
|
+
} else if (Array.isArray(v) && Array.isArray(existing)) {
|
|
694
|
+
if (onNonPrimitiveArrayReplace && !isPrimitiveArray(v)) {
|
|
695
|
+
onNonPrimitiveArrayReplace(k, existing.length, v.length);
|
|
549
696
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
cls += `\\${ch}`;
|
|
556
|
-
} else {
|
|
557
|
-
cls += ch;
|
|
558
|
-
}
|
|
559
|
-
i++;
|
|
697
|
+
out[k] = deepMerge(existing, v, options);
|
|
698
|
+
} else if (v !== void 0) {
|
|
699
|
+
if (onNonPrimitiveArrayReplace && Array.isArray(v) && !isPrimitiveArray(v)) {
|
|
700
|
+
const existingLen = Array.isArray(existing) ? existing.length : 0;
|
|
701
|
+
onNonPrimitiveArrayReplace(k, existingLen, v.length);
|
|
560
702
|
}
|
|
561
|
-
|
|
562
|
-
re += cls;
|
|
563
|
-
i++;
|
|
564
|
-
} else {
|
|
565
|
-
re += escapeRegex(c ?? "");
|
|
566
|
-
i++;
|
|
703
|
+
out[k] = v;
|
|
567
704
|
}
|
|
568
705
|
}
|
|
569
|
-
|
|
570
|
-
return new RegExp(re);
|
|
571
|
-
}
|
|
572
|
-
function matchGlob(pattern, input) {
|
|
573
|
-
return getCachedGlob(pattern).test(input);
|
|
574
|
-
}
|
|
575
|
-
function matchAny(patterns, input) {
|
|
576
|
-
return patterns.some((p) => matchGlob(p, input));
|
|
706
|
+
return out;
|
|
577
707
|
}
|
|
578
708
|
|
|
579
709
|
// src/utils/diff.ts
|
|
@@ -727,577 +857,437 @@ function unifiedDiff(oldText, newText, opts = {}) {
|
|
|
727
857
|
}
|
|
728
858
|
return out;
|
|
729
859
|
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
const base = slugify(path2.basename(absRoot));
|
|
735
|
-
const hash = createHash("sha256").update(path2.resolve(absRoot)).digest("hex").slice(0, 6);
|
|
736
|
-
return `${base}-${hash}`;
|
|
737
|
-
}
|
|
738
|
-
function slugify(name) {
|
|
739
|
-
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
|
|
740
|
-
}
|
|
741
|
-
function wstackGlobalRoot() {
|
|
742
|
-
const fromEnv = process.env["WRONGSTACK_HOME"];
|
|
743
|
-
if (fromEnv && fromEnv.trim().length > 0) return path2.resolve(fromEnv);
|
|
744
|
-
return path2.join(os.homedir(), ".wrongstack");
|
|
745
|
-
}
|
|
746
|
-
function resolveWstackPaths(opts) {
|
|
747
|
-
const globalRoot = opts.globalRoot ?? (opts.userHome ? path2.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
|
|
748
|
-
const hash = projectHash(opts.projectRoot);
|
|
749
|
-
const slug = projectSlug(opts.projectRoot);
|
|
750
|
-
const projectDir = path2.join(globalRoot, "projects", slug);
|
|
751
|
-
return {
|
|
752
|
-
globalRoot,
|
|
753
|
-
configDir: globalRoot,
|
|
754
|
-
globalConfig: path2.join(globalRoot, "config.json"),
|
|
755
|
-
secretsKey: path2.join(globalRoot, ".key"),
|
|
756
|
-
globalMemory: path2.join(globalRoot, "memory.md"),
|
|
757
|
-
globalSkills: path2.join(globalRoot, "skills"),
|
|
758
|
-
globalPrompts: path2.join(globalRoot, "prompts"),
|
|
759
|
-
cacheDir: path2.join(globalRoot, "cache"),
|
|
760
|
-
modelsCache: path2.join(globalRoot, "cache", "models.dev.json"),
|
|
761
|
-
modelsOverlayCache: path2.join(globalRoot, "cache", "models-overlay.json"),
|
|
762
|
-
historyFile: path2.join(globalRoot, "history"),
|
|
763
|
-
logFile: path2.join(globalRoot, "logs", "wrongstack.log"),
|
|
764
|
-
projectDir,
|
|
765
|
-
projectCodebaseIndex: path2.join(projectDir, "codebase-index"),
|
|
766
|
-
projectMemory: path2.join(projectDir, "memory.md"),
|
|
767
|
-
projectSessions: path2.join(projectDir, "sessions"),
|
|
768
|
-
projectTrust: path2.join(projectDir, "trust.json"),
|
|
769
|
-
projectMeta: path2.join(projectDir, "meta.json"),
|
|
770
|
-
projectLocalConfig: path2.join(projectDir, "config.local.json"),
|
|
771
|
-
inProjectConfig: path2.join(opts.projectRoot, ".wrongstack", "config.json"),
|
|
772
|
-
inProjectAgentsFile: path2.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
|
|
773
|
-
inProjectSkills: path2.join(opts.projectRoot, ".wrongstack", "skills"),
|
|
774
|
-
inProjectWorktrees: path2.join(opts.projectRoot, ".wrongstack", "worktrees"),
|
|
775
|
-
projectHash: hash,
|
|
776
|
-
projectSlug: slug,
|
|
777
|
-
projectGoal: path2.join(projectDir, "goal.json"),
|
|
778
|
-
projectSpecs: path2.join(projectDir, "specs"),
|
|
779
|
-
projectTaskGraphs: path2.join(projectDir, "task-graphs"),
|
|
780
|
-
projectSddSession: path2.join(projectDir, "sdd-session.json"),
|
|
781
|
-
projectPlan: path2.join(projectDir, "plan.json"),
|
|
782
|
-
projectAutophase: path2.join(projectDir, "autophase"),
|
|
783
|
-
syncConfig: path2.join(globalRoot, "sync.json"),
|
|
784
|
-
projectStatus: (projectHash2) => path2.join(globalRoot, "projects", projectHash2, "status.json")
|
|
785
|
-
};
|
|
860
|
+
|
|
861
|
+
// src/utils/error.ts
|
|
862
|
+
function toErrorMessage(err) {
|
|
863
|
+
return err instanceof Error ? err.message : String(err);
|
|
786
864
|
}
|
|
787
865
|
|
|
788
|
-
// src/utils/
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
"LOGNAME",
|
|
795
|
-
"SHELL",
|
|
796
|
-
"LANG",
|
|
797
|
-
"LC_ALL",
|
|
798
|
-
"LC_CTYPE",
|
|
799
|
-
"TERM",
|
|
800
|
-
"TZ",
|
|
801
|
-
"TMPDIR",
|
|
802
|
-
"TEMP",
|
|
803
|
-
"TMP",
|
|
804
|
-
"PWD",
|
|
805
|
-
"OLDPWD",
|
|
806
|
-
"COMSPEC",
|
|
807
|
-
"SYSTEMROOT",
|
|
808
|
-
"SYSTEMDRIVE",
|
|
809
|
-
"WINDIR",
|
|
810
|
-
"PROGRAMFILES",
|
|
811
|
-
"PROGRAMFILES(X86)",
|
|
812
|
-
"PROGRAMDATA",
|
|
813
|
-
"APPDATA",
|
|
814
|
-
"LOCALAPPDATA",
|
|
815
|
-
"USERPROFILE",
|
|
816
|
-
"PUBLIC",
|
|
817
|
-
"PATHEXT"
|
|
818
|
-
]);
|
|
819
|
-
var SECRET_NAME_PARTS = [
|
|
820
|
-
"TOKEN",
|
|
821
|
-
"SECRET",
|
|
822
|
-
"PASSWORD",
|
|
823
|
-
"PASSWD",
|
|
824
|
-
"AUTH",
|
|
825
|
-
"CRED",
|
|
826
|
-
"BEARER",
|
|
827
|
-
"COOKIE",
|
|
828
|
-
"PRIVATE"
|
|
829
|
-
];
|
|
830
|
-
function looksSecret(name) {
|
|
831
|
-
const upper = name.toUpperCase();
|
|
832
|
-
for (const p of SECRET_NAME_PARTS) {
|
|
833
|
-
if (upper.includes(p)) return true;
|
|
866
|
+
// src/utils/expect-defined.ts
|
|
867
|
+
function expectDefined(value, label) {
|
|
868
|
+
if (value === null || value === void 0) {
|
|
869
|
+
const err = new Error(label ? `Expected ${label} to be defined` : "Expected value to be defined");
|
|
870
|
+
err.name = "ExpectDefinedError";
|
|
871
|
+
throw err;
|
|
834
872
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
873
|
+
return value;
|
|
874
|
+
}
|
|
875
|
+
var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
|
|
876
|
+
var IS_WINDOWS = process.platform === "win32";
|
|
877
|
+
var SEP = IS_WINDOWS ? "\\" : "/";
|
|
878
|
+
function isGlob(p) {
|
|
879
|
+
for (const c of p) {
|
|
880
|
+
if (GLOB_CHARS.has(c)) return true;
|
|
840
881
|
}
|
|
841
882
|
return false;
|
|
842
883
|
}
|
|
843
|
-
function
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
884
|
+
function globToRegex(pat) {
|
|
885
|
+
let i = 0;
|
|
886
|
+
let re = "^";
|
|
887
|
+
while (i < pat.length) {
|
|
888
|
+
const c = expectDefined(pat[i]);
|
|
889
|
+
if (c === "*") {
|
|
890
|
+
if (pat[i + 1] === "*") {
|
|
891
|
+
re += ".*";
|
|
892
|
+
i += 2;
|
|
893
|
+
if (pat[i] === "/") i++;
|
|
894
|
+
} else {
|
|
895
|
+
re += "[^/\\\\]*";
|
|
896
|
+
i++;
|
|
897
|
+
}
|
|
898
|
+
} else if (c === "?") {
|
|
899
|
+
re += "[^/\\\\]";
|
|
900
|
+
i++;
|
|
901
|
+
} else if (c === "[") {
|
|
902
|
+
let cls = "[";
|
|
903
|
+
i++;
|
|
904
|
+
if (pat[i] === "!" || pat[i] === "^") {
|
|
905
|
+
cls += "^";
|
|
906
|
+
i++;
|
|
907
|
+
}
|
|
908
|
+
while (i < pat.length && pat[i] !== "]") {
|
|
909
|
+
const ch = pat[i] ?? "";
|
|
910
|
+
if (ch === "\\") cls += "\\\\";
|
|
911
|
+
else if (ch === "]" || ch === "^") cls += `\\${ch}`;
|
|
912
|
+
else cls += ch;
|
|
913
|
+
i++;
|
|
914
|
+
}
|
|
915
|
+
cls += "]";
|
|
916
|
+
re += cls;
|
|
917
|
+
i++;
|
|
918
|
+
} else {
|
|
919
|
+
re += c.replace(/[.+^${}()|\\]/g, "\\$&");
|
|
920
|
+
i++;
|
|
921
|
+
}
|
|
852
922
|
}
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
923
|
+
return new RegExp(re + "$");
|
|
924
|
+
}
|
|
925
|
+
function baseDir(pat) {
|
|
926
|
+
let i = pat.length - 1;
|
|
927
|
+
while (i >= 0 && !GLOB_CHARS.has(expectDefined(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
|
|
928
|
+
const cut = i >= 0 ? pat.lastIndexOf(SEP, i) : pat.lastIndexOf("/", i);
|
|
929
|
+
return cut < 0 ? "." : pat.slice(0, cut);
|
|
930
|
+
}
|
|
931
|
+
async function expandGlob(pattern) {
|
|
932
|
+
if (!isGlob(pattern)) return [pattern];
|
|
933
|
+
const results = /* @__PURE__ */ new Set();
|
|
934
|
+
const abs = isAbsolute(pattern);
|
|
935
|
+
const base = abs ? baseDir(pattern) : baseDir(pattern);
|
|
936
|
+
const relPat = base === "." ? pattern : pattern.slice(base.length + 1);
|
|
937
|
+
async function walk2(dir, pat) {
|
|
938
|
+
let entries;
|
|
939
|
+
try {
|
|
940
|
+
entries = await fs.readdir(dir);
|
|
941
|
+
} catch {
|
|
942
|
+
return;
|
|
861
943
|
}
|
|
862
|
-
const
|
|
863
|
-
if (
|
|
864
|
-
|
|
865
|
-
|
|
944
|
+
const firstGlob = pat.search(/[*?[[]/);
|
|
945
|
+
if (firstGlob < 0) {
|
|
946
|
+
const re = globToRegex(pat);
|
|
947
|
+
for (const e of entries) {
|
|
948
|
+
if (re.test(e)) {
|
|
949
|
+
const full = `${dir}${SEP}${e}`;
|
|
950
|
+
results.add(abs ? resolve(full) : full);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
return;
|
|
866
954
|
}
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
955
|
+
const before = pat.slice(0, firstGlob);
|
|
956
|
+
const rest = pat.slice(firstGlob);
|
|
957
|
+
if (before.endsWith("**")) {
|
|
958
|
+
await walk2(dir, rest);
|
|
959
|
+
for (const e of entries) {
|
|
960
|
+
const full = `${dir}${SEP}${e}`;
|
|
961
|
+
try {
|
|
962
|
+
const stat3 = await fs.stat(full);
|
|
963
|
+
if (stat3.isDirectory()) await walk2(full, rest);
|
|
964
|
+
} catch {
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
} else if (before === "") {
|
|
968
|
+
const re = globToRegex(rest);
|
|
969
|
+
for (const e of entries) {
|
|
970
|
+
if (re.test(e)) {
|
|
971
|
+
const full = `${dir}${SEP}${e}`;
|
|
972
|
+
results.add(abs ? resolve(full) : full);
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
} else {
|
|
976
|
+
const seg = before.replace(/[*?[\]]/g, "").replace(/\/$/, "");
|
|
977
|
+
if (entries.includes(seg)) {
|
|
978
|
+
const full = `${dir}${SEP}${seg}`;
|
|
979
|
+
try {
|
|
980
|
+
const stat3 = await fs.stat(full);
|
|
981
|
+
if (stat3.isDirectory()) await walk2(full, rest);
|
|
982
|
+
} catch {
|
|
983
|
+
}
|
|
984
|
+
}
|
|
874
985
|
}
|
|
875
986
|
}
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
}
|
|
879
|
-
if (opts.sessionId) out["WRONGSTACK_SESSION_ID"] = opts.sessionId;
|
|
880
|
-
return out;
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
// src/utils/sleep.ts
|
|
884
|
-
function sleep(ms) {
|
|
885
|
-
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
987
|
+
await walk2(base === "." ? "." : base, relPat);
|
|
988
|
+
return [...results];
|
|
886
989
|
}
|
|
887
990
|
|
|
888
|
-
// src/utils/
|
|
889
|
-
function
|
|
890
|
-
|
|
891
|
-
message ?? `Unhandled case: ${JSON.stringify(x)}`
|
|
892
|
-
);
|
|
893
|
-
err.name = "AssertNeverError";
|
|
894
|
-
throw err;
|
|
991
|
+
// src/utils/glob-match.ts
|
|
992
|
+
function escapeRegex(s) {
|
|
993
|
+
return s.replace(/[.+^${}()|\\]/g, "\\$&");
|
|
895
994
|
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
995
|
+
var COMPILED_GLOB_CACHE = /* @__PURE__ */ new Map();
|
|
996
|
+
var CACHE_MAX_SIZE = 2e3;
|
|
997
|
+
function getCachedGlob(pattern) {
|
|
998
|
+
const cached = COMPILED_GLOB_CACHE.get(pattern);
|
|
999
|
+
if (cached) return cached;
|
|
1000
|
+
if (COMPILED_GLOB_CACHE.size >= CACHE_MAX_SIZE) {
|
|
1001
|
+
const keys = [...COMPILED_GLOB_CACHE.keys()];
|
|
1002
|
+
for (let i = 0; i < Math.floor(CACHE_MAX_SIZE / 4); i++) {
|
|
1003
|
+
COMPILED_GLOB_CACHE.delete(expectDefined(keys[i]));
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
const re = compileGlob(pattern);
|
|
1007
|
+
COMPILED_GLOB_CACHE.set(pattern, re);
|
|
1008
|
+
return re;
|
|
909
1009
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
protectProto = true,
|
|
915
|
-
onNonPrimitiveArrayReplace
|
|
916
|
-
} = options;
|
|
917
|
-
if (typeof base !== "object" || base === null) {
|
|
918
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
919
|
-
}
|
|
920
|
-
if (typeof patch !== "object" || patch === null) {
|
|
921
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
922
|
-
}
|
|
923
|
-
if (Array.isArray(base) && Array.isArray(patch)) {
|
|
924
|
-
if (arrayMode === "concat-primitives" && isPrimitiveArray(base) && isPrimitiveArray(patch)) {
|
|
925
|
-
return [.../* @__PURE__ */ new Set([...base, ...patch])];
|
|
926
|
-
}
|
|
927
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
928
|
-
}
|
|
929
|
-
if (Array.isArray(base) || Array.isArray(patch)) {
|
|
930
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
1010
|
+
var MAX_GLOB_PATTERN_LEN = 1024;
|
|
1011
|
+
function compileGlob(pattern) {
|
|
1012
|
+
if (pattern.length > MAX_GLOB_PATTERN_LEN) {
|
|
1013
|
+
throw new Error(`Glob pattern exceeds ${MAX_GLOB_PATTERN_LEN} characters`);
|
|
931
1014
|
}
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
if (
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
out[k] = deepMerge(existing, v, options);
|
|
945
|
-
} else if (v !== void 0) {
|
|
946
|
-
if (onNonPrimitiveArrayReplace && Array.isArray(v) && !isPrimitiveArray(v)) {
|
|
947
|
-
const existingLen = Array.isArray(existing) ? existing.length : 0;
|
|
948
|
-
onNonPrimitiveArrayReplace(k, existingLen, v.length);
|
|
1015
|
+
let i = 0;
|
|
1016
|
+
let re = "^";
|
|
1017
|
+
while (i < pattern.length) {
|
|
1018
|
+
const c = pattern[i];
|
|
1019
|
+
if (c === "*") {
|
|
1020
|
+
if (pattern[i + 1] === "*") {
|
|
1021
|
+
re += ".*";
|
|
1022
|
+
i += 2;
|
|
1023
|
+
if (pattern[i] === "/") i++;
|
|
1024
|
+
} else {
|
|
1025
|
+
re += "[^/]*";
|
|
1026
|
+
i++;
|
|
949
1027
|
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
function serialize(value) {
|
|
960
|
-
if (typeof value === "string") return value;
|
|
961
|
-
if (value === null || value === void 0) return "";
|
|
962
|
-
if (typeof value === "object") {
|
|
963
|
-
if (Array.isArray(value)) return value.map(serialize).join("\n");
|
|
964
|
-
if ("text" in value) {
|
|
965
|
-
const t = value.text;
|
|
966
|
-
return typeof t === "string" ? t : JSON.stringify(value, null, 2);
|
|
1028
|
+
} else if (c === "?") {
|
|
1029
|
+
re += "[^/]";
|
|
1030
|
+
i++;
|
|
1031
|
+
} else if (c === "[") {
|
|
1032
|
+
let cls = "[";
|
|
1033
|
+
i++;
|
|
1034
|
+
if (pattern[i] === "!" || pattern[i] === "^") {
|
|
1035
|
+
cls += "^";
|
|
1036
|
+
i++;
|
|
967
1037
|
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1038
|
+
while (i < pattern.length && pattern[i] !== "]") {
|
|
1039
|
+
const ch = pattern[i] ?? "";
|
|
1040
|
+
if (ch === "\\") {
|
|
1041
|
+
cls += "\\\\";
|
|
1042
|
+
} else if (ch === "]" || ch === "^") {
|
|
1043
|
+
cls += `\\${ch}`;
|
|
1044
|
+
} else {
|
|
1045
|
+
cls += ch;
|
|
1046
|
+
}
|
|
1047
|
+
i++;
|
|
972
1048
|
}
|
|
1049
|
+
cls += "]";
|
|
1050
|
+
re += cls;
|
|
1051
|
+
i++;
|
|
1052
|
+
} else {
|
|
1053
|
+
re += escapeRegex(c ?? "");
|
|
1054
|
+
i++;
|
|
973
1055
|
}
|
|
974
|
-
return String(value);
|
|
975
|
-
}
|
|
976
|
-
function enforceCap(text, remainingBudget) {
|
|
977
|
-
if (remainingBudget <= 0) {
|
|
978
|
-
return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
|
|
979
|
-
}
|
|
980
|
-
const textBytes = Buffer.byteLength(text, "utf8");
|
|
981
|
-
if (textBytes <= remainingBudget) {
|
|
982
|
-
return { text, newBudget: remainingBudget - textBytes };
|
|
983
|
-
}
|
|
984
|
-
const marker = `
|
|
985
|
-
\u2026[truncated ${textBytes - remainingBudget} bytes]\u2026
|
|
986
|
-
`;
|
|
987
|
-
const markerBytes = Buffer.byteLength(marker, "utf8");
|
|
988
|
-
const available = remainingBudget - markerBytes;
|
|
989
|
-
if (available <= 0) {
|
|
990
|
-
return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
|
|
991
|
-
}
|
|
992
|
-
const half = Math.floor(available / 2);
|
|
993
|
-
const first = text.slice(0, half);
|
|
994
|
-
const second = text.slice(text.length - half);
|
|
995
|
-
return { text: `${first}${marker}${second}`, newBudget: 0 };
|
|
996
1056
|
}
|
|
997
|
-
|
|
1057
|
+
re += "$";
|
|
1058
|
+
return new RegExp(re);
|
|
998
1059
|
}
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
|
|
1002
|
-
var CAL_ALPHA = 0.3;
|
|
1003
|
-
var CALIBRATION_GLOBAL_KEY = "__global__";
|
|
1004
|
-
var _cals = /* @__PURE__ */ new Map();
|
|
1005
|
-
function calState(key) {
|
|
1006
|
-
let state = _cals.get(key);
|
|
1007
|
-
if (!state) {
|
|
1008
|
-
state = { ratio: 1, count: 0, prevEst: 0 };
|
|
1009
|
-
_cals.set(key, state);
|
|
1010
|
-
}
|
|
1011
|
-
return state;
|
|
1060
|
+
function matchGlob(pattern, input) {
|
|
1061
|
+
return getCachedGlob(pattern).test(input);
|
|
1012
1062
|
}
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
var ESTIMATE_CACHE_MAX_SIZE = 1e4;
|
|
1016
|
-
function getCachedEstimate(key, compute) {
|
|
1017
|
-
const existing = ESTIMATE_CACHE.get(key);
|
|
1018
|
-
if (existing !== void 0) return existing;
|
|
1019
|
-
if (ESTIMATE_CACHE.size >= ESTIMATE_CACHE_MAX_SIZE) {
|
|
1020
|
-
for (const k of ESTIMATE_CACHE.keys()) {
|
|
1021
|
-
if (ESTIMATE_CACHE.size <= Math.floor(ESTIMATE_CACHE_MAX_SIZE / 2)) break;
|
|
1022
|
-
ESTIMATE_CACHE.delete(k);
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
const estimate = compute(key);
|
|
1026
|
-
ESTIMATE_CACHE.set(key, estimate);
|
|
1027
|
-
return estimate;
|
|
1063
|
+
function matchAny(patterns, input) {
|
|
1064
|
+
return patterns.some((p) => matchGlob(p, input));
|
|
1028
1065
|
}
|
|
1029
|
-
function
|
|
1030
|
-
|
|
1031
|
-
if (
|
|
1032
|
-
return
|
|
1066
|
+
function isPrivateIPv4(addr) {
|
|
1067
|
+
const parts = addr.split(".").map((p) => Number.parseInt(p, 10));
|
|
1068
|
+
if (parts.length !== 4 || parts.some((n) => Number.isNaN(n) || n < 0 || n > 255)) {
|
|
1069
|
+
return true;
|
|
1033
1070
|
}
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
if (
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1071
|
+
const [a, b, c] = parts;
|
|
1072
|
+
if (a === 0) return true;
|
|
1073
|
+
if (a === 10) return true;
|
|
1074
|
+
if (a === 127) return true;
|
|
1075
|
+
if (a === 169 && b === 254) return true;
|
|
1076
|
+
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
1077
|
+
if (a === 192 && b === 168) return true;
|
|
1078
|
+
if (a === 192 && b === 0 && c === 0) return true;
|
|
1079
|
+
if (a === 100 && b >= 64 && b <= 127) return true;
|
|
1080
|
+
if (a >= 224) return true;
|
|
1081
|
+
return false;
|
|
1042
1082
|
}
|
|
1043
|
-
function
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1083
|
+
function isPrivateIPv6(raw) {
|
|
1084
|
+
const lower = raw.toLowerCase();
|
|
1085
|
+
if (lower === "::" || lower === "::1") return true;
|
|
1086
|
+
const groups = expandIPv6(lower);
|
|
1087
|
+
if (!groups) return true;
|
|
1088
|
+
if (groups[0] === 0 && groups[1] === 0 && groups[2] === 0 && groups[3] === 0 && groups[4] === 0 && groups[5] === 65535) {
|
|
1089
|
+
const a = (groups[6] ?? 0) >> 8;
|
|
1090
|
+
const b = (groups[6] ?? 0) & 255;
|
|
1091
|
+
const c = (groups[7] ?? 0) >> 8;
|
|
1092
|
+
const d = (groups[7] ?? 0) & 255;
|
|
1093
|
+
return isPrivateIPv4(`${a}.${b}.${c}.${d}`);
|
|
1051
1094
|
}
|
|
1052
|
-
|
|
1095
|
+
const high = groups[0] ?? 0;
|
|
1096
|
+
if ((high & 65024) === 64512) return true;
|
|
1097
|
+
if ((high & 65472) === 65152) return true;
|
|
1098
|
+
if ((high & 65280) === 65280) return true;
|
|
1099
|
+
return false;
|
|
1053
1100
|
}
|
|
1054
|
-
function
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1101
|
+
function expandIPv6(addr) {
|
|
1102
|
+
const parts = addr.split("::");
|
|
1103
|
+
if (parts.length > 2) return null;
|
|
1104
|
+
const parseGroups = (s) => {
|
|
1105
|
+
if (s === "") return [];
|
|
1106
|
+
const out = [];
|
|
1107
|
+
for (const g of s.split(":")) {
|
|
1108
|
+
if (g.length === 0 || g.length > 4) return null;
|
|
1109
|
+
const n = Number.parseInt(g, 16);
|
|
1110
|
+
if (Number.isNaN(n) || n < 0 || n > 65535) return null;
|
|
1111
|
+
out.push(n);
|
|
1060
1112
|
}
|
|
1061
|
-
|
|
1113
|
+
return out;
|
|
1114
|
+
};
|
|
1115
|
+
if (parts.length === 1) {
|
|
1116
|
+
const groups = parseGroups(parts[0] ?? "");
|
|
1117
|
+
if (!groups || groups.length !== 8) return null;
|
|
1118
|
+
return groups;
|
|
1062
1119
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
const
|
|
1067
|
-
if (
|
|
1068
|
-
return
|
|
1120
|
+
const head = parseGroups(parts[0] ?? "");
|
|
1121
|
+
const tail = parseGroups(parts[1] ?? "");
|
|
1122
|
+
if (!head || !tail) return null;
|
|
1123
|
+
const fill = 8 - head.length - tail.length;
|
|
1124
|
+
if (fill < 0) return null;
|
|
1125
|
+
return [...head, ...new Array(fill).fill(0), ...tail];
|
|
1069
1126
|
}
|
|
1070
|
-
function
|
|
1071
|
-
|
|
1072
|
-
if (
|
|
1073
|
-
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
messagesTokens += RoughTokenEstimate(JSON.stringify(b));
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1127
|
+
async function assertNotPrivateHost(hostname) {
|
|
1128
|
+
const host = hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
|
|
1129
|
+
if (host === "localhost" || host.endsWith(".localhost")) {
|
|
1130
|
+
throw new Error("fetch: blocked localhost target");
|
|
1131
|
+
}
|
|
1132
|
+
const ipVersion = net.isIP(host);
|
|
1133
|
+
if (ipVersion === 4) {
|
|
1134
|
+
if (isPrivateIPv4(host)) {
|
|
1135
|
+
throw new Error(`fetch: blocked private/loopback address "${host}"`);
|
|
1136
|
+
}
|
|
1137
|
+
} else if (ipVersion === 6) {
|
|
1138
|
+
if (isPrivateIPv6(host)) {
|
|
1139
|
+
throw new Error(`fetch: blocked private/loopback address "${host}"`);
|
|
1140
|
+
}
|
|
1141
|
+
} else {
|
|
1142
|
+
try {
|
|
1143
|
+
const records = await dns.lookup(host, { all: true });
|
|
1144
|
+
for (const r of records) {
|
|
1145
|
+
const bad = r.family === 4 ? isPrivateIPv4(r.address) : isPrivateIPv6(r.address);
|
|
1146
|
+
if (bad) {
|
|
1147
|
+
throw new Error(`fetch: resolved to private address ${r.address}`);
|
|
1095
1148
|
}
|
|
1096
1149
|
}
|
|
1150
|
+
} catch (err) {
|
|
1151
|
+
if (err instanceof Error && err.message.startsWith("fetch:")) throw err;
|
|
1097
1152
|
}
|
|
1098
1153
|
}
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
// src/utils/json-repair.ts
|
|
1157
|
+
function completePartialObject(s) {
|
|
1158
|
+
if (!s.trim().startsWith("{")) return s;
|
|
1159
|
+
if (tryParse(s).ok) return s;
|
|
1160
|
+
return repairTruncated(s);
|
|
1161
|
+
}
|
|
1162
|
+
function repairTruncated(s) {
|
|
1163
|
+
const stack = [];
|
|
1164
|
+
let inString = false;
|
|
1165
|
+
let escaped = false;
|
|
1166
|
+
let sawKey = false;
|
|
1167
|
+
let prevSig = "";
|
|
1168
|
+
let contentEnd = 0;
|
|
1169
|
+
let stringBraceDepth = 0;
|
|
1170
|
+
for (let i = 0; i < s.length; i++) {
|
|
1171
|
+
const ch = expectDefined(s[i]);
|
|
1172
|
+
if (inString) {
|
|
1173
|
+
contentEnd = i + 1;
|
|
1174
|
+
if (escaped) {
|
|
1175
|
+
escaped = false;
|
|
1176
|
+
continue;
|
|
1177
|
+
}
|
|
1178
|
+
if (ch === "\\") {
|
|
1179
|
+
escaped = true;
|
|
1180
|
+
continue;
|
|
1181
|
+
}
|
|
1182
|
+
if (ch === '"') {
|
|
1183
|
+
inString = false;
|
|
1184
|
+
prevSig = '"';
|
|
1185
|
+
stringBraceDepth = 0;
|
|
1186
|
+
continue;
|
|
1106
1187
|
}
|
|
1188
|
+
if (ch === "{") stringBraceDepth++;
|
|
1189
|
+
else if (ch === "}" && stringBraceDepth > 0) stringBraceDepth--;
|
|
1190
|
+
continue;
|
|
1191
|
+
}
|
|
1192
|
+
if (ch === " " || ch === " " || ch === "\n" || ch === "\r") continue;
|
|
1193
|
+
contentEnd = i + 1;
|
|
1194
|
+
if (ch === '"') {
|
|
1195
|
+
inString = true;
|
|
1196
|
+
sawKey = true;
|
|
1197
|
+
stringBraceDepth = 0;
|
|
1198
|
+
prevSig = '"';
|
|
1199
|
+
} else if (ch === "{" || ch === "[") {
|
|
1200
|
+
stack.push(ch);
|
|
1201
|
+
prevSig = ch;
|
|
1202
|
+
} else if (ch === "}" || ch === "]") {
|
|
1203
|
+
stack.pop();
|
|
1204
|
+
prevSig = ch;
|
|
1205
|
+
} else {
|
|
1206
|
+
prevSig = ch;
|
|
1107
1207
|
}
|
|
1108
1208
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1209
|
+
if (!sawKey && !inString) return s;
|
|
1210
|
+
let result = s.slice(0, contentEnd);
|
|
1211
|
+
if (inString) {
|
|
1212
|
+
if (escaped) {
|
|
1213
|
+
result = result.slice(0, -1);
|
|
1214
|
+
} else if (endsWithInvalidEscape(result)) {
|
|
1215
|
+
result = result.slice(0, -2);
|
|
1216
|
+
}
|
|
1217
|
+
if (stringBraceDepth > 0) result += "}".repeat(stringBraceDepth);
|
|
1218
|
+
result += '"';
|
|
1219
|
+
} else if (prevSig === ":") {
|
|
1220
|
+
result += "null";
|
|
1112
1221
|
}
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
return {
|
|
1116
|
-
messages: messagesTokens,
|
|
1117
|
-
systemPrompt: systemTokens,
|
|
1118
|
-
tools: toolsTokens,
|
|
1119
|
-
total
|
|
1120
|
-
};
|
|
1121
|
-
}
|
|
1122
|
-
function recordActualUsage(actualInputTokens, estimatedInputTokens, calibrationKey = CALIBRATION_GLOBAL_KEY) {
|
|
1123
|
-
if (actualInputTokens <= 0) return;
|
|
1124
|
-
const cal = calState(calibrationKey);
|
|
1125
|
-
const est = estimatedInputTokens ?? cal.prevEst;
|
|
1126
|
-
if (est <= 0) return;
|
|
1127
|
-
const sampleRatio = actualInputTokens / est;
|
|
1128
|
-
if (cal.count === 0) {
|
|
1129
|
-
cal.ratio = sampleRatio;
|
|
1130
|
-
} else {
|
|
1131
|
-
cal.ratio = CAL_ALPHA * sampleRatio + (1 - CAL_ALPHA) * cal.ratio;
|
|
1222
|
+
for (let k = stack.length - 1; k >= 0; k--) {
|
|
1223
|
+
result += stack[k] === "{" ? "}" : "]";
|
|
1132
1224
|
}
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
function getCalibrationState(calibrationKey = CALIBRATION_GLOBAL_KEY) {
|
|
1137
|
-
const cal = calState(calibrationKey);
|
|
1138
|
-
return {
|
|
1139
|
-
ratio: cal.ratio,
|
|
1140
|
-
count: cal.count,
|
|
1141
|
-
calibrated: cal.count >= MIN_SAMPLES_FOR_CALIBRATION
|
|
1142
|
-
};
|
|
1143
|
-
}
|
|
1144
|
-
function estimateRequestTokensCalibrated(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
|
|
1145
|
-
const result = estimateRequestTokens(messages, systemPrompt, tools, calibrationKey);
|
|
1146
|
-
const cal = calState(calibrationKey);
|
|
1147
|
-
if (cal.count >= MIN_SAMPLES_FOR_CALIBRATION) {
|
|
1148
|
-
const safeRatio = Math.min(1.5, Math.max(0.5, cal.ratio));
|
|
1149
|
-
return {
|
|
1150
|
-
messages: Math.round(result.messages * safeRatio),
|
|
1151
|
-
systemPrompt: Math.round(result.systemPrompt * safeRatio),
|
|
1152
|
-
tools: Math.round(result.tools * safeRatio),
|
|
1153
|
-
total: Math.round(result.total * safeRatio)
|
|
1154
|
-
};
|
|
1225
|
+
if (!tryParse(result).ok) {
|
|
1226
|
+
const patched = result.replace(/:(\s*)([}\]])/g, ":null$2");
|
|
1227
|
+
if (tryParse(patched).ok) result = patched;
|
|
1155
1228
|
}
|
|
1156
1229
|
return result;
|
|
1157
1230
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1231
|
+
var VALID_ESCAPE = /* @__PURE__ */ new Set(['"', "\\", "/", "b", "f", "n", "r", "t", "u"]);
|
|
1232
|
+
function endsWithInvalidEscape(str) {
|
|
1233
|
+
const last = str[str.length - 1];
|
|
1234
|
+
if (str[str.length - 2] !== "\\" || last === void 0) return false;
|
|
1235
|
+
if (VALID_ESCAPE.has(last)) return false;
|
|
1236
|
+
let backslashes = 0;
|
|
1237
|
+
for (let k = str.length - 2; k >= 0 && str[k] === "\\"; k--) backslashes++;
|
|
1238
|
+
return backslashes % 2 === 1;
|
|
1239
|
+
}
|
|
1240
|
+
function tryParse(s) {
|
|
1241
|
+
try {
|
|
1242
|
+
return { ok: true, value: JSON.parse(s) };
|
|
1243
|
+
} catch {
|
|
1244
|
+
return { ok: false };
|
|
1162
1245
|
}
|
|
1163
|
-
_cals.delete(calibrationKey);
|
|
1164
1246
|
}
|
|
1165
1247
|
|
|
1166
|
-
// src/utils/
|
|
1167
|
-
function
|
|
1168
|
-
const
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
const filtered = mapContent(msg, (blocks) => {
|
|
1179
|
-
const next = [];
|
|
1180
|
-
for (const block of blocks) {
|
|
1181
|
-
if (block.type === "tool_use" && !nextIds.has(block.id)) {
|
|
1182
|
-
removedToolUses.push(block.id);
|
|
1183
|
-
changed = true;
|
|
1184
|
-
continue;
|
|
1185
|
-
}
|
|
1186
|
-
next.push(block);
|
|
1187
|
-
}
|
|
1188
|
-
return next;
|
|
1248
|
+
// src/utils/json-schema-validate.ts
|
|
1249
|
+
function validateAgainstSchema(value, schema) {
|
|
1250
|
+
const errors = [];
|
|
1251
|
+
walk(value, schema, "", errors);
|
|
1252
|
+
return { ok: errors.length === 0, errors };
|
|
1253
|
+
}
|
|
1254
|
+
function walk(value, schema, path4, errors) {
|
|
1255
|
+
if (schema.enum !== void 0) {
|
|
1256
|
+
if (!schema.enum.some((e) => deepEqual(e, value))) {
|
|
1257
|
+
errors.push({
|
|
1258
|
+
path: path4 || "<root>",
|
|
1259
|
+
message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
|
|
1189
1260
|
});
|
|
1190
|
-
|
|
1261
|
+
return;
|
|
1191
1262
|
}
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
removedToolResults.push(block.tool_use_id);
|
|
1199
|
-
changed = true;
|
|
1200
|
-
continue;
|
|
1201
|
-
}
|
|
1202
|
-
next.push(block);
|
|
1203
|
-
}
|
|
1204
|
-
return next;
|
|
1263
|
+
}
|
|
1264
|
+
if (typeof schema.type === "string") {
|
|
1265
|
+
if (!checkType(value, schema.type)) {
|
|
1266
|
+
errors.push({
|
|
1267
|
+
path: path4 || "<root>",
|
|
1268
|
+
message: `expected ${schema.type}, got ${describeType(value)}`
|
|
1205
1269
|
});
|
|
1206
|
-
|
|
1270
|
+
return;
|
|
1207
1271
|
}
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
const ids = /* @__PURE__ */ new Set();
|
|
1228
|
-
if (!msg || msg.role !== "assistant") return ids;
|
|
1229
|
-
for (const block of contentBlocks(msg)) {
|
|
1230
|
-
if (block.type === "tool_use") ids.add(block.id);
|
|
1231
|
-
}
|
|
1232
|
-
return ids;
|
|
1233
|
-
}
|
|
1234
|
-
function toolResultIds(msg) {
|
|
1235
|
-
const ids = /* @__PURE__ */ new Set();
|
|
1236
|
-
if (!msg || msg.role !== "user") return ids;
|
|
1237
|
-
for (const block of contentBlocks(msg)) {
|
|
1238
|
-
if (block.type === "tool_result") ids.add(block.tool_use_id);
|
|
1239
|
-
}
|
|
1240
|
-
return ids;
|
|
1241
|
-
}
|
|
1242
|
-
function contentBlocks(msg) {
|
|
1243
|
-
return msg && Array.isArray(msg.content) ? msg.content : [];
|
|
1244
|
-
}
|
|
1245
|
-
function mapContent(msg, fn) {
|
|
1246
|
-
if (!Array.isArray(msg.content)) return msg;
|
|
1247
|
-
const next = fn(msg.content);
|
|
1248
|
-
if (next.length === msg.content.length && next.every((b, idx) => b === msg.content[idx])) {
|
|
1249
|
-
return msg;
|
|
1250
|
-
}
|
|
1251
|
-
return { ...msg, content: next };
|
|
1252
|
-
}
|
|
1253
|
-
function isEmptyMessage(msg) {
|
|
1254
|
-
if (typeof msg.content === "string") return msg.content.trim().length === 0;
|
|
1255
|
-
return msg.content.length === 0;
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
// src/utils/json-schema-validate.ts
|
|
1259
|
-
function validateAgainstSchema(value, schema) {
|
|
1260
|
-
const errors = [];
|
|
1261
|
-
walk(value, schema, "", errors);
|
|
1262
|
-
return { ok: errors.length === 0, errors };
|
|
1263
|
-
}
|
|
1264
|
-
function walk(value, schema, path3, errors) {
|
|
1265
|
-
if (schema.enum !== void 0) {
|
|
1266
|
-
if (!schema.enum.some((e) => deepEqual(e, value))) {
|
|
1267
|
-
errors.push({
|
|
1268
|
-
path: path3 || "<root>",
|
|
1269
|
-
message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
|
|
1270
|
-
});
|
|
1271
|
-
return;
|
|
1272
|
-
}
|
|
1273
|
-
}
|
|
1274
|
-
if (typeof schema.type === "string") {
|
|
1275
|
-
if (!checkType(value, schema.type)) {
|
|
1276
|
-
errors.push({
|
|
1277
|
-
path: path3 || "<root>",
|
|
1278
|
-
message: `expected ${schema.type}, got ${describeType(value)}`
|
|
1279
|
-
});
|
|
1280
|
-
return;
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1283
|
-
if (schema.type === "object" && isPlainObject(value)) {
|
|
1284
|
-
const obj = value;
|
|
1285
|
-
for (const req of schema.required ?? []) {
|
|
1286
|
-
if (!(req in obj)) {
|
|
1287
|
-
errors.push({ path: joinPath(path3, req), message: "required property missing" });
|
|
1288
|
-
}
|
|
1289
|
-
}
|
|
1290
|
-
if (schema.properties) {
|
|
1291
|
-
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
1292
|
-
if (key in obj) {
|
|
1293
|
-
walk(obj[key], subSchema, joinPath(path3, key), errors);
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
if (schema.type === "array" && Array.isArray(value) && schema.items) {
|
|
1299
|
-
for (let i = 0; i < value.length; i++) {
|
|
1300
|
-
walk(value[i], schema.items, `${path3}[${i}]`, errors);
|
|
1272
|
+
}
|
|
1273
|
+
if (schema.type === "object" && isPlainObject(value)) {
|
|
1274
|
+
const obj = value;
|
|
1275
|
+
for (const req of schema.required ?? []) {
|
|
1276
|
+
if (!(req in obj)) {
|
|
1277
|
+
errors.push({ path: joinPath(path4, req), message: "required property missing" });
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
if (schema.properties) {
|
|
1281
|
+
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
1282
|
+
if (key in obj) {
|
|
1283
|
+
walk(obj[key], subSchema, joinPath(path4, key), errors);
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
if (schema.type === "array" && Array.isArray(value) && schema.items) {
|
|
1289
|
+
for (let i = 0; i < value.length; i++) {
|
|
1290
|
+
walk(value[i], schema.items, `${path4}[${i}]`, errors);
|
|
1301
1291
|
}
|
|
1302
1292
|
}
|
|
1303
1293
|
}
|
|
@@ -1351,418 +1341,1418 @@ function deepEqual(a, b) {
|
|
|
1351
1341
|
return false;
|
|
1352
1342
|
}
|
|
1353
1343
|
|
|
1354
|
-
// src/utils/
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
];
|
|
1362
|
-
function compileUserRegex(pattern, flags) {
|
|
1363
|
-
if (typeof pattern !== "string") {
|
|
1364
|
-
return { ok: false, reason: "pattern must be a string" };
|
|
1344
|
+
// src/utils/merge-custom-models.ts
|
|
1345
|
+
function mergeCustomModelDefs(providerCustomModels, configModels) {
|
|
1346
|
+
const out = {};
|
|
1347
|
+
if (providerCustomModels) {
|
|
1348
|
+
for (const [id, def] of Object.entries(providerCustomModels)) {
|
|
1349
|
+
out[id] = { ...def };
|
|
1350
|
+
}
|
|
1365
1351
|
}
|
|
1366
|
-
if (
|
|
1367
|
-
|
|
1352
|
+
if (configModels) {
|
|
1353
|
+
for (const [id, def] of Object.entries(configModels)) {
|
|
1354
|
+
out[id] = { ...def };
|
|
1355
|
+
}
|
|
1368
1356
|
}
|
|
1369
|
-
if (
|
|
1370
|
-
|
|
1357
|
+
if (Object.keys(out).length === 0) return void 0;
|
|
1358
|
+
return out;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
// src/utils/merge-models-payload.ts
|
|
1362
|
+
function mergeModelsPayload(base, overlay) {
|
|
1363
|
+
const out = {};
|
|
1364
|
+
for (const [id, provider] of Object.entries(base)) {
|
|
1365
|
+
out[id] = cloneProvider(provider);
|
|
1371
1366
|
}
|
|
1372
|
-
for (const
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
ok: false,
|
|
1376
|
-
reason: "pattern looks vulnerable to catastrophic backtracking \u2014 rewrite without nested quantifiers"
|
|
1377
|
-
};
|
|
1378
|
-
}
|
|
1367
|
+
for (const [id, ovProvider] of Object.entries(overlay)) {
|
|
1368
|
+
const existing = out[id];
|
|
1369
|
+
out[id] = existing ? mergeProvider(existing, ovProvider) : cloneProvider(ovProvider);
|
|
1379
1370
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1371
|
+
return out;
|
|
1372
|
+
}
|
|
1373
|
+
function mergeProvider(base, overlay) {
|
|
1374
|
+
const models = {};
|
|
1375
|
+
for (const [mid, m] of Object.entries(base.models ?? {})) {
|
|
1376
|
+
models[mid] = { ...m };
|
|
1377
|
+
}
|
|
1378
|
+
for (const [mid, ovModel] of Object.entries(overlay.models ?? {})) {
|
|
1379
|
+
const existing = models[mid];
|
|
1380
|
+
models[mid] = existing ? mergeModel(existing, ovModel) : { ...ovModel };
|
|
1387
1381
|
}
|
|
1382
|
+
return {
|
|
1383
|
+
...base,
|
|
1384
|
+
// Overlay scalar fields win when explicitly provided; otherwise keep base.
|
|
1385
|
+
...stripUndefined({
|
|
1386
|
+
id: overlay.id,
|
|
1387
|
+
name: overlay.name,
|
|
1388
|
+
npm: overlay.npm,
|
|
1389
|
+
api: overlay.api,
|
|
1390
|
+
env: overlay.env,
|
|
1391
|
+
doc: overlay.doc
|
|
1392
|
+
}),
|
|
1393
|
+
models
|
|
1394
|
+
};
|
|
1388
1395
|
}
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
for (const c of p) {
|
|
1394
|
-
if (GLOB_CHARS.has(c)) return true;
|
|
1396
|
+
function mergeModel(base, overlay) {
|
|
1397
|
+
const merged = { ...base, ...overlay };
|
|
1398
|
+
if (base.limit || overlay.limit) {
|
|
1399
|
+
merged.limit = { ...base.limit, ...overlay.limit };
|
|
1395
1400
|
}
|
|
1396
|
-
|
|
1401
|
+
if (base.cost || overlay.cost) {
|
|
1402
|
+
merged.cost = { ...base.cost, ...overlay.cost };
|
|
1403
|
+
}
|
|
1404
|
+
if (base.modalities || overlay.modalities) {
|
|
1405
|
+
merged.modalities = { ...base.modalities, ...overlay.modalities };
|
|
1406
|
+
}
|
|
1407
|
+
return merged;
|
|
1397
1408
|
}
|
|
1398
|
-
function
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
const c = expectDefined(pat[i]);
|
|
1403
|
-
if (c === "*") {
|
|
1404
|
-
if (pat[i + 1] === "*") {
|
|
1405
|
-
re += ".*";
|
|
1406
|
-
i += 2;
|
|
1407
|
-
if (pat[i] === "/") i++;
|
|
1408
|
-
} else {
|
|
1409
|
-
re += "[^/\\\\]*";
|
|
1410
|
-
i++;
|
|
1411
|
-
}
|
|
1412
|
-
} else if (c === "?") {
|
|
1413
|
-
re += "[^/\\\\]";
|
|
1414
|
-
i++;
|
|
1415
|
-
} else if (c === "[") {
|
|
1416
|
-
let cls = "[";
|
|
1417
|
-
i++;
|
|
1418
|
-
if (pat[i] === "!" || pat[i] === "^") {
|
|
1419
|
-
cls += "^";
|
|
1420
|
-
i++;
|
|
1421
|
-
}
|
|
1422
|
-
while (i < pat.length && pat[i] !== "]") {
|
|
1423
|
-
const ch = pat[i] ?? "";
|
|
1424
|
-
if (ch === "\\") cls += "\\\\";
|
|
1425
|
-
else if (ch === "]" || ch === "^") cls += `\\${ch}`;
|
|
1426
|
-
else cls += ch;
|
|
1427
|
-
i++;
|
|
1428
|
-
}
|
|
1429
|
-
cls += "]";
|
|
1430
|
-
re += cls;
|
|
1431
|
-
i++;
|
|
1432
|
-
} else {
|
|
1433
|
-
re += c.replace(/[.+^${}()|\\]/g, "\\$&");
|
|
1434
|
-
i++;
|
|
1435
|
-
}
|
|
1409
|
+
function cloneProvider(p) {
|
|
1410
|
+
const models = {};
|
|
1411
|
+
for (const [mid, m] of Object.entries(p.models ?? {})) {
|
|
1412
|
+
models[mid] = { ...m };
|
|
1436
1413
|
}
|
|
1437
|
-
return
|
|
1414
|
+
return { ...p, models };
|
|
1438
1415
|
}
|
|
1439
|
-
function
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1416
|
+
function stripUndefined(obj) {
|
|
1417
|
+
const out = {};
|
|
1418
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
1419
|
+
if (v !== void 0) out[k] = v;
|
|
1420
|
+
}
|
|
1421
|
+
return out;
|
|
1444
1422
|
}
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
const
|
|
1449
|
-
const
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
return;
|
|
1468
|
-
}
|
|
1469
|
-
const before = pat.slice(0, firstGlob);
|
|
1470
|
-
const rest = pat.slice(firstGlob);
|
|
1471
|
-
if (before.endsWith("**")) {
|
|
1472
|
-
await walk2(dir, rest);
|
|
1473
|
-
for (const e of entries) {
|
|
1474
|
-
const full = `${dir}${SEP}${e}`;
|
|
1475
|
-
try {
|
|
1476
|
-
const stat3 = await fs.stat(full);
|
|
1477
|
-
if (stat3.isDirectory()) await walk2(full, rest);
|
|
1478
|
-
} catch {
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
|
-
} else if (before === "") {
|
|
1482
|
-
const re = globToRegex(rest);
|
|
1483
|
-
for (const e of entries) {
|
|
1484
|
-
if (re.test(e)) {
|
|
1485
|
-
const full = `${dir}${SEP}${e}`;
|
|
1486
|
-
results.add(abs ? resolve(full) : full);
|
|
1423
|
+
|
|
1424
|
+
// src/utils/message-invariants.ts
|
|
1425
|
+
function repairToolUseAdjacency(messages) {
|
|
1426
|
+
const removedToolUses = [];
|
|
1427
|
+
const removedToolResults = [];
|
|
1428
|
+
let removedMessages = 0;
|
|
1429
|
+
let changed = false;
|
|
1430
|
+
const out = [];
|
|
1431
|
+
for (let i = 0; i < messages.length; i++) {
|
|
1432
|
+
const original = expectDefined(messages[i]);
|
|
1433
|
+
let msg = original;
|
|
1434
|
+
if (hasToolUse(msg)) {
|
|
1435
|
+
const nextIds = toolResultIds(messages[i + 1]);
|
|
1436
|
+
const filtered = mapContent(msg, (blocks) => {
|
|
1437
|
+
const next = [];
|
|
1438
|
+
for (const block of blocks) {
|
|
1439
|
+
if (block.type === "tool_use" && !nextIds.has(block.id)) {
|
|
1440
|
+
removedToolUses.push(block.id);
|
|
1441
|
+
changed = true;
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1444
|
+
next.push(block);
|
|
1487
1445
|
}
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1446
|
+
return next;
|
|
1447
|
+
});
|
|
1448
|
+
msg = filtered ?? msg;
|
|
1449
|
+
}
|
|
1450
|
+
if (hasToolResult(msg)) {
|
|
1451
|
+
const allowed = toolUseIds(out[out.length - 1]);
|
|
1452
|
+
const filtered = mapContent(msg, (blocks) => {
|
|
1453
|
+
const next = [];
|
|
1454
|
+
for (const block of blocks) {
|
|
1455
|
+
if (block.type === "tool_result" && !allowed.has(block.tool_use_id)) {
|
|
1456
|
+
removedToolResults.push(block.tool_use_id);
|
|
1457
|
+
changed = true;
|
|
1458
|
+
continue;
|
|
1459
|
+
}
|
|
1460
|
+
next.push(block);
|
|
1497
1461
|
}
|
|
1462
|
+
return next;
|
|
1463
|
+
});
|
|
1464
|
+
msg = filtered ?? msg;
|
|
1465
|
+
}
|
|
1466
|
+
if (isEmptyMessage(msg)) {
|
|
1467
|
+
removedMessages++;
|
|
1468
|
+
changed = true;
|
|
1469
|
+
continue;
|
|
1470
|
+
}
|
|
1471
|
+
out.push(msg);
|
|
1472
|
+
}
|
|
1473
|
+
return {
|
|
1474
|
+
messages: changed ? out : messages,
|
|
1475
|
+
report: { changed, removedToolUses, removedToolResults, removedMessages }
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
function hasToolUse(msg) {
|
|
1479
|
+
return contentBlocks(msg).some((b) => b.type === "tool_use");
|
|
1480
|
+
}
|
|
1481
|
+
function hasToolResult(msg) {
|
|
1482
|
+
return contentBlocks(msg).some((b) => b.type === "tool_result");
|
|
1483
|
+
}
|
|
1484
|
+
function toolUseIds(msg) {
|
|
1485
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1486
|
+
if (!msg || msg.role !== "assistant") return ids;
|
|
1487
|
+
for (const block of contentBlocks(msg)) {
|
|
1488
|
+
if (block.type === "tool_use") ids.add(block.id);
|
|
1489
|
+
}
|
|
1490
|
+
return ids;
|
|
1491
|
+
}
|
|
1492
|
+
function toolResultIds(msg) {
|
|
1493
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1494
|
+
if (!msg || msg.role !== "user") return ids;
|
|
1495
|
+
for (const block of contentBlocks(msg)) {
|
|
1496
|
+
if (block.type === "tool_result") ids.add(block.tool_use_id);
|
|
1497
|
+
}
|
|
1498
|
+
return ids;
|
|
1499
|
+
}
|
|
1500
|
+
function contentBlocks(msg) {
|
|
1501
|
+
return msg && Array.isArray(msg.content) ? msg.content : [];
|
|
1502
|
+
}
|
|
1503
|
+
function mapContent(msg, fn) {
|
|
1504
|
+
if (!Array.isArray(msg.content)) return msg;
|
|
1505
|
+
const next = fn(msg.content);
|
|
1506
|
+
if (next.length === msg.content.length && next.every((b, idx) => b === msg.content[idx])) {
|
|
1507
|
+
return msg;
|
|
1508
|
+
}
|
|
1509
|
+
return { ...msg, content: next };
|
|
1510
|
+
}
|
|
1511
|
+
function isEmptyMessage(msg) {
|
|
1512
|
+
if (typeof msg.content === "string") return msg.content.trim().length === 0;
|
|
1513
|
+
return msg.content.length === 0;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
// src/utils/newline-normalize.ts
|
|
1517
|
+
function detectNewlineStyle(text) {
|
|
1518
|
+
let lf = 0;
|
|
1519
|
+
let crlf = 0;
|
|
1520
|
+
let cr = 0;
|
|
1521
|
+
for (let i = 0; i < text.length; i++) {
|
|
1522
|
+
const c = text.charCodeAt(i);
|
|
1523
|
+
if (c === 13) {
|
|
1524
|
+
if (text.charCodeAt(i + 1) === 10) {
|
|
1525
|
+
crlf++;
|
|
1526
|
+
i++;
|
|
1527
|
+
} else {
|
|
1528
|
+
cr++;
|
|
1498
1529
|
}
|
|
1530
|
+
} else if (c === 10) {
|
|
1531
|
+
lf++;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
if (crlf > lf && crlf > cr) return "crlf";
|
|
1535
|
+
if (cr > lf && cr > crlf) return "cr";
|
|
1536
|
+
return "lf";
|
|
1537
|
+
}
|
|
1538
|
+
function toStyle(text, style) {
|
|
1539
|
+
const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
1540
|
+
if (style === "lf") return normalized;
|
|
1541
|
+
if (style === "crlf") return normalized.replace(/\n/g, "\r\n");
|
|
1542
|
+
return normalized.replace(/\n/g, "\r");
|
|
1543
|
+
}
|
|
1544
|
+
function normalizeToLf(text) {
|
|
1545
|
+
return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
// src/utils/regex-guard.ts
|
|
1549
|
+
var MAX_PATTERN_LEN = 512;
|
|
1550
|
+
var DANGEROUS_PATTERNS = [
|
|
1551
|
+
/(\([^)]*[+*][^)]*\))[+*]/,
|
|
1552
|
+
// (a+)+, (.*)+, etc
|
|
1553
|
+
/(\(\?:[^)]*[+*][^)]*\))[+*]/
|
|
1554
|
+
// same, with non-capturing group
|
|
1555
|
+
];
|
|
1556
|
+
function compileUserRegex(pattern, flags) {
|
|
1557
|
+
if (typeof pattern !== "string") {
|
|
1558
|
+
return { ok: false, reason: "pattern must be a string" };
|
|
1559
|
+
}
|
|
1560
|
+
if (pattern.length === 0) {
|
|
1561
|
+
return { ok: false, reason: "pattern is empty" };
|
|
1562
|
+
}
|
|
1563
|
+
if (pattern.length > MAX_PATTERN_LEN) {
|
|
1564
|
+
return { ok: false, reason: `pattern exceeds ${MAX_PATTERN_LEN} characters` };
|
|
1565
|
+
}
|
|
1566
|
+
for (const rx of DANGEROUS_PATTERNS) {
|
|
1567
|
+
if (rx.test(pattern)) {
|
|
1568
|
+
return {
|
|
1569
|
+
ok: false,
|
|
1570
|
+
reason: "pattern looks vulnerable to catastrophic backtracking \u2014 rewrite without nested quantifiers"
|
|
1571
|
+
};
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
try {
|
|
1575
|
+
return { ok: true, regex: new RegExp(pattern, flags) };
|
|
1576
|
+
} catch (err) {
|
|
1577
|
+
return {
|
|
1578
|
+
ok: false,
|
|
1579
|
+
reason: err instanceof Error ? err.message : "invalid regex"
|
|
1580
|
+
};
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
// src/utils/safe-json.ts
|
|
1585
|
+
function safeParse(input, maxBytes = 5e6) {
|
|
1586
|
+
if (input.length > maxBytes) {
|
|
1587
|
+
return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
|
|
1588
|
+
}
|
|
1589
|
+
try {
|
|
1590
|
+
return { ok: true, value: JSON.parse(input) };
|
|
1591
|
+
} catch (err) {
|
|
1592
|
+
return {
|
|
1593
|
+
ok: false,
|
|
1594
|
+
error: toErrorMessage(err)
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
function safeStringify(value, pretty = false) {
|
|
1599
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
1600
|
+
const replacer = (_k, v) => {
|
|
1601
|
+
if (typeof v === "bigint") return v.toString();
|
|
1602
|
+
if (v instanceof Error) {
|
|
1603
|
+
return { name: v.name, message: v.message, stack: v.stack };
|
|
1604
|
+
}
|
|
1605
|
+
if (typeof v === "object" && v !== null) {
|
|
1606
|
+
if (seen.has(v)) return "[Circular]";
|
|
1607
|
+
seen.add(v);
|
|
1608
|
+
}
|
|
1609
|
+
return v;
|
|
1610
|
+
};
|
|
1611
|
+
try {
|
|
1612
|
+
return JSON.stringify(value, replacer, pretty ? 2 : void 0) ?? "null";
|
|
1613
|
+
} catch (err) {
|
|
1614
|
+
return JSON.stringify({
|
|
1615
|
+
__serialization_error: toErrorMessage(err)
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
function sanitizeJsonString(s) {
|
|
1620
|
+
let out = s.trim();
|
|
1621
|
+
out = stripSingleLineComments(out);
|
|
1622
|
+
out = out.replace(/,(\s*[}\]])/g, "$1");
|
|
1623
|
+
out = escapeControlCharsInStrings(out);
|
|
1624
|
+
try {
|
|
1625
|
+
JSON.parse(out);
|
|
1626
|
+
return out;
|
|
1627
|
+
} catch {
|
|
1628
|
+
return null;
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
function escapeControlCharsInStrings(s) {
|
|
1632
|
+
let inString = false;
|
|
1633
|
+
let out = "";
|
|
1634
|
+
for (let i = 0; i < s.length; i++) {
|
|
1635
|
+
const c = s.charAt(i);
|
|
1636
|
+
if (c === '"' && (i === 0 || s[i - 1] !== "\\")) {
|
|
1637
|
+
inString = !inString;
|
|
1638
|
+
out += c;
|
|
1639
|
+
continue;
|
|
1640
|
+
}
|
|
1641
|
+
const code = c.charCodeAt(0);
|
|
1642
|
+
if (inString && code < 32) {
|
|
1643
|
+
switch (c) {
|
|
1644
|
+
case "\n":
|
|
1645
|
+
out += "\\n";
|
|
1646
|
+
break;
|
|
1647
|
+
case "\r":
|
|
1648
|
+
out += "\\r";
|
|
1649
|
+
break;
|
|
1650
|
+
case " ":
|
|
1651
|
+
out += "\\t";
|
|
1652
|
+
break;
|
|
1653
|
+
case "\b":
|
|
1654
|
+
out += "\\b";
|
|
1655
|
+
break;
|
|
1656
|
+
case "\f":
|
|
1657
|
+
out += "\\f";
|
|
1658
|
+
break;
|
|
1659
|
+
default:
|
|
1660
|
+
out += `\\u${code.toString(16).padStart(4, "0")}`;
|
|
1661
|
+
}
|
|
1662
|
+
continue;
|
|
1663
|
+
}
|
|
1664
|
+
out += c;
|
|
1665
|
+
}
|
|
1666
|
+
return out;
|
|
1667
|
+
}
|
|
1668
|
+
function stripSingleLineComments(s) {
|
|
1669
|
+
let inString = false;
|
|
1670
|
+
const chars = [];
|
|
1671
|
+
let i = 0;
|
|
1672
|
+
while (i < s.length) {
|
|
1673
|
+
const c = s.charAt(i);
|
|
1674
|
+
if (c === '"' && (i === 0 || s.charAt(i - 1) !== "\\")) {
|
|
1675
|
+
inString = !inString;
|
|
1676
|
+
chars.push(c);
|
|
1677
|
+
} else if (c === "/" && s.charAt(i + 1) === "/" && !inString) {
|
|
1678
|
+
while (i < s.length && s.charAt(i) !== "\n") i++;
|
|
1679
|
+
} else {
|
|
1680
|
+
chars.push(c);
|
|
1681
|
+
}
|
|
1682
|
+
i++;
|
|
1683
|
+
}
|
|
1684
|
+
return chars.join("");
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
// src/utils/sleep.ts
|
|
1688
|
+
function sleep(ms) {
|
|
1689
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
// src/utils/string.ts
|
|
1693
|
+
function truncate(s, max) {
|
|
1694
|
+
return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
// src/utils/task-format.ts
|
|
1698
|
+
function computeTaskItemProgress(tasks) {
|
|
1699
|
+
let completed = 0;
|
|
1700
|
+
let pending = 0;
|
|
1701
|
+
let inProgress = 0;
|
|
1702
|
+
let blocked = 0;
|
|
1703
|
+
let failed = 0;
|
|
1704
|
+
let review = 0;
|
|
1705
|
+
let estimatedHours = 0;
|
|
1706
|
+
const actualHours = 0;
|
|
1707
|
+
for (const t of tasks) {
|
|
1708
|
+
switch (t.status) {
|
|
1709
|
+
case "completed":
|
|
1710
|
+
completed++;
|
|
1711
|
+
break;
|
|
1712
|
+
case "pending":
|
|
1713
|
+
pending++;
|
|
1714
|
+
break;
|
|
1715
|
+
case "in_progress":
|
|
1716
|
+
inProgress++;
|
|
1717
|
+
break;
|
|
1718
|
+
case "blocked":
|
|
1719
|
+
blocked++;
|
|
1720
|
+
break;
|
|
1721
|
+
case "failed":
|
|
1722
|
+
failed++;
|
|
1723
|
+
break;
|
|
1724
|
+
case "review":
|
|
1725
|
+
review++;
|
|
1726
|
+
break;
|
|
1727
|
+
}
|
|
1728
|
+
estimatedHours += t.estimateHours ?? 0;
|
|
1729
|
+
}
|
|
1730
|
+
return {
|
|
1731
|
+
total: tasks.length,
|
|
1732
|
+
pending,
|
|
1733
|
+
inProgress,
|
|
1734
|
+
blocked,
|
|
1735
|
+
failed,
|
|
1736
|
+
review,
|
|
1737
|
+
completed,
|
|
1738
|
+
percentComplete: tasks.length > 0 ? Math.round(completed / tasks.length * 100) : 0,
|
|
1739
|
+
estimatedHours,
|
|
1740
|
+
actualHours
|
|
1741
|
+
};
|
|
1742
|
+
}
|
|
1743
|
+
var STATUS_ICON = {
|
|
1744
|
+
pending: "\u25CB",
|
|
1745
|
+
in_progress: "\u25D0",
|
|
1746
|
+
blocked: "\u2298",
|
|
1747
|
+
failed: "\u2717",
|
|
1748
|
+
review: "\u25D1",
|
|
1749
|
+
completed: "\u25CF"
|
|
1750
|
+
};
|
|
1751
|
+
var PRIORITY_ICON = {
|
|
1752
|
+
critical: "\u{1F534}",
|
|
1753
|
+
high: "\u{1F7E0}",
|
|
1754
|
+
medium: "\u{1F7E1}",
|
|
1755
|
+
low: "\u{1F7E2}"
|
|
1756
|
+
};
|
|
1757
|
+
var TYPE_ICON = {
|
|
1758
|
+
feature: "\u26A1",
|
|
1759
|
+
bugfix: "\u{1F41B}",
|
|
1760
|
+
refactor: "\u267B\uFE0F",
|
|
1761
|
+
docs: "\u{1F4DD}",
|
|
1762
|
+
test: "\u{1F9EA}",
|
|
1763
|
+
chore: "\u{1F527}"
|
|
1764
|
+
};
|
|
1765
|
+
function formatTaskProgress(tasks) {
|
|
1766
|
+
const p = computeTaskItemProgress(tasks);
|
|
1767
|
+
if (p.total === 0) return "No tasks.";
|
|
1768
|
+
const barWidth = 24;
|
|
1769
|
+
const filled = Math.round(p.percentComplete / 100 * barWidth);
|
|
1770
|
+
const empty = barWidth - filled;
|
|
1771
|
+
const bar = "\u2588".repeat(filled) + "\u2591".repeat(empty);
|
|
1772
|
+
return [
|
|
1773
|
+
`${color.bold("Tasks")} [${bar}] ${p.percentComplete}%`,
|
|
1774
|
+
` ${color.green("\u25CF")} ${p.completed} done \u2502 ${color.yellow("\u25D0")} ${p.inProgress} active \u2502 ${color.dim("\u25CB")} ${p.pending} pending \u2502 \u2298 ${p.blocked} blocked \u2502 \u2717 ${p.failed} failed`,
|
|
1775
|
+
p.estimatedHours > 0 ? ` ${color.dim(`est. ${p.estimatedHours}h`)}` : ""
|
|
1776
|
+
].filter(Boolean).join("\n");
|
|
1777
|
+
}
|
|
1778
|
+
function formatTaskList(tasks) {
|
|
1779
|
+
if (tasks.length === 0) return "No tasks.";
|
|
1780
|
+
const order = ["in_progress", "blocked", "review", "pending", "failed", "completed"];
|
|
1781
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1782
|
+
for (const t of tasks) {
|
|
1783
|
+
const list = groups.get(t.status) ?? [];
|
|
1784
|
+
list.push(t);
|
|
1785
|
+
groups.set(t.status, list);
|
|
1786
|
+
}
|
|
1787
|
+
const lines = [];
|
|
1788
|
+
lines.push(color.dim(`Tasks (${tasks.length} total):`));
|
|
1789
|
+
for (const status of order) {
|
|
1790
|
+
const group = groups.get(status);
|
|
1791
|
+
if (!group || group.length === 0) continue;
|
|
1792
|
+
const icon = STATUS_ICON[status];
|
|
1793
|
+
lines.push(` ${icon} ${status.toUpperCase()} (${group.length})`);
|
|
1794
|
+
for (const t of group) {
|
|
1795
|
+
const prio = PRIORITY_ICON[t.priority];
|
|
1796
|
+
const type = TYPE_ICON[t.type];
|
|
1797
|
+
const deps = t.dependsOn && t.dependsOn.length > 0 ? ` ${color.dim("\u2190")} ${color.dim(t.dependsOn.map((d) => d.slice(0, 8)).join(", "))}` : "";
|
|
1798
|
+
const who = t.assignee ? ` ${color.dim(`@${t.assignee}`)}` : "";
|
|
1799
|
+
const hrs = t.estimateHours ? ` ${color.dim(`${t.estimateHours}h`)}` : "";
|
|
1800
|
+
lines.push(` ${type} ${prio} ${t.title}${deps}${who}${hrs}`);
|
|
1499
1801
|
}
|
|
1500
1802
|
}
|
|
1501
|
-
|
|
1502
|
-
return [...results];
|
|
1803
|
+
return lines.join("\n");
|
|
1503
1804
|
}
|
|
1504
1805
|
|
|
1505
|
-
// src/utils/
|
|
1506
|
-
function
|
|
1507
|
-
if (
|
|
1508
|
-
|
|
1509
|
-
|
|
1806
|
+
// src/utils/todos-format.ts
|
|
1807
|
+
function formatTodosList(todos) {
|
|
1808
|
+
if (todos.length === 0) return "No todos.";
|
|
1809
|
+
const lines = [];
|
|
1810
|
+
const done = todos.filter((t) => t.status === "completed").length;
|
|
1811
|
+
lines.push(color.dim(`Todos (${done}/${todos.length} done):`));
|
|
1812
|
+
todos.forEach((t, i) => {
|
|
1813
|
+
const mark = t.status === "completed" ? color.green("[x]") : t.status === "in_progress" ? color.yellow("[~]") : color.dim("[ ]");
|
|
1814
|
+
const text = t.status === "in_progress" && t.activeForm ? t.activeForm : t.content;
|
|
1815
|
+
const label = t.status === "completed" ? color.dim(text) : text;
|
|
1816
|
+
lines.push(` ${color.dim(String(i + 1).padStart(2))}. ${mark} ${label}`);
|
|
1817
|
+
});
|
|
1818
|
+
return lines.join("\n");
|
|
1510
1819
|
}
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
} else if (ch === "}" || ch === "]") {
|
|
1552
|
-
stack.pop();
|
|
1553
|
-
prevSig = ch;
|
|
1820
|
+
|
|
1821
|
+
// src/utils/tool-wire-compact.ts
|
|
1822
|
+
var TOOL_DESCRIPTION_MAX_CHARS = 640;
|
|
1823
|
+
var SCHEMA_DESCRIPTION_MAX_CHARS = 180;
|
|
1824
|
+
var compactCache = /* @__PURE__ */ new WeakMap();
|
|
1825
|
+
function compactToolDefinitionForWire(tool, opts = {}) {
|
|
1826
|
+
const useDefaultOptions = opts.descriptionMaxChars === void 0 && opts.schemaDescriptionMaxChars === void 0;
|
|
1827
|
+
if (useDefaultOptions && typeof tool === "object" && tool !== null) {
|
|
1828
|
+
const cached = compactCache.get(tool);
|
|
1829
|
+
if (cached) return cached;
|
|
1830
|
+
}
|
|
1831
|
+
const compact = {
|
|
1832
|
+
name: tool.name,
|
|
1833
|
+
description: compactDescription(
|
|
1834
|
+
tool.description ?? "",
|
|
1835
|
+
opts.descriptionMaxChars ?? TOOL_DESCRIPTION_MAX_CHARS
|
|
1836
|
+
),
|
|
1837
|
+
inputSchema: compactSchemaDescriptions(
|
|
1838
|
+
tool.inputSchema,
|
|
1839
|
+
opts.schemaDescriptionMaxChars ?? SCHEMA_DESCRIPTION_MAX_CHARS
|
|
1840
|
+
)
|
|
1841
|
+
};
|
|
1842
|
+
if (useDefaultOptions && typeof tool === "object" && tool !== null) {
|
|
1843
|
+
compactCache.set(tool, compact);
|
|
1844
|
+
}
|
|
1845
|
+
return compact;
|
|
1846
|
+
}
|
|
1847
|
+
function compactSchemaDescriptions(schema, maxDescriptionChars = SCHEMA_DESCRIPTION_MAX_CHARS) {
|
|
1848
|
+
const compact = compactSchemaNode(schema, maxDescriptionChars);
|
|
1849
|
+
return isRecord(compact) ? compact : { type: "object", properties: {} };
|
|
1850
|
+
}
|
|
1851
|
+
function compactSchemaNode(node, maxDescriptionChars) {
|
|
1852
|
+
if (Array.isArray(node)) {
|
|
1853
|
+
return node.map((item) => compactSchemaNode(item, maxDescriptionChars));
|
|
1854
|
+
}
|
|
1855
|
+
if (!isRecord(node)) return node;
|
|
1856
|
+
const out = {};
|
|
1857
|
+
for (const [key, value] of Object.entries(node)) {
|
|
1858
|
+
if (key === "description" && typeof value === "string") {
|
|
1859
|
+
out[key] = compactDescription(value, maxDescriptionChars);
|
|
1554
1860
|
} else {
|
|
1555
|
-
|
|
1861
|
+
out[key] = compactSchemaNode(value, maxDescriptionChars);
|
|
1556
1862
|
}
|
|
1557
1863
|
}
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1864
|
+
return out;
|
|
1865
|
+
}
|
|
1866
|
+
function compactDescription(text, maxChars) {
|
|
1867
|
+
const normalized = text.replace(/\s+/g, " ").trim();
|
|
1868
|
+
if (normalized.length <= maxChars) return normalized;
|
|
1869
|
+
if (maxChars <= 20) return normalized.slice(0, maxChars);
|
|
1870
|
+
const hardLimit = maxChars - 12;
|
|
1871
|
+
const boundary = findSemanticBoundary(normalized, hardLimit);
|
|
1872
|
+
const head = normalized.slice(0, boundary > 0 ? boundary : hardLimit).trimEnd();
|
|
1873
|
+
return `${head} ...`;
|
|
1874
|
+
}
|
|
1875
|
+
function findSemanticBoundary(text, limit) {
|
|
1876
|
+
const punctuation = Math.max(
|
|
1877
|
+
text.lastIndexOf(". ", limit),
|
|
1878
|
+
text.lastIndexOf("; ", limit),
|
|
1879
|
+
text.lastIndexOf(": ", limit)
|
|
1880
|
+
);
|
|
1881
|
+
if (punctuation >= Math.floor(limit * 0.45)) return punctuation + 1;
|
|
1882
|
+
const comma = text.lastIndexOf(", ", limit);
|
|
1883
|
+
if (comma >= Math.floor(limit * 0.6)) return comma + 1;
|
|
1884
|
+
const space = text.lastIndexOf(" ", limit);
|
|
1885
|
+
return space >= Math.floor(limit * 0.6) ? space : limit;
|
|
1886
|
+
}
|
|
1887
|
+
function isRecord(value) {
|
|
1888
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
// src/utils/token-estimate.ts
|
|
1892
|
+
var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
|
|
1893
|
+
var CAL_ALPHA = 0.3;
|
|
1894
|
+
var CALIBRATION_GLOBAL_KEY = "__global__";
|
|
1895
|
+
var _cals = /* @__PURE__ */ new Map();
|
|
1896
|
+
function calState(key) {
|
|
1897
|
+
let state = _cals.get(key);
|
|
1898
|
+
if (!state) {
|
|
1899
|
+
state = { ratio: 1, count: 0, prevEst: 0 };
|
|
1900
|
+
_cals.set(key, state);
|
|
1570
1901
|
}
|
|
1571
|
-
|
|
1572
|
-
|
|
1902
|
+
return state;
|
|
1903
|
+
}
|
|
1904
|
+
var MIN_SAMPLES_FOR_CALIBRATION = 3;
|
|
1905
|
+
var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
|
|
1906
|
+
var ESTIMATE_CACHE_MAX_SIZE = 1e4;
|
|
1907
|
+
function getCachedEstimate(key, compute) {
|
|
1908
|
+
const existing = ESTIMATE_CACHE.get(key);
|
|
1909
|
+
if (existing !== void 0) return existing;
|
|
1910
|
+
if (ESTIMATE_CACHE.size >= ESTIMATE_CACHE_MAX_SIZE) {
|
|
1911
|
+
for (const k of ESTIMATE_CACHE.keys()) {
|
|
1912
|
+
if (ESTIMATE_CACHE.size <= Math.floor(ESTIMATE_CACHE_MAX_SIZE / 2)) break;
|
|
1913
|
+
ESTIMATE_CACHE.delete(k);
|
|
1914
|
+
}
|
|
1573
1915
|
}
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1916
|
+
const estimate = compute(key);
|
|
1917
|
+
ESTIMATE_CACHE.set(key, estimate);
|
|
1918
|
+
return estimate;
|
|
1919
|
+
}
|
|
1920
|
+
function estimateToolInputTokens(input) {
|
|
1921
|
+
if (typeof input === "string") return RoughTokenEstimate(input);
|
|
1922
|
+
if (input === null || typeof input !== "object") {
|
|
1923
|
+
return RoughTokenEstimate(String(input));
|
|
1577
1924
|
}
|
|
1578
|
-
return
|
|
1925
|
+
return getCachedEstimate(JSON.stringify(input), (key) => RoughTokenEstimate(key));
|
|
1579
1926
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
if (str[str.length - 2] !== "\\" || last === void 0) return false;
|
|
1584
|
-
if (VALID_ESCAPE.has(last)) return false;
|
|
1585
|
-
let backslashes = 0;
|
|
1586
|
-
for (let k = str.length - 2; k >= 0 && str[k] === "\\"; k--) backslashes++;
|
|
1587
|
-
return backslashes % 2 === 1;
|
|
1927
|
+
function estimateToolResultTokens(content) {
|
|
1928
|
+
if (typeof content === "string") return RoughTokenEstimate(content);
|
|
1929
|
+
return getCachedEstimate(JSON.stringify(content), (key) => RoughTokenEstimate(key));
|
|
1588
1930
|
}
|
|
1589
|
-
function
|
|
1590
|
-
|
|
1591
|
-
return { ok: true, value: JSON.parse(s) };
|
|
1592
|
-
} catch {
|
|
1593
|
-
return { ok: false };
|
|
1594
|
-
}
|
|
1931
|
+
function estimateTextTokens(text) {
|
|
1932
|
+
return RoughTokenEstimate(text);
|
|
1595
1933
|
}
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
const
|
|
1600
|
-
|
|
1601
|
-
|
|
1934
|
+
function computeMessageTokens(msg) {
|
|
1935
|
+
if (typeof msg.content === "string") return estimateTextTokens(msg.content);
|
|
1936
|
+
let total = 0;
|
|
1937
|
+
for (const b of msg.content) {
|
|
1938
|
+
if (b.type === "text") total += estimateTextTokens(b.text);
|
|
1939
|
+
else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
|
|
1940
|
+
else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
|
|
1941
|
+
else total += RoughTokenEstimate(JSON.stringify(b));
|
|
1602
1942
|
}
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1943
|
+
return total;
|
|
1944
|
+
}
|
|
1945
|
+
function estimateMessageTokens(messages) {
|
|
1946
|
+
let total = 0;
|
|
1947
|
+
for (const m of messages) {
|
|
1948
|
+
if (typeof m._estTokens === "number" && m._estTokens > 0) {
|
|
1949
|
+
total += m._estTokens;
|
|
1950
|
+
continue;
|
|
1951
|
+
}
|
|
1952
|
+
total += computeMessageTokens(m);
|
|
1606
1953
|
}
|
|
1607
|
-
return
|
|
1954
|
+
return total;
|
|
1608
1955
|
}
|
|
1609
|
-
function
|
|
1610
|
-
const
|
|
1611
|
-
|
|
1612
|
-
|
|
1956
|
+
function estimateToolDefTokens(tool) {
|
|
1957
|
+
const cached = tool._estDefTokens;
|
|
1958
|
+
if (typeof cached === "number" && cached > 0) return cached;
|
|
1959
|
+
const compact = compactToolDefinitionForWire(tool);
|
|
1960
|
+
return RoughTokenEstimate(tool.name) + RoughTokenEstimate(compact.description) + RoughTokenEstimate(JSON.stringify(compact.inputSchema));
|
|
1961
|
+
}
|
|
1962
|
+
function estimateRequestTokens(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
|
|
1963
|
+
let messagesTokens = 0;
|
|
1964
|
+
if (typeof messages === "string") {
|
|
1965
|
+
messagesTokens = RoughTokenEstimate(messages);
|
|
1966
|
+
} else if (Array.isArray(messages)) {
|
|
1967
|
+
for (const m of messages) {
|
|
1968
|
+
if (typeof m === "object" && m !== null && "content" in m) {
|
|
1969
|
+
const cached = m._estTokens;
|
|
1970
|
+
if (typeof cached === "number" && cached > 0) {
|
|
1971
|
+
messagesTokens += cached;
|
|
1972
|
+
continue;
|
|
1973
|
+
}
|
|
1974
|
+
const content = m.content;
|
|
1975
|
+
if (typeof content === "string") {
|
|
1976
|
+
messagesTokens += RoughTokenEstimate(content);
|
|
1977
|
+
} else if (Array.isArray(content)) {
|
|
1978
|
+
for (const b of content) {
|
|
1979
|
+
if (typeof b === "object" && b !== null) {
|
|
1980
|
+
if (b.type === "text") {
|
|
1981
|
+
messagesTokens += RoughTokenEstimate(b.text);
|
|
1982
|
+
} else {
|
|
1983
|
+
messagesTokens += RoughTokenEstimate(JSON.stringify(b));
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1613
1990
|
}
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1991
|
+
let systemTokens = 0;
|
|
1992
|
+
if (typeof systemPrompt === "string") {
|
|
1993
|
+
systemTokens = RoughTokenEstimate(systemPrompt);
|
|
1994
|
+
} else if (Array.isArray(systemPrompt)) {
|
|
1995
|
+
for (const b of systemPrompt) {
|
|
1996
|
+
if (typeof b === "object" && b !== null && b.type === "text") {
|
|
1997
|
+
systemTokens += RoughTokenEstimate(b.text);
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
let toolsTokens = 0;
|
|
2002
|
+
for (const t of tools) {
|
|
2003
|
+
toolsTokens += estimateToolDefTokens(t);
|
|
1617
2004
|
}
|
|
2005
|
+
const total = messagesTokens + systemTokens + toolsTokens;
|
|
2006
|
+
calState(calibrationKey).prevEst = total;
|
|
1618
2007
|
return {
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
name: overlay.name,
|
|
1624
|
-
npm: overlay.npm,
|
|
1625
|
-
api: overlay.api,
|
|
1626
|
-
env: overlay.env,
|
|
1627
|
-
doc: overlay.doc
|
|
1628
|
-
}),
|
|
1629
|
-
models
|
|
2008
|
+
messages: messagesTokens,
|
|
2009
|
+
systemPrompt: systemTokens,
|
|
2010
|
+
tools: toolsTokens,
|
|
2011
|
+
total
|
|
1630
2012
|
};
|
|
1631
2013
|
}
|
|
1632
|
-
function
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
2014
|
+
function recordActualUsage(actualInputTokens, estimatedInputTokens, calibrationKey = CALIBRATION_GLOBAL_KEY) {
|
|
2015
|
+
if (actualInputTokens <= 0) return;
|
|
2016
|
+
const cal = calState(calibrationKey);
|
|
2017
|
+
const est = estimatedInputTokens ?? cal.prevEst;
|
|
2018
|
+
if (est <= 0) return;
|
|
2019
|
+
const sampleRatio = actualInputTokens / est;
|
|
2020
|
+
if (cal.count === 0) {
|
|
2021
|
+
cal.ratio = sampleRatio;
|
|
2022
|
+
} else {
|
|
2023
|
+
cal.ratio = CAL_ALPHA * sampleRatio + (1 - CAL_ALPHA) * cal.ratio;
|
|
1642
2024
|
}
|
|
1643
|
-
|
|
2025
|
+
cal.ratio = Math.min(1.5, Math.max(0.5, cal.ratio));
|
|
2026
|
+
cal.count++;
|
|
1644
2027
|
}
|
|
1645
|
-
function
|
|
1646
|
-
const
|
|
1647
|
-
|
|
1648
|
-
|
|
2028
|
+
function getCalibrationState(calibrationKey = CALIBRATION_GLOBAL_KEY) {
|
|
2029
|
+
const cal = calState(calibrationKey);
|
|
2030
|
+
return {
|
|
2031
|
+
ratio: cal.ratio,
|
|
2032
|
+
count: cal.count,
|
|
2033
|
+
calibrated: cal.count >= MIN_SAMPLES_FOR_CALIBRATION
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
2036
|
+
function estimateRequestTokensCalibrated(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
|
|
2037
|
+
const result = estimateRequestTokens(messages, systemPrompt, tools, calibrationKey);
|
|
2038
|
+
const cal = calState(calibrationKey);
|
|
2039
|
+
if (cal.count >= MIN_SAMPLES_FOR_CALIBRATION) {
|
|
2040
|
+
const safeRatio = Math.min(1.5, Math.max(0.5, cal.ratio));
|
|
2041
|
+
return {
|
|
2042
|
+
messages: Math.round(result.messages * safeRatio),
|
|
2043
|
+
systemPrompt: Math.round(result.systemPrompt * safeRatio),
|
|
2044
|
+
tools: Math.round(result.tools * safeRatio),
|
|
2045
|
+
total: Math.round(result.total * safeRatio)
|
|
2046
|
+
};
|
|
1649
2047
|
}
|
|
1650
|
-
return
|
|
2048
|
+
return result;
|
|
1651
2049
|
}
|
|
1652
|
-
function
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
2050
|
+
function resetCalibration(calibrationKey) {
|
|
2051
|
+
if (calibrationKey === void 0) {
|
|
2052
|
+
_cals.clear();
|
|
2053
|
+
return;
|
|
1656
2054
|
}
|
|
1657
|
-
|
|
2055
|
+
_cals.delete(calibrationKey);
|
|
1658
2056
|
}
|
|
1659
2057
|
|
|
1660
|
-
// src/utils/
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
2058
|
+
// src/utils/tool-output-serializer.ts
|
|
2059
|
+
var DEFAULT_LIST_LIMIT = 500;
|
|
2060
|
+
var LOG_ENTRY_LIMIT = 200;
|
|
2061
|
+
var INLINE_LIMIT = 240;
|
|
2062
|
+
var GREP_FILE_LIMIT = 80;
|
|
2063
|
+
var GREP_MATCHES_PER_FILE = 3;
|
|
2064
|
+
var DIFF_INLINE_LINE_LIMIT = 260;
|
|
2065
|
+
var DIFF_HUNK_LIMIT = 8;
|
|
2066
|
+
var DIFF_HUNK_CONTEXT = 14;
|
|
2067
|
+
function createToolOutputSerializer(opts = {}) {
|
|
2068
|
+
const capBytes = opts.perIterationOutputCapBytes ?? 1e5;
|
|
2069
|
+
function serialize(value, context = {}) {
|
|
2070
|
+
if (typeof value === "string") return value;
|
|
2071
|
+
if (value === null || value === void 0) return "";
|
|
2072
|
+
if (typeof value === "object") {
|
|
2073
|
+
if (Array.isArray(value)) return value.map((item) => serialize(item)).join("\n");
|
|
2074
|
+
if (context.toolName) {
|
|
2075
|
+
const compact = renderToolObject(context.toolName, value, context.input);
|
|
2076
|
+
if (compact !== void 0) return compact;
|
|
2077
|
+
return renderGenericToolObject(context.toolName, value);
|
|
2078
|
+
}
|
|
2079
|
+
if ("text" in value) {
|
|
2080
|
+
const t = value.text;
|
|
2081
|
+
return typeof t === "string" ? t : JSON.stringify(value, null, 2);
|
|
2082
|
+
}
|
|
2083
|
+
try {
|
|
2084
|
+
return JSON.stringify(value, null, 2);
|
|
2085
|
+
} catch {
|
|
2086
|
+
return String(value);
|
|
2087
|
+
}
|
|
1666
2088
|
}
|
|
2089
|
+
return String(value);
|
|
1667
2090
|
}
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
2091
|
+
function enforceCap(text, remainingBudget) {
|
|
2092
|
+
if (remainingBudget <= 0) {
|
|
2093
|
+
return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
|
|
2094
|
+
}
|
|
2095
|
+
const textBytes = Buffer.byteLength(text, "utf8");
|
|
2096
|
+
if (textBytes <= remainingBudget) {
|
|
2097
|
+
return { text, newBudget: remainingBudget - textBytes };
|
|
2098
|
+
}
|
|
2099
|
+
const marker = `
|
|
2100
|
+
\u2026[truncated ${textBytes - remainingBudget} bytes]\u2026
|
|
2101
|
+
`;
|
|
2102
|
+
const markerBytes = Buffer.byteLength(marker, "utf8");
|
|
2103
|
+
const available = remainingBudget - markerBytes;
|
|
2104
|
+
if (available <= 0) {
|
|
2105
|
+
return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
|
|
1671
2106
|
}
|
|
2107
|
+
const half = Math.floor(available / 2);
|
|
2108
|
+
const first = text.slice(0, half);
|
|
2109
|
+
const second = text.slice(text.length - half);
|
|
2110
|
+
return { text: `${first}${marker}${second}`, newBudget: 0 };
|
|
1672
2111
|
}
|
|
1673
|
-
|
|
1674
|
-
return out;
|
|
2112
|
+
return { serialize, enforceCap, capBytes };
|
|
1675
2113
|
}
|
|
1676
|
-
function
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
2114
|
+
function renderToolObject(toolName, obj, input) {
|
|
2115
|
+
if (toolName === "read" && typeof obj["text"] === "string") {
|
|
2116
|
+
return joinSections([
|
|
2117
|
+
renderHeader(
|
|
2118
|
+
`read: ${stringFromInput(input, "path") ?? stringField(obj, "path") ?? "<unknown>"}`,
|
|
2119
|
+
{
|
|
2120
|
+
offset: numberFromInput(input, "offset"),
|
|
2121
|
+
limit: numberFromInput(input, "limit"),
|
|
2122
|
+
total_lines: obj["total_lines"],
|
|
2123
|
+
encoding: obj["encoding"],
|
|
2124
|
+
truncated: obj["truncated"],
|
|
2125
|
+
cached: obj["cached"],
|
|
2126
|
+
note: obj["note"]
|
|
2127
|
+
}
|
|
2128
|
+
),
|
|
2129
|
+
obj["text"]
|
|
2130
|
+
]);
|
|
2131
|
+
}
|
|
2132
|
+
if (toolName === "grep" && Array.isArray(obj["matches"])) {
|
|
2133
|
+
const matches = stringArrayField(obj, "matches");
|
|
2134
|
+
return joinSections([
|
|
2135
|
+
renderHeader(`grep: ${stringFromInput(input, "pattern") ?? "<pattern>"}`, {
|
|
2136
|
+
path: stringFromInput(input, "path"),
|
|
2137
|
+
glob: stringFromInput(input, "glob"),
|
|
2138
|
+
mode: stringFromInput(input, "output_mode"),
|
|
2139
|
+
count: obj["count"],
|
|
2140
|
+
shown: matches.length,
|
|
2141
|
+
truncated: obj["truncated"],
|
|
2142
|
+
used: obj["used"]
|
|
2143
|
+
}),
|
|
2144
|
+
renderGrepMatches(matches, stringFromInput(input, "output_mode"))
|
|
2145
|
+
]);
|
|
2146
|
+
}
|
|
2147
|
+
if (toolName === "patch" && Array.isArray(obj["files"])) {
|
|
2148
|
+
const files = stringArrayField(obj, "files");
|
|
2149
|
+
return joinSections([
|
|
2150
|
+
renderHeader("patch", {
|
|
2151
|
+
applied: obj["applied"],
|
|
2152
|
+
rejected: obj["rejected"],
|
|
2153
|
+
files: files.length,
|
|
2154
|
+
dry_run: obj["dry_run"]
|
|
2155
|
+
}),
|
|
2156
|
+
typeof obj["message"] === "string" ? `message:
|
|
2157
|
+
${obj["message"]}` : void 0,
|
|
2158
|
+
files.length > 0 ? `files:
|
|
2159
|
+
${renderStringList(files)}` : void 0
|
|
2160
|
+
]);
|
|
2161
|
+
}
|
|
2162
|
+
if (toolName === "glob" && Array.isArray(obj["files"])) {
|
|
2163
|
+
const files = stringArrayField(obj, "files");
|
|
2164
|
+
return joinSections([
|
|
2165
|
+
renderHeader(
|
|
2166
|
+
`${toolName}: ${stringFromInput(input, "pattern") ?? stringFromInput(input, "files") ?? stringFromInput(input, "path") ?? ""}`.trim(),
|
|
2167
|
+
{
|
|
2168
|
+
path: stringFromInput(input, "path"),
|
|
2169
|
+
files: files.length,
|
|
2170
|
+
truncated: obj["truncated"]
|
|
2171
|
+
}
|
|
2172
|
+
),
|
|
2173
|
+
renderStringList(files, "(no files)")
|
|
2174
|
+
]);
|
|
2175
|
+
}
|
|
2176
|
+
if (toolName === "tree" && typeof obj["tree"] === "string") {
|
|
2177
|
+
return joinSections([
|
|
2178
|
+
renderHeader(
|
|
2179
|
+
`tree: ${stringField(obj, "path") ?? stringFromInput(input, "path") ?? "<cwd>"}`,
|
|
2180
|
+
{
|
|
2181
|
+
total_files: obj["total_files"],
|
|
2182
|
+
total_dirs: obj["total_dirs"],
|
|
2183
|
+
truncated: obj["truncated"]
|
|
2184
|
+
}
|
|
2185
|
+
),
|
|
2186
|
+
obj["tree"]
|
|
2187
|
+
]);
|
|
2188
|
+
}
|
|
2189
|
+
if (toolName === "fetch" && typeof obj["content"] === "string") {
|
|
2190
|
+
return joinSections([
|
|
2191
|
+
renderHeader(
|
|
2192
|
+
`fetch: ${stringField(obj, "url") ?? stringFromInput(input, "url") ?? "<url>"}`,
|
|
2193
|
+
{
|
|
2194
|
+
status: obj["status"],
|
|
2195
|
+
content_type: obj["content_type"]
|
|
2196
|
+
}
|
|
2197
|
+
),
|
|
2198
|
+
obj["content"]
|
|
2199
|
+
]);
|
|
2200
|
+
}
|
|
2201
|
+
if (toolName === "replace" && Array.isArray(obj["results"])) {
|
|
2202
|
+
const results = obj["results"].filter(isRecord2);
|
|
2203
|
+
const sections = [
|
|
2204
|
+
renderHeader("replace", {
|
|
2205
|
+
files_modified: obj["files_modified"],
|
|
2206
|
+
total_replacements: obj["total_replacements"],
|
|
2207
|
+
dry_run: obj["dry_run"]
|
|
2208
|
+
})
|
|
2209
|
+
];
|
|
2210
|
+
for (const r of results.slice(0, DEFAULT_LIST_LIMIT)) {
|
|
2211
|
+
sections.push(
|
|
2212
|
+
joinSections([
|
|
2213
|
+
renderHeader(`file: ${stringField(r, "path") ?? "<unknown>"}`, {
|
|
2214
|
+
replacements: r["replacements"]
|
|
2215
|
+
}),
|
|
2216
|
+
typeof r["diff"] === "string" ? r["diff"] : void 0
|
|
2217
|
+
])
|
|
2218
|
+
);
|
|
2219
|
+
}
|
|
2220
|
+
if (results.length > DEFAULT_LIST_LIMIT) {
|
|
2221
|
+
sections.push(`[serializer omitted ${results.length - DEFAULT_LIST_LIMIT} result item(s)]`);
|
|
2222
|
+
}
|
|
2223
|
+
return joinSections(sections);
|
|
2224
|
+
}
|
|
2225
|
+
if (typeof obj["diff"] === "string") {
|
|
2226
|
+
const diff = obj["diff"];
|
|
2227
|
+
return joinSections([
|
|
2228
|
+
renderHeader(toolName, {
|
|
2229
|
+
path: obj["path"],
|
|
2230
|
+
replacements: obj["replacements"],
|
|
2231
|
+
bytes_written: obj["bytes_written"],
|
|
2232
|
+
created: obj["created"],
|
|
2233
|
+
note: obj["note"],
|
|
2234
|
+
files: Array.isArray(obj["files"]) ? obj["files"].length : void 0,
|
|
2235
|
+
truncated: obj["truncated"],
|
|
2236
|
+
mode: obj["mode"]
|
|
2237
|
+
}),
|
|
2238
|
+
compactDiff(diff)
|
|
2239
|
+
]);
|
|
2240
|
+
}
|
|
2241
|
+
if (toolName === "test" && typeof obj["output"] === "string") {
|
|
2242
|
+
return renderTestOutput(obj, input);
|
|
2243
|
+
}
|
|
2244
|
+
if ((toolName === "typecheck" || toolName === "lint" || toolName === "format") && typeof obj["output"] === "string") {
|
|
2245
|
+
return renderVerifierOutput(toolName, obj, input);
|
|
2246
|
+
}
|
|
2247
|
+
if (hasCommandOutputShape(obj)) {
|
|
2248
|
+
return renderCommandOutput(toolName, obj, input);
|
|
2249
|
+
}
|
|
2250
|
+
if (toolName === "json" && typeof obj["formatted"] === "string") {
|
|
2251
|
+
return joinSections([
|
|
2252
|
+
renderHeader("json", {
|
|
2253
|
+
type: obj["type"],
|
|
2254
|
+
keys: Array.isArray(obj["keys"]) ? obj["keys"].length : void 0,
|
|
2255
|
+
query: stringFromInput(input, "query"),
|
|
2256
|
+
error: obj["error"]
|
|
2257
|
+
}),
|
|
2258
|
+
obj["formatted"]
|
|
2259
|
+
]);
|
|
2260
|
+
}
|
|
2261
|
+
if (toolName === "logs" && Array.isArray(obj["entries"])) {
|
|
2262
|
+
const entries = obj["entries"].filter(isRecord2);
|
|
2263
|
+
const lines = entries.slice(0, LOG_ENTRY_LIMIT).map((entry) => {
|
|
2264
|
+
const ts = stringField(entry, "timestamp") ?? "";
|
|
2265
|
+
const level = stringField(entry, "level") ?? "info";
|
|
2266
|
+
const message = stringField(entry, "message") ?? "";
|
|
2267
|
+
const source = stringField(entry, "source");
|
|
2268
|
+
return [ts, level, source, message].filter(Boolean).join(" ");
|
|
2269
|
+
});
|
|
2270
|
+
if (entries.length > LOG_ENTRY_LIMIT) {
|
|
2271
|
+
lines.push(`[serializer omitted ${entries.length - LOG_ENTRY_LIMIT} log entry item(s)]`);
|
|
2272
|
+
}
|
|
2273
|
+
return joinSections([
|
|
2274
|
+
renderHeader(`logs: ${stringField(obj, "source") ?? "<source>"}`, {
|
|
2275
|
+
total: obj["total"],
|
|
2276
|
+
shown: Math.min(entries.length, LOG_ENTRY_LIMIT),
|
|
2277
|
+
truncated: obj["truncated"],
|
|
2278
|
+
stream_mode: obj["stream_mode"]
|
|
2279
|
+
}),
|
|
2280
|
+
lines.length > 0 ? lines.join("\n") : "(no log entries)"
|
|
2281
|
+
]);
|
|
2282
|
+
}
|
|
2283
|
+
if (toolName === "audit" && Array.isArray(obj["vulnerabilities"])) {
|
|
2284
|
+
const vulns = obj["vulnerabilities"].filter(isRecord2);
|
|
2285
|
+
const lines = vulns.slice(0, DEFAULT_LIST_LIMIT).map((v) => {
|
|
2286
|
+
const severity = stringField(v, "severity") ?? "unknown";
|
|
2287
|
+
const pkg = stringField(v, "package") ?? "<package>";
|
|
2288
|
+
const title = stringField(v, "title") ?? "";
|
|
2289
|
+
const url = stringField(v, "url");
|
|
2290
|
+
return [severity, pkg, title, url].filter(Boolean).join(" | ");
|
|
2291
|
+
});
|
|
2292
|
+
if (vulns.length > DEFAULT_LIST_LIMIT) {
|
|
2293
|
+
lines.push(`[serializer omitted ${vulns.length - DEFAULT_LIST_LIMIT} vulnerability item(s)]`);
|
|
2294
|
+
}
|
|
2295
|
+
return joinSections([
|
|
2296
|
+
renderHeader("audit", {
|
|
2297
|
+
exit_code: obj["exit_code"],
|
|
2298
|
+
total: obj["total"],
|
|
2299
|
+
summary: obj["summary"],
|
|
2300
|
+
truncated: obj["truncated"]
|
|
2301
|
+
}),
|
|
2302
|
+
lines.length > 0 ? lines.join("\n") : stringField(obj, "output")
|
|
2303
|
+
]);
|
|
2304
|
+
}
|
|
2305
|
+
if (toolName === "outdated" && Array.isArray(obj["packages"])) {
|
|
2306
|
+
const packages = obj["packages"].filter(isRecord2);
|
|
2307
|
+
const lines = packages.slice(0, DEFAULT_LIST_LIMIT).map(
|
|
2308
|
+
(p) => [
|
|
2309
|
+
stringField(p, "name") ?? "<package>",
|
|
2310
|
+
`current=${stringField(p, "current") ?? "unknown"}`,
|
|
2311
|
+
`wanted=${stringField(p, "wanted") ?? "unknown"}`,
|
|
2312
|
+
`latest=${stringField(p, "latest") ?? "unknown"}`,
|
|
2313
|
+
stringField(p, "type")
|
|
2314
|
+
].filter(Boolean).join(" | ")
|
|
2315
|
+
);
|
|
2316
|
+
if (packages.length > DEFAULT_LIST_LIMIT) {
|
|
2317
|
+
lines.push(`[serializer omitted ${packages.length - DEFAULT_LIST_LIMIT} package item(s)]`);
|
|
2318
|
+
}
|
|
2319
|
+
return joinSections([
|
|
2320
|
+
renderHeader("outdated", {
|
|
2321
|
+
exit_code: obj["exit_code"],
|
|
2322
|
+
total: obj["total"],
|
|
2323
|
+
truncated: obj["truncated"]
|
|
2324
|
+
}),
|
|
2325
|
+
lines.length > 0 ? lines.join("\n") : stringField(obj, "output")
|
|
2326
|
+
]);
|
|
2327
|
+
}
|
|
2328
|
+
return void 0;
|
|
2329
|
+
}
|
|
2330
|
+
function renderTestOutput(obj, input) {
|
|
2331
|
+
const exitCode = numberField(obj, "exit_code") ?? 0;
|
|
2332
|
+
const failed = numberField(obj, "failed") ?? 0;
|
|
2333
|
+
const output = stringField(obj, "output") ?? "";
|
|
2334
|
+
const header = renderHeader(`test: ${stringField(obj, "runner") ?? "runner"}`, {
|
|
2335
|
+
exit_code: obj["exit_code"],
|
|
2336
|
+
tests_run: obj["tests_run"],
|
|
2337
|
+
passed: obj["passed"],
|
|
2338
|
+
failed: obj["failed"],
|
|
2339
|
+
duration_ms: obj["duration_ms"],
|
|
2340
|
+
truncated: obj["truncated"],
|
|
2341
|
+
files: inputListSummary(input, "files"),
|
|
2342
|
+
grep: stringFromInput(input, "grep")
|
|
2343
|
+
});
|
|
2344
|
+
if (exitCode === 0 && failed === 0) {
|
|
2345
|
+
return joinSections([
|
|
2346
|
+
header,
|
|
2347
|
+
joinSections([
|
|
2348
|
+
"report:",
|
|
2349
|
+
`status=passed`,
|
|
2350
|
+
`tests_run=${obj["tests_run"] ?? 0}`,
|
|
2351
|
+
`passed=${obj["passed"] ?? 0}`,
|
|
2352
|
+
`failed=${obj["failed"] ?? 0}`,
|
|
2353
|
+
`duration_ms=${obj["duration_ms"] ?? 0}`,
|
|
2354
|
+
extractSpoolNote(output)
|
|
2355
|
+
])
|
|
2356
|
+
]);
|
|
2357
|
+
}
|
|
2358
|
+
return joinSections([
|
|
2359
|
+
header,
|
|
2360
|
+
`error_context:
|
|
2361
|
+
${compactFailureOutput(output || "(no runner output)")}`
|
|
2362
|
+
]);
|
|
2363
|
+
}
|
|
2364
|
+
function renderVerifierOutput(toolName, obj, input) {
|
|
2365
|
+
const exitCode = numberField(obj, "exit_code") ?? 0;
|
|
2366
|
+
const errors = numberField(obj, "errors") ?? 0;
|
|
2367
|
+
const warnings = numberField(obj, "warnings") ?? 0;
|
|
2368
|
+
const output = stringField(obj, "output") ?? "";
|
|
2369
|
+
const changed = numberField(obj, "files_changed") ?? 0;
|
|
2370
|
+
const header = renderHeader(toolName, {
|
|
2371
|
+
exit_code: obj["exit_code"],
|
|
2372
|
+
errors: obj["errors"],
|
|
2373
|
+
warnings: obj["warnings"],
|
|
2374
|
+
files_checked: obj["files_checked"],
|
|
2375
|
+
files_changed: obj["files_changed"],
|
|
2376
|
+
fix_applied: obj["fix_applied"],
|
|
2377
|
+
fixer: obj["fixer"],
|
|
2378
|
+
linter: obj["linter"],
|
|
2379
|
+
project: obj["project"],
|
|
2380
|
+
truncated: obj["truncated"],
|
|
2381
|
+
files: inputListSummary(input, "files"),
|
|
2382
|
+
cwd: stringFromInput(input, "cwd")
|
|
2383
|
+
});
|
|
2384
|
+
if (exitCode === 0 && errors === 0 && (toolName !== "format" || changed === 0)) {
|
|
2385
|
+
return joinSections([
|
|
2386
|
+
header,
|
|
2387
|
+
joinSections([
|
|
2388
|
+
"report:",
|
|
2389
|
+
"status=passed",
|
|
2390
|
+
`errors=${errors}`,
|
|
2391
|
+
`warnings=${warnings}`,
|
|
2392
|
+
toolName === "format" ? `files_changed=${changed}` : void 0,
|
|
2393
|
+
extractSpoolNote(output)
|
|
2394
|
+
])
|
|
2395
|
+
]);
|
|
2396
|
+
}
|
|
2397
|
+
if (exitCode === 0 && toolName === "format") {
|
|
2398
|
+
return joinSections([
|
|
2399
|
+
header,
|
|
2400
|
+
joinSections([
|
|
2401
|
+
"report:",
|
|
2402
|
+
"status=changed",
|
|
2403
|
+
`files_changed=${changed}`,
|
|
2404
|
+
extractSpoolNote(output)
|
|
2405
|
+
])
|
|
2406
|
+
]);
|
|
2407
|
+
}
|
|
2408
|
+
return joinSections([
|
|
2409
|
+
header,
|
|
2410
|
+
`error_context:
|
|
2411
|
+
${compactFailureOutput(output || "(no verifier output)")}`
|
|
2412
|
+
]);
|
|
2413
|
+
}
|
|
2414
|
+
function renderGrepMatches(matches, mode) {
|
|
2415
|
+
if (matches.length === 0) return "(no matches)";
|
|
2416
|
+
if (mode === "files_with_matches") return renderStringList(matches, "(no files)");
|
|
2417
|
+
if (mode === "count") return renderStringList(matches, "(no counts)");
|
|
2418
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2419
|
+
const passthrough = [];
|
|
2420
|
+
for (const match of matches) {
|
|
2421
|
+
const parsed = parseGrepContentLine(match);
|
|
2422
|
+
if (!parsed) {
|
|
2423
|
+
passthrough.push(match);
|
|
2424
|
+
continue;
|
|
2425
|
+
}
|
|
2426
|
+
const list = groups.get(parsed.file) ?? [];
|
|
2427
|
+
list.push(`${parsed.line}:${parsed.text}`);
|
|
2428
|
+
groups.set(parsed.file, list);
|
|
2429
|
+
}
|
|
2430
|
+
if (groups.size === 0) return renderStringList(matches, "(no matches)");
|
|
2431
|
+
const sections = [];
|
|
2432
|
+
let fileIndex = 0;
|
|
2433
|
+
for (const [file, lines] of groups) {
|
|
2434
|
+
fileIndex++;
|
|
2435
|
+
if (fileIndex > GREP_FILE_LIMIT) break;
|
|
2436
|
+
const shown = lines.slice(0, GREP_MATCHES_PER_FILE);
|
|
2437
|
+
sections.push(
|
|
2438
|
+
`${file} (${lines.length} match(es), showing ${shown.length})
|
|
2439
|
+
${shown.join("\n")}`
|
|
2440
|
+
);
|
|
1680
2441
|
}
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
if (
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
const
|
|
1697
|
-
if (
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
2442
|
+
if (groups.size > GREP_FILE_LIMIT) {
|
|
2443
|
+
sections.push(`[serializer omitted ${groups.size - GREP_FILE_LIMIT} file group(s)]`);
|
|
2444
|
+
}
|
|
2445
|
+
if (passthrough.length > 0) {
|
|
2446
|
+
sections.push(`ungrouped:
|
|
2447
|
+
${renderStringList(passthrough, "", 50)}`);
|
|
2448
|
+
}
|
|
2449
|
+
return sections.join("\n");
|
|
2450
|
+
}
|
|
2451
|
+
function parseGrepContentLine(line) {
|
|
2452
|
+
const match = /^(.+?):(\d+):(.*)$/.exec(line);
|
|
2453
|
+
if (!match?.[1] || !match[2]) return void 0;
|
|
2454
|
+
return { file: match[1], line: match[2], text: match[3] ?? "" };
|
|
2455
|
+
}
|
|
2456
|
+
function compactDiff(diff) {
|
|
2457
|
+
const lines = diff.split(/\r?\n/);
|
|
2458
|
+
if (lines.length <= DIFF_INLINE_LINE_LIMIT) return diff;
|
|
2459
|
+
const fileCount = Math.max(
|
|
2460
|
+
new Set(
|
|
2461
|
+
lines.map(
|
|
2462
|
+
(line) => /^diff --git\s+a\/(.+?)\s+b\//.exec(line)?.[1] ?? /^---\s+(.+)/.exec(line)?.[1]
|
|
2463
|
+
).filter(Boolean)
|
|
2464
|
+
).size,
|
|
2465
|
+
0
|
|
2466
|
+
);
|
|
2467
|
+
const hunks = lines.filter((line) => line.startsWith("@@")).length;
|
|
2468
|
+
const added = lines.filter((line) => line.startsWith("+") && !line.startsWith("+++")).length;
|
|
2469
|
+
const removed = lines.filter((line) => line.startsWith("-") && !line.startsWith("---")).length;
|
|
2470
|
+
const selected = /* @__PURE__ */ new Set();
|
|
2471
|
+
let hunkCount = 0;
|
|
2472
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2473
|
+
const line = lines[i] ?? "";
|
|
2474
|
+
if (line.startsWith("diff --git") || line.startsWith("--- ") || line.startsWith("+++ ")) {
|
|
2475
|
+
selected.add(i);
|
|
2476
|
+
continue;
|
|
2477
|
+
}
|
|
2478
|
+
if (!line.startsWith("@@")) continue;
|
|
2479
|
+
if (hunkCount >= DIFF_HUNK_LIMIT) continue;
|
|
2480
|
+
hunkCount++;
|
|
2481
|
+
for (let j = i; j <= Math.min(lines.length - 1, i + DIFF_HUNK_CONTEXT); j++) {
|
|
2482
|
+
selected.add(j);
|
|
2483
|
+
}
|
|
1704
2484
|
}
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
2485
|
+
if (selected.size === 0) {
|
|
2486
|
+
return joinSections([
|
|
2487
|
+
renderHeader("diff_summary", {
|
|
2488
|
+
files: fileCount,
|
|
2489
|
+
hunks,
|
|
2490
|
+
added,
|
|
2491
|
+
removed,
|
|
2492
|
+
lines: lines.length
|
|
2493
|
+
}),
|
|
2494
|
+
lines.slice(0, DIFF_INLINE_LINE_LIMIT).join("\n"),
|
|
2495
|
+
`[serializer omitted ${Math.max(0, lines.length - DIFF_INLINE_LINE_LIMIT)} diff line(s)]`
|
|
2496
|
+
]);
|
|
2497
|
+
}
|
|
2498
|
+
const excerpt = [];
|
|
2499
|
+
let previous = -1;
|
|
2500
|
+
for (const index of [...selected].sort((a, b) => a - b)) {
|
|
2501
|
+
if (index > previous + 1) {
|
|
2502
|
+
const omitted = previous === -1 ? index : index - previous - 1;
|
|
2503
|
+
excerpt.push(`[serializer omitted ${omitted} diff line(s)]`);
|
|
2504
|
+
}
|
|
2505
|
+
excerpt.push(lines[index] ?? "");
|
|
2506
|
+
previous = index;
|
|
2507
|
+
}
|
|
2508
|
+
const trailing = lines.length - previous - 1;
|
|
2509
|
+
if (trailing > 0) excerpt.push(`[serializer omitted ${trailing} trailing diff line(s)]`);
|
|
2510
|
+
return joinSections([
|
|
2511
|
+
renderHeader("diff_summary", {
|
|
2512
|
+
files: fileCount,
|
|
2513
|
+
hunks,
|
|
2514
|
+
shown_hunks: Math.min(hunks, DIFF_HUNK_LIMIT),
|
|
2515
|
+
added,
|
|
2516
|
+
removed,
|
|
2517
|
+
lines: lines.length
|
|
2518
|
+
}),
|
|
2519
|
+
excerpt.join("\n")
|
|
2520
|
+
]);
|
|
2521
|
+
}
|
|
2522
|
+
function compactFailureOutput(output) {
|
|
2523
|
+
const lines = output.split(/\r?\n/);
|
|
2524
|
+
if (lines.length <= 260) return output.trimEnd();
|
|
2525
|
+
const selected = /* @__PURE__ */ new Set();
|
|
2526
|
+
const marker = /\b(fail|failed|failure|error|exception|assertionerror|expected|received|actual|timeout|stack)\b/i;
|
|
2527
|
+
let markerHits = 0;
|
|
2528
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2529
|
+
if (!marker.test(lines[i] ?? "")) continue;
|
|
2530
|
+
markerHits++;
|
|
2531
|
+
for (let j = Math.max(0, i - 4); j <= Math.min(lines.length - 1, i + 10); j++) {
|
|
2532
|
+
selected.add(j);
|
|
1722
2533
|
}
|
|
1723
|
-
return out;
|
|
1724
|
-
};
|
|
1725
|
-
if (parts.length === 1) {
|
|
1726
|
-
const groups = parseGroups(parts[0] ?? "");
|
|
1727
|
-
if (!groups || groups.length !== 8) return null;
|
|
1728
|
-
return groups;
|
|
1729
2534
|
}
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
if (!head || !tail) return null;
|
|
1733
|
-
const fill = 8 - head.length - tail.length;
|
|
1734
|
-
if (fill < 0) return null;
|
|
1735
|
-
return [...head, ...new Array(fill).fill(0), ...tail];
|
|
1736
|
-
}
|
|
1737
|
-
async function assertNotPrivateHost(hostname) {
|
|
1738
|
-
const host = hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
|
|
1739
|
-
if (host === "localhost" || host.endsWith(".localhost")) {
|
|
1740
|
-
throw new Error("fetch: blocked localhost target");
|
|
2535
|
+
if (markerHits === 0) {
|
|
2536
|
+
return lines.slice(-220).join("\n").trimEnd();
|
|
1741
2537
|
}
|
|
1742
|
-
const
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
2538
|
+
const ordered = [...selected].sort((a, b) => a - b);
|
|
2539
|
+
const out = [];
|
|
2540
|
+
let previous = -1;
|
|
2541
|
+
for (const index of ordered) {
|
|
2542
|
+
if (index > previous + 1) {
|
|
2543
|
+
const omitted = previous === -1 ? index : index - previous - 1;
|
|
2544
|
+
out.push(`[serializer omitted ${omitted} line(s)]`);
|
|
1746
2545
|
}
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
2546
|
+
out.push(lines[index] ?? "");
|
|
2547
|
+
previous = index;
|
|
2548
|
+
}
|
|
2549
|
+
return out.join("\n").trimEnd();
|
|
2550
|
+
}
|
|
2551
|
+
function extractSpoolNote(output) {
|
|
2552
|
+
return output.split(/\r?\n/).find((line) => line.startsWith("[output truncated") && line.includes("full"));
|
|
2553
|
+
}
|
|
2554
|
+
function hasCommandOutputShape(obj) {
|
|
2555
|
+
return typeof obj["stdout"] === "string" || typeof obj["stderr"] === "string" || typeof obj["output"] === "string" || typeof obj["exitCode"] === "number" || typeof obj["exit_code"] === "number";
|
|
2556
|
+
}
|
|
2557
|
+
function renderCommandOutput(toolName, obj, input) {
|
|
2558
|
+
const command = stringField(obj, "command") ?? stringFromInput(input, "command");
|
|
2559
|
+
const args = stringArrayField(obj, "args");
|
|
2560
|
+
const commandLine = command ? [command, ...args].join(" ") : void 0;
|
|
2561
|
+
const output = stringField(obj, "output");
|
|
2562
|
+
const stdout = stringField(obj, "stdout");
|
|
2563
|
+
const stderr = stringField(obj, "stderr");
|
|
2564
|
+
return joinSections([
|
|
2565
|
+
renderHeader(commandLine ? `${toolName}: ${commandLine}` : toolName, {
|
|
2566
|
+
exit_code: obj["exit_code"] ?? obj["exitCode"],
|
|
2567
|
+
timed_out: obj["timed_out"],
|
|
2568
|
+
pid: obj["pid"],
|
|
2569
|
+
allowed: obj["allowed"],
|
|
2570
|
+
truncated: obj["truncated"],
|
|
2571
|
+
runner: obj["runner"],
|
|
2572
|
+
linter: obj["linter"],
|
|
2573
|
+
fixer: obj["fixer"],
|
|
2574
|
+
project: obj["project"],
|
|
2575
|
+
tests_run: obj["tests_run"],
|
|
2576
|
+
passed: obj["passed"],
|
|
2577
|
+
failed: obj["failed"],
|
|
2578
|
+
duration_ms: obj["duration_ms"],
|
|
2579
|
+
errors: obj["errors"],
|
|
2580
|
+
warnings: obj["warnings"],
|
|
2581
|
+
files_checked: obj["files_checked"],
|
|
2582
|
+
files_changed: obj["files_changed"],
|
|
2583
|
+
fix_applied: obj["fix_applied"]
|
|
2584
|
+
}),
|
|
2585
|
+
stringField(obj, "error") ? `error:
|
|
2586
|
+
${stringField(obj, "error")}` : void 0,
|
|
2587
|
+
output ? `output:
|
|
2588
|
+
${output}` : void 0,
|
|
2589
|
+
stdout ? `stdout:
|
|
2590
|
+
${stdout}` : void 0,
|
|
2591
|
+
stderr ? `stderr:
|
|
2592
|
+
${stderr}` : void 0
|
|
2593
|
+
]);
|
|
2594
|
+
}
|
|
2595
|
+
function renderGenericToolObject(toolName, obj) {
|
|
2596
|
+
const scalars = {};
|
|
2597
|
+
const blocks = [];
|
|
2598
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
2599
|
+
if (value === void 0) continue;
|
|
2600
|
+
if (isScalar(value)) {
|
|
2601
|
+
const inline = String(value);
|
|
2602
|
+
if (inline.length <= INLINE_LIMIT && !inline.includes("\n")) {
|
|
2603
|
+
scalars[key] = value;
|
|
2604
|
+
} else {
|
|
2605
|
+
blocks.push(`${key}:
|
|
2606
|
+
${inline}`);
|
|
2607
|
+
}
|
|
2608
|
+
continue;
|
|
1750
2609
|
}
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
}
|
|
2610
|
+
if (Array.isArray(value)) {
|
|
2611
|
+
if (value.every((item) => typeof item === "string")) {
|
|
2612
|
+
blocks.push(`${key}:
|
|
2613
|
+
${renderStringList(value)}`);
|
|
2614
|
+
} else {
|
|
2615
|
+
blocks.push(`${key}:
|
|
2616
|
+
${renderUnknownList(value)}`);
|
|
1759
2617
|
}
|
|
1760
|
-
|
|
1761
|
-
if (err instanceof Error && err.message.startsWith("fetch:")) throw err;
|
|
2618
|
+
continue;
|
|
1762
2619
|
}
|
|
2620
|
+
blocks.push(`${key}: ${clipInline(oneLineJson(value))}`);
|
|
2621
|
+
}
|
|
2622
|
+
return joinSections([renderHeader(toolName, scalars), ...blocks]);
|
|
2623
|
+
}
|
|
2624
|
+
function renderHeader(label, fields) {
|
|
2625
|
+
const parts = Object.entries(fields).filter(([, value]) => value !== void 0 && value !== null && value !== "").map(([key, value]) => `${key}=${clipInline(formatInlineValue(value))}`);
|
|
2626
|
+
return parts.length > 0 ? `${label} (${parts.join(" ")})` : label;
|
|
2627
|
+
}
|
|
2628
|
+
function renderStringList(items, empty = "", limit = DEFAULT_LIST_LIMIT) {
|
|
2629
|
+
if (items.length === 0) return empty;
|
|
2630
|
+
const shown = items.slice(0, limit);
|
|
2631
|
+
const omitted = items.length - shown.length;
|
|
2632
|
+
return [
|
|
2633
|
+
...shown,
|
|
2634
|
+
...omitted > 0 ? [`[serializer omitted ${omitted} item(s); narrow the request for more]`] : []
|
|
2635
|
+
].join("\n");
|
|
2636
|
+
}
|
|
2637
|
+
function renderUnknownList(items, limit = DEFAULT_LIST_LIMIT) {
|
|
2638
|
+
const shown = items.slice(0, limit).map((item) => clipInline(oneLineJson(item), 1e3));
|
|
2639
|
+
const omitted = items.length - shown.length;
|
|
2640
|
+
if (omitted > 0)
|
|
2641
|
+
shown.push(`[serializer omitted ${omitted} item(s); narrow the request for more]`);
|
|
2642
|
+
return shown.join("\n");
|
|
2643
|
+
}
|
|
2644
|
+
function joinSections(sections) {
|
|
2645
|
+
return sections.map((section) => typeof section === "string" ? section.trimEnd() : void 0).filter((section) => !!section).join("\n");
|
|
2646
|
+
}
|
|
2647
|
+
function formatInlineValue(value) {
|
|
2648
|
+
if (Array.isArray(value)) return `[${value.map(formatInlineValue).join(",")}]`;
|
|
2649
|
+
if (isScalar(value)) return String(value);
|
|
2650
|
+
return oneLineJson(value);
|
|
2651
|
+
}
|
|
2652
|
+
function clipInline(value, max = INLINE_LIMIT) {
|
|
2653
|
+
const compact = value.replace(/\s+/g, " ").trim();
|
|
2654
|
+
return compact.length <= max ? compact : `${compact.slice(0, max - 15)}...(${compact.length} chars)`;
|
|
2655
|
+
}
|
|
2656
|
+
function oneLineJson(value) {
|
|
2657
|
+
try {
|
|
2658
|
+
return JSON.stringify(value);
|
|
2659
|
+
} catch {
|
|
2660
|
+
return String(value);
|
|
1763
2661
|
}
|
|
1764
2662
|
}
|
|
2663
|
+
function stringField(obj, key) {
|
|
2664
|
+
const value = obj[key];
|
|
2665
|
+
return typeof value === "string" ? value : void 0;
|
|
2666
|
+
}
|
|
2667
|
+
function numberField(obj, key) {
|
|
2668
|
+
const value = obj[key];
|
|
2669
|
+
return typeof value === "number" ? value : void 0;
|
|
2670
|
+
}
|
|
2671
|
+
function stringArrayField(obj, key) {
|
|
2672
|
+
const value = obj[key];
|
|
2673
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
2674
|
+
}
|
|
2675
|
+
function stringFromInput(input, key) {
|
|
2676
|
+
if (!isRecord2(input)) return void 0;
|
|
2677
|
+
const value = input[key];
|
|
2678
|
+
return typeof value === "string" ? value : void 0;
|
|
2679
|
+
}
|
|
2680
|
+
function numberFromInput(input, key) {
|
|
2681
|
+
if (!isRecord2(input)) return void 0;
|
|
2682
|
+
const value = input[key];
|
|
2683
|
+
return typeof value === "number" ? value : void 0;
|
|
2684
|
+
}
|
|
2685
|
+
function inputListSummary(input, key) {
|
|
2686
|
+
if (!isRecord2(input)) return void 0;
|
|
2687
|
+
const value = input[key];
|
|
2688
|
+
if (typeof value === "string") return value;
|
|
2689
|
+
if (Array.isArray(value)) return value.filter((item) => typeof item === "string").join(",");
|
|
2690
|
+
return void 0;
|
|
2691
|
+
}
|
|
2692
|
+
function isRecord2(value) {
|
|
2693
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
2694
|
+
}
|
|
2695
|
+
function isScalar(value) {
|
|
2696
|
+
return value === null || ["string", "number", "boolean"].includes(typeof value);
|
|
2697
|
+
}
|
|
2698
|
+
function projectHash(absRoot) {
|
|
2699
|
+
return createHash("sha256").update(path3.resolve(absRoot)).digest("hex").slice(0, 12);
|
|
2700
|
+
}
|
|
2701
|
+
function projectSlug(absRoot) {
|
|
2702
|
+
const base = slugify(path3.basename(absRoot));
|
|
2703
|
+
const hash = createHash("sha256").update(path3.resolve(absRoot)).digest("hex").slice(0, 6);
|
|
2704
|
+
return `${base}-${hash}`;
|
|
2705
|
+
}
|
|
2706
|
+
function slugify(name) {
|
|
2707
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
|
|
2708
|
+
}
|
|
2709
|
+
function wstackGlobalRoot() {
|
|
2710
|
+
const fromEnv = process.env["WRONGSTACK_HOME"];
|
|
2711
|
+
if (fromEnv && fromEnv.trim().length > 0) return path3.resolve(fromEnv);
|
|
2712
|
+
return path3.join(os.homedir(), ".wrongstack");
|
|
2713
|
+
}
|
|
2714
|
+
function resolveWstackPaths(opts) {
|
|
2715
|
+
const globalRoot = opts.globalRoot ?? (opts.userHome ? path3.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
|
|
2716
|
+
const hash = projectHash(opts.projectRoot);
|
|
2717
|
+
const slug = projectSlug(opts.projectRoot);
|
|
2718
|
+
const projectDir = path3.join(globalRoot, "projects", slug);
|
|
2719
|
+
return {
|
|
2720
|
+
globalRoot,
|
|
2721
|
+
configDir: globalRoot,
|
|
2722
|
+
globalConfig: path3.join(globalRoot, "config.json"),
|
|
2723
|
+
secretsKey: path3.join(globalRoot, ".key"),
|
|
2724
|
+
globalMemory: path3.join(globalRoot, "memory.md"),
|
|
2725
|
+
globalSkills: path3.join(globalRoot, "skills"),
|
|
2726
|
+
globalPrompts: path3.join(globalRoot, "prompts"),
|
|
2727
|
+
cacheDir: path3.join(globalRoot, "cache"),
|
|
2728
|
+
modelsCache: path3.join(globalRoot, "cache", "models.dev.json"),
|
|
2729
|
+
modelsOverlayCache: path3.join(globalRoot, "cache", "models-overlay.json"),
|
|
2730
|
+
historyFile: path3.join(globalRoot, "history"),
|
|
2731
|
+
logFile: path3.join(globalRoot, "logs", "wrongstack.log"),
|
|
2732
|
+
projectDir,
|
|
2733
|
+
projectCodebaseIndex: path3.join(projectDir, "codebase-index"),
|
|
2734
|
+
projectMemory: path3.join(projectDir, "memory.md"),
|
|
2735
|
+
projectSessions: path3.join(projectDir, "sessions"),
|
|
2736
|
+
projectTrust: path3.join(projectDir, "trust.json"),
|
|
2737
|
+
projectMeta: path3.join(projectDir, "meta.json"),
|
|
2738
|
+
projectLocalConfig: path3.join(projectDir, "config.local.json"),
|
|
2739
|
+
inProjectConfig: path3.join(opts.projectRoot, ".wrongstack", "config.json"),
|
|
2740
|
+
inProjectAgentsFile: path3.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
|
|
2741
|
+
inProjectSkills: path3.join(opts.projectRoot, ".wrongstack", "skills"),
|
|
2742
|
+
inProjectWorktrees: path3.join(opts.projectRoot, ".wrongstack", "worktrees"),
|
|
2743
|
+
projectHash: hash,
|
|
2744
|
+
projectSlug: slug,
|
|
2745
|
+
projectGoal: path3.join(projectDir, "goal.json"),
|
|
2746
|
+
projectSpecs: path3.join(projectDir, "specs"),
|
|
2747
|
+
projectTaskGraphs: path3.join(projectDir, "task-graphs"),
|
|
2748
|
+
projectSddSession: path3.join(projectDir, "sdd-session.json"),
|
|
2749
|
+
projectPlan: path3.join(projectDir, "plan.json"),
|
|
2750
|
+
projectAutophase: path3.join(projectDir, "autophase"),
|
|
2751
|
+
syncConfig: path3.join(globalRoot, "sync.json"),
|
|
2752
|
+
projectStatus: (projectHash2) => path3.join(globalRoot, "projects", projectHash2, "status.json")
|
|
2753
|
+
};
|
|
2754
|
+
}
|
|
1765
2755
|
|
|
1766
|
-
export { FORBIDDEN_PROTO_KEYS, assertNever, assertNotPrivateHost, atomicWrite, buildChildEnv, color, compileGlob, compileUserRegex, completePartialObject, computeMessageTokens, computeTaskItemProgress, createToolOutputSerializer, deepMerge, detectNewlineStyle, ensureDir, estimateMessageTokens, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, expandGlob, expandIPv6, expectDefined, formatTaskList, formatTaskProgress, formatTodosList, getCalibrationState, getTermSize, isInteractive, isPrimitiveArray, isPrivateIPv4, isPrivateIPv6, isStdinTTY, isStdoutTTY, matchAny, matchGlob, mergeCustomModelDefs, mergeModelsPayload, normalizeToLf, onResize, projectHash, projectSlug, recordActualUsage, repairToolUseAdjacency, resetCalibration, resolveWstackPaths, safeParse, safeStringify, sanitizeJsonString, setOutputLineGuard, setRawMode, sleep, stripAnsi, toErrorMessage, toStyle, truncate, unifiedDiff, validateAgainstSchema, withFileLock, writeErr, writeOut, wstackGlobalRoot };
|
|
2756
|
+
export { FORBIDDEN_PROTO_KEYS, assertNever, assertNotPrivateHost, atomicWrite, buildChildEnv, buildContextEvidenceDigest, color, compactSchemaDescriptions, compactToolDefinitionForWire, compileGlob, compileUserRegex, completePartialObject, computeMessageTokens, computeTaskItemProgress, createContextEvidenceState, createToolOutputSerializer, deepMerge, detectNewlineStyle, ensureDir, estimateMessageTokens, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, expandGlob, expandIPv6, expectDefined, formatTaskList, formatTaskProgress, formatTodosList, getCalibrationState, getTermSize, isInteractive, isPrimitiveArray, isPrivateIPv4, isPrivateIPv6, isStdinTTY, isStdoutTTY, markAssistantReferencedEvidence, matchAny, matchGlob, mergeCustomModelDefs, mergeModelsPayload, normalizeToLf, onResize, projectHash, projectSlug, recordActualUsage, recordToolOutputEvidence, recordUserIntentEvidence, repairToolUseAdjacency, repeatedReadPressure, resetCalibration, resolveWstackPaths, safeParse, safeStringify, sanitizeJsonString, setOutputLineGuard, setRawMode, sleep, stripAnsi, toErrorMessage, toStyle, truncate, unifiedDiff, validateAgainstSchema, withFileLock, writeErr, writeOut, wstackGlobalRoot };
|
|
1767
2757
|
//# sourceMappingURL=index.js.map
|
|
1768
2758
|
//# sourceMappingURL=index.js.map
|