zidane 5.2.1 → 5.3.1
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 +7 -5
- package/dist/{agent-CGQajqtC.d.ts → agent-bKs7MRT2.d.ts} +429 -4
- package/dist/agent-bKs7MRT2.d.ts.map +1 -0
- package/dist/chat.d.ts +212 -58
- 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-DwbcFBr_.d.ts → index-BlMvPh9X.d.ts} +29 -3
- package/dist/index-BlMvPh9X.d.ts.map +1 -0
- package/dist/{index-BDP6mA3Y.d.ts → index-CTmNaIDb.d.ts} +2 -2
- package/dist/{index-BDP6mA3Y.d.ts.map → index-CTmNaIDb.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-D7Tp-K5f.js → login-CNS9_8Ue.js} +3 -3
- package/dist/{login-D7Tp-K5f.js.map → login-CNS9_8Ue.js.map} +1 -1
- package/dist/{mcp-B1psg7jf.js → mcp-ZsSFo4Dp.js} +2 -2
- package/dist/{mcp-B1psg7jf.js.map → mcp-ZsSFo4Dp.js.map} +1 -1
- 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-AgF0RFx1.js → presets-h5i3kpOP.js} +2 -2
- package/dist/{presets-AgF0RFx1.js.map → presets-h5i3kpOP.js.map} +1 -1
- 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-BRbbfdJh.js → tools-CWEDS2ZT.js} +251 -47
- package/dist/tools-CWEDS2ZT.js.map +1 -0
- package/dist/tools.d.ts +2 -2
- package/dist/tools.js +1 -1
- package/dist/{transcript-anchors-BBuIoU0x.d.ts → transcript-anchors-DOUqyvXR.d.ts} +28 -4
- package/dist/transcript-anchors-DOUqyvXR.d.ts.map +1 -0
- package/dist/tui.d.ts +29 -3
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +363 -28
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-gJ0qtLPv.js → turn-operations-D9HvatsR.js} +396 -89
- package/dist/turn-operations-D9HvatsR.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 +3 -2
- package/docs/CHAT.md +55 -16
- package/docs/TUI.md +22 -2
- package/package.json +1 -1
- package/dist/agent-CGQajqtC.d.ts.map +0 -1
- package/dist/errors-COmsomd5.js.map +0 -1
- package/dist/index-DwbcFBr_.d.ts.map +0 -1
- package/dist/messages-DsbMYNmt.js.map +0 -1
- package/dist/providers-v1Rn2rqG.js.map +0 -1
- package/dist/tools-BRbbfdJh.js.map +0 -1
- package/dist/transcript-anchors-BBuIoU0x.d.ts.map +0 -1
- package/dist/turn-operations-gJ0qtLPv.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-ZsSFo4Dp.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";
|
|
@@ -756,6 +756,30 @@ function formatValue(value) {
|
|
|
756
756
|
//#region src/loop.ts
|
|
757
757
|
const IMAGE_OMITTED_MARKER = "[image omitted — model does not support vision]";
|
|
758
758
|
/**
|
|
759
|
+
* Canonical tool_result text emitted when a tool call is interrupted by the
|
|
760
|
+
* user mid-flight (Esc / Ctrl-C / external `AbortSignal`). Mirrors Claude
|
|
761
|
+
* Code's `INTERRUPT_MESSAGE_FOR_TOOL_USE` so downstream consumers can pattern
|
|
762
|
+
* match a single string across both harnesses. Always paired with
|
|
763
|
+
* `isError: true` on the wire — the model treats it as a failed call rather
|
|
764
|
+
* than a successful tool response.
|
|
765
|
+
*/
|
|
766
|
+
const INTERRUPT_MESSAGE_FOR_TOOL_USE = "[Request interrupted by user for tool use]";
|
|
767
|
+
/**
|
|
768
|
+
* Canonical tool_result text emitted when a tool call is skipped because a
|
|
769
|
+
* sibling sequential call errored or a steering message arrived between
|
|
770
|
+
* iterations of {@link executeToolsSequential}. Distinguished from
|
|
771
|
+
* {@link INTERRUPT_MESSAGE_FOR_TOOL_USE} so consumers can distinguish "user
|
|
772
|
+
* cancelled" from "framework superseded".
|
|
773
|
+
*/
|
|
774
|
+
const TOOL_USE_SKIPPED_MESSAGE = "[Tool use skipped — superseded by user message]";
|
|
775
|
+
/**
|
|
776
|
+
* Canonical tool_result text emitted when the loop catches a sequential
|
|
777
|
+
* sibling that threw and synthesizes follow-up results for the remaining
|
|
778
|
+
* queued calls. Distinct from {@link TOOL_USE_SKIPPED_MESSAGE} so telemetry
|
|
779
|
+
* can split "skipped by user steering" from "skipped after error".
|
|
780
|
+
*/
|
|
781
|
+
const TOOL_USE_AFTER_ERROR_MESSAGE = "[Tool use skipped — previous tool call in batch threw]";
|
|
782
|
+
/**
|
|
759
783
|
* Compute the effective thinking budget for a given run-relative turn, given
|
|
760
784
|
* the configured decay schedule. Pure helper — exported for tests and so
|
|
761
785
|
* downstream tooling can preview decay curves without spinning up the loop.
|
|
@@ -1057,6 +1081,32 @@ function invalidateReadStateForElidedPaths(session, elidedPaths) {
|
|
|
1057
1081
|
const suffixes = elidedPaths.map((p) => `::${p}`);
|
|
1058
1082
|
for (const key of [...readState.keys()]) if (suffixes.some((s) => key.endsWith(s))) readState.delete(key);
|
|
1059
1083
|
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Run {@link ensureToolResultPairing} with the loop's hook + strict-mode
|
|
1086
|
+
* context plugged in. Centralized so the pre-send path and the schema-
|
|
1087
|
+
* enforcement path share identical telemetry + throw semantics.
|
|
1088
|
+
*
|
|
1089
|
+
* The captured `repairs` array is what `AgentToolPairingError` carries on
|
|
1090
|
+
* strict-mode throws, and it's what `pairing:repair` fires from. Hook
|
|
1091
|
+
* notifications run AFTER the pass completes so they don't interfere with
|
|
1092
|
+
* the synchronous walk — and we drop them on the floor in strict mode (the
|
|
1093
|
+
* throw is more informative than a fire-and-forget log).
|
|
1094
|
+
*/
|
|
1095
|
+
function applyPairingRepair(ctx, messages, turnId) {
|
|
1096
|
+
const repairs = [];
|
|
1097
|
+
const repaired = ensureToolResultPairing(messages, { onRepair: (repair) => repairs.push(repair) });
|
|
1098
|
+
if (repairs.length === 0) return repaired;
|
|
1099
|
+
if (ctx.strictToolPairing) throw new AgentToolPairingError({
|
|
1100
|
+
message: `Tool pairing corruption detected (${repairs.length} repair${repairs.length === 1 ? "" : "s"}); strict mode is on so the request was not sent.`,
|
|
1101
|
+
...ctx.providerName ? { provider: ctx.providerName } : {},
|
|
1102
|
+
repairs
|
|
1103
|
+
});
|
|
1104
|
+
for (const repair of repairs) ctx.hooks.callHook("pairing:repair", {
|
|
1105
|
+
...repair,
|
|
1106
|
+
turnId
|
|
1107
|
+
});
|
|
1108
|
+
return repaired;
|
|
1109
|
+
}
|
|
1060
1110
|
function sanitizeStoredToolResults(provider, messages) {
|
|
1061
1111
|
if (provider.meta.capabilities?.vision !== false) return messages;
|
|
1062
1112
|
return messages.map((msg) => {
|
|
@@ -1188,6 +1238,27 @@ function wrapProviderError(err, ctx) {
|
|
|
1188
1238
|
cause: err
|
|
1189
1239
|
});
|
|
1190
1240
|
}
|
|
1241
|
+
/** Max bytes of provider error text inlined into the assistant turn placeholder. */
|
|
1242
|
+
const ERROR_PLACEHOLDER_MAX = 280;
|
|
1243
|
+
/**
|
|
1244
|
+
* Build the assistant-turn placeholder text when the provider throws before
|
|
1245
|
+
* streaming any output. Inlines the underlying error message (truncated and
|
|
1246
|
+
* stripped of stack-like newlines) so the persisted turn carries a useful
|
|
1247
|
+
* diagnostic — the human reading the transcript sees the failure mode
|
|
1248
|
+
* without having to attach a debugger, and `tool_search` schema rejections
|
|
1249
|
+
* become self-explanatory.
|
|
1250
|
+
*
|
|
1251
|
+
* The bracketed `[✗ Streaming failed: ...]` shape preserves the prior
|
|
1252
|
+
* format that hosts may pattern-match on while adding the new payload
|
|
1253
|
+
* suffix. Falls back to the original generic placeholder when no message
|
|
1254
|
+
* can be extracted.
|
|
1255
|
+
*/
|
|
1256
|
+
function buildStreamErrorPlaceholder(err) {
|
|
1257
|
+
const raw = errorMessage(err).trim();
|
|
1258
|
+
if (raw.length === 0) return "[✗ Streaming failed before any output.]";
|
|
1259
|
+
const oneLine = raw.replace(/\s+/g, " ");
|
|
1260
|
+
return `[✗ Streaming failed before any output: ${oneLine.length > ERROR_PLACEHOLDER_MAX ? `${oneLine.slice(0, ERROR_PLACEHOLDER_MAX - 1).trimEnd()}…` : oneLine}]`;
|
|
1261
|
+
}
|
|
1191
1262
|
async function executeTurn(ctx, turn) {
|
|
1192
1263
|
const turnId = await ctx.generateTurnId();
|
|
1193
1264
|
let canonicalMessages = turnsToMessages(applyCompactSummaryCutoff(ctx.turns));
|
|
@@ -1219,7 +1290,7 @@ async function executeTurn(ctx, turn) {
|
|
|
1219
1290
|
const transformCtx = { messages: streamOptions.messages };
|
|
1220
1291
|
await ctx.hooks.callHook("context:transform", transformCtx);
|
|
1221
1292
|
streamOptions.messages = transformCtx.messages;
|
|
1222
|
-
streamOptions.messages =
|
|
1293
|
+
streamOptions.messages = applyPairingRepair(ctx, streamOptions.messages, turnId);
|
|
1223
1294
|
const systemCtx = {
|
|
1224
1295
|
system: streamOptions.system,
|
|
1225
1296
|
messages: streamOptions.messages,
|
|
@@ -1265,12 +1336,13 @@ async function executeTurn(ctx, turn) {
|
|
|
1265
1336
|
input: 0,
|
|
1266
1337
|
output: 0
|
|
1267
1338
|
};
|
|
1339
|
+
const placeholderText = wasAborted ? "[⏹ Streaming was aborted.]" : buildStreamErrorPlaceholder(err);
|
|
1268
1340
|
const errorContent = currentText ? [{
|
|
1269
1341
|
type: "text",
|
|
1270
1342
|
text: currentText
|
|
1271
1343
|
}] : [{
|
|
1272
1344
|
type: "text",
|
|
1273
|
-
text:
|
|
1345
|
+
text: placeholderText
|
|
1274
1346
|
}];
|
|
1275
1347
|
const errorTurn = {
|
|
1276
1348
|
id: turnId,
|
|
@@ -1281,6 +1353,10 @@ async function executeTurn(ctx, turn) {
|
|
|
1281
1353
|
createdAt: Date.now()
|
|
1282
1354
|
};
|
|
1283
1355
|
ctx.turns.push(errorTurn);
|
|
1356
|
+
if (!wasAborted) await ctx.hooks.callHook("stream:error", {
|
|
1357
|
+
err,
|
|
1358
|
+
turnId
|
|
1359
|
+
});
|
|
1284
1360
|
await ctx.hooks.callHook("turn:after", {
|
|
1285
1361
|
turn,
|
|
1286
1362
|
turnId,
|
|
@@ -1333,7 +1409,7 @@ async function executeTurn(ctx, turn) {
|
|
|
1333
1409
|
description: "Return the final structured output matching the required schema.",
|
|
1334
1410
|
inputSchema: ctx.schema
|
|
1335
1411
|
};
|
|
1336
|
-
const schemaMessages =
|
|
1412
|
+
const schemaMessages = applyPairingRepair(ctx, rewriteMessagesToWire(turnsToMessages(applyCompactSummaryCutoff(ctx.turns)), ctx.aliasMaps), turnId);
|
|
1337
1413
|
let schemaResult;
|
|
1338
1414
|
try {
|
|
1339
1415
|
schemaResult = await ctx.provider.stream({
|
|
@@ -1479,14 +1555,35 @@ async function executeSingleTool(ctx, call, turnId) {
|
|
|
1479
1555
|
runToolCounts
|
|
1480
1556
|
};
|
|
1481
1557
|
await ctx.hooks.callHook("tool:gate", gateCtx);
|
|
1482
|
-
if (gateCtx.block)
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1558
|
+
if (gateCtx.block) {
|
|
1559
|
+
await fireDispatched(ctx, {
|
|
1560
|
+
turnId,
|
|
1561
|
+
callId,
|
|
1562
|
+
name: call.name,
|
|
1563
|
+
displayName,
|
|
1564
|
+
input: gateCtx.input,
|
|
1565
|
+
outcome: "gate-block",
|
|
1566
|
+
reason: gateCtx.reason,
|
|
1567
|
+
runToolCounts
|
|
1568
|
+
});
|
|
1569
|
+
return { result: {
|
|
1570
|
+
id: callId,
|
|
1571
|
+
content: `Blocked: ${gateCtx.reason}`,
|
|
1572
|
+
isError: true
|
|
1573
|
+
} };
|
|
1574
|
+
}
|
|
1486
1575
|
ctx.runToolCounts[call.name] = (ctx.runToolCounts[call.name] ?? 0) + 1;
|
|
1487
|
-
if (gateCtx.result !== void 0)
|
|
1488
|
-
|
|
1489
|
-
|
|
1576
|
+
if (gateCtx.result !== void 0) {
|
|
1577
|
+
await fireDispatched(ctx, {
|
|
1578
|
+
turnId,
|
|
1579
|
+
callId,
|
|
1580
|
+
name: call.name,
|
|
1581
|
+
displayName,
|
|
1582
|
+
input: gateCtx.input,
|
|
1583
|
+
outcome: "gate-substitute",
|
|
1584
|
+
runToolCounts
|
|
1585
|
+
});
|
|
1586
|
+
const emitted = await emitToolResult(ctx, {
|
|
1490
1587
|
turnId,
|
|
1491
1588
|
callId,
|
|
1492
1589
|
name: call.name,
|
|
@@ -1495,8 +1592,13 @@ async function executeSingleTool(ctx, call, turnId) {
|
|
|
1495
1592
|
output: gateCtx.result,
|
|
1496
1593
|
isError: false,
|
|
1497
1594
|
runToolCounts
|
|
1498
|
-
})
|
|
1499
|
-
|
|
1595
|
+
});
|
|
1596
|
+
return { result: {
|
|
1597
|
+
id: callId,
|
|
1598
|
+
content: emitted.output,
|
|
1599
|
+
...emitted.isError ? { isError: true } : {}
|
|
1600
|
+
} };
|
|
1601
|
+
}
|
|
1500
1602
|
let effectiveInput = gateCtx.input;
|
|
1501
1603
|
if (!toolDef) {
|
|
1502
1604
|
const unknownCtx = {
|
|
@@ -1509,6 +1611,7 @@ async function executeSingleTool(ctx, call, turnId) {
|
|
|
1509
1611
|
};
|
|
1510
1612
|
await ctx.hooks.callHook("tool:unknown", unknownCtx);
|
|
1511
1613
|
const content = unknownCtx.result ?? `Tool error: Unknown tool: ${call.name}`;
|
|
1614
|
+
const isError = unknownCtx.result === void 0;
|
|
1512
1615
|
if (!unknownCtx.suppressError) {
|
|
1513
1616
|
const err = /* @__PURE__ */ new Error(`Unknown tool: ${call.name}`);
|
|
1514
1617
|
await ctx.hooks.callHook("tool:error", {
|
|
@@ -1520,9 +1623,19 @@ async function executeSingleTool(ctx, call, turnId) {
|
|
|
1520
1623
|
error: err
|
|
1521
1624
|
});
|
|
1522
1625
|
}
|
|
1626
|
+
await fireDispatched(ctx, {
|
|
1627
|
+
turnId,
|
|
1628
|
+
callId,
|
|
1629
|
+
name: call.name,
|
|
1630
|
+
displayName,
|
|
1631
|
+
input: effectiveInput,
|
|
1632
|
+
outcome: "unknown",
|
|
1633
|
+
runToolCounts
|
|
1634
|
+
});
|
|
1523
1635
|
return { result: {
|
|
1524
1636
|
id: callId,
|
|
1525
|
-
content
|
|
1637
|
+
content,
|
|
1638
|
+
...isError ? { isError: true } : {}
|
|
1526
1639
|
} };
|
|
1527
1640
|
}
|
|
1528
1641
|
const validation = validateToolArgs(effectiveInput, toolDef.spec.inputSchema);
|
|
@@ -1536,9 +1649,19 @@ async function executeSingleTool(ctx, call, turnId) {
|
|
|
1536
1649
|
reason: validation.error ?? "invalid input",
|
|
1537
1650
|
schema: toolDef.spec.inputSchema
|
|
1538
1651
|
});
|
|
1652
|
+
await fireDispatched(ctx, {
|
|
1653
|
+
turnId,
|
|
1654
|
+
callId,
|
|
1655
|
+
name: call.name,
|
|
1656
|
+
displayName,
|
|
1657
|
+
input: effectiveInput,
|
|
1658
|
+
outcome: "invalid-input",
|
|
1659
|
+
runToolCounts
|
|
1660
|
+
});
|
|
1539
1661
|
return { result: {
|
|
1540
1662
|
id: callId,
|
|
1541
|
-
content: `Validation error: ${validation.error}
|
|
1663
|
+
content: `Validation error: ${validation.error}`,
|
|
1664
|
+
isError: true
|
|
1542
1665
|
} };
|
|
1543
1666
|
}
|
|
1544
1667
|
effectiveInput = validation.coercedInput ?? effectiveInput;
|
|
@@ -1552,6 +1675,15 @@ async function executeSingleTool(ctx, call, turnId) {
|
|
|
1552
1675
|
coercions,
|
|
1553
1676
|
schema: toolDef.spec.inputSchema
|
|
1554
1677
|
});
|
|
1678
|
+
await fireDispatched(ctx, {
|
|
1679
|
+
turnId,
|
|
1680
|
+
callId,
|
|
1681
|
+
name: call.name,
|
|
1682
|
+
displayName,
|
|
1683
|
+
input: effectiveInput,
|
|
1684
|
+
outcome: "execute",
|
|
1685
|
+
runToolCounts
|
|
1686
|
+
});
|
|
1555
1687
|
await ctx.hooks.callHook("tool:before", {
|
|
1556
1688
|
turnId,
|
|
1557
1689
|
callId,
|
|
@@ -1598,26 +1730,54 @@ async function executeSingleTool(ctx, call, turnId) {
|
|
|
1598
1730
|
output = errorCtx.result ?? `Tool error: ${error.message}`;
|
|
1599
1731
|
isError = true;
|
|
1600
1732
|
}
|
|
1733
|
+
const emitted = await emitToolResult(ctx, {
|
|
1734
|
+
turnId,
|
|
1735
|
+
callId,
|
|
1736
|
+
name: call.name,
|
|
1737
|
+
displayName,
|
|
1738
|
+
input: effectiveInput,
|
|
1739
|
+
output,
|
|
1740
|
+
isError,
|
|
1741
|
+
runToolCounts,
|
|
1742
|
+
...coercions ? { coercions } : {}
|
|
1743
|
+
});
|
|
1601
1744
|
return { result: {
|
|
1602
1745
|
id: callId,
|
|
1603
|
-
content:
|
|
1604
|
-
|
|
1605
|
-
callId,
|
|
1606
|
-
name: call.name,
|
|
1607
|
-
displayName,
|
|
1608
|
-
input: effectiveInput,
|
|
1609
|
-
output,
|
|
1610
|
-
isError,
|
|
1611
|
-
runToolCounts,
|
|
1612
|
-
...coercions ? { coercions } : {}
|
|
1613
|
-
})
|
|
1746
|
+
content: emitted.output,
|
|
1747
|
+
...emitted.isError ? { isError: true } : {}
|
|
1614
1748
|
} };
|
|
1615
1749
|
}
|
|
1616
1750
|
/**
|
|
1751
|
+
* Fire `tool:dispatched` with the resolved path discriminator. Every code
|
|
1752
|
+
* path in {@link executeSingleTool} that produces a tool_result must call
|
|
1753
|
+
* this exactly once — that contract is what makes `tool:dispatched` ↔
|
|
1754
|
+
* `tool:after` a guaranteed symmetric pairing for live-event consumers
|
|
1755
|
+
* (the chat layer, SDK consumers reconstructing wire history from events,
|
|
1756
|
+
* tracing spans that want to record refused calls separately from
|
|
1757
|
+
* executed ones).
|
|
1758
|
+
*
|
|
1759
|
+
* Helper exists to centralize the optional-field plumbing for `reason`
|
|
1760
|
+
* (only set on `gate-block`) without sprinkling spread-conditional logic
|
|
1761
|
+
* across five call sites.
|
|
1762
|
+
*/
|
|
1763
|
+
async function fireDispatched(ctx, params) {
|
|
1764
|
+
const { reason, ...rest } = params;
|
|
1765
|
+
await ctx.hooks.callHook("tool:dispatched", {
|
|
1766
|
+
...rest,
|
|
1767
|
+
...reason !== void 0 ? { reason } : {}
|
|
1768
|
+
});
|
|
1769
|
+
}
|
|
1770
|
+
/**
|
|
1617
1771
|
* Shared post-output emission: fire `tool:transform` (mutate-allowed), strip
|
|
1618
1772
|
* images for non-vision providers, fire `tool:after`. Used by both the
|
|
1619
1773
|
* gate-substitute (Z20) and post-execute paths so they stay byte-for-byte
|
|
1620
1774
|
* identical from the consumer's perspective.
|
|
1775
|
+
*
|
|
1776
|
+
* Returns both the (possibly transformed) output and the final `isError`
|
|
1777
|
+
* flag — `tool:transform` listeners can flip the flag in either direction
|
|
1778
|
+
* (e.g. rewrite a structured error response to a graceful retry hint), and
|
|
1779
|
+
* the caller needs the post-transform value to populate `ToolResult.isError`
|
|
1780
|
+
* on the wire.
|
|
1621
1781
|
*/
|
|
1622
1782
|
async function emitToolResult(ctx, params) {
|
|
1623
1783
|
const { turnId, callId, name, displayName, input, runToolCounts, coercions } = params;
|
|
@@ -1663,7 +1823,10 @@ async function emitToolResult(ctx, params) {
|
|
|
1663
1823
|
runToolCounts,
|
|
1664
1824
|
...coercions ? { coercions } : {}
|
|
1665
1825
|
});
|
|
1666
|
-
return
|
|
1826
|
+
return {
|
|
1827
|
+
output,
|
|
1828
|
+
isError
|
|
1829
|
+
};
|
|
1667
1830
|
}
|
|
1668
1831
|
async function executeToolsSequential(ctx, toolCalls, turnId) {
|
|
1669
1832
|
const results = [];
|
|
@@ -1672,14 +1835,16 @@ async function executeToolsSequential(ctx, toolCalls, turnId) {
|
|
|
1672
1835
|
if (ctx.signal.aborted) {
|
|
1673
1836
|
for (let j = i; j < toolCalls.length; j++) results.push({
|
|
1674
1837
|
id: toolCalls[j].id,
|
|
1675
|
-
content:
|
|
1838
|
+
content: INTERRUPT_MESSAGE_FOR_TOOL_USE,
|
|
1839
|
+
isError: true
|
|
1676
1840
|
});
|
|
1677
1841
|
return results;
|
|
1678
1842
|
}
|
|
1679
1843
|
if (ctx.steeringQueue.length > 0) {
|
|
1680
1844
|
for (let j = i; j < toolCalls.length; j++) results.push({
|
|
1681
1845
|
id: toolCalls[j].id,
|
|
1682
|
-
content:
|
|
1846
|
+
content: TOOL_USE_SKIPPED_MESSAGE,
|
|
1847
|
+
isError: true
|
|
1683
1848
|
});
|
|
1684
1849
|
return results;
|
|
1685
1850
|
}
|
|
@@ -1689,11 +1854,13 @@ async function executeToolsSequential(ctx, toolCalls, turnId) {
|
|
|
1689
1854
|
} catch (err) {
|
|
1690
1855
|
results.push({
|
|
1691
1856
|
id: call.id,
|
|
1692
|
-
content: `Error: ${errorMessage(err)}
|
|
1857
|
+
content: `Error: ${errorMessage(err)}`,
|
|
1858
|
+
isError: true
|
|
1693
1859
|
});
|
|
1694
1860
|
for (let j = i + 1; j < toolCalls.length; j++) results.push({
|
|
1695
1861
|
id: toolCalls[j].id,
|
|
1696
|
-
content:
|
|
1862
|
+
content: TOOL_USE_AFTER_ERROR_MESSAGE,
|
|
1863
|
+
isError: true
|
|
1697
1864
|
});
|
|
1698
1865
|
return results;
|
|
1699
1866
|
}
|
|
@@ -1704,9 +1871,12 @@ async function executeToolsParallel(ctx, toolCalls, turnId) {
|
|
|
1704
1871
|
const executions = toolCalls.map((call) => executeSingleTool(ctx, call, turnId));
|
|
1705
1872
|
return (await Promise.allSettled(executions)).map((s, i) => {
|
|
1706
1873
|
if (s.status === "fulfilled") return s.value.result;
|
|
1874
|
+
const reason = s.reason;
|
|
1875
|
+
const isAbort = ctx.signal.aborted || reason instanceof Error && reason.name === "AbortError";
|
|
1707
1876
|
return {
|
|
1708
1877
|
id: toolCalls[i].id,
|
|
1709
|
-
content: `Error: ${
|
|
1878
|
+
content: isAbort ? INTERRUPT_MESSAGE_FOR_TOOL_USE : `Error: ${reason instanceof Error ? reason.message : String(reason)}`,
|
|
1879
|
+
isError: true
|
|
1710
1880
|
};
|
|
1711
1881
|
});
|
|
1712
1882
|
}
|
|
@@ -2285,8 +2455,10 @@ const HOOK_EVENT_SET = new Set([
|
|
|
2285
2455
|
"stream:text",
|
|
2286
2456
|
"stream:end",
|
|
2287
2457
|
"stream:thinking",
|
|
2458
|
+
"stream:error",
|
|
2288
2459
|
"oauth:refresh",
|
|
2289
2460
|
"tool:gate",
|
|
2461
|
+
"tool:dispatched",
|
|
2290
2462
|
"tool:before",
|
|
2291
2463
|
"tool:after",
|
|
2292
2464
|
"tool:error",
|
|
@@ -2303,8 +2475,10 @@ const HOOK_EVENT_SET = new Set([
|
|
|
2303
2475
|
"child:stream:text",
|
|
2304
2476
|
"child:stream:thinking",
|
|
2305
2477
|
"child:stream:end",
|
|
2478
|
+
"child:stream:error",
|
|
2306
2479
|
"child:tool:gate",
|
|
2307
2480
|
"child:mcp:tool:gate",
|
|
2481
|
+
"child:tool:dispatched",
|
|
2308
2482
|
"child:tool:before",
|
|
2309
2483
|
"child:tool:after",
|
|
2310
2484
|
"child:tool:transform",
|
|
@@ -2333,6 +2507,7 @@ const HOOK_EVENT_SET = new Set([
|
|
|
2333
2507
|
"output",
|
|
2334
2508
|
"budget:exceeded",
|
|
2335
2509
|
"tool-budget:exceeded",
|
|
2510
|
+
"pairing:repair",
|
|
2336
2511
|
"agent:abort",
|
|
2337
2512
|
"agent:done",
|
|
2338
2513
|
"session:start",
|
|
@@ -2352,6 +2527,16 @@ function isKnownHookEvent(event) {
|
|
|
2352
2527
|
* loop from leaving the persisted session with an orphan tool_use — which
|
|
2353
2528
|
* Anthropic rejects on resume.
|
|
2354
2529
|
*
|
|
2530
|
+
* Each synthetic result carries the shared
|
|
2531
|
+
* {@link SYNTHETIC_TOOL_RESULT_PLACEHOLDER} text (so consumers can pattern-
|
|
2532
|
+
* match the same way they would on a live pre-send repair) and
|
|
2533
|
+
* `isError: true` so the model treats it as a failed call.
|
|
2534
|
+
*
|
|
2535
|
+
* Fires `pairing:repair` (mode `orphan-tool-use-append`) for each synthetic
|
|
2536
|
+
* result, mirroring the wire-level repair pass's observability so postmortem
|
|
2537
|
+
* dashboards see the same telemetry shape regardless of where the orphan
|
|
2538
|
+
* was detected.
|
|
2539
|
+
*
|
|
2355
2540
|
* No-op when:
|
|
2356
2541
|
* - The trailing turn isn't an assistant turn (already closed, or session
|
|
2357
2542
|
* ends with the seeded user prompt).
|
|
@@ -2359,7 +2544,7 @@ function isKnownHookEvent(event) {
|
|
|
2359
2544
|
* - All tool_use ids are already answered by a tool_result somewhere later
|
|
2360
2545
|
* in the conversation (defensive — shouldn't happen but cheap to check).
|
|
2361
2546
|
*/
|
|
2362
|
-
function synthesizeMissingToolResults(turns, syntheticTurnId, runId, provider) {
|
|
2547
|
+
async function synthesizeMissingToolResults(turns, syntheticTurnId, runId, provider, hooks) {
|
|
2363
2548
|
if (turns.length === 0) return;
|
|
2364
2549
|
const last = turns[turns.length - 1];
|
|
2365
2550
|
if (last.role !== "assistant") return;
|
|
@@ -2372,7 +2557,8 @@ function synthesizeMissingToolResults(turns, syntheticTurnId, runId, provider) {
|
|
|
2372
2557
|
if (dangling.length === 0) return;
|
|
2373
2558
|
const results = dangling.map((id) => ({
|
|
2374
2559
|
id,
|
|
2375
|
-
content:
|
|
2560
|
+
content: SYNTHETIC_TOOL_RESULT_PLACEHOLDER,
|
|
2561
|
+
isError: true
|
|
2376
2562
|
}));
|
|
2377
2563
|
const msg = provider.toolResultsMessage(results);
|
|
2378
2564
|
turns.push({
|
|
@@ -2382,6 +2568,11 @@ function synthesizeMissingToolResults(turns, syntheticTurnId, runId, provider) {
|
|
|
2382
2568
|
content: msg.content,
|
|
2383
2569
|
createdAt: Date.now()
|
|
2384
2570
|
});
|
|
2571
|
+
for (const callId of dangling) await hooks.callHook("pairing:repair", {
|
|
2572
|
+
mode: "orphan-tool-use-append",
|
|
2573
|
+
callId,
|
|
2574
|
+
messageIndex: turns.length - 2
|
|
2575
|
+
});
|
|
2385
2576
|
}
|
|
2386
2577
|
function resolveBehavior(agentBehavior, runBehavior) {
|
|
2387
2578
|
return {
|
|
@@ -2406,7 +2597,8 @@ function resolveBehavior(agentBehavior, runBehavior) {
|
|
|
2406
2597
|
toolSearch: runBehavior?.toolSearch ?? agentBehavior?.toolSearch,
|
|
2407
2598
|
persistThreshold: runBehavior?.persistThreshold ?? agentBehavior?.persistThreshold,
|
|
2408
2599
|
persistExcludeTools: runBehavior?.persistExcludeTools ?? agentBehavior?.persistExcludeTools,
|
|
2409
|
-
persistDir: runBehavior?.persistDir ?? agentBehavior?.persistDir
|
|
2600
|
+
persistDir: runBehavior?.persistDir ?? agentBehavior?.persistDir,
|
|
2601
|
+
strictToolPairing: runBehavior?.strictToolPairing ?? agentBehavior?.strictToolPairing ?? false
|
|
2410
2602
|
};
|
|
2411
2603
|
}
|
|
2412
2604
|
/**
|
|
@@ -2494,7 +2686,7 @@ function buildSearchableCatalog(entries, options) {
|
|
|
2494
2686
|
}
|
|
2495
2687
|
const serverNames = [...byServer.keys()].sort();
|
|
2496
2688
|
const parts = [];
|
|
2497
|
-
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
|
|
2689
|
+
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.`, "");
|
|
2498
2690
|
parts.push("<searchable_tools>");
|
|
2499
2691
|
for (const server of serverNames) {
|
|
2500
2692
|
parts.push(` <server name="${escapeXml(server)}">`);
|
|
@@ -2622,9 +2814,14 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
2622
2814
|
if (running) throw new Error("Agent is already running. Use steer() or followUp() to queue messages, or waitForIdle().");
|
|
2623
2815
|
const hasSessionTurns = session && session.turns.length > 0;
|
|
2624
2816
|
if (!options.prompt && !hasSessionTurns) throw new Error("prompt is required when no session with existing turns is provided");
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2817
|
+
let resumeFilteredTurns;
|
|
2818
|
+
if (hasSessionTurns) resumeFilteredTurns = filterUnresolvedToolUses(session.turns);
|
|
2819
|
+
if (!options.prompt && resumeFilteredTurns) {
|
|
2820
|
+
const lastTurn = resumeFilteredTurns.at(-1);
|
|
2821
|
+
if (lastTurn && lastTurn.role !== "user") {
|
|
2822
|
+
const detail = detectTurnInterruption(resumeFilteredTurns) === "completed" ? "last turn is a completed assistant message" : "last turn is mid-stream assistant content";
|
|
2823
|
+
throw new Error(`cannot resume without prompt: ${detail}. Pass a prompt to agent.run({ prompt: … }).`);
|
|
2824
|
+
}
|
|
2628
2825
|
}
|
|
2629
2826
|
let externalAbortListener;
|
|
2630
2827
|
const externalSignal = options.signal;
|
|
@@ -2689,7 +2886,7 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
2689
2886
|
const thinking = options.thinking ?? "off";
|
|
2690
2887
|
const model = options.model ?? provider.meta.defaultModel;
|
|
2691
2888
|
const resolvedBehavior = resolveBehavior(agentBehavior, options.behavior);
|
|
2692
|
-
const { toolExecution, maxTurns, maxTokens, thinkingBudget, schema, cache, toolOutputBudget, compactStrategy, compactThreshold, compactKeepTurns, thinkingDecay, dedupTools, toolBudgets, elideStaleReads, toolDisclosure, toolSearch, persistThreshold, persistExcludeTools, persistDir } = resolvedBehavior;
|
|
2889
|
+
const { toolExecution, maxTurns, maxTokens, thinkingBudget, schema, cache, toolOutputBudget, compactStrategy, compactThreshold, compactKeepTurns, thinkingDecay, dedupTools, toolBudgets, elideStaleReads, toolDisclosure, toolSearch, persistThreshold, persistExcludeTools, persistDir, strictToolPairing } = resolvedBehavior;
|
|
2693
2890
|
let system = options.system || agentSystem || "You are a helpful assistant.";
|
|
2694
2891
|
if (skillsCatalog) system = `${system}\n\n${skillsCatalog}`;
|
|
2695
2892
|
const runBaseTools = options.tools !== void 0 ? options.tools : mcpConnection ? {
|
|
@@ -2754,7 +2951,8 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
2754
2951
|
if (isResume) {
|
|
2755
2952
|
const childRunIds = new Set(session.runs.filter((r) => (r.depth ?? 0) > 0).map((r) => r.id));
|
|
2756
2953
|
const resumed = childRunIds.size === 0 ? session.turns : session.turns.filter((t) => !t.runId || !childRunIds.has(t.runId));
|
|
2757
|
-
turns
|
|
2954
|
+
const filteredForRuntime = resumeFilteredTurns && resumed === session.turns ? resumeFilteredTurns : filterUnresolvedToolUses(resumed);
|
|
2955
|
+
turns.push(...filteredForRuntime);
|
|
2758
2956
|
}
|
|
2759
2957
|
const runTurnStart = turns.length;
|
|
2760
2958
|
if (options.system) await hooks.callHook("system:before", { system: options.system });
|
|
@@ -2797,7 +2995,7 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
2797
2995
|
const unregisterToolResultsSync = session ? hooks.hook("tool-results:after", persistPendingTurns) : void 0;
|
|
2798
2996
|
async function flushTurns(opts = {}) {
|
|
2799
2997
|
if (!session) return;
|
|
2800
|
-
if (opts.failureFallback) synthesizeMissingToolResults(turns, await session.generateTurnId?.() ?? crypto.randomUUID(), runId, provider);
|
|
2998
|
+
if (opts.failureFallback) await synthesizeMissingToolResults(turns, await session.generateTurnId?.() ?? crypto.randomUUID(), runId, provider, hooks);
|
|
2801
2999
|
const remaining = turns.slice(lastPersistedTurnCount);
|
|
2802
3000
|
if (remaining.length > 0) {
|
|
2803
3001
|
await session.appendTurns(remaining);
|
|
@@ -2876,6 +3074,8 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
2876
3074
|
...persistThreshold !== void 0 ? { persistThreshold } : {},
|
|
2877
3075
|
...persistExcludeTools !== void 0 ? { persistExcludeTools } : {},
|
|
2878
3076
|
...persistDir !== void 0 ? { persistDir } : {},
|
|
3077
|
+
...strictToolPairing ? { strictToolPairing: true } : {},
|
|
3078
|
+
providerName: provider.name,
|
|
2879
3079
|
runStartMs,
|
|
2880
3080
|
runToolCounts: {}
|
|
2881
3081
|
});
|
|
@@ -4289,6 +4489,8 @@ const BUBBLED_EVENTS = [
|
|
|
4289
4489
|
"stream:text",
|
|
4290
4490
|
"stream:thinking",
|
|
4291
4491
|
"stream:end",
|
|
4492
|
+
"stream:error",
|
|
4493
|
+
"tool:dispatched",
|
|
4292
4494
|
"tool:before",
|
|
4293
4495
|
"tool:after",
|
|
4294
4496
|
"tool:error",
|
|
@@ -4303,6 +4505,8 @@ const CHILD_EVENT_NAME = {
|
|
|
4303
4505
|
"stream:text": "child:stream:text",
|
|
4304
4506
|
"stream:thinking": "child:stream:thinking",
|
|
4305
4507
|
"stream:end": "child:stream:end",
|
|
4508
|
+
"stream:error": "child:stream:error",
|
|
4509
|
+
"tool:dispatched": "child:tool:dispatched",
|
|
4306
4510
|
"tool:before": "child:tool:before",
|
|
4307
4511
|
"tool:after": "child:tool:after",
|
|
4308
4512
|
"tool:error": "child:tool:error",
|
|
@@ -4699,6 +4903,6 @@ const writeFile$1 = {
|
|
|
4699
4903
|
}
|
|
4700
4904
|
};
|
|
4701
4905
|
//#endregion
|
|
4702
|
-
export {
|
|
4906
|
+
export { 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, 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 };
|
|
4703
4907
|
|
|
4704
|
-
//# sourceMappingURL=tools-
|
|
4908
|
+
//# sourceMappingURL=tools-CWEDS2ZT.js.map
|