zidane 3.1.2 → 3.3.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-C9q5VMGa.d.ts → agent-BBzkPRAu.d.ts} +61 -27
- package/dist/{chunk-BXO7CZHJ.js → chunk-3D5Q527Y.js} +3 -3
- package/dist/{chunk-BW3WTFIR.js → chunk-LIVB5W4B.js} +1 -1
- package/dist/{chunk-IUBBVF53.js → chunk-UD25QF3H.js} +66 -8
- package/dist/{chunk-BRMURQA2.js → chunk-VGFQEOHF.js} +2 -15
- package/dist/{chunk-YQ7LY6CL.js → chunk-X244RS5H.js} +25 -27
- package/dist/{chunk-TPXPVEH6.js → chunk-X3VOTPVM.js} +12 -5
- package/dist/{chunk-2AE3VM5O.js → chunk-Z2E5QN5X.js} +277 -147
- package/dist/contexts.js +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +7 -9
- package/dist/mcp.d.ts +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +4 -4
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +2 -2
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session/sqlite.js +6 -7
- package/dist/session.d.ts +1 -1
- package/dist/session.js +1 -1
- package/dist/{skills-use-DU0unNP4.d.ts → skills-use-JMcA5NrF.d.ts} +1 -1
- package/dist/skills.d.ts +114 -96
- package/dist/skills.js +2 -2
- package/dist/tools.d.ts +4 -4
- package/dist/tools.js +3 -5
- package/dist/types.d.ts +2 -2
- package/dist/{validation-BKA33eqb.d.ts → validation-c3MQlCza.d.ts} +12 -22
- package/package.json +1 -1
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
interpolateShellCommands,
|
|
6
6
|
resolveSkills,
|
|
7
7
|
validateResourcePath
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-X3VOTPVM.js";
|
|
9
9
|
import {
|
|
10
10
|
createProcessContext
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-UD25QF3H.js";
|
|
12
12
|
import {
|
|
13
13
|
connectMcpServers
|
|
14
14
|
} from "./chunk-7H34OFDA.js";
|
|
@@ -69,6 +69,26 @@ function desanitize(s) {
|
|
|
69
69
|
out = out.replaceAll(from, to);
|
|
70
70
|
return out;
|
|
71
71
|
}
|
|
72
|
+
var LINE_NUMBER_PREFIX_RE = /^[ \t]*\d{1,9}[\t|\u2192]/gm;
|
|
73
|
+
function stripLineNumberPrefixes(s) {
|
|
74
|
+
return s.replace(LINE_NUMBER_PREFIX_RE, "");
|
|
75
|
+
}
|
|
76
|
+
function locateAndCount(haystack, normFile, target, via) {
|
|
77
|
+
const idx = normFile.indexOf(target);
|
|
78
|
+
if (idx === -1)
|
|
79
|
+
return null;
|
|
80
|
+
const actual = haystack.slice(idx, idx + target.length);
|
|
81
|
+
let occ = 0;
|
|
82
|
+
let cursor = 0;
|
|
83
|
+
while (true) {
|
|
84
|
+
const next = normFile.indexOf(target, cursor);
|
|
85
|
+
if (next === -1)
|
|
86
|
+
break;
|
|
87
|
+
occ++;
|
|
88
|
+
cursor = next + target.length;
|
|
89
|
+
}
|
|
90
|
+
return { actual, occurrences: occ, via };
|
|
91
|
+
}
|
|
72
92
|
function resolveOldString(haystack, needle) {
|
|
73
93
|
const exact = countExactMatches(haystack, needle);
|
|
74
94
|
if (exact > 0)
|
|
@@ -76,20 +96,9 @@ function resolveOldString(haystack, needle) {
|
|
|
76
96
|
const normNeedle = normalizeQuotes(needle);
|
|
77
97
|
const normFile = normalizeQuotes(haystack);
|
|
78
98
|
if (normNeedle !== needle || normFile !== haystack) {
|
|
79
|
-
const
|
|
80
|
-
if (
|
|
81
|
-
|
|
82
|
-
let occ = 0;
|
|
83
|
-
let cursor = 0;
|
|
84
|
-
while (true) {
|
|
85
|
-
const next = normFile.indexOf(normNeedle, cursor);
|
|
86
|
-
if (next === -1)
|
|
87
|
-
break;
|
|
88
|
-
occ++;
|
|
89
|
-
cursor = next + normNeedle.length;
|
|
90
|
-
}
|
|
91
|
-
return { actual, occurrences: occ, via: "quotes" };
|
|
92
|
-
}
|
|
99
|
+
const m = locateAndCount(haystack, normFile, normNeedle, "quotes");
|
|
100
|
+
if (m)
|
|
101
|
+
return m;
|
|
93
102
|
}
|
|
94
103
|
const desan = desanitize(needle);
|
|
95
104
|
if (desan !== needle) {
|
|
@@ -99,23 +108,34 @@ function resolveOldString(haystack, needle) {
|
|
|
99
108
|
}
|
|
100
109
|
const combo = desanitize(normNeedle);
|
|
101
110
|
if (combo !== needle) {
|
|
102
|
-
const
|
|
103
|
-
if (
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
const m = locateAndCount(haystack, normFile, combo, "quotes+desanitize");
|
|
112
|
+
if (m)
|
|
113
|
+
return m;
|
|
114
|
+
}
|
|
115
|
+
const stripped = stripLineNumberPrefixes(needle);
|
|
116
|
+
if (stripped !== needle && stripped.trim().length > 0) {
|
|
117
|
+
const count = countExactMatches(haystack, stripped);
|
|
118
|
+
if (count > 0)
|
|
119
|
+
return { actual: stripped, occurrences: count, via: "line-numbers" };
|
|
120
|
+
const strippedNorm = normalizeQuotes(stripped);
|
|
121
|
+
if (strippedNorm !== stripped || normFile !== haystack) {
|
|
122
|
+
const m = locateAndCount(haystack, normFile, strippedNorm, "quotes+line-numbers");
|
|
123
|
+
if (m)
|
|
124
|
+
return m;
|
|
115
125
|
}
|
|
116
126
|
}
|
|
117
127
|
return null;
|
|
118
128
|
}
|
|
129
|
+
function styleReplacementForVia(replacement, via, actual) {
|
|
130
|
+
let out = replacement;
|
|
131
|
+
if (via === "desanitize" || via === "quotes+desanitize")
|
|
132
|
+
out = desanitize(out);
|
|
133
|
+
if (via === "line-numbers" || via === "quotes+line-numbers")
|
|
134
|
+
out = stripLineNumberPrefixes(out);
|
|
135
|
+
if (via === "quotes" || via === "quotes+desanitize" || via === "quotes+line-numbers")
|
|
136
|
+
out = preserveQuoteStyle(actual, out);
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
119
139
|
function preserveQuoteStyle(actual, replacement) {
|
|
120
140
|
const hasDouble = actual.includes(LEFT_DOUBLE_CURLY_QUOTE) || actual.includes(RIGHT_DOUBLE_CURLY_QUOTE);
|
|
121
141
|
const hasSingle = actual.includes(LEFT_SINGLE_CURLY_QUOTE) || actual.includes(RIGHT_SINGLE_CURLY_QUOTE);
|
|
@@ -221,7 +241,7 @@ function hashContent(text) {
|
|
|
221
241
|
var edit = {
|
|
222
242
|
spec: {
|
|
223
243
|
name: "edit",
|
|
224
|
-
description: "Replace exact `old_string` with `new_string` in a file. Fails if `old_string` is not unique unless `replace_all: true`. Prefer over `write_file` for surgical changes \u2014 preserves the rest of the file.",
|
|
244
|
+
description: "Replace exact `old_string` with `new_string` in a file. Fails if `old_string` is not unique unless `replace_all: true`. Prefer over `write_file` for surgical changes \u2014 preserves the rest of the file. Tolerates `read_file` line-number prefixes (`<N>\\t\u2026`, `<N>|\u2026`, or `<N>\u2192\u2026`) in `old_string` / `new_string` \u2014 they are stripped before matching/writing, so you can paste a numbered chunk verbatim.",
|
|
225
245
|
inputSchema: {
|
|
226
246
|
type: "object",
|
|
227
247
|
properties: {
|
|
@@ -266,9 +286,7 @@ var edit = {
|
|
|
266
286
|
const { actual, occurrences, via } = match;
|
|
267
287
|
if (occurrences > 1 && !replaceAll)
|
|
268
288
|
return `Edit error: old_string appears ${occurrences} times in ${target}. Pass replace_all=true or expand old_string for uniqueness.`;
|
|
269
|
-
|
|
270
|
-
if (via === "quotes" || via === "quotes+desanitize")
|
|
271
|
-
styledReplacement = preserveQuoteStyle(actual, styledReplacement);
|
|
289
|
+
const styledReplacement = styleReplacementForVia(replacement, via, actual);
|
|
272
290
|
const updated = replaceAll ? original.split(actual).join(styledReplacement) : original.replace(actual, styledReplacement);
|
|
273
291
|
if (updated === original)
|
|
274
292
|
return `Edit error: replacement produced no change in ${target}.`;
|
|
@@ -285,7 +303,8 @@ var edit = {
|
|
|
285
303
|
}
|
|
286
304
|
};
|
|
287
305
|
function nearestMatchPreview(haystack, needle) {
|
|
288
|
-
const
|
|
306
|
+
const normalizedNeedle = stripLineNumberPrefixes(needle);
|
|
307
|
+
const needleFirstLine = normalizedNeedle.split("\n")[0];
|
|
289
308
|
if (needleFirstLine.length < 3)
|
|
290
309
|
return null;
|
|
291
310
|
const lines = haystack.split("\n");
|
|
@@ -312,6 +331,8 @@ function sharedPrefixLength(a, b) {
|
|
|
312
331
|
}
|
|
313
332
|
|
|
314
333
|
// src/tools/glob.ts
|
|
334
|
+
import { stat } from "fs/promises";
|
|
335
|
+
import { resolve } from "path";
|
|
315
336
|
var DEFAULT_LIMIT = 1e3;
|
|
316
337
|
var SAFE_GLOB_PATTERN_RE = /^[\w./*?[\]{}!,^@+-]+$/;
|
|
317
338
|
async function globInProcess(pattern, cwd, limit) {
|
|
@@ -338,7 +359,7 @@ async function globViaShell(pattern, ctx, limit) {
|
|
|
338
359
|
var glob = {
|
|
339
360
|
spec: {
|
|
340
361
|
name: "glob",
|
|
341
|
-
description: "Match files by glob pattern (supports **, *, ?). Relative to the execution context cwd.
|
|
362
|
+
description: "Match files by glob pattern (supports **, *, ?). Relative to the execution context cwd. By default each row is `<path>\\t<size-bytes>\\t<mtime-iso>`; set `metadata: false` for a plain newline-separated list of paths. Always sorted.",
|
|
342
363
|
inputSchema: {
|
|
343
364
|
type: "object",
|
|
344
365
|
properties: {
|
|
@@ -349,17 +370,34 @@ var glob = {
|
|
|
349
370
|
limit: {
|
|
350
371
|
type: "number",
|
|
351
372
|
description: `Maximum number of matches to return. Default: ${DEFAULT_LIMIT}.`
|
|
373
|
+
},
|
|
374
|
+
metadata: {
|
|
375
|
+
type: "boolean",
|
|
376
|
+
description: "Append size (bytes) and mtime (ISO) per row, tab-separated. Default: true. In-process only \u2014 non-process execution contexts always return paths."
|
|
352
377
|
}
|
|
353
378
|
},
|
|
354
379
|
required: ["pattern"]
|
|
355
380
|
}
|
|
356
381
|
},
|
|
357
|
-
async execute({ pattern, limit }, ctx) {
|
|
382
|
+
async execute({ pattern, limit, metadata }, ctx) {
|
|
358
383
|
const pat = pattern;
|
|
359
384
|
const max = typeof limit === "number" && limit > 0 ? limit : DEFAULT_LIMIT;
|
|
385
|
+
const wantMetadata = metadata !== false;
|
|
360
386
|
try {
|
|
361
387
|
const entries = ctx.execution.type === "process" ? await globInProcess(pat, ctx.handle.cwd, max) : await globViaShell(pat, ctx, max);
|
|
362
|
-
|
|
388
|
+
if (entries.length === 0)
|
|
389
|
+
return "(no matches)";
|
|
390
|
+
if (!wantMetadata || ctx.execution.type !== "process")
|
|
391
|
+
return entries.join("\n");
|
|
392
|
+
const rows = await Promise.all(entries.map(async (rel) => {
|
|
393
|
+
try {
|
|
394
|
+
const s = await stat(resolve(ctx.handle.cwd, rel));
|
|
395
|
+
return `${rel} ${s.size} ${new Date(s.mtimeMs).toISOString()}`;
|
|
396
|
+
} catch {
|
|
397
|
+
return `${rel} `;
|
|
398
|
+
}
|
|
399
|
+
}));
|
|
400
|
+
return rows.join("\n");
|
|
363
401
|
} catch (err) {
|
|
364
402
|
const message = err instanceof Error ? err.message : String(err);
|
|
365
403
|
return `Glob error: ${message}`;
|
|
@@ -367,6 +405,18 @@ var glob = {
|
|
|
367
405
|
}
|
|
368
406
|
};
|
|
369
407
|
|
|
408
|
+
// src/tools/shell-quote.ts
|
|
409
|
+
var SAFE_TOKEN_RE = /^[\w@%+=:,./-]+$/;
|
|
410
|
+
var SINGLE_QUOTE_RE = /'/g;
|
|
411
|
+
function shellQuote(arg) {
|
|
412
|
+
if (SAFE_TOKEN_RE.test(arg))
|
|
413
|
+
return arg;
|
|
414
|
+
return `'${arg.replace(SINGLE_QUOTE_RE, "'\\''")}'`;
|
|
415
|
+
}
|
|
416
|
+
function alwaysQuote(arg) {
|
|
417
|
+
return `'${arg.replace(SINGLE_QUOTE_RE, "'\\''")}'`;
|
|
418
|
+
}
|
|
419
|
+
|
|
370
420
|
// src/tools/grep.ts
|
|
371
421
|
var DEFAULT_HEAD_LIMIT = 250;
|
|
372
422
|
var DEFAULT_OUTPUT_MODE = "files_with_matches";
|
|
@@ -442,11 +492,6 @@ async function runViaRipgrep(input, ctx) {
|
|
|
442
492
|
}
|
|
443
493
|
return formatPaginated(result.stdout, input);
|
|
444
494
|
}
|
|
445
|
-
function shellQuote(arg) {
|
|
446
|
-
if (/^[\w@%+=:,./-]+$/.test(arg))
|
|
447
|
-
return arg;
|
|
448
|
-
return `'${arg.replace(/'/g, "'\\''")}'`;
|
|
449
|
-
}
|
|
450
495
|
async function runInProcess(input, ctx) {
|
|
451
496
|
const mode = input.output_mode ?? DEFAULT_OUTPUT_MODE;
|
|
452
497
|
const flags = `${input["-i"] ? "i" : ""}${input.multiline ? "s" : ""}${mode !== "content" ? "" : "g"}`;
|
|
@@ -531,8 +576,8 @@ async function enumerateFiles(input, ctx) {
|
|
|
531
576
|
const root = input.path ?? ".";
|
|
532
577
|
if (input.path && !input.path.includes("*") && !input.path.includes("?")) {
|
|
533
578
|
try {
|
|
534
|
-
const
|
|
535
|
-
if (
|
|
579
|
+
const stat2 = await ctx.execution.exec(ctx.handle, `test -f ${shellQuote(input.path)} && echo file || echo dir`);
|
|
580
|
+
if (stat2.stdout.trim() === "file")
|
|
536
581
|
return [input.path];
|
|
537
582
|
} catch {
|
|
538
583
|
}
|
|
@@ -612,7 +657,7 @@ var listFiles = {
|
|
|
612
657
|
var multiEdit = {
|
|
613
658
|
spec: {
|
|
614
659
|
name: "multi_edit",
|
|
615
|
-
description: "Apply a sequential list of edits to a file atomically. Each edit operates on the result of the previous edit. All edits must succeed for any to be written. Prefer this over multiple `edit` calls when several non-overlapping changes are needed in the same file.",
|
|
660
|
+
description: "Apply a sequential list of edits to a file atomically. Each edit operates on the result of the previous edit. All edits must succeed for any to be written. Prefer this over multiple `edit` calls when several non-overlapping changes are needed in the same file. Each step tolerates `read_file` line-number prefixes (`<N>\\t\u2026`, `<N>|\u2026`, or `<N>\u2192\u2026`) in `old_string` / `new_string`.",
|
|
616
661
|
inputSchema: {
|
|
617
662
|
type: "object",
|
|
618
663
|
properties: {
|
|
@@ -673,9 +718,7 @@ var multiEdit = {
|
|
|
673
718
|
const { actual, occurrences, via } = match;
|
|
674
719
|
if (occurrences > 1 && !replaceAll)
|
|
675
720
|
return `multi_edit error: edit #${i + 1} old_string appears ${occurrences} times. Pass replace_all=true on this edit or expand old_string for uniqueness.`;
|
|
676
|
-
|
|
677
|
-
if (via === "quotes" || via === "quotes+desanitize")
|
|
678
|
-
styledReplacement = preserveQuoteStyle(actual, styledReplacement);
|
|
721
|
+
const styledReplacement = styleReplacementForVia(replacement, via, actual);
|
|
679
722
|
current = replaceAll ? current.split(actual).join(styledReplacement) : current.replace(actual, styledReplacement);
|
|
680
723
|
applied += occurrences;
|
|
681
724
|
}
|
|
@@ -695,6 +738,33 @@ var multiEdit = {
|
|
|
695
738
|
// src/tools/read-file.ts
|
|
696
739
|
import { Buffer as Buffer2 } from "buffer";
|
|
697
740
|
|
|
741
|
+
// src/tools/binary-detect.ts
|
|
742
|
+
var SNIFF_BYTES = 8192;
|
|
743
|
+
var REPLACEMENT_RATIO_THRESHOLD = 0.01;
|
|
744
|
+
var REPLACEMENT_MIN_COUNT = 5;
|
|
745
|
+
function containsNullByte(text, sniffBytes = SNIFF_BYTES) {
|
|
746
|
+
const sample = text.length > sniffBytes ? text.slice(0, sniffBytes) : text;
|
|
747
|
+
for (let i = 0; i < sample.length; i++) {
|
|
748
|
+
if (sample.charCodeAt(i) === 0)
|
|
749
|
+
return true;
|
|
750
|
+
}
|
|
751
|
+
return false;
|
|
752
|
+
}
|
|
753
|
+
function looksBinary(text, sniffBytes = SNIFF_BYTES) {
|
|
754
|
+
const sample = text.length > sniffBytes ? text.slice(0, sniffBytes) : text;
|
|
755
|
+
if (sample.length === 0)
|
|
756
|
+
return false;
|
|
757
|
+
let replacementCount = 0;
|
|
758
|
+
for (let i = 0; i < sample.length; i++) {
|
|
759
|
+
const code = sample.charCodeAt(i);
|
|
760
|
+
if (code === 0)
|
|
761
|
+
return true;
|
|
762
|
+
if (code === 65533)
|
|
763
|
+
replacementCount++;
|
|
764
|
+
}
|
|
765
|
+
return replacementCount >= REPLACEMENT_MIN_COUNT && replacementCount / sample.length > REPLACEMENT_RATIO_THRESHOLD;
|
|
766
|
+
}
|
|
767
|
+
|
|
698
768
|
// src/tools/binary-read.ts
|
|
699
769
|
import { Buffer } from "buffer";
|
|
700
770
|
function imageMediaTypeFor(path) {
|
|
@@ -722,38 +792,45 @@ async function readFileAsBase64(execution, handle, path) {
|
|
|
722
792
|
const b642 = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
|
|
723
793
|
return { base64: b642, byteLength: bytes.byteLength };
|
|
724
794
|
}
|
|
725
|
-
const cmd = `base64 < ${
|
|
795
|
+
const cmd = `base64 < ${alwaysQuote(path)}`;
|
|
726
796
|
const result = await execution.exec(handle, cmd);
|
|
727
797
|
if (result.exitCode !== 0)
|
|
728
798
|
throw new Error(`base64 read failed: ${result.stderr || `exit ${result.exitCode}`}`);
|
|
729
799
|
const b64 = result.stdout.replace(/\s+/g, "");
|
|
730
|
-
return { base64: b64, byteLength:
|
|
800
|
+
return { base64: b64, byteLength: decodedBase64ByteLength(b64) };
|
|
731
801
|
}
|
|
732
|
-
function
|
|
733
|
-
|
|
802
|
+
function decodedBase64ByteLength(b64) {
|
|
803
|
+
if (b64.length === 0)
|
|
804
|
+
return 0;
|
|
805
|
+
let pad = 0;
|
|
806
|
+
if (b64.endsWith("=="))
|
|
807
|
+
pad = 2;
|
|
808
|
+
else if (b64.endsWith("="))
|
|
809
|
+
pad = 1;
|
|
810
|
+
return Math.max(0, b64.length * 3 / 4 - pad);
|
|
734
811
|
}
|
|
735
812
|
|
|
736
813
|
// src/tools/read-file.ts
|
|
737
814
|
var DEFAULT_LINE_LIMIT = 2e3;
|
|
738
815
|
var DEFAULT_BYTE_CAP = 65536;
|
|
739
|
-
var BINARY_PROBE_BYTES = 8e3;
|
|
740
816
|
var DEFAULT_IMAGE_BYTE_CAP = 5 * 1024 * 1024;
|
|
741
817
|
var readFile = {
|
|
742
818
|
spec: {
|
|
743
819
|
name: "read_file",
|
|
744
|
-
description: "Read a file by path. Returns lines [offset..offset+limit). Default offset=1, limit=2000. A trailing footer explains how to read the rest when truncated. Binary files return a short marker rather than mojibake.",
|
|
820
|
+
description: "Read a file by path. Returns lines [offset..offset+limit). Default offset=1, limit=2000. Each line is prefixed with its 1-indexed line number followed by a tab (e.g. `42\\tconst foo = bar`); the prefix is metadata, not part of the file. Mirrors Claude Code's `cat -n`-style compact output for token efficiency. A trailing footer explains how to read the rest when truncated. Binary files return a short marker rather than mojibake.",
|
|
745
821
|
inputSchema: {
|
|
746
822
|
type: "object",
|
|
747
823
|
properties: {
|
|
748
824
|
path: { type: "string", description: "Relative file path." },
|
|
749
825
|
offset: { type: "integer", description: "1-indexed line number to start from. Default: 1." },
|
|
750
826
|
limit: { type: "integer", description: "Max lines to return. Default: 2000. Set 0 for unlimited." },
|
|
751
|
-
maxBytes: { type: "integer", description: "Hard byte cap regardless of line count. Default: 65536. Set 0 for unlimited." }
|
|
827
|
+
maxBytes: { type: "integer", description: "Hard byte cap on file content read, regardless of line count. Default: 65536. Set 0 for unlimited. The rendered output may be slightly larger than this cap when `lineNumbers` is on (each line carries a `<N>\\t` prefix)." },
|
|
828
|
+
lineNumbers: { type: "boolean", description: "Prefix each line with its 1-indexed line number. Default: true. Override the agent-wide `behavior.readLineNumbers` for this call." }
|
|
752
829
|
},
|
|
753
830
|
required: ["path"]
|
|
754
831
|
}
|
|
755
832
|
},
|
|
756
|
-
async execute({ path, offset, limit, maxBytes }, ctx) {
|
|
833
|
+
async execute({ path, offset, limit, maxBytes, lineNumbers }, ctx) {
|
|
757
834
|
const imgMedia = imageMediaTypeFor(path);
|
|
758
835
|
if (imgMedia) {
|
|
759
836
|
const sizeCap = maxBytes !== void 0 ? normalizeInteger(maxBytes, DEFAULT_IMAGE_BYTE_CAP) : DEFAULT_IMAGE_BYTE_CAP;
|
|
@@ -787,10 +864,11 @@ var readFile = {
|
|
|
787
864
|
const offsetForKey = normalizeInteger(offset, 1);
|
|
788
865
|
const limitForKey = normalizeInteger(limit, DEFAULT_LINE_LIMIT);
|
|
789
866
|
const maxBytesForKey = normalizeInteger(maxBytes, DEFAULT_BYTE_CAP);
|
|
867
|
+
const showLineNumbers = typeof lineNumbers === "boolean" ? lineNumbers : ctx.behavior?.readLineNumbers ?? true;
|
|
790
868
|
const currentHash = readState ? hashContent(raw) : "";
|
|
791
869
|
if (readState) {
|
|
792
870
|
const prior = readState.get(absKey);
|
|
793
|
-
if (prior && prior.contentHash === currentHash && prior.offset === offsetForKey && prior.limit === limitForKey && prior.maxBytes === maxBytesForKey) {
|
|
871
|
+
if (prior && prior.contentHash === currentHash && prior.offset === offsetForKey && prior.limit === limitForKey && prior.maxBytes === maxBytesForKey && prior.lineNumbers === showLineNumbers) {
|
|
794
872
|
return `File ${path} unchanged since the previous read in this session \u2014 the prior result is still current.`;
|
|
795
873
|
}
|
|
796
874
|
}
|
|
@@ -805,10 +883,10 @@ var readFile = {
|
|
|
805
883
|
const startIdx = Math.max(0, offsetN - 1);
|
|
806
884
|
const endIdx = limitN > 0 ? Math.min(totalLines, startIdx + limitN) : totalLines;
|
|
807
885
|
let slice = lines.slice(startIdx, endIdx);
|
|
808
|
-
let bytesUsed = 0;
|
|
809
886
|
let bytesCut = false;
|
|
810
887
|
if (maxBytesN > 0) {
|
|
811
888
|
const truncatedSlice = [];
|
|
889
|
+
let bytesUsed = 0;
|
|
812
890
|
for (const line of slice) {
|
|
813
891
|
const lineBytes = Buffer2.byteLength(line) + 1;
|
|
814
892
|
if (bytesUsed + lineBytes > maxBytesN && truncatedSlice.length > 0) {
|
|
@@ -841,15 +919,16 @@ var readFile = {
|
|
|
841
919
|
bytesCut = true;
|
|
842
920
|
}
|
|
843
921
|
}
|
|
844
|
-
const body = slice.join("\n");
|
|
845
922
|
const linesReturned = slice.length;
|
|
846
923
|
const lastLineRead = startIdx + linesReturned;
|
|
924
|
+
const body = showLineNumbers ? slice.map((line, i) => `${startIdx + i + 1} ${line}`).join("\n") : slice.join("\n");
|
|
847
925
|
if (readState) {
|
|
848
926
|
readState.set(absKey, {
|
|
849
927
|
contentHash: currentHash,
|
|
850
928
|
offset: offsetN,
|
|
851
929
|
limit: limitN,
|
|
852
930
|
maxBytes: maxBytesN,
|
|
931
|
+
lineNumbers: showLineNumbers,
|
|
853
932
|
mtimeMs: Date.now()
|
|
854
933
|
});
|
|
855
934
|
}
|
|
@@ -879,21 +958,6 @@ function normalizeInteger(value, fallback) {
|
|
|
879
958
|
return fallback;
|
|
880
959
|
return Math.floor(value);
|
|
881
960
|
}
|
|
882
|
-
var REPLACEMENT_RATIO_THRESHOLD = 0.01;
|
|
883
|
-
var REPLACEMENT_MIN_COUNT = 5;
|
|
884
|
-
function looksBinary(text) {
|
|
885
|
-
const sample = text.length > BINARY_PROBE_BYTES ? text.slice(0, BINARY_PROBE_BYTES) : text;
|
|
886
|
-
if (sample.includes("\0"))
|
|
887
|
-
return true;
|
|
888
|
-
if (sample.length === 0)
|
|
889
|
-
return false;
|
|
890
|
-
let replacementCount = 0;
|
|
891
|
-
for (let i = 0; i < sample.length; i++) {
|
|
892
|
-
if (sample.charCodeAt(i) === 65533)
|
|
893
|
-
replacementCount++;
|
|
894
|
-
}
|
|
895
|
-
return replacementCount >= REPLACEMENT_MIN_COUNT && replacementCount / sample.length > REPLACEMENT_RATIO_THRESHOLD;
|
|
896
|
-
}
|
|
897
961
|
|
|
898
962
|
// src/tools/shell.ts
|
|
899
963
|
import { Buffer as Buffer3 } from "buffer";
|
|
@@ -932,37 +996,54 @@ var DEFAULT_MAX_OUTPUT_BYTES = 8192;
|
|
|
932
996
|
var shell = {
|
|
933
997
|
spec: {
|
|
934
998
|
name: "shell",
|
|
935
|
-
description: "Execute a shell command in the project root and return its combined stdout/stderr. Output is tail-priority truncated at 8 KB by default; errors and exit-code summaries live in the tail. Set maxOutputBytes=0 to disable truncation.",
|
|
999
|
+
description: "Execute a shell command in the project root and return its combined stdout/stderr. Output is tail-priority truncated at 8 KB by default; errors and exit-code summaries live in the tail. By default each call appends a `(exit N, Nms)` footer and surfaces non-empty stderr in a separate section even on success \u2014 set `metadata: false` to return only stdout. Set maxOutputBytes=0 to disable truncation.",
|
|
936
1000
|
inputSchema: {
|
|
937
1001
|
type: "object",
|
|
938
1002
|
properties: {
|
|
939
1003
|
command: { type: "string", description: "Shell command to run." },
|
|
940
1004
|
timeout: { type: "integer", description: "Per-call timeout in milliseconds." },
|
|
941
|
-
maxOutputBytes: { type: "integer", description: "Truncate combined stdout+stderr beyond this many bytes. Default: 8192. Set 0 for unlimited." }
|
|
1005
|
+
maxOutputBytes: { type: "integer", description: "Truncate combined stdout+stderr beyond this many bytes. Default: 8192. Set 0 for unlimited." },
|
|
1006
|
+
metadata: { type: "boolean", description: "Append `(exit N, Nms)` footer and surface non-empty stderr on success. Default: true." }
|
|
942
1007
|
},
|
|
943
1008
|
required: ["command"]
|
|
944
1009
|
}
|
|
945
1010
|
},
|
|
946
|
-
async execute({ command, timeout, maxOutputBytes }, ctx) {
|
|
1011
|
+
async execute({ command, timeout, maxOutputBytes, metadata }, ctx) {
|
|
947
1012
|
const execOpts = {};
|
|
948
1013
|
if (typeof timeout === "number" && Number.isFinite(timeout) && timeout > 0)
|
|
949
1014
|
execOpts.timeout = Math.max(1, Math.ceil(timeout / 1e3));
|
|
950
1015
|
const cmd = command;
|
|
1016
|
+
const wantMetadata = metadata !== false;
|
|
1017
|
+
const startedAt = Date.now();
|
|
951
1018
|
const result = await ctx.execution.exec(ctx.handle, cmd, execOpts);
|
|
1019
|
+
const durationMs = Date.now() - startedAt;
|
|
952
1020
|
const cap = normalizeCap(maxOutputBytes);
|
|
953
1021
|
const semantic = interpretShellResult(cmd, result.exitCode);
|
|
954
|
-
if (result.exitCode === 0)
|
|
955
|
-
|
|
1022
|
+
if (result.exitCode === 0) {
|
|
1023
|
+
const stdoutTail = truncateTail(result.stdout || "(no output)", cap);
|
|
1024
|
+
if (!wantMetadata)
|
|
1025
|
+
return stdoutTail;
|
|
1026
|
+
const stderrTrimmed = result.stderr.trim();
|
|
1027
|
+
const stderrSection = stderrTrimmed ? `
|
|
1028
|
+
[stderr]
|
|
1029
|
+
${truncateTail(stderrTrimmed, Math.min(cap, 2048))}` : "";
|
|
1030
|
+
return `${stdoutTail}${stderrSection}
|
|
1031
|
+
(exit 0, ${durationMs}ms)`;
|
|
1032
|
+
}
|
|
956
1033
|
if (!semantic.isError) {
|
|
957
1034
|
const body = (result.stdout || result.stderr || "").trim();
|
|
958
1035
|
const tail = truncateTail(body, cap);
|
|
959
|
-
const
|
|
1036
|
+
const semanticFooter = semantic.message ? `
|
|
960
1037
|
(${semantic.message})` : "";
|
|
961
|
-
|
|
1038
|
+
const timingFooter = wantMetadata ? `
|
|
1039
|
+
(exit ${result.exitCode}, ${durationMs}ms)` : "";
|
|
1040
|
+
const head = tail.length > 0 ? tail : semantic.message ?? "(no output)";
|
|
1041
|
+
return `${head}${semanticFooter}${timingFooter}`;
|
|
962
1042
|
}
|
|
963
1043
|
const combined = `${result.stdout}
|
|
964
1044
|
${result.stderr}`.trim();
|
|
965
|
-
|
|
1045
|
+
const header = wantMetadata ? `Exit code ${result.exitCode} (${durationMs}ms)` : `Exit code ${result.exitCode}`;
|
|
1046
|
+
return `${header}
|
|
966
1047
|
${truncateTail(combined, cap)}`;
|
|
967
1048
|
}
|
|
968
1049
|
};
|
|
@@ -996,15 +1077,6 @@ ${tail}`;
|
|
|
996
1077
|
}
|
|
997
1078
|
|
|
998
1079
|
// src/tools/skills-read.ts
|
|
999
|
-
var SNIFF_BYTES = 8192;
|
|
1000
|
-
function looksBinary2(text) {
|
|
1001
|
-
const len = Math.min(text.length, SNIFF_BYTES);
|
|
1002
|
-
for (let i = 0; i < len; i++) {
|
|
1003
|
-
if (text.charCodeAt(i) === 0)
|
|
1004
|
-
return true;
|
|
1005
|
-
}
|
|
1006
|
-
return false;
|
|
1007
|
-
}
|
|
1008
1080
|
function createSkillsReadTool(options) {
|
|
1009
1081
|
const byName = new Map(options.catalog.map((s) => [s.name, s]));
|
|
1010
1082
|
return {
|
|
@@ -1049,7 +1121,7 @@ function createSkillsReadTool(options) {
|
|
|
1049
1121
|
const message = err instanceof Error ? err.message : String(err);
|
|
1050
1122
|
return `Error reading "${relPath}" in skill "${skillName}": ${message}`;
|
|
1051
1123
|
}
|
|
1052
|
-
if (
|
|
1124
|
+
if (containsNullByte(content)) {
|
|
1053
1125
|
return JSON.stringify({
|
|
1054
1126
|
kind: "binary-unsupported",
|
|
1055
1127
|
path: validated.absolutePath,
|
|
@@ -1062,12 +1134,8 @@ function createSkillsReadTool(options) {
|
|
|
1062
1134
|
}
|
|
1063
1135
|
|
|
1064
1136
|
// src/tools/skills-run-script.ts
|
|
1065
|
-
var SINGLE_QUOTE_RE = /'/g;
|
|
1066
1137
|
var ABS_WINDOWS_RE = /^[a-z]:[\\/]/i;
|
|
1067
1138
|
var COLLAPSE_SLASHES_RE = /\/+/g;
|
|
1068
|
-
function quoteShellArg(arg) {
|
|
1069
|
-
return `'${arg.replace(SINGLE_QUOTE_RE, `'\\''`)}'`;
|
|
1070
|
-
}
|
|
1071
1139
|
function createSkillsRunScriptTool(options) {
|
|
1072
1140
|
const byName = new Map(options.catalog.map((s) => [s.name, s]));
|
|
1073
1141
|
const timeoutMs = options.scriptTimeoutMs ?? 6e4;
|
|
@@ -1114,7 +1182,7 @@ function createSkillsRunScriptTool(options) {
|
|
|
1114
1182
|
const validated = validateResourcePath(joinedPath, skill.baseDir);
|
|
1115
1183
|
if (!validated.valid)
|
|
1116
1184
|
return `Error: ${validated.error}`;
|
|
1117
|
-
const cmd = [validated.absolutePath, ...args].map(
|
|
1185
|
+
const cmd = [validated.absolutePath, ...args].map(alwaysQuote).join(" ");
|
|
1118
1186
|
try {
|
|
1119
1187
|
const result = await ctx.execution.exec(ctx.handle, cmd, {
|
|
1120
1188
|
timeout: Math.max(1, Math.round(timeoutMs / 1e3))
|
|
@@ -1556,6 +1624,74 @@ function applyTailCompaction(messages, threshold, keepTurns) {
|
|
|
1556
1624
|
}
|
|
1557
1625
|
return changed ? out : messages;
|
|
1558
1626
|
}
|
|
1627
|
+
var STALE_READ_STUB = "[\u2026elided: file edited later in this run; re-read if still needed.]";
|
|
1628
|
+
function applyStaleReadElision(messages) {
|
|
1629
|
+
if (messages.length === 0)
|
|
1630
|
+
return messages;
|
|
1631
|
+
const resultByCallId = /* @__PURE__ */ new Map();
|
|
1632
|
+
for (const msg of messages) {
|
|
1633
|
+
for (const block of msg.content) {
|
|
1634
|
+
if (block.type === "tool_result" && typeof block.output === "string")
|
|
1635
|
+
resultByCallId.set(block.callId, block.output);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
const maxMutationIdxByPath = /* @__PURE__ */ new Map();
|
|
1639
|
+
const readCallInfo = /* @__PURE__ */ new Map();
|
|
1640
|
+
for (let i = 0; i < messages.length; i++) {
|
|
1641
|
+
for (const block of messages[i].content) {
|
|
1642
|
+
if (block.type !== "tool_call")
|
|
1643
|
+
continue;
|
|
1644
|
+
const path = block.input.path;
|
|
1645
|
+
if (typeof path !== "string")
|
|
1646
|
+
continue;
|
|
1647
|
+
if (block.name === "read_file") {
|
|
1648
|
+
readCallInfo.set(block.id, { path, msgIdx: i });
|
|
1649
|
+
continue;
|
|
1650
|
+
}
|
|
1651
|
+
const isEdit = block.name === "edit" || block.name === "multi_edit";
|
|
1652
|
+
const isWrite = block.name === "write_file";
|
|
1653
|
+
if (!isEdit && !isWrite)
|
|
1654
|
+
continue;
|
|
1655
|
+
const result = resultByCallId.get(block.id);
|
|
1656
|
+
if (typeof result !== "string")
|
|
1657
|
+
continue;
|
|
1658
|
+
const succeeded = isEdit ? result.startsWith("Edited ") : result.startsWith("Created ") || result.startsWith("Updated ");
|
|
1659
|
+
if (!succeeded)
|
|
1660
|
+
continue;
|
|
1661
|
+
const prior = maxMutationIdxByPath.get(path);
|
|
1662
|
+
if (prior === void 0 || i > prior)
|
|
1663
|
+
maxMutationIdxByPath.set(path, i);
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
if (maxMutationIdxByPath.size === 0)
|
|
1667
|
+
return messages;
|
|
1668
|
+
const staleCallIds = /* @__PURE__ */ new Set();
|
|
1669
|
+
for (const [callId, info] of readCallInfo) {
|
|
1670
|
+
const lastMutationIdx = maxMutationIdxByPath.get(info.path);
|
|
1671
|
+
if (typeof lastMutationIdx === "number" && info.msgIdx < lastMutationIdx)
|
|
1672
|
+
staleCallIds.add(callId);
|
|
1673
|
+
}
|
|
1674
|
+
if (staleCallIds.size === 0)
|
|
1675
|
+
return messages;
|
|
1676
|
+
let changed = false;
|
|
1677
|
+
const out = messages.slice();
|
|
1678
|
+
for (let i = 0; i < out.length; i++) {
|
|
1679
|
+
const msg = out[i];
|
|
1680
|
+
let msgChanged = false;
|
|
1681
|
+
const newContent = msg.content.map((block) => {
|
|
1682
|
+
if (block.type !== "tool_result" || !staleCallIds.has(block.callId))
|
|
1683
|
+
return block;
|
|
1684
|
+
if (block.output === STALE_READ_STUB)
|
|
1685
|
+
return block;
|
|
1686
|
+
msgChanged = true;
|
|
1687
|
+
changed = true;
|
|
1688
|
+
return { ...block, output: STALE_READ_STUB };
|
|
1689
|
+
});
|
|
1690
|
+
if (msgChanged)
|
|
1691
|
+
out[i] = { ...msg, content: newContent };
|
|
1692
|
+
}
|
|
1693
|
+
return changed ? out : messages;
|
|
1694
|
+
}
|
|
1559
1695
|
function sanitizeStoredToolResults(provider, messages) {
|
|
1560
1696
|
if (provider.meta.capabilities?.vision !== false)
|
|
1561
1697
|
return messages;
|
|
@@ -1663,7 +1799,9 @@ function wrapProviderError(err, ctx) {
|
|
|
1663
1799
|
}
|
|
1664
1800
|
async function executeTurn(ctx, turn) {
|
|
1665
1801
|
const turnId = await ctx.generateTurnId();
|
|
1666
|
-
|
|
1802
|
+
let canonicalMessages = turnsToMessages(ctx.turns);
|
|
1803
|
+
if (ctx.elideStaleReads === true)
|
|
1804
|
+
canonicalMessages = applyStaleReadElision(canonicalMessages);
|
|
1667
1805
|
const wireMessages = rewriteMessagesToWire(canonicalMessages, ctx.aliasMaps);
|
|
1668
1806
|
let sanitizedMessages = sanitizeStoredToolResults(ctx.provider, wireMessages);
|
|
1669
1807
|
if (ctx.compactStrategy === "tail") {
|
|
@@ -1718,11 +1856,12 @@ async function executeTurn(ctx, turn) {
|
|
|
1718
1856
|
);
|
|
1719
1857
|
} catch (err) {
|
|
1720
1858
|
const errorUsage = { input: 0, output: 0 };
|
|
1859
|
+
const errorContent = currentText ? [{ type: "text", text: currentText }] : [{ type: "text", text: "[provider error before any output]" }];
|
|
1721
1860
|
const errorTurn = {
|
|
1722
1861
|
id: turnId,
|
|
1723
1862
|
runId: ctx.runId,
|
|
1724
1863
|
role: "assistant",
|
|
1725
|
-
content:
|
|
1864
|
+
content: errorContent,
|
|
1726
1865
|
usage: errorUsage,
|
|
1727
1866
|
createdAt: Date.now()
|
|
1728
1867
|
};
|
|
@@ -2055,28 +2194,10 @@ async function executeToolsSequential(ctx, toolCalls, turnId) {
|
|
|
2055
2194
|
if (ctx.signal.aborted)
|
|
2056
2195
|
break;
|
|
2057
2196
|
if (ctx.steeringQueue.length > 0) {
|
|
2058
|
-
const
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
}
|
|
2063
|
-
const toolResultMsg = ctx.provider.toolResultsMessage(results);
|
|
2064
|
-
ctx.turns.push({
|
|
2065
|
-
id: await ctx.generateTurnId(),
|
|
2066
|
-
runId: ctx.runId,
|
|
2067
|
-
role: toolResultMsg.role,
|
|
2068
|
-
content: toolResultMsg.content,
|
|
2069
|
-
createdAt: Date.now()
|
|
2070
|
-
});
|
|
2071
|
-
const steerUserMsg = ctx.provider.userMessage(steerMsg);
|
|
2072
|
-
ctx.turns.push({
|
|
2073
|
-
id: await ctx.generateTurnId(),
|
|
2074
|
-
runId: ctx.runId,
|
|
2075
|
-
role: steerUserMsg.role,
|
|
2076
|
-
content: steerUserMsg.content,
|
|
2077
|
-
createdAt: Date.now()
|
|
2078
|
-
});
|
|
2079
|
-
return [];
|
|
2197
|
+
const fromIdx = toolCalls.indexOf(call);
|
|
2198
|
+
for (let i = fromIdx; i < toolCalls.length; i++)
|
|
2199
|
+
results.push({ id: toolCalls[i].id, content: "Skipped: steering message received" });
|
|
2200
|
+
return results;
|
|
2080
2201
|
}
|
|
2081
2202
|
const { result } = await executeSingleTool(ctx, call, turnId);
|
|
2082
2203
|
results.push(result);
|
|
@@ -2097,29 +2218,25 @@ async function executeToolsParallel(ctx, toolCalls, turnId) {
|
|
|
2097
2218
|
}
|
|
2098
2219
|
|
|
2099
2220
|
// src/prompt.ts
|
|
2100
|
-
function canonicalizePrompt(prompt
|
|
2221
|
+
function canonicalizePrompt(prompt) {
|
|
2101
2222
|
if (prompt === void 0)
|
|
2102
2223
|
return void 0;
|
|
2103
2224
|
if (typeof prompt === "string") {
|
|
2104
|
-
|
|
2105
|
-
if (prompt.length === 0 && !hasImages)
|
|
2225
|
+
if (prompt.length === 0)
|
|
2106
2226
|
return void 0;
|
|
2107
|
-
|
|
2108
|
-
if (prompt.length > 0)
|
|
2109
|
-
parts.push({ type: "text", text: prompt });
|
|
2110
|
-
if (hasImages) {
|
|
2111
|
-
for (const img of legacyImages) {
|
|
2112
|
-
parts.push({
|
|
2113
|
-
type: "image",
|
|
2114
|
-
mediaType: img.source.media_type,
|
|
2115
|
-
data: img.source.data
|
|
2116
|
-
});
|
|
2117
|
-
}
|
|
2118
|
-
}
|
|
2119
|
-
return parts;
|
|
2227
|
+
return [{ type: "text", text: prompt }];
|
|
2120
2228
|
}
|
|
2121
2229
|
if (prompt.length === 0)
|
|
2122
2230
|
return void 0;
|
|
2231
|
+
for (const part of prompt) {
|
|
2232
|
+
if (!part || typeof part !== "object" || typeof part.type !== "string") {
|
|
2233
|
+
throw new Error("Invalid PromptPart: each part must be an object with a `type` field.");
|
|
2234
|
+
}
|
|
2235
|
+
const type = part.type;
|
|
2236
|
+
if (type !== "text" && type !== "image" && type !== "document") {
|
|
2237
|
+
throw new Error(`Invalid PromptPart type "${type}". Expected "text" | "image" | "document".`);
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2123
2240
|
const hasMeaningfulPart = prompt.some((part) => part.type === "text" && part.text.length > 0 || part.type === "image" || part.type === "document");
|
|
2124
2241
|
if (!hasMeaningfulPart)
|
|
2125
2242
|
return void 0;
|
|
@@ -2307,7 +2424,9 @@ function resolveBehavior(agentBehavior, runBehavior) {
|
|
|
2307
2424
|
dedupReads: runBehavior?.dedupReads ?? agentBehavior?.dedupReads,
|
|
2308
2425
|
dedupTools: runBehavior?.dedupTools ?? agentBehavior?.dedupTools,
|
|
2309
2426
|
requireReadBeforeEdit: runBehavior?.requireReadBeforeEdit ?? agentBehavior?.requireReadBeforeEdit,
|
|
2310
|
-
toolBudgets: runBehavior?.toolBudgets ?? agentBehavior?.toolBudgets
|
|
2427
|
+
toolBudgets: runBehavior?.toolBudgets ?? agentBehavior?.toolBudgets,
|
|
2428
|
+
readLineNumbers: runBehavior?.readLineNumbers ?? agentBehavior?.readLineNumbers,
|
|
2429
|
+
elideStaleReads: runBehavior?.elideStaleReads ?? agentBehavior?.elideStaleReads
|
|
2311
2430
|
};
|
|
2312
2431
|
}
|
|
2313
2432
|
function createAgent({ provider, name: agentName, system: agentSystem, tools: agentTools, toolAliases, behavior: agentBehavior, execution, mcpServers, session, skills: agentSkills, mcpConnector, eager }) {
|
|
@@ -2331,6 +2450,8 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
2331
2450
|
const skillsDisabled = skillsEnabledValue === false || Array.isArray(skillsEnabledValue) && skillsEnabledValue.length === 0;
|
|
2332
2451
|
let resolvedSkills = null;
|
|
2333
2452
|
let skillsCatalog = null;
|
|
2453
|
+
let skillsCleanup = () => {
|
|
2454
|
+
};
|
|
2334
2455
|
const skillActivationState = createSkillActivationState({
|
|
2335
2456
|
maxActive: skillsConfig?.maxActive
|
|
2336
2457
|
});
|
|
@@ -2368,8 +2489,8 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
2368
2489
|
options.signal.addEventListener("abort", onExternalAbort, { once: true });
|
|
2369
2490
|
}
|
|
2370
2491
|
}
|
|
2371
|
-
idlePromise = new Promise((
|
|
2372
|
-
idleResolve =
|
|
2492
|
+
idlePromise = new Promise((resolve2) => {
|
|
2493
|
+
idleResolve = resolve2;
|
|
2373
2494
|
});
|
|
2374
2495
|
const childrenStats = [];
|
|
2375
2496
|
const unregisterSpawnHook = hooks.hook("spawn:complete", (ctx) => {
|
|
@@ -2398,7 +2519,9 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
2398
2519
|
await warmup();
|
|
2399
2520
|
}
|
|
2400
2521
|
if (!skillsDisabled && skillsConfig && !resolvedSkills) {
|
|
2401
|
-
|
|
2522
|
+
const bundle = await resolveSkills(skillsConfig);
|
|
2523
|
+
resolvedSkills = bundle.skills;
|
|
2524
|
+
skillsCleanup = bundle.cleanup;
|
|
2402
2525
|
await hooks.callHook("skills:resolve", { skills: resolvedSkills });
|
|
2403
2526
|
const skillsToolRegistered = skillsConfig?.tool !== false && resolvedSkills.length > 0;
|
|
2404
2527
|
const catalogCtx = {
|
|
@@ -2431,7 +2554,7 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
2431
2554
|
const thinking = options.thinking ?? "off";
|
|
2432
2555
|
const model = options.model ?? provider.meta.defaultModel;
|
|
2433
2556
|
const resolvedBehavior = resolveBehavior(agentBehavior, options.behavior);
|
|
2434
|
-
const { toolExecution, maxTurns, maxTokens, thinkingBudget, schema, cache, toolOutputBudget, compactStrategy, compactThreshold, compactKeepTurns, thinkingDecay, dedupTools, toolBudgets } = resolvedBehavior;
|
|
2557
|
+
const { toolExecution, maxTurns, maxTokens, thinkingBudget, schema, cache, toolOutputBudget, compactStrategy, compactThreshold, compactKeepTurns, thinkingDecay, dedupTools, toolBudgets, elideStaleReads } = resolvedBehavior;
|
|
2435
2558
|
let system = options.system || agentSystem || "You are a helpful assistant.";
|
|
2436
2559
|
if (skillsCatalog) {
|
|
2437
2560
|
system = `${system}
|
|
@@ -2480,7 +2603,7 @@ ${skillsCatalog}`;
|
|
|
2480
2603
|
if (options.system) {
|
|
2481
2604
|
await hooks.callHook("system:before", { system: options.system });
|
|
2482
2605
|
}
|
|
2483
|
-
const promptParts = canonicalizePrompt(options.prompt
|
|
2606
|
+
const promptParts = canonicalizePrompt(options.prompt);
|
|
2484
2607
|
if (promptParts) {
|
|
2485
2608
|
const promptMsg = buildPromptMessage(provider, promptParts);
|
|
2486
2609
|
turns.push({
|
|
@@ -2583,6 +2706,7 @@ ${skillsCatalog}`;
|
|
|
2583
2706
|
compactStrategy,
|
|
2584
2707
|
compactThreshold,
|
|
2585
2708
|
compactKeepTurns,
|
|
2709
|
+
...elideStaleReads !== void 0 ? { elideStaleReads } : {},
|
|
2586
2710
|
...thinkingDecay !== void 0 ? { thinkingDecay } : {},
|
|
2587
2711
|
runStartMs,
|
|
2588
2712
|
runToolCounts: {}
|
|
@@ -2655,6 +2779,11 @@ ${skillsCatalog}`;
|
|
|
2655
2779
|
return idlePromise ?? Promise.resolve();
|
|
2656
2780
|
}
|
|
2657
2781
|
async function reset() {
|
|
2782
|
+
if (running) {
|
|
2783
|
+
throw new Error(
|
|
2784
|
+
"Cannot reset() while the agent is running. Call `agent.abort()` and `await agent.waitForIdle()` first."
|
|
2785
|
+
);
|
|
2786
|
+
}
|
|
2658
2787
|
conversationTurns = [];
|
|
2659
2788
|
steeringQueue.length = 0;
|
|
2660
2789
|
followUpQueue.length = 0;
|
|
@@ -2743,6 +2872,9 @@ ${skillsCatalog}`;
|
|
|
2743
2872
|
await executionContext.destroy(executionHandle);
|
|
2744
2873
|
executionHandle = null;
|
|
2745
2874
|
}
|
|
2875
|
+
skillsCleanup();
|
|
2876
|
+
skillsCleanup = () => {
|
|
2877
|
+
};
|
|
2746
2878
|
}
|
|
2747
2879
|
if (eager && allMcpServers.length > 0) {
|
|
2748
2880
|
void warmup().catch(() => {
|
|
@@ -2822,9 +2954,9 @@ async function raceWithTimeout(task, timeoutMs) {
|
|
|
2822
2954
|
return task;
|
|
2823
2955
|
let timer;
|
|
2824
2956
|
try {
|
|
2825
|
-
return await new Promise((
|
|
2957
|
+
return await new Promise((resolve2, reject) => {
|
|
2826
2958
|
timer = setTimeout(() => reject(new SpawnTimeoutError(timeoutMs)), timeoutMs);
|
|
2827
|
-
task.then(
|
|
2959
|
+
task.then(resolve2, reject);
|
|
2828
2960
|
});
|
|
2829
2961
|
} finally {
|
|
2830
2962
|
if (timer)
|
|
@@ -3028,7 +3160,6 @@ function createSpawnTool(options = {}) {
|
|
|
3028
3160
|
}
|
|
3029
3161
|
};
|
|
3030
3162
|
}
|
|
3031
|
-
var spawn = createSpawnTool();
|
|
3032
3163
|
|
|
3033
3164
|
// src/tools/write-file.ts
|
|
3034
3165
|
import { Buffer as Buffer4 } from "buffer";
|
|
@@ -3076,6 +3207,5 @@ export {
|
|
|
3076
3207
|
readFile,
|
|
3077
3208
|
shell,
|
|
3078
3209
|
createSpawnTool,
|
|
3079
|
-
spawn,
|
|
3080
3210
|
writeFile
|
|
3081
3211
|
};
|