zidane 5.3.0 → 5.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/{agent-CYpPKn5Z.d.ts → agent-BXRCCHeq.d.ts} +557 -5
- package/dist/agent-BXRCCHeq.d.ts.map +1 -0
- package/dist/chat.d.ts +310 -6
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +2 -2
- package/dist/{errors-COmsomd5.js → errors-Byb0F8B9.js} +44 -2
- package/dist/errors-Byb0F8B9.js.map +1 -0
- package/dist/{index-D-cTScN3.d.ts → index-BPk8-Slm.d.ts} +81 -10
- package/dist/index-BPk8-Slm.d.ts.map +1 -0
- package/dist/{index-Cc-q1hLT.d.ts → index-CT5_p-3P.d.ts} +2 -2
- package/dist/{index-Cc-q1hLT.d.ts.map → index-CT5_p-3P.d.ts.map} +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +10 -10
- package/dist/{interpolate-BhmHKD6x.js → interpolate-ERgZUxgg.js} +2 -2
- package/dist/{interpolate-BhmHKD6x.js.map → interpolate-ERgZUxgg.js.map} +1 -1
- package/dist/{login-BXVt5wuA.js → login-DrBZ15G7.js} +3 -3
- package/dist/{login-BXVt5wuA.js.map → login-DrBZ15G7.js.map} +1 -1
- package/dist/{mcp-B1psg7jf.js → mcp-DhmmJfxK.js} +16 -3
- package/dist/mcp-DhmmJfxK.js.map +1 -0
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{messages-DsbMYNmt.js → messages-D0xT979U.js} +631 -68
- package/dist/messages-D0xT979U.js.map +1 -0
- package/dist/{presets-tvD28pCu.js → presets-0_IRJAYF.js} +29 -10
- package/dist/presets-0_IRJAYF.js.map +1 -0
- package/dist/presets.d.ts +2 -2
- package/dist/presets.js +1 -1
- package/dist/{providers-v1Rn2rqG.js → providers-x3LZByR5.js} +38 -6
- package/dist/providers-x3LZByR5.js.map +1 -0
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +3 -3
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session/sqlite.js +1 -1
- package/dist/{session-DOJgRXvF.js → session-BHZwxmfr.js} +2 -2
- package/dist/{session-DOJgRXvF.js.map → session-BHZwxmfr.js.map} +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/skills.d.ts +2 -2
- package/dist/skills.js +1 -1
- package/dist/{tools-CMVruxF0.js → tools-CCsL5SCO.js} +516 -140
- package/dist/tools-CCsL5SCO.js.map +1 -0
- package/dist/tools.d.ts +3 -3
- package/dist/tools.js +2 -2
- package/dist/{transcript-anchors-eyhlGeBI.d.ts → transcript-anchors-DSk8LlWt.d.ts} +28 -4
- package/dist/transcript-anchors-DSk8LlWt.d.ts.map +1 -0
- package/dist/tui.d.ts +29 -3
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +365 -80
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-Y7e15gJf.js → turn-operations-CutZin8X.js} +678 -33
- package/dist/turn-operations-CutZin8X.js.map +1 -0
- package/dist/types-IcokUOyC.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/SKILL.md +23 -1
- package/package.json +1 -1
- package/dist/agent-CYpPKn5Z.d.ts.map +0 -1
- package/dist/errors-COmsomd5.js.map +0 -1
- package/dist/index-D-cTScN3.d.ts.map +0 -1
- package/dist/mcp-B1psg7jf.js.map +0 -1
- package/dist/messages-DsbMYNmt.js.map +0 -1
- package/dist/presets-tvD28pCu.js.map +0 -1
- package/dist/providers-v1Rn2rqG.js.map +0 -1
- package/dist/tools-CMVruxF0.js.map +0 -1
- package/dist/transcript-anchors-eyhlGeBI.d.ts.map +0 -1
- package/dist/turn-operations-Y7e15gJf.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { n as createProcessContext } from "./contexts-BwiHIr2w.js";
|
|
2
|
-
import {
|
|
2
|
+
import { a as AgentToolPairingError, l as toTypedError, r as AgentProviderError, s as errorMessage, t as AgentAbortedError } from "./errors-Byb0F8B9.js";
|
|
3
3
|
import { t as toolOutputByteLength } from "./types-IcokUOyC.js";
|
|
4
|
-
import {
|
|
5
|
-
import { t as connectMcpServers } from "./mcp-
|
|
6
|
-
import { _ as validateResourcePath, b as createSkillActivationState, d as escapeXml, n as resolveSkills, p as installAllowedToolsGate, t as interpolateShellCommands, u as buildCatalog } from "./interpolate-
|
|
4
|
+
import { a as detectTurnInterruption, n as SYNTHETIC_TOOL_RESULT_PLACEHOLDER, o as ensureToolResultPairing, s as filterUnresolvedToolUses } from "./messages-D0xT979U.js";
|
|
5
|
+
import { t as connectMcpServers } from "./mcp-DhmmJfxK.js";
|
|
6
|
+
import { _ as validateResourcePath, b as createSkillActivationState, d as escapeXml, n as resolveSkills, p as installAllowedToolsGate, t as interpolateShellCommands, u as buildCatalog } from "./interpolate-ERgZUxgg.js";
|
|
7
7
|
import { n as formatTokenUsage, t as flattenTurns } from "./stats-DgOvY7wd.js";
|
|
8
8
|
import { createHooks } from "hookable";
|
|
9
9
|
import { mkdir, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
@@ -107,8 +107,14 @@ function rewriteMessagesToWire(messages, maps) {
|
|
|
107
107
|
const STATE = /* @__PURE__ */ new WeakMap();
|
|
108
108
|
/**
|
|
109
109
|
* Get or lazily create the per-session read-state map. Returns `undefined`
|
|
110
|
-
* when no session is provided
|
|
111
|
-
*
|
|
110
|
+
* when no session is provided.
|
|
111
|
+
*
|
|
112
|
+
* Most tool callers should prefer {@link resolveReadStateMap}, which
|
|
113
|
+
* additionally honors an explicit `ctx.readState` (the path
|
|
114
|
+
* `spawn`'s `shareReadState: true` uses to forward the parent's map
|
|
115
|
+
* into a sessionless child). Use this helper directly only when the
|
|
116
|
+
* Session-keyed map is exactly what you want and you don't need to
|
|
117
|
+
* accept an override.
|
|
112
118
|
*/
|
|
113
119
|
function getReadState(session) {
|
|
114
120
|
if (!session) return void 0;
|
|
@@ -119,19 +125,38 @@ function getReadState(session) {
|
|
|
119
125
|
}
|
|
120
126
|
return map;
|
|
121
127
|
}
|
|
122
|
-
const TOOL_DEDUP_STATE = /* @__PURE__ */ new WeakMap();
|
|
123
128
|
/**
|
|
124
|
-
*
|
|
125
|
-
*
|
|
129
|
+
* Resolve the active read-state map from a tool context. An explicit
|
|
130
|
+
* `ctx.readState` wins (used by `spawn`'s `shareReadState` opt-in to
|
|
131
|
+
* forward the parent's map into a sessionless child); otherwise the
|
|
132
|
+
* usual `Session`-keyed lookup applies.
|
|
133
|
+
*
|
|
134
|
+
* Tools should call this helper instead of `getReadState(ctx.session)`
|
|
135
|
+
* directly so the `shareReadState` plumbing is honored uniformly.
|
|
126
136
|
*/
|
|
127
|
-
function
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
137
|
+
function resolveReadStateMap(ctx) {
|
|
138
|
+
return ctx.readState ?? getReadState(ctx.session);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Canonical read-state key for a `(cwd, path)` pair.
|
|
142
|
+
*
|
|
143
|
+
* `ctx.execution.readFile(handle, path)` resolves the model-provided
|
|
144
|
+
* path against `handle.cwd` before touching disk — so `src/App.tsx`,
|
|
145
|
+
* `./src/App.tsx`, and `/abs/cwd/src/App.tsx` all read the **same**
|
|
146
|
+
* bytes. The read-state map MUST mirror that resolution: without it,
|
|
147
|
+
* a model that reads `src/App.tsx` and then edits `./src/App.tsx`
|
|
148
|
+
* trips the `requireReadBeforeEdit` gate for a file it demonstrably
|
|
149
|
+
* already saw (the gate's "has not been read in this session" message
|
|
150
|
+
* fires on a stale key).
|
|
151
|
+
*
|
|
152
|
+
* `node:path`'s `resolve(cwd, path)` short-circuits when `path` is
|
|
153
|
+
* already absolute, so absolute and relative shapes converge on the
|
|
154
|
+
* same canonical form. We don't `realpath()` symlinks — the file IS
|
|
155
|
+
* the path the model addressed, not the realpath behind it; the host's
|
|
156
|
+
* execution context decides how to dereference.
|
|
157
|
+
*/
|
|
158
|
+
function readStateKey(cwd, path) {
|
|
159
|
+
return resolve(cwd, path);
|
|
135
160
|
}
|
|
136
161
|
/**
|
|
137
162
|
* FNV-1a 32-bit hash, hex-encoded. Fast, non-cryptographic — we only need
|
|
@@ -147,6 +172,20 @@ function hashContent(text) {
|
|
|
147
172
|
}
|
|
148
173
|
return h.toString(16).padStart(8, "0");
|
|
149
174
|
}
|
|
175
|
+
const TOOL_DEDUP_STATE = /* @__PURE__ */ new WeakMap();
|
|
176
|
+
/**
|
|
177
|
+
* Get or lazily create the per-session tool-dedup map. Returns `undefined`
|
|
178
|
+
* when no session is provided — middleware should treat that as "no dedup".
|
|
179
|
+
*/
|
|
180
|
+
function getToolDedupState(session) {
|
|
181
|
+
if (!session) return void 0;
|
|
182
|
+
let map = TOOL_DEDUP_STATE.get(session);
|
|
183
|
+
if (!map) {
|
|
184
|
+
map = /* @__PURE__ */ new Map();
|
|
185
|
+
TOOL_DEDUP_STATE.set(session, map);
|
|
186
|
+
}
|
|
187
|
+
return map;
|
|
188
|
+
}
|
|
150
189
|
//#endregion
|
|
151
190
|
//#region src/dedup-tools.ts
|
|
152
191
|
/**
|
|
@@ -480,6 +519,7 @@ function validateToolArgs(input, schema) {
|
|
|
480
519
|
};
|
|
481
520
|
let coerced;
|
|
482
521
|
const coercions = [];
|
|
522
|
+
let droppedItems;
|
|
483
523
|
for (const [key, value] of Object.entries(input)) {
|
|
484
524
|
const propSchema = properties[key];
|
|
485
525
|
if (!propSchema?.type) continue;
|
|
@@ -494,11 +534,138 @@ function validateToolArgs(input, schema) {
|
|
|
494
534
|
coerced[key] = outcome.value;
|
|
495
535
|
coercions.push(key);
|
|
496
536
|
}
|
|
537
|
+
const arrayValue = outcome.changed ? outcome.value : value;
|
|
538
|
+
if (propSchema.type === "array" && propSchema.items && Array.isArray(arrayValue)) {
|
|
539
|
+
const itemOutcome = validateArrayItems(arrayValue, propSchema);
|
|
540
|
+
if (itemOutcome.error) return {
|
|
541
|
+
valid: false,
|
|
542
|
+
error: `Field "${key}": ${itemOutcome.error}`
|
|
543
|
+
};
|
|
544
|
+
if (itemOutcome.changed || itemOutcome.dropped.length > 0 || itemOutcome.truncated) {
|
|
545
|
+
if (!coerced) coerced = { ...input };
|
|
546
|
+
coerced[key] = itemOutcome.items;
|
|
547
|
+
if (!coercions.includes(key)) coercions.push(key);
|
|
548
|
+
}
|
|
549
|
+
if (itemOutcome.dropped.length > 0) {
|
|
550
|
+
if (!droppedItems) droppedItems = {};
|
|
551
|
+
droppedItems[key] = itemOutcome.dropped;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
497
554
|
}
|
|
498
555
|
return {
|
|
499
556
|
valid: true,
|
|
500
557
|
coercedInput: coerced ?? input,
|
|
501
|
-
coercions
|
|
558
|
+
coercions,
|
|
559
|
+
...droppedItems ? { droppedItems } : {}
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
function validateArrayItems(items, schema) {
|
|
563
|
+
if (schema.minItems !== void 0 && items.length < schema.minItems) return {
|
|
564
|
+
items,
|
|
565
|
+
changed: false,
|
|
566
|
+
truncated: false,
|
|
567
|
+
dropped: [],
|
|
568
|
+
error: `expected at least ${schema.minItems} item${schema.minItems === 1 ? "" : "s"}, got ${items.length}`
|
|
569
|
+
};
|
|
570
|
+
const itemSchema = schema.items;
|
|
571
|
+
if (!itemSchema) return {
|
|
572
|
+
items,
|
|
573
|
+
changed: false,
|
|
574
|
+
truncated: false,
|
|
575
|
+
dropped: []
|
|
576
|
+
};
|
|
577
|
+
const out = [];
|
|
578
|
+
const outOriginalIdx = [];
|
|
579
|
+
const dropped = [];
|
|
580
|
+
let changed = false;
|
|
581
|
+
for (let i = 0; i < items.length; i++) {
|
|
582
|
+
const item = items[i];
|
|
583
|
+
const v = validateOneItem(item, itemSchema);
|
|
584
|
+
if (v.dropped) {
|
|
585
|
+
dropped.push(i);
|
|
586
|
+
changed = true;
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
if (v.changed) changed = true;
|
|
590
|
+
out.push(v.value);
|
|
591
|
+
outOriginalIdx.push(i);
|
|
592
|
+
}
|
|
593
|
+
let truncated = false;
|
|
594
|
+
if (schema.maxItems !== void 0 && out.length > schema.maxItems) {
|
|
595
|
+
for (let i = schema.maxItems; i < out.length; i++) dropped.push(outOriginalIdx[i]);
|
|
596
|
+
out.length = schema.maxItems;
|
|
597
|
+
truncated = true;
|
|
598
|
+
changed = true;
|
|
599
|
+
}
|
|
600
|
+
dropped.sort((a, b) => a - b);
|
|
601
|
+
return {
|
|
602
|
+
items: out,
|
|
603
|
+
changed,
|
|
604
|
+
truncated,
|
|
605
|
+
dropped
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
function validateOneItem(item, schema) {
|
|
609
|
+
if (schema.type === "object") {
|
|
610
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return {
|
|
611
|
+
value: item,
|
|
612
|
+
changed: false,
|
|
613
|
+
dropped: true
|
|
614
|
+
};
|
|
615
|
+
const obj = item;
|
|
616
|
+
const required = schema.required ?? [];
|
|
617
|
+
for (const field of required) {
|
|
618
|
+
const v = obj[field];
|
|
619
|
+
if (v === void 0 || v === null) return {
|
|
620
|
+
value: item,
|
|
621
|
+
changed: false,
|
|
622
|
+
dropped: true
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
const properties = schema.properties ?? {};
|
|
626
|
+
let coercedItem;
|
|
627
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
628
|
+
const subSchema = properties[key];
|
|
629
|
+
if (!subSchema?.type) continue;
|
|
630
|
+
if (value === void 0 || value === null) continue;
|
|
631
|
+
const outcome = coerceValue(value, subSchema);
|
|
632
|
+
if (outcome.error) return {
|
|
633
|
+
value: item,
|
|
634
|
+
changed: false,
|
|
635
|
+
dropped: true
|
|
636
|
+
};
|
|
637
|
+
if (outcome.changed) {
|
|
638
|
+
if (!coercedItem) coercedItem = { ...obj };
|
|
639
|
+
coercedItem[key] = outcome.value;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
return coercedItem ? {
|
|
643
|
+
value: coercedItem,
|
|
644
|
+
changed: true,
|
|
645
|
+
dropped: false
|
|
646
|
+
} : {
|
|
647
|
+
value: item,
|
|
648
|
+
changed: false,
|
|
649
|
+
dropped: false
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
if (schema.type) {
|
|
653
|
+
const outcome = coerceValue(item, schema);
|
|
654
|
+
if (outcome.error) return {
|
|
655
|
+
value: item,
|
|
656
|
+
changed: false,
|
|
657
|
+
dropped: true
|
|
658
|
+
};
|
|
659
|
+
return {
|
|
660
|
+
value: outcome.value,
|
|
661
|
+
changed: outcome.changed,
|
|
662
|
+
dropped: false
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
return {
|
|
666
|
+
value: item,
|
|
667
|
+
changed: false,
|
|
668
|
+
dropped: false
|
|
502
669
|
};
|
|
503
670
|
}
|
|
504
671
|
function coerceValue(value, schema) {
|
|
@@ -628,6 +795,30 @@ function formatValue(value) {
|
|
|
628
795
|
//#region src/loop.ts
|
|
629
796
|
const IMAGE_OMITTED_MARKER = "[image omitted — model does not support vision]";
|
|
630
797
|
/**
|
|
798
|
+
* Canonical tool_result text emitted when a tool call is interrupted by the
|
|
799
|
+
* user mid-flight (Esc / Ctrl-C / external `AbortSignal`). Mirrors Claude
|
|
800
|
+
* Code's `INTERRUPT_MESSAGE_FOR_TOOL_USE` so downstream consumers can pattern
|
|
801
|
+
* match a single string across both harnesses. Always paired with
|
|
802
|
+
* `isError: true` on the wire — the model treats it as a failed call rather
|
|
803
|
+
* than a successful tool response.
|
|
804
|
+
*/
|
|
805
|
+
const INTERRUPT_MESSAGE_FOR_TOOL_USE = "[Request interrupted by user for tool use]";
|
|
806
|
+
/**
|
|
807
|
+
* Canonical tool_result text emitted when a tool call is skipped because a
|
|
808
|
+
* sibling sequential call errored or a steering message arrived between
|
|
809
|
+
* iterations of {@link executeToolsSequential}. Distinguished from
|
|
810
|
+
* {@link INTERRUPT_MESSAGE_FOR_TOOL_USE} so consumers can distinguish "user
|
|
811
|
+
* cancelled" from "framework superseded".
|
|
812
|
+
*/
|
|
813
|
+
const TOOL_USE_SKIPPED_MESSAGE = "[Tool use skipped — superseded by user message]";
|
|
814
|
+
/**
|
|
815
|
+
* Canonical tool_result text emitted when the loop catches a sequential
|
|
816
|
+
* sibling that threw and synthesizes follow-up results for the remaining
|
|
817
|
+
* queued calls. Distinct from {@link TOOL_USE_SKIPPED_MESSAGE} so telemetry
|
|
818
|
+
* can split "skipped by user steering" from "skipped after error".
|
|
819
|
+
*/
|
|
820
|
+
const TOOL_USE_AFTER_ERROR_MESSAGE = "[Tool use skipped — previous tool call in batch threw]";
|
|
821
|
+
/**
|
|
631
822
|
* Compute the effective thinking budget for a given run-relative turn, given
|
|
632
823
|
* the configured decay schedule. Pure helper — exported for tests and so
|
|
633
824
|
* downstream tooling can preview decay curves without spinning up the loop.
|
|
@@ -915,19 +1106,47 @@ function applyStaleReadElision(messages) {
|
|
|
915
1106
|
}
|
|
916
1107
|
/**
|
|
917
1108
|
* Drop read-state entries for paths whose reads got elided. Keys are
|
|
918
|
-
*
|
|
919
|
-
*
|
|
920
|
-
*
|
|
921
|
-
*
|
|
922
|
-
*
|
|
923
|
-
*
|
|
1109
|
+
* canonical absolute paths produced by `readStateKey(cwd, path)` (see
|
|
1110
|
+
* `src/tools/read-state.ts`); the loop has `ctx.handle.cwd` in scope,
|
|
1111
|
+
* so we re-derive each elided path's canonical key and delete by
|
|
1112
|
+
* direct lookup. No suffix matching, no cross-cwd ambiguity.
|
|
1113
|
+
*
|
|
1114
|
+
* Resolves the map via `resolveReadStateMap` so a child agent running
|
|
1115
|
+
* with a parent's shared map (via `shareReadState`) invalidates the
|
|
1116
|
+
* shared entries too — otherwise a child's `read → edit → re-read`
|
|
1117
|
+
* would dedup-hit on the now-stale parent entry.
|
|
924
1118
|
*/
|
|
925
|
-
function invalidateReadStateForElidedPaths(
|
|
926
|
-
if (
|
|
927
|
-
const readState =
|
|
1119
|
+
function invalidateReadStateForElidedPaths(ctx, cwd, elidedPaths) {
|
|
1120
|
+
if (elidedPaths.length === 0) return;
|
|
1121
|
+
const readState = resolveReadStateMap(ctx);
|
|
928
1122
|
if (!readState || readState.size === 0) return;
|
|
929
|
-
const
|
|
930
|
-
|
|
1123
|
+
for (const p of elidedPaths) readState.delete(readStateKey(cwd, p));
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* Run {@link ensureToolResultPairing} with the loop's hook + strict-mode
|
|
1127
|
+
* context plugged in. Centralized so the pre-send path and the schema-
|
|
1128
|
+
* enforcement path share identical telemetry + throw semantics.
|
|
1129
|
+
*
|
|
1130
|
+
* The captured `repairs` array is what `AgentToolPairingError` carries on
|
|
1131
|
+
* strict-mode throws, and it's what `pairing:repair` fires from. Hook
|
|
1132
|
+
* notifications run AFTER the pass completes so they don't interfere with
|
|
1133
|
+
* the synchronous walk — and we drop them on the floor in strict mode (the
|
|
1134
|
+
* throw is more informative than a fire-and-forget log).
|
|
1135
|
+
*/
|
|
1136
|
+
function applyPairingRepair(ctx, messages, turnId) {
|
|
1137
|
+
const repairs = [];
|
|
1138
|
+
const repaired = ensureToolResultPairing(messages, { onRepair: (repair) => repairs.push(repair) });
|
|
1139
|
+
if (repairs.length === 0) return repaired;
|
|
1140
|
+
if (ctx.strictToolPairing) throw new AgentToolPairingError({
|
|
1141
|
+
message: `Tool pairing corruption detected (${repairs.length} repair${repairs.length === 1 ? "" : "s"}); strict mode is on so the request was not sent.`,
|
|
1142
|
+
...ctx.providerName ? { provider: ctx.providerName } : {},
|
|
1143
|
+
repairs
|
|
1144
|
+
});
|
|
1145
|
+
for (const repair of repairs) ctx.hooks.callHook("pairing:repair", {
|
|
1146
|
+
...repair,
|
|
1147
|
+
turnId
|
|
1148
|
+
});
|
|
1149
|
+
return repaired;
|
|
931
1150
|
}
|
|
932
1151
|
function sanitizeStoredToolResults(provider, messages) {
|
|
933
1152
|
if (provider.meta.capabilities?.vision !== false) return messages;
|
|
@@ -1060,13 +1279,34 @@ function wrapProviderError(err, ctx) {
|
|
|
1060
1279
|
cause: err
|
|
1061
1280
|
});
|
|
1062
1281
|
}
|
|
1282
|
+
/** Max bytes of provider error text inlined into the assistant turn placeholder. */
|
|
1283
|
+
const ERROR_PLACEHOLDER_MAX = 280;
|
|
1284
|
+
/**
|
|
1285
|
+
* Build the assistant-turn placeholder text when the provider throws before
|
|
1286
|
+
* streaming any output. Inlines the underlying error message (truncated and
|
|
1287
|
+
* stripped of stack-like newlines) so the persisted turn carries a useful
|
|
1288
|
+
* diagnostic — the human reading the transcript sees the failure mode
|
|
1289
|
+
* without having to attach a debugger, and `tool_search` schema rejections
|
|
1290
|
+
* become self-explanatory.
|
|
1291
|
+
*
|
|
1292
|
+
* The bracketed `[✗ Streaming failed: ...]` shape preserves the prior
|
|
1293
|
+
* format that hosts may pattern-match on while adding the new payload
|
|
1294
|
+
* suffix. Falls back to the original generic placeholder when no message
|
|
1295
|
+
* can be extracted.
|
|
1296
|
+
*/
|
|
1297
|
+
function buildStreamErrorPlaceholder(err) {
|
|
1298
|
+
const raw = errorMessage(err).trim();
|
|
1299
|
+
if (raw.length === 0) return "[✗ Streaming failed before any output.]";
|
|
1300
|
+
const oneLine = raw.replace(/\s+/g, " ");
|
|
1301
|
+
return `[✗ Streaming failed before any output: ${oneLine.length > ERROR_PLACEHOLDER_MAX ? `${oneLine.slice(0, ERROR_PLACEHOLDER_MAX - 1).trimEnd()}…` : oneLine}]`;
|
|
1302
|
+
}
|
|
1063
1303
|
async function executeTurn(ctx, turn) {
|
|
1064
1304
|
const turnId = await ctx.generateTurnId();
|
|
1065
1305
|
let canonicalMessages = turnsToMessages(applyCompactSummaryCutoff(ctx.turns));
|
|
1066
1306
|
if (ctx.elideStaleReads === true) {
|
|
1067
1307
|
const elision = applyStaleReadElision(canonicalMessages);
|
|
1068
1308
|
canonicalMessages = elision.messages;
|
|
1069
|
-
invalidateReadStateForElidedPaths(ctx.
|
|
1309
|
+
invalidateReadStateForElidedPaths(ctx, ctx.handle.cwd, elision.elidedPaths);
|
|
1070
1310
|
}
|
|
1071
1311
|
const wireMessages = rewriteMessagesToWire(canonicalMessages, ctx.aliasMaps);
|
|
1072
1312
|
let sanitizedMessages = sanitizeStoredToolResults(ctx.provider, wireMessages);
|
|
@@ -1091,7 +1331,7 @@ async function executeTurn(ctx, turn) {
|
|
|
1091
1331
|
const transformCtx = { messages: streamOptions.messages };
|
|
1092
1332
|
await ctx.hooks.callHook("context:transform", transformCtx);
|
|
1093
1333
|
streamOptions.messages = transformCtx.messages;
|
|
1094
|
-
streamOptions.messages =
|
|
1334
|
+
streamOptions.messages = applyPairingRepair(ctx, streamOptions.messages, turnId);
|
|
1095
1335
|
const systemCtx = {
|
|
1096
1336
|
system: streamOptions.system,
|
|
1097
1337
|
messages: streamOptions.messages,
|
|
@@ -1137,12 +1377,13 @@ async function executeTurn(ctx, turn) {
|
|
|
1137
1377
|
input: 0,
|
|
1138
1378
|
output: 0
|
|
1139
1379
|
};
|
|
1380
|
+
const placeholderText = wasAborted ? "[⏹ Streaming was aborted.]" : buildStreamErrorPlaceholder(err);
|
|
1140
1381
|
const errorContent = currentText ? [{
|
|
1141
1382
|
type: "text",
|
|
1142
1383
|
text: currentText
|
|
1143
1384
|
}] : [{
|
|
1144
1385
|
type: "text",
|
|
1145
|
-
text:
|
|
1386
|
+
text: placeholderText
|
|
1146
1387
|
}];
|
|
1147
1388
|
const errorTurn = {
|
|
1148
1389
|
id: turnId,
|
|
@@ -1153,6 +1394,10 @@ async function executeTurn(ctx, turn) {
|
|
|
1153
1394
|
createdAt: Date.now()
|
|
1154
1395
|
};
|
|
1155
1396
|
ctx.turns.push(errorTurn);
|
|
1397
|
+
if (!wasAborted) await ctx.hooks.callHook("stream:error", {
|
|
1398
|
+
err,
|
|
1399
|
+
turnId
|
|
1400
|
+
});
|
|
1156
1401
|
await ctx.hooks.callHook("turn:after", {
|
|
1157
1402
|
turn,
|
|
1158
1403
|
turnId,
|
|
@@ -1205,7 +1450,7 @@ async function executeTurn(ctx, turn) {
|
|
|
1205
1450
|
description: "Return the final structured output matching the required schema.",
|
|
1206
1451
|
inputSchema: ctx.schema
|
|
1207
1452
|
};
|
|
1208
|
-
const schemaMessages =
|
|
1453
|
+
const schemaMessages = applyPairingRepair(ctx, rewriteMessagesToWire(turnsToMessages(applyCompactSummaryCutoff(ctx.turns)), ctx.aliasMaps), turnId);
|
|
1209
1454
|
let schemaResult;
|
|
1210
1455
|
try {
|
|
1211
1456
|
schemaResult = await ctx.provider.stream({
|
|
@@ -1335,30 +1580,69 @@ function stripImagesForNonVision(provider, output) {
|
|
|
1335
1580
|
if (provider.meta.capabilities?.vision !== false) return output;
|
|
1336
1581
|
return output.map((b) => b.type === "image" ? IMAGE_OMITTED_MARKER : b.text).join("\n");
|
|
1337
1582
|
}
|
|
1583
|
+
/**
|
|
1584
|
+
* Build the per-call base for every `tool:*` hook ctx (and the
|
|
1585
|
+
* matching shape for `mcp:tool:*`). Centralized so the `runId` /
|
|
1586
|
+
* `parentRunId` / `depth` identity fields land uniformly on every
|
|
1587
|
+
* event the loop fires — without one helper they drift across ~14
|
|
1588
|
+
* inline construction sites. The returned object IS the
|
|
1589
|
+
* {@link ToolHookContext} canonical shape; specialized hook payloads
|
|
1590
|
+
* (`gateCtx`, `transformCtx`, etc.) spread it and append their own
|
|
1591
|
+
* fields.
|
|
1592
|
+
*/
|
|
1593
|
+
function buildToolHookBase(ctx, turnId, callId, name, displayName, input) {
|
|
1594
|
+
return {
|
|
1595
|
+
turnId,
|
|
1596
|
+
callId,
|
|
1597
|
+
name,
|
|
1598
|
+
displayName,
|
|
1599
|
+
input,
|
|
1600
|
+
...ctx.runId !== void 0 ? { runId: ctx.runId } : {},
|
|
1601
|
+
...ctx.parentRunId !== void 0 ? { parentRunId: ctx.parentRunId } : {},
|
|
1602
|
+
...typeof ctx.depth === "number" ? { depth: ctx.depth } : {}
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1338
1605
|
async function executeSingleTool(ctx, call, turnId) {
|
|
1339
1606
|
const toolDef = ctx.tools[call.name];
|
|
1340
1607
|
const callId = call.id;
|
|
1341
1608
|
const displayName = toWireName(call.name, ctx.aliasMaps);
|
|
1342
1609
|
const runToolCounts = Object.freeze({ ...ctx.runToolCounts });
|
|
1343
1610
|
const gateCtx = {
|
|
1344
|
-
turnId,
|
|
1345
|
-
callId,
|
|
1346
|
-
name: call.name,
|
|
1347
|
-
displayName,
|
|
1348
|
-
input: call.input,
|
|
1611
|
+
...buildToolHookBase(ctx, turnId, callId, call.name, displayName, call.input),
|
|
1349
1612
|
block: false,
|
|
1350
1613
|
reason: "Tool execution was blocked",
|
|
1351
1614
|
runToolCounts
|
|
1352
1615
|
};
|
|
1353
1616
|
await ctx.hooks.callHook("tool:gate", gateCtx);
|
|
1354
|
-
if (gateCtx.block)
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1617
|
+
if (gateCtx.block) {
|
|
1618
|
+
await fireDispatched(ctx, {
|
|
1619
|
+
turnId,
|
|
1620
|
+
callId,
|
|
1621
|
+
name: call.name,
|
|
1622
|
+
displayName,
|
|
1623
|
+
input: gateCtx.input,
|
|
1624
|
+
outcome: "gate-block",
|
|
1625
|
+
reason: gateCtx.reason,
|
|
1626
|
+
runToolCounts
|
|
1627
|
+
});
|
|
1628
|
+
return { result: {
|
|
1629
|
+
id: callId,
|
|
1630
|
+
content: `Blocked: ${gateCtx.reason}`,
|
|
1631
|
+
isError: true
|
|
1632
|
+
} };
|
|
1633
|
+
}
|
|
1358
1634
|
ctx.runToolCounts[call.name] = (ctx.runToolCounts[call.name] ?? 0) + 1;
|
|
1359
|
-
if (gateCtx.result !== void 0)
|
|
1360
|
-
|
|
1361
|
-
|
|
1635
|
+
if (gateCtx.result !== void 0) {
|
|
1636
|
+
await fireDispatched(ctx, {
|
|
1637
|
+
turnId,
|
|
1638
|
+
callId,
|
|
1639
|
+
name: call.name,
|
|
1640
|
+
displayName,
|
|
1641
|
+
input: gateCtx.input,
|
|
1642
|
+
outcome: "gate-substitute",
|
|
1643
|
+
runToolCounts
|
|
1644
|
+
});
|
|
1645
|
+
const emitted = await emitToolResult(ctx, {
|
|
1362
1646
|
turnId,
|
|
1363
1647
|
callId,
|
|
1364
1648
|
name: call.name,
|
|
@@ -1367,69 +1651,84 @@ async function executeSingleTool(ctx, call, turnId) {
|
|
|
1367
1651
|
output: gateCtx.result,
|
|
1368
1652
|
isError: false,
|
|
1369
1653
|
runToolCounts
|
|
1370
|
-
})
|
|
1371
|
-
|
|
1654
|
+
});
|
|
1655
|
+
return { result: {
|
|
1656
|
+
id: callId,
|
|
1657
|
+
content: emitted.output,
|
|
1658
|
+
...emitted.isError ? { isError: true } : {}
|
|
1659
|
+
} };
|
|
1660
|
+
}
|
|
1372
1661
|
let effectiveInput = gateCtx.input;
|
|
1373
1662
|
if (!toolDef) {
|
|
1374
1663
|
const unknownCtx = {
|
|
1375
|
-
turnId,
|
|
1376
|
-
callId,
|
|
1377
|
-
name: call.name,
|
|
1378
|
-
displayName,
|
|
1379
|
-
input: effectiveInput,
|
|
1664
|
+
...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),
|
|
1380
1665
|
suppressError: false
|
|
1381
1666
|
};
|
|
1382
1667
|
await ctx.hooks.callHook("tool:unknown", unknownCtx);
|
|
1383
1668
|
const content = unknownCtx.result ?? `Tool error: Unknown tool: ${call.name}`;
|
|
1669
|
+
const isError = unknownCtx.result === void 0;
|
|
1384
1670
|
if (!unknownCtx.suppressError) {
|
|
1385
1671
|
const err = /* @__PURE__ */ new Error(`Unknown tool: ${call.name}`);
|
|
1386
1672
|
await ctx.hooks.callHook("tool:error", {
|
|
1387
|
-
turnId,
|
|
1388
|
-
callId,
|
|
1389
|
-
name: call.name,
|
|
1390
|
-
displayName,
|
|
1391
|
-
input: effectiveInput,
|
|
1673
|
+
...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),
|
|
1392
1674
|
error: err
|
|
1393
1675
|
});
|
|
1394
1676
|
}
|
|
1677
|
+
await fireDispatched(ctx, {
|
|
1678
|
+
turnId,
|
|
1679
|
+
callId,
|
|
1680
|
+
name: call.name,
|
|
1681
|
+
displayName,
|
|
1682
|
+
input: effectiveInput,
|
|
1683
|
+
outcome: "unknown",
|
|
1684
|
+
runToolCounts
|
|
1685
|
+
});
|
|
1395
1686
|
return { result: {
|
|
1396
1687
|
id: callId,
|
|
1397
|
-
content
|
|
1688
|
+
content,
|
|
1689
|
+
...isError ? { isError: true } : {}
|
|
1398
1690
|
} };
|
|
1399
1691
|
}
|
|
1400
1692
|
const validation = validateToolArgs(effectiveInput, toolDef.spec.inputSchema);
|
|
1401
1693
|
if (!validation.valid) {
|
|
1402
1694
|
await ctx.hooks.callHook("validation:reject", {
|
|
1695
|
+
...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),
|
|
1696
|
+
reason: validation.error ?? "invalid input",
|
|
1697
|
+
schema: toolDef.spec.inputSchema
|
|
1698
|
+
});
|
|
1699
|
+
await fireDispatched(ctx, {
|
|
1403
1700
|
turnId,
|
|
1404
1701
|
callId,
|
|
1405
1702
|
name: call.name,
|
|
1406
1703
|
displayName,
|
|
1407
1704
|
input: effectiveInput,
|
|
1408
|
-
|
|
1409
|
-
|
|
1705
|
+
outcome: "invalid-input",
|
|
1706
|
+
runToolCounts
|
|
1410
1707
|
});
|
|
1411
1708
|
return { result: {
|
|
1412
1709
|
id: callId,
|
|
1413
|
-
content: `Validation error: ${validation.error}
|
|
1710
|
+
content: `Validation error: ${validation.error}`,
|
|
1711
|
+
isError: true
|
|
1414
1712
|
} };
|
|
1415
1713
|
}
|
|
1416
1714
|
effectiveInput = validation.coercedInput ?? effectiveInput;
|
|
1417
1715
|
const coercions = validation.coercions && validation.coercions.length > 0 ? validation.coercions : void 0;
|
|
1418
1716
|
if (coercions) await ctx.hooks.callHook("validation:coerce", {
|
|
1419
|
-
turnId,
|
|
1420
|
-
callId,
|
|
1421
|
-
name: call.name,
|
|
1422
|
-
displayName,
|
|
1423
|
-
input: effectiveInput,
|
|
1717
|
+
...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),
|
|
1424
1718
|
coercions,
|
|
1425
1719
|
schema: toolDef.spec.inputSchema
|
|
1426
1720
|
});
|
|
1427
|
-
await ctx
|
|
1721
|
+
await fireDispatched(ctx, {
|
|
1428
1722
|
turnId,
|
|
1429
1723
|
callId,
|
|
1430
1724
|
name: call.name,
|
|
1431
1725
|
displayName,
|
|
1432
1726
|
input: effectiveInput,
|
|
1727
|
+
outcome: "execute",
|
|
1728
|
+
runToolCounts
|
|
1729
|
+
});
|
|
1730
|
+
await ctx.hooks.callHook("tool:before", {
|
|
1731
|
+
...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),
|
|
1433
1732
|
runToolCounts,
|
|
1434
1733
|
...coercions ? { coercions } : {}
|
|
1435
1734
|
});
|
|
@@ -1452,55 +1751,79 @@ async function executeSingleTool(ctx, call, turnId) {
|
|
|
1452
1751
|
turnId,
|
|
1453
1752
|
callId,
|
|
1454
1753
|
runId: ctx.runId,
|
|
1754
|
+
...ctx.parentRunId !== void 0 ? { parentRunId: ctx.parentRunId } : {},
|
|
1455
1755
|
...ctx.session ? { session: ctx.session } : {},
|
|
1756
|
+
...ctx.readState ? { readState: ctx.readState } : {},
|
|
1456
1757
|
...typeof ctx.depth === "number" ? { depth: ctx.depth } : {}
|
|
1457
1758
|
};
|
|
1458
1759
|
output = await toolDef.execute(effectiveInput, toolCtx);
|
|
1459
1760
|
} catch (err) {
|
|
1460
1761
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
1461
1762
|
const errorCtx = {
|
|
1462
|
-
turnId,
|
|
1463
|
-
callId,
|
|
1464
|
-
name: call.name,
|
|
1465
|
-
displayName,
|
|
1466
|
-
input: effectiveInput,
|
|
1763
|
+
...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),
|
|
1467
1764
|
error
|
|
1468
1765
|
};
|
|
1469
1766
|
await ctx.hooks.callHook("tool:error", errorCtx);
|
|
1470
1767
|
output = errorCtx.result ?? `Tool error: ${error.message}`;
|
|
1471
1768
|
isError = true;
|
|
1472
1769
|
}
|
|
1770
|
+
const emitted = await emitToolResult(ctx, {
|
|
1771
|
+
turnId,
|
|
1772
|
+
callId,
|
|
1773
|
+
name: call.name,
|
|
1774
|
+
displayName,
|
|
1775
|
+
input: effectiveInput,
|
|
1776
|
+
output,
|
|
1777
|
+
isError,
|
|
1778
|
+
runToolCounts,
|
|
1779
|
+
...coercions ? { coercions } : {}
|
|
1780
|
+
});
|
|
1473
1781
|
return { result: {
|
|
1474
1782
|
id: callId,
|
|
1475
|
-
content:
|
|
1476
|
-
|
|
1477
|
-
callId,
|
|
1478
|
-
name: call.name,
|
|
1479
|
-
displayName,
|
|
1480
|
-
input: effectiveInput,
|
|
1481
|
-
output,
|
|
1482
|
-
isError,
|
|
1483
|
-
runToolCounts,
|
|
1484
|
-
...coercions ? { coercions } : {}
|
|
1485
|
-
})
|
|
1783
|
+
content: emitted.output,
|
|
1784
|
+
...emitted.isError ? { isError: true } : {}
|
|
1486
1785
|
} };
|
|
1487
1786
|
}
|
|
1488
1787
|
/**
|
|
1788
|
+
* Fire `tool:dispatched` with the resolved path discriminator. Every code
|
|
1789
|
+
* path in {@link executeSingleTool} that produces a tool_result must call
|
|
1790
|
+
* this exactly once — that contract is what makes `tool:dispatched` ↔
|
|
1791
|
+
* `tool:after` a guaranteed symmetric pairing for live-event consumers
|
|
1792
|
+
* (the chat layer, SDK consumers reconstructing wire history from events,
|
|
1793
|
+
* tracing spans that want to record refused calls separately from
|
|
1794
|
+
* executed ones).
|
|
1795
|
+
*
|
|
1796
|
+
* Helper exists to centralize the optional-field plumbing for `reason`
|
|
1797
|
+
* (only set on `gate-block`) without sprinkling spread-conditional logic
|
|
1798
|
+
* across five call sites.
|
|
1799
|
+
*/
|
|
1800
|
+
async function fireDispatched(ctx, params) {
|
|
1801
|
+
const { turnId, callId, name, displayName, input, outcome, reason, runToolCounts } = params;
|
|
1802
|
+
await ctx.hooks.callHook("tool:dispatched", {
|
|
1803
|
+
...buildToolHookBase(ctx, turnId, callId, name, displayName, input),
|
|
1804
|
+
outcome,
|
|
1805
|
+
runToolCounts,
|
|
1806
|
+
...reason !== void 0 ? { reason } : {}
|
|
1807
|
+
});
|
|
1808
|
+
}
|
|
1809
|
+
/**
|
|
1489
1810
|
* Shared post-output emission: fire `tool:transform` (mutate-allowed), strip
|
|
1490
1811
|
* images for non-vision providers, fire `tool:after`. Used by both the
|
|
1491
1812
|
* gate-substitute (Z20) and post-execute paths so they stay byte-for-byte
|
|
1492
1813
|
* identical from the consumer's perspective.
|
|
1814
|
+
*
|
|
1815
|
+
* Returns both the (possibly transformed) output and the final `isError`
|
|
1816
|
+
* flag — `tool:transform` listeners can flip the flag in either direction
|
|
1817
|
+
* (e.g. rewrite a structured error response to a graceful retry hint), and
|
|
1818
|
+
* the caller needs the post-transform value to populate `ToolResult.isError`
|
|
1819
|
+
* on the wire.
|
|
1493
1820
|
*/
|
|
1494
1821
|
async function emitToolResult(ctx, params) {
|
|
1495
1822
|
const { turnId, callId, name, displayName, input, runToolCounts, coercions } = params;
|
|
1496
1823
|
let output = params.output;
|
|
1497
1824
|
let isError = params.isError;
|
|
1498
1825
|
const transformCtx = {
|
|
1499
|
-
turnId,
|
|
1500
|
-
callId,
|
|
1501
|
-
name,
|
|
1502
|
-
displayName,
|
|
1503
|
-
input,
|
|
1826
|
+
...buildToolHookBase(ctx, turnId, callId, name, displayName, input),
|
|
1504
1827
|
result: output,
|
|
1505
1828
|
isError,
|
|
1506
1829
|
outputBytes: toolOutputByteLength(output),
|
|
@@ -1525,17 +1848,16 @@ async function emitToolResult(ctx, params) {
|
|
|
1525
1848
|
}
|
|
1526
1849
|
output = stripImagesForNonVision(ctx.provider, output);
|
|
1527
1850
|
await ctx.hooks.callHook("tool:after", {
|
|
1528
|
-
turnId,
|
|
1529
|
-
callId,
|
|
1530
|
-
name,
|
|
1531
|
-
displayName,
|
|
1532
|
-
input,
|
|
1851
|
+
...buildToolHookBase(ctx, turnId, callId, name, displayName, input),
|
|
1533
1852
|
result: output,
|
|
1534
1853
|
outputBytes: toolOutputByteLength(output),
|
|
1535
1854
|
runToolCounts,
|
|
1536
1855
|
...coercions ? { coercions } : {}
|
|
1537
1856
|
});
|
|
1538
|
-
return
|
|
1857
|
+
return {
|
|
1858
|
+
output,
|
|
1859
|
+
isError
|
|
1860
|
+
};
|
|
1539
1861
|
}
|
|
1540
1862
|
async function executeToolsSequential(ctx, toolCalls, turnId) {
|
|
1541
1863
|
const results = [];
|
|
@@ -1544,14 +1866,16 @@ async function executeToolsSequential(ctx, toolCalls, turnId) {
|
|
|
1544
1866
|
if (ctx.signal.aborted) {
|
|
1545
1867
|
for (let j = i; j < toolCalls.length; j++) results.push({
|
|
1546
1868
|
id: toolCalls[j].id,
|
|
1547
|
-
content:
|
|
1869
|
+
content: INTERRUPT_MESSAGE_FOR_TOOL_USE,
|
|
1870
|
+
isError: true
|
|
1548
1871
|
});
|
|
1549
1872
|
return results;
|
|
1550
1873
|
}
|
|
1551
1874
|
if (ctx.steeringQueue.length > 0) {
|
|
1552
1875
|
for (let j = i; j < toolCalls.length; j++) results.push({
|
|
1553
1876
|
id: toolCalls[j].id,
|
|
1554
|
-
content:
|
|
1877
|
+
content: TOOL_USE_SKIPPED_MESSAGE,
|
|
1878
|
+
isError: true
|
|
1555
1879
|
});
|
|
1556
1880
|
return results;
|
|
1557
1881
|
}
|
|
@@ -1561,11 +1885,13 @@ async function executeToolsSequential(ctx, toolCalls, turnId) {
|
|
|
1561
1885
|
} catch (err) {
|
|
1562
1886
|
results.push({
|
|
1563
1887
|
id: call.id,
|
|
1564
|
-
content: `Error: ${errorMessage(err)}
|
|
1888
|
+
content: `Error: ${errorMessage(err)}`,
|
|
1889
|
+
isError: true
|
|
1565
1890
|
});
|
|
1566
1891
|
for (let j = i + 1; j < toolCalls.length; j++) results.push({
|
|
1567
1892
|
id: toolCalls[j].id,
|
|
1568
|
-
content:
|
|
1893
|
+
content: TOOL_USE_AFTER_ERROR_MESSAGE,
|
|
1894
|
+
isError: true
|
|
1569
1895
|
});
|
|
1570
1896
|
return results;
|
|
1571
1897
|
}
|
|
@@ -1576,9 +1902,12 @@ async function executeToolsParallel(ctx, toolCalls, turnId) {
|
|
|
1576
1902
|
const executions = toolCalls.map((call) => executeSingleTool(ctx, call, turnId));
|
|
1577
1903
|
return (await Promise.allSettled(executions)).map((s, i) => {
|
|
1578
1904
|
if (s.status === "fulfilled") return s.value.result;
|
|
1905
|
+
const reason = s.reason;
|
|
1906
|
+
const isAbort = ctx.signal.aborted || reason instanceof Error && reason.name === "AbortError";
|
|
1579
1907
|
return {
|
|
1580
1908
|
id: toolCalls[i].id,
|
|
1581
|
-
content: `Error: ${
|
|
1909
|
+
content: isAbort ? INTERRUPT_MESSAGE_FOR_TOOL_USE : `Error: ${reason instanceof Error ? reason.message : String(reason)}`,
|
|
1910
|
+
isError: true
|
|
1582
1911
|
};
|
|
1583
1912
|
});
|
|
1584
1913
|
}
|
|
@@ -2157,8 +2486,10 @@ const HOOK_EVENT_SET = new Set([
|
|
|
2157
2486
|
"stream:text",
|
|
2158
2487
|
"stream:end",
|
|
2159
2488
|
"stream:thinking",
|
|
2489
|
+
"stream:error",
|
|
2160
2490
|
"oauth:refresh",
|
|
2161
2491
|
"tool:gate",
|
|
2492
|
+
"tool:dispatched",
|
|
2162
2493
|
"tool:before",
|
|
2163
2494
|
"tool:after",
|
|
2164
2495
|
"tool:error",
|
|
@@ -2175,8 +2506,10 @@ const HOOK_EVENT_SET = new Set([
|
|
|
2175
2506
|
"child:stream:text",
|
|
2176
2507
|
"child:stream:thinking",
|
|
2177
2508
|
"child:stream:end",
|
|
2509
|
+
"child:stream:error",
|
|
2178
2510
|
"child:tool:gate",
|
|
2179
2511
|
"child:mcp:tool:gate",
|
|
2512
|
+
"child:tool:dispatched",
|
|
2180
2513
|
"child:tool:before",
|
|
2181
2514
|
"child:tool:after",
|
|
2182
2515
|
"child:tool:transform",
|
|
@@ -2205,6 +2538,7 @@ const HOOK_EVENT_SET = new Set([
|
|
|
2205
2538
|
"output",
|
|
2206
2539
|
"budget:exceeded",
|
|
2207
2540
|
"tool-budget:exceeded",
|
|
2541
|
+
"pairing:repair",
|
|
2208
2542
|
"agent:abort",
|
|
2209
2543
|
"agent:done",
|
|
2210
2544
|
"session:start",
|
|
@@ -2224,6 +2558,16 @@ function isKnownHookEvent(event) {
|
|
|
2224
2558
|
* loop from leaving the persisted session with an orphan tool_use — which
|
|
2225
2559
|
* Anthropic rejects on resume.
|
|
2226
2560
|
*
|
|
2561
|
+
* Each synthetic result carries the shared
|
|
2562
|
+
* {@link SYNTHETIC_TOOL_RESULT_PLACEHOLDER} text (so consumers can pattern-
|
|
2563
|
+
* match the same way they would on a live pre-send repair) and
|
|
2564
|
+
* `isError: true` so the model treats it as a failed call.
|
|
2565
|
+
*
|
|
2566
|
+
* Fires `pairing:repair` (mode `orphan-tool-use-append`) for each synthetic
|
|
2567
|
+
* result, mirroring the wire-level repair pass's observability so postmortem
|
|
2568
|
+
* dashboards see the same telemetry shape regardless of where the orphan
|
|
2569
|
+
* was detected.
|
|
2570
|
+
*
|
|
2227
2571
|
* No-op when:
|
|
2228
2572
|
* - The trailing turn isn't an assistant turn (already closed, or session
|
|
2229
2573
|
* ends with the seeded user prompt).
|
|
@@ -2231,7 +2575,7 @@ function isKnownHookEvent(event) {
|
|
|
2231
2575
|
* - All tool_use ids are already answered by a tool_result somewhere later
|
|
2232
2576
|
* in the conversation (defensive — shouldn't happen but cheap to check).
|
|
2233
2577
|
*/
|
|
2234
|
-
function synthesizeMissingToolResults(turns, syntheticTurnId, runId, provider) {
|
|
2578
|
+
async function synthesizeMissingToolResults(turns, syntheticTurnId, runId, provider, hooks) {
|
|
2235
2579
|
if (turns.length === 0) return;
|
|
2236
2580
|
const last = turns[turns.length - 1];
|
|
2237
2581
|
if (last.role !== "assistant") return;
|
|
@@ -2244,7 +2588,8 @@ function synthesizeMissingToolResults(turns, syntheticTurnId, runId, provider) {
|
|
|
2244
2588
|
if (dangling.length === 0) return;
|
|
2245
2589
|
const results = dangling.map((id) => ({
|
|
2246
2590
|
id,
|
|
2247
|
-
content:
|
|
2591
|
+
content: SYNTHETIC_TOOL_RESULT_PLACEHOLDER,
|
|
2592
|
+
isError: true
|
|
2248
2593
|
}));
|
|
2249
2594
|
const msg = provider.toolResultsMessage(results);
|
|
2250
2595
|
turns.push({
|
|
@@ -2254,6 +2599,11 @@ function synthesizeMissingToolResults(turns, syntheticTurnId, runId, provider) {
|
|
|
2254
2599
|
content: msg.content,
|
|
2255
2600
|
createdAt: Date.now()
|
|
2256
2601
|
});
|
|
2602
|
+
for (const callId of dangling) await hooks.callHook("pairing:repair", {
|
|
2603
|
+
mode: "orphan-tool-use-append",
|
|
2604
|
+
callId,
|
|
2605
|
+
messageIndex: turns.length - 2
|
|
2606
|
+
});
|
|
2257
2607
|
}
|
|
2258
2608
|
function resolveBehavior(agentBehavior, runBehavior) {
|
|
2259
2609
|
return {
|
|
@@ -2278,7 +2628,8 @@ function resolveBehavior(agentBehavior, runBehavior) {
|
|
|
2278
2628
|
toolSearch: runBehavior?.toolSearch ?? agentBehavior?.toolSearch,
|
|
2279
2629
|
persistThreshold: runBehavior?.persistThreshold ?? agentBehavior?.persistThreshold,
|
|
2280
2630
|
persistExcludeTools: runBehavior?.persistExcludeTools ?? agentBehavior?.persistExcludeTools,
|
|
2281
|
-
persistDir: runBehavior?.persistDir ?? agentBehavior?.persistDir
|
|
2631
|
+
persistDir: runBehavior?.persistDir ?? agentBehavior?.persistDir,
|
|
2632
|
+
strictToolPairing: runBehavior?.strictToolPairing ?? agentBehavior?.strictToolPairing ?? false
|
|
2282
2633
|
};
|
|
2283
2634
|
}
|
|
2284
2635
|
/**
|
|
@@ -2366,7 +2717,7 @@ function buildSearchableCatalog(entries, options) {
|
|
|
2366
2717
|
}
|
|
2367
2718
|
const serverNames = [...byServer.keys()].sort();
|
|
2368
2719
|
const parts = [];
|
|
2369
|
-
if (options.discoveryToolName) parts.push("The following tools are available but their input schemas are NOT loaded in your context.", `Call the \`${options.discoveryToolName}\` tool to load schemas
|
|
2720
|
+
if (options.discoveryToolName) parts.push("The following tools are available but their input schemas are NOT loaded in your context.", `Call the \`${options.discoveryToolName}\` tool to load schemas for any tool below that you have not already surfaced. Surfaced tools persist for the rest of the run.`, "");
|
|
2370
2721
|
parts.push("<searchable_tools>");
|
|
2371
2722
|
for (const server of serverNames) {
|
|
2372
2723
|
parts.push(` <server name="${escapeXml(server)}">`);
|
|
@@ -2425,7 +2776,7 @@ function initialRunCounter(session) {
|
|
|
2425
2776
|
for (const t of session.turns) consider(t.runId);
|
|
2426
2777
|
return max;
|
|
2427
2778
|
}
|
|
2428
|
-
function createAgent({ provider, name: agentName, system: agentSystem, tools: agentTools, toolAliases, behavior: agentBehavior, execution, mcpServers, session, skills: agentSkills, mcpConnector, eager, hooks: initialHooks }) {
|
|
2779
|
+
function createAgent({ provider, name: agentName, system: agentSystem, tools: agentTools, toolAliases, behavior: agentBehavior, execution, mcpServers, session, readState: agentReadState, skills: agentSkills, mcpConnector, eager, hooks: initialHooks }) {
|
|
2429
2780
|
const hooks = createHooks();
|
|
2430
2781
|
const executionContext = execution ?? createProcessContext();
|
|
2431
2782
|
const sourceTools = agentTools ?? {};
|
|
@@ -2494,9 +2845,14 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
2494
2845
|
if (running) throw new Error("Agent is already running. Use steer() or followUp() to queue messages, or waitForIdle().");
|
|
2495
2846
|
const hasSessionTurns = session && session.turns.length > 0;
|
|
2496
2847
|
if (!options.prompt && !hasSessionTurns) throw new Error("prompt is required when no session with existing turns is provided");
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2848
|
+
let resumeFilteredTurns;
|
|
2849
|
+
if (hasSessionTurns) resumeFilteredTurns = filterUnresolvedToolUses(session.turns);
|
|
2850
|
+
if (!options.prompt && resumeFilteredTurns) {
|
|
2851
|
+
const lastTurn = resumeFilteredTurns.at(-1);
|
|
2852
|
+
if (lastTurn && lastTurn.role !== "user") {
|
|
2853
|
+
const detail = detectTurnInterruption(resumeFilteredTurns) === "completed" ? "last turn is a completed assistant message" : "last turn is mid-stream assistant content";
|
|
2854
|
+
throw new Error(`cannot resume without prompt: ${detail}. Pass a prompt to agent.run({ prompt: … }).`);
|
|
2855
|
+
}
|
|
2500
2856
|
}
|
|
2501
2857
|
let externalAbortListener;
|
|
2502
2858
|
const externalSignal = options.signal;
|
|
@@ -2561,7 +2917,7 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
2561
2917
|
const thinking = options.thinking ?? "off";
|
|
2562
2918
|
const model = options.model ?? provider.meta.defaultModel;
|
|
2563
2919
|
const resolvedBehavior = resolveBehavior(agentBehavior, options.behavior);
|
|
2564
|
-
const { toolExecution, maxTurns, maxTokens, thinkingBudget, schema, cache, toolOutputBudget, compactStrategy, compactThreshold, compactKeepTurns, thinkingDecay, dedupTools, toolBudgets, elideStaleReads, toolDisclosure, toolSearch, persistThreshold, persistExcludeTools, persistDir } = resolvedBehavior;
|
|
2920
|
+
const { toolExecution, maxTurns, maxTokens, thinkingBudget, schema, cache, toolOutputBudget, compactStrategy, compactThreshold, compactKeepTurns, thinkingDecay, dedupTools, toolBudgets, elideStaleReads, toolDisclosure, toolSearch, persistThreshold, persistExcludeTools, persistDir, strictToolPairing } = resolvedBehavior;
|
|
2565
2921
|
let system = options.system || agentSystem || "You are a helpful assistant.";
|
|
2566
2922
|
if (skillsCatalog) system = `${system}\n\n${skillsCatalog}`;
|
|
2567
2923
|
const runBaseTools = options.tools !== void 0 ? options.tools : mcpConnection ? {
|
|
@@ -2626,7 +2982,8 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
2626
2982
|
if (isResume) {
|
|
2627
2983
|
const childRunIds = new Set(session.runs.filter((r) => (r.depth ?? 0) > 0).map((r) => r.id));
|
|
2628
2984
|
const resumed = childRunIds.size === 0 ? session.turns : session.turns.filter((t) => !t.runId || !childRunIds.has(t.runId));
|
|
2629
|
-
turns
|
|
2985
|
+
const filteredForRuntime = resumeFilteredTurns && resumed === session.turns ? resumeFilteredTurns : filterUnresolvedToolUses(resumed);
|
|
2986
|
+
turns.push(...filteredForRuntime);
|
|
2630
2987
|
}
|
|
2631
2988
|
const runTurnStart = turns.length;
|
|
2632
2989
|
if (options.system) await hooks.callHook("system:before", { system: options.system });
|
|
@@ -2669,7 +3026,7 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
2669
3026
|
const unregisterToolResultsSync = session ? hooks.hook("tool-results:after", persistPendingTurns) : void 0;
|
|
2670
3027
|
async function flushTurns(opts = {}) {
|
|
2671
3028
|
if (!session) return;
|
|
2672
|
-
if (opts.failureFallback) synthesizeMissingToolResults(turns, await session.generateTurnId?.() ?? crypto.randomUUID(), runId, provider);
|
|
3029
|
+
if (opts.failureFallback) await synthesizeMissingToolResults(turns, await session.generateTurnId?.() ?? crypto.randomUUID(), runId, provider, hooks);
|
|
2673
3030
|
const remaining = turns.slice(lastPersistedTurnCount);
|
|
2674
3031
|
if (remaining.length > 0) {
|
|
2675
3032
|
await session.appendTurns(remaining);
|
|
@@ -2735,6 +3092,8 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
2735
3092
|
maxTurns,
|
|
2736
3093
|
maxTokens,
|
|
2737
3094
|
...session ? { session } : {},
|
|
3095
|
+
...agentReadState ? { readState: agentReadState } : {},
|
|
3096
|
+
...options.parentRunId ? { parentRunId: options.parentRunId } : {},
|
|
2738
3097
|
depth: runDepth,
|
|
2739
3098
|
thinkingBudget,
|
|
2740
3099
|
schema,
|
|
@@ -2748,6 +3107,8 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
2748
3107
|
...persistThreshold !== void 0 ? { persistThreshold } : {},
|
|
2749
3108
|
...persistExcludeTools !== void 0 ? { persistExcludeTools } : {},
|
|
2750
3109
|
...persistDir !== void 0 ? { persistDir } : {},
|
|
3110
|
+
...strictToolPairing ? { strictToolPairing: true } : {},
|
|
3111
|
+
providerName: provider.name,
|
|
2751
3112
|
runStartMs,
|
|
2752
3113
|
runToolCounts: {}
|
|
2753
3114
|
});
|
|
@@ -3296,12 +3657,14 @@ const edit = {
|
|
|
3296
3657
|
} catch {
|
|
3297
3658
|
return `Edit error: file not found: ${target}.${await suggestionFor(ctx.execution, ctx.handle, target)}`;
|
|
3298
3659
|
}
|
|
3299
|
-
if (ctx.behavior?.requireReadBeforeEdit
|
|
3300
|
-
const readState =
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3660
|
+
if (ctx.behavior?.requireReadBeforeEdit) {
|
|
3661
|
+
const readState = resolveReadStateMap(ctx);
|
|
3662
|
+
if (readState) {
|
|
3663
|
+
const absKey = readStateKey(ctx.handle.cwd, target);
|
|
3664
|
+
const prior = readState.get(absKey);
|
|
3665
|
+
if (!prior) return `Edit error: ${target} has not been read in this session. Call read_file first so the edit applies against the current contents. (Reads inside a \`spawn\` subagent with \`persist: false\` and without \`shareReadState: true\` do NOT propagate to the parent — re-read in the calling context.)`;
|
|
3666
|
+
if (prior.contentHash !== hashContent(original)) return `Edit error: ${target} has changed on disk since the last read. Re-read the file before editing.`;
|
|
3667
|
+
}
|
|
3305
3668
|
}
|
|
3306
3669
|
const match = resolveOldString(original, find);
|
|
3307
3670
|
if (!match) {
|
|
@@ -3314,11 +3677,11 @@ const edit = {
|
|
|
3314
3677
|
const updated = replaceAll ? original.split(actual).join(styledReplacement) : original.replace(actual, styledReplacement);
|
|
3315
3678
|
if (updated === original) return `Edit error: replacement produced no change in ${target}.`;
|
|
3316
3679
|
await ctx.execution.writeFile(ctx.handle, target, updated);
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
const absKey =
|
|
3320
|
-
const prior = readState
|
|
3321
|
-
if (
|
|
3680
|
+
const readState = resolveReadStateMap(ctx);
|
|
3681
|
+
if (readState) {
|
|
3682
|
+
const absKey = readStateKey(ctx.handle.cwd, target);
|
|
3683
|
+
const prior = readState.get(absKey);
|
|
3684
|
+
if (prior) readState.set(absKey, {
|
|
3322
3685
|
...prior,
|
|
3323
3686
|
contentHash: hashContent(updated),
|
|
3324
3687
|
mtimeMs: Date.now()
|
|
@@ -3757,12 +4120,14 @@ const multiEdit = {
|
|
|
3757
4120
|
} catch {
|
|
3758
4121
|
return `multi_edit error: file not found: ${target}.${await suggestionFor(ctx.execution, ctx.handle, target)}`;
|
|
3759
4122
|
}
|
|
3760
|
-
if (ctx.behavior?.requireReadBeforeEdit
|
|
3761
|
-
const readState =
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
4123
|
+
if (ctx.behavior?.requireReadBeforeEdit) {
|
|
4124
|
+
const readState = resolveReadStateMap(ctx);
|
|
4125
|
+
if (readState) {
|
|
4126
|
+
const absKey = readStateKey(ctx.handle.cwd, target);
|
|
4127
|
+
const prior = readState.get(absKey);
|
|
4128
|
+
if (!prior) return `multi_edit error: ${target} has not been read in this session. Call read_file first so the edits apply against the current contents. (Reads inside a \`spawn\` subagent with \`persist: false\` and without \`shareReadState: true\` do NOT propagate to the parent — re-read in the calling context.)`;
|
|
4129
|
+
if (prior.contentHash !== hashContent(current)) return `multi_edit error: ${target} has changed on disk since the last read. Re-read the file before editing.`;
|
|
4130
|
+
}
|
|
3766
4131
|
}
|
|
3767
4132
|
let totalReplacements = 0;
|
|
3768
4133
|
for (let i = 0; i < steps.length; i++) {
|
|
@@ -3782,11 +4147,11 @@ const multiEdit = {
|
|
|
3782
4147
|
totalReplacements += occurrences;
|
|
3783
4148
|
}
|
|
3784
4149
|
await ctx.execution.writeFile(ctx.handle, target, current);
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
const absKey =
|
|
3788
|
-
const prior = readState
|
|
3789
|
-
if (
|
|
4150
|
+
const readState = resolveReadStateMap(ctx);
|
|
4151
|
+
if (readState) {
|
|
4152
|
+
const absKey = readStateKey(ctx.handle.cwd, target);
|
|
4153
|
+
const prior = readState.get(absKey);
|
|
4154
|
+
if (prior) readState.set(absKey, {
|
|
3790
4155
|
...prior,
|
|
3791
4156
|
contentHash: hashContent(current),
|
|
3792
4157
|
mtimeMs: Date.now()
|
|
@@ -3936,14 +4301,16 @@ const readFile$1 = {
|
|
|
3936
4301
|
return `File not found: ${path}.${await suggestionFor(ctx.execution, ctx.handle, path)}`;
|
|
3937
4302
|
}
|
|
3938
4303
|
const totalBytes = Buffer.byteLength(raw);
|
|
3939
|
-
const
|
|
3940
|
-
const
|
|
4304
|
+
const dedupEnabled = ctx.behavior?.dedupReads !== false;
|
|
4305
|
+
const gateEnabled = ctx.behavior?.requireReadBeforeEdit === true;
|
|
4306
|
+
const readState = dedupEnabled || gateEnabled ? resolveReadStateMap(ctx) : void 0;
|
|
4307
|
+
const absKey = readStateKey(ctx.handle.cwd, path);
|
|
3941
4308
|
const offsetForKey = normalizeInteger(offset, 1);
|
|
3942
4309
|
const limitForKey = normalizeInteger(limit, DEFAULT_LINE_LIMIT);
|
|
3943
4310
|
const maxBytesForKey = normalizeInteger(maxBytes, DEFAULT_BYTE_CAP);
|
|
3944
4311
|
const showLineNumbers = typeof lineNumbers === "boolean" ? lineNumbers : ctx.behavior?.readLineNumbers ?? true;
|
|
3945
4312
|
const currentHash = readState ? hashContent(raw) : "";
|
|
3946
|
-
if (readState) {
|
|
4313
|
+
if (dedupEnabled && readState) {
|
|
3947
4314
|
const prior = readState.get(absKey);
|
|
3948
4315
|
if (prior && prior.contentHash === currentHash && prior.offset === offsetForKey && prior.limit === limitForKey && prior.maxBytes === maxBytesForKey && prior.lineNumbers === showLineNumbers) return `File ${path} unchanged since the previous read in this session — the prior result is still current.`;
|
|
3949
4316
|
}
|
|
@@ -4161,6 +4528,8 @@ const BUBBLED_EVENTS = [
|
|
|
4161
4528
|
"stream:text",
|
|
4162
4529
|
"stream:thinking",
|
|
4163
4530
|
"stream:end",
|
|
4531
|
+
"stream:error",
|
|
4532
|
+
"tool:dispatched",
|
|
4164
4533
|
"tool:before",
|
|
4165
4534
|
"tool:after",
|
|
4166
4535
|
"tool:error",
|
|
@@ -4175,6 +4544,8 @@ const CHILD_EVENT_NAME = {
|
|
|
4175
4544
|
"stream:text": "child:stream:text",
|
|
4176
4545
|
"stream:thinking": "child:stream:thinking",
|
|
4177
4546
|
"stream:end": "child:stream:end",
|
|
4547
|
+
"stream:error": "child:stream:error",
|
|
4548
|
+
"tool:dispatched": "child:tool:dispatched",
|
|
4178
4549
|
"tool:before": "child:tool:before",
|
|
4179
4550
|
"tool:after": "child:tool:after",
|
|
4180
4551
|
"tool:error": "child:tool:error",
|
|
@@ -4385,18 +4756,23 @@ function createSpawnTool(options = {}) {
|
|
|
4385
4756
|
let result = "";
|
|
4386
4757
|
let unbubble;
|
|
4387
4758
|
try {
|
|
4388
|
-
const
|
|
4759
|
+
const parentPreset = {
|
|
4389
4760
|
...ctx.name !== void 0 ? { name: ctx.name } : {},
|
|
4390
4761
|
...ctx.system !== void 0 ? { system: ctx.system } : {},
|
|
4391
4762
|
tools: ctx.tools,
|
|
4392
4763
|
...ctx.toolAliases !== void 0 ? { toolAliases: ctx.toolAliases } : {},
|
|
4393
4764
|
...ctx.mcpServers !== void 0 ? { mcpServers: ctx.mcpServers } : {},
|
|
4394
4765
|
...ctx.skills !== void 0 ? { skills: ctx.skills } : {},
|
|
4395
|
-
...ctx.behavior !== void 0 ? { behavior: ctx.behavior } : {}
|
|
4766
|
+
...ctx.behavior !== void 0 ? { behavior: ctx.behavior } : {}
|
|
4767
|
+
};
|
|
4768
|
+
const sharedReadState = options.shareReadState ? resolveReadStateMap(ctx) : void 0;
|
|
4769
|
+
const agent = createAgent({
|
|
4770
|
+
...parentPreset,
|
|
4396
4771
|
...options.preset,
|
|
4397
4772
|
provider: ctx.provider,
|
|
4398
4773
|
execution: ctx.execution,
|
|
4399
|
-
...options.persist && ctx.session ? { session: ctx.session } : {}
|
|
4774
|
+
...options.persist && ctx.session ? { session: ctx.session } : {},
|
|
4775
|
+
...sharedReadState ? { readState: sharedReadState } : {}
|
|
4400
4776
|
});
|
|
4401
4777
|
if (forwardHooks) {
|
|
4402
4778
|
const unregisterEnricher = agent.hooks.hook("tool:before", async (toolCtx) => {
|
|
@@ -4571,6 +4947,6 @@ const writeFile$1 = {
|
|
|
4571
4947
|
}
|
|
4572
4948
|
};
|
|
4573
4949
|
//#endregion
|
|
4574
|
-
export {
|
|
4950
|
+
export { readStateKey as A, PERSISTENCE_PREVIEW_BYTES as C, resolvePersistDir as D, maybePersistToolResult as E, getReadState as O, PERSISTED_STUB_PREFIX as S, cleanupPersistedSession as T, createSkillsReadTool as _, multiEdit as a, TOOL_USE_SKIPPED_MESSAGE as b, grep as c, resolveOldString as d, styleReplacementForVia as f, createSkillsRunScriptTool as g, createSkillsUseTool as h, readFile$1 as i, resolveReadStateMap as j, hashContent as k, glob as l, createToolSearchTool as m, createSpawnTool as n, listFiles as o, createAgent as p, shell as r, createInteractionTool as s, writeFile$1 as t, edit as u, INTERRUPT_MESSAGE_FOR_TOOL_USE as v, buildPersistedStub as w, validateToolArgs as x, TOOL_USE_AFTER_ERROR_MESSAGE as y };
|
|
4575
4951
|
|
|
4576
|
-
//# sourceMappingURL=tools-
|
|
4952
|
+
//# sourceMappingURL=tools-CCsL5SCO.js.map
|