zidane 5.4.1 → 5.4.3

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.
Files changed (68) hide show
  1. package/README.md +15 -0
  2. package/dist/{agent-DHQAsdj6.d.ts → agent-Yu8uhpy-.d.ts} +213 -3
  3. package/dist/agent-Yu8uhpy-.d.ts.map +1 -0
  4. package/dist/chat.d.ts +49 -6
  5. package/dist/chat.d.ts.map +1 -1
  6. package/dist/chat.js +2 -2
  7. package/dist/{errors-Byb0F8B9.js → errors-CDwtPIMX.js} +4 -2
  8. package/dist/{errors-Byb0F8B9.js.map → errors-CDwtPIMX.js.map} +1 -1
  9. package/dist/{index-CHSaLab5.d.ts → index-DklfxeYy.d.ts} +8 -2
  10. package/dist/index-DklfxeYy.d.ts.map +1 -0
  11. package/dist/{index-CrqFoaQA.d.ts → index-j9tY28ah.d.ts} +474 -8
  12. package/dist/index-j9tY28ah.d.ts.map +1 -0
  13. package/dist/index.d.ts +4 -4
  14. package/dist/index.js +1528 -53
  15. package/dist/index.js.map +1 -1
  16. package/dist/{interpolate-ERgZUxgg.js → interpolate-CmtjEyRJ.js} +155 -18
  17. package/dist/interpolate-CmtjEyRJ.js.map +1 -0
  18. package/dist/{login-8c5C0FYq.js → login-DxyAERe1.js} +3 -3
  19. package/dist/{login-8c5C0FYq.js.map → login-DxyAERe1.js.map} +1 -1
  20. package/dist/{mcp-DhmmJfxK.js → mcp-CNUbvbsy.js} +2 -2
  21. package/dist/{mcp-DhmmJfxK.js.map → mcp-CNUbvbsy.js.map} +1 -1
  22. package/dist/mcp.d.ts +1 -1
  23. package/dist/mcp.js +1 -1
  24. package/dist/{messages-D0xT979U.js → messages-fTR19Ga6.js} +2 -2
  25. package/dist/{messages-D0xT979U.js.map → messages-fTR19Ga6.js.map} +1 -1
  26. package/dist/{presets-Ck4VusTo.js → presets-D9IbaI40.js} +2 -2
  27. package/dist/{presets-Ck4VusTo.js.map → presets-D9IbaI40.js.map} +1 -1
  28. package/dist/presets.d.ts +2 -2
  29. package/dist/presets.js +1 -1
  30. package/dist/{providers-x3LZByR5.js → providers-CEzRFYtS.js} +3 -3
  31. package/dist/{providers-x3LZByR5.js.map → providers-CEzRFYtS.js.map} +1 -1
  32. package/dist/providers.d.ts +1 -1
  33. package/dist/providers.js +2 -2
  34. package/dist/session/sqlite.d.ts +1 -1
  35. package/dist/session/sqlite.js +1 -1
  36. package/dist/{session-BHZwxmfr.js → session-kwsNnOmt.js} +2 -2
  37. package/dist/{session-BHZwxmfr.js.map → session-kwsNnOmt.js.map} +1 -1
  38. package/dist/session.d.ts +1 -1
  39. package/dist/session.js +2 -2
  40. package/dist/skills.d.ts +2 -2
  41. package/dist/skills.js +1 -1
  42. package/dist/{tools-PQH1Ge4M.js → tools-BK2vG9UX.js} +246 -44
  43. package/dist/tools-BK2vG9UX.js.map +1 -0
  44. package/dist/tools.d.ts +2 -2
  45. package/dist/tools.js +1 -1
  46. package/dist/{transcript-anchors-ByB2MSCB.d.ts → transcript-anchors-DnaBcJej.d.ts} +52 -8
  47. package/dist/transcript-anchors-DnaBcJej.d.ts.map +1 -0
  48. package/dist/tui.d.ts +4 -2
  49. package/dist/tui.d.ts.map +1 -1
  50. package/dist/tui.js +651 -42
  51. package/dist/tui.js.map +1 -1
  52. package/dist/{turn-operations-Bqs4YbbH.js → turn-operations-OzKEOXul.js} +240 -52
  53. package/dist/turn-operations-OzKEOXul.js.map +1 -0
  54. package/dist/types-IcokUOyC.js.map +1 -1
  55. package/dist/types.d.ts +2 -2
  56. package/dist/types.js +1 -1
  57. package/docs/ARCHITECTURE.md +16 -3
  58. package/docs/CHAT.md +1 -1
  59. package/docs/SKILL.md +24 -14
  60. package/docs/TUI.md +24 -0
  61. package/package.json +3 -3
  62. package/dist/agent-DHQAsdj6.d.ts.map +0 -1
  63. package/dist/index-CHSaLab5.d.ts.map +0 -1
  64. package/dist/index-CrqFoaQA.d.ts.map +0 -1
  65. package/dist/interpolate-ERgZUxgg.js.map +0 -1
  66. package/dist/tools-PQH1Ge4M.js.map +0 -1
  67. package/dist/transcript-anchors-ByB2MSCB.d.ts.map +0 -1
  68. package/dist/turn-operations-Bqs4YbbH.js.map +0 -1
@@ -1,9 +1,9 @@
1
1
  import { n as createProcessContext } from "./contexts-BwiHIr2w.js";
2
- import { a as AgentToolPairingError, l as toTypedError, r as AgentProviderError, s as errorMessage, t as AgentAbortedError } from "./errors-Byb0F8B9.js";
2
+ import { a as AgentToolPairingError, l as toTypedError, r as AgentProviderError, s as errorMessage, t as AgentAbortedError } from "./errors-CDwtPIMX.js";
3
3
  import { t as toolOutputByteLength } from "./types-IcokUOyC.js";
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";
4
+ import { a as detectTurnInterruption, n as SYNTHETIC_TOOL_RESULT_PLACEHOLDER, o as ensureToolResultPairing, s as filterUnresolvedToolUses } from "./messages-fTR19Ga6.js";
5
+ import { t as connectMcpServers } from "./mcp-CNUbvbsy.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-CmtjEyRJ.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";
@@ -812,6 +812,27 @@ const INTERRUPT_MESSAGE_FOR_TOOL_USE = "[Request interrupted by user for tool us
812
812
  */
813
813
  const TOOL_USE_SKIPPED_MESSAGE = "[Tool use skipped — superseded by user message]";
814
814
  /**
815
+ * Canonical tool_result text emitted when a single tool call is cancelled
816
+ * mid-flight via `agent.cancelTool(callId)` (typically the TUI's
817
+ * "cancel this tool" affordance). Distinguished from
818
+ * {@link INTERRUPT_MESSAGE_FOR_TOOL_USE} (run-wide user abort) and
819
+ * {@link TOOL_USE_SKIPPED_MESSAGE} (steered) so the model — and downstream
820
+ * consumers — can tell the three apart by string match.
821
+ *
822
+ * Always paired with `isError: true` on the wire so the model treats the
823
+ * call as failed rather than as a successful response. The remaining tool
824
+ * calls in the batch continue running, in contrast with a full-run abort.
825
+ */
826
+ const TOOL_USE_CANCELLED_MESSAGE = "[Tool call cancelled by user]";
827
+ /**
828
+ * Sentinel message rejected from the per-call cancellation promise inside
829
+ * {@link executeSingleTool}'s race. Plain-string and module-private — only
830
+ * the surrounding code reads it (via the `perCallAbort.signal.aborted`
831
+ * guard, NOT the message itself), so it just needs to be a stable
832
+ * non-empty identifier the rejection can carry.
833
+ */
834
+ const CANCELLED_BY_USER_SENTINEL = "zidane:tool:cancelled-by-user";
835
+ /**
815
836
  * Compute the effective thinking budget for a given run-relative turn, given
816
837
  * the configured decay schedule. Pure helper — exported for tests and so
817
838
  * downstream tooling can preview decay curves without spinning up the loop.
@@ -1184,7 +1205,14 @@ async function runLoop(ctx) {
1184
1205
  await ctx.hooks.callHook("agent:abort", {});
1185
1206
  break;
1186
1207
  }
1187
- const result = await executeTurn(ctx, turn);
1208
+ const result = await executeTurn(ctx, turn, {
1209
+ input: totalIn,
1210
+ output: totalOut,
1211
+ cacheRead: totalCacheRead,
1212
+ cacheCreation: totalCacheCreation,
1213
+ priorCost: turnUsages.reduce((s, t) => s + (t.cost ?? 0), 0),
1214
+ priorTurns: turnsCompleted
1215
+ });
1188
1216
  turnsCompleted = turn + 1;
1189
1217
  totalIn += result.usage.input;
1190
1218
  totalOut += result.usage.output;
@@ -1295,7 +1323,49 @@ function buildStreamErrorPlaceholder(err) {
1295
1323
  const oneLine = raw.replace(/\s+/g, " ");
1296
1324
  return `[✗ Streaming failed before any output: ${oneLine.length > ERROR_PLACEHOLDER_MAX ? `${oneLine.slice(0, ERROR_PLACEHOLDER_MAX - 1).trimEnd()}…` : oneLine}]`;
1297
1325
  }
1298
- async function executeTurn(ctx, turn) {
1326
+ function buildCumulativeUsage(prior, turnUsage) {
1327
+ const cost = prior.priorCost + (turnUsage.cost ?? 0);
1328
+ return Object.freeze({
1329
+ input: prior.input + turnUsage.input,
1330
+ output: prior.output + turnUsage.output,
1331
+ cacheRead: prior.cacheRead + (turnUsage.cacheRead ?? 0),
1332
+ cacheCreation: prior.cacheCreation + (turnUsage.cacheCreation ?? 0),
1333
+ ...cost > 0 ? { cost } : {},
1334
+ turns: prior.priorTurns + 1
1335
+ });
1336
+ }
1337
+ /**
1338
+ * Best-effort extraction of `statusCode` / `requestId` from native provider
1339
+ * SDK exceptions, attached to `stream:error` ctx so observability handlers
1340
+ * don't have to walk `cause` chains. Recognized shapes:
1341
+ *
1342
+ * - Anthropic SDK `APIError` (`status`, `headers['request-id']`)
1343
+ * - OpenAI SDK error (`status`, `headers['x-request-id']`)
1344
+ * - OpenRouter / OpenAI-compat HTTP errors (`status`)
1345
+ * - Cerebras (`status`)
1346
+ *
1347
+ * Silent no-op for arbitrary `Error`s and primitives — the fields stay
1348
+ * undefined and the raw `err` is still forwarded for handlers that need
1349
+ * full context.
1350
+ */
1351
+ function extractStreamErrorMeta(err) {
1352
+ if (!err || typeof err !== "object") return {};
1353
+ const e = err;
1354
+ const out = {};
1355
+ const statusCandidate = typeof e.status === "number" ? e.status : typeof e.statusCode === "number" ? e.statusCode : void 0;
1356
+ if (typeof statusCandidate === "number" && Number.isFinite(statusCandidate)) out.statusCode = statusCandidate;
1357
+ if (typeof e.requestId === "string") out.requestId = e.requestId;
1358
+ else {
1359
+ const headers = e.headers;
1360
+ if (headers && typeof headers === "object") {
1361
+ const h = headers;
1362
+ const candidate = h["request-id"] ?? h["x-request-id"] ?? h.requestId;
1363
+ if (typeof candidate === "string") out.requestId = candidate;
1364
+ }
1365
+ }
1366
+ return out;
1367
+ }
1368
+ async function executeTurn(ctx, turn, priorUsage) {
1299
1369
  const turnId = await ctx.generateTurnId();
1300
1370
  let canonicalMessages = turnsToMessages(applyCompactSummaryCutoff(ctx.turns));
1301
1371
  if (ctx.elideStaleReads === true) {
@@ -1343,10 +1413,20 @@ async function executeTurn(ctx, turn) {
1343
1413
  });
1344
1414
  let currentText = "";
1345
1415
  let currentThinking = "";
1416
+ const streamStartedAt = Date.now();
1417
+ let turnTtftMs;
1418
+ const markTurnTtft = () => {
1419
+ if (turnTtftMs === void 0) turnTtftMs = Date.now() - streamStartedAt;
1420
+ };
1421
+ await ctx.hooks.callHook("stream:start", {
1422
+ turnId,
1423
+ startedAt: streamStartedAt
1424
+ });
1346
1425
  let result;
1347
1426
  try {
1348
1427
  result = await ctx.provider.stream(streamOptions, {
1349
1428
  onText(delta) {
1429
+ markTurnTtft();
1350
1430
  currentText += delta;
1351
1431
  ctx.hooks.callHook("stream:text", {
1352
1432
  delta,
@@ -1355,6 +1435,7 @@ async function executeTurn(ctx, turn) {
1355
1435
  });
1356
1436
  },
1357
1437
  onThinking(delta) {
1438
+ markTurnTtft();
1358
1439
  currentThinking += delta;
1359
1440
  ctx.hooks.callHook("stream:thinking", {
1360
1441
  delta,
@@ -1389,10 +1470,14 @@ async function executeTurn(ctx, turn) {
1389
1470
  createdAt: Date.now()
1390
1471
  };
1391
1472
  ctx.turns.push(errorTurn);
1392
- if (!wasAborted) await ctx.hooks.callHook("stream:error", {
1393
- err,
1394
- turnId
1395
- });
1473
+ if (!wasAborted) {
1474
+ const meta = extractStreamErrorMeta(err);
1475
+ await ctx.hooks.callHook("stream:error", {
1476
+ err,
1477
+ turnId,
1478
+ ...meta
1479
+ });
1480
+ }
1396
1481
  await ctx.hooks.callHook("turn:after", {
1397
1482
  turn,
1398
1483
  turnId,
@@ -1401,7 +1486,8 @@ async function executeTurn(ctx, turn) {
1401
1486
  toolCounts: {
1402
1487
  turn: Object.freeze({}),
1403
1488
  run: Object.freeze({ ...ctx.runToolCounts })
1404
- }
1489
+ },
1490
+ cumulativeUsage: buildCumulativeUsage(priorUsage, errorUsage)
1405
1491
  });
1406
1492
  throw wrapProviderError(err, ctx);
1407
1493
  }
@@ -1409,11 +1495,13 @@ async function executeTurn(ctx, turn) {
1409
1495
  text: currentText,
1410
1496
  turnId
1411
1497
  });
1498
+ if (turnTtftMs === void 0 && result.toolCalls.length > 0) turnTtftMs = Date.now() - streamStartedAt;
1412
1499
  const canonicalToolCalls = result.toolCalls.map((tc) => ({
1413
1500
  ...tc,
1414
1501
  name: toCanonicalName(tc.name, ctx.aliasMaps)
1415
1502
  }));
1416
1503
  const canonicalContent = rewriteContentToCanonical(result.assistantMessage?.content ?? [], ctx.aliasMaps);
1504
+ if (turnTtftMs !== void 0 && result.usage.timeToFirstTokenMs === void 0) result.usage.timeToFirstTokenMs = turnTtftMs;
1417
1505
  const assistantTurn = {
1418
1506
  id: turnId,
1419
1507
  runId: ctx.runId,
@@ -1436,7 +1524,8 @@ async function executeTurn(ctx, turn) {
1436
1524
  toolCounts: {
1437
1525
  turn: Object.freeze(turnCounts),
1438
1526
  run: Object.freeze({ ...ctx.runToolCounts })
1439
- }
1527
+ },
1528
+ cumulativeUsage: buildCumulativeUsage(priorUsage, result.usage)
1440
1529
  });
1441
1530
  if (result.done) {
1442
1531
  if (ctx.schema && !ctx.signal.aborted) {
@@ -1602,6 +1691,29 @@ async function executeSingleTool(ctx, call, turnId) {
1602
1691
  const callId = call.id;
1603
1692
  const displayName = toWireName(call.name, ctx.aliasMaps);
1604
1693
  const runToolCounts = Object.freeze({ ...ctx.runToolCounts });
1694
+ const perCallAbort = new AbortController();
1695
+ ctx.pendingToolCancels?.set(callId, perCallAbort);
1696
+ try {
1697
+ return await runSingleToolDispatch(ctx, call, turnId, {
1698
+ toolDef,
1699
+ callId,
1700
+ displayName,
1701
+ runToolCounts,
1702
+ perCallAbort
1703
+ });
1704
+ } finally {
1705
+ ctx.pendingToolCancels?.delete(callId);
1706
+ }
1707
+ }
1708
+ /**
1709
+ * Body of {@link executeSingleTool}. Hoisted into its own function purely so
1710
+ * the per-call cancel registration (which spans every exit path) can sit in
1711
+ * a tight `try / finally` at the call site. Behavior is unchanged from the
1712
+ * pre-cancel implementation aside from the user-cancel branch in the
1713
+ * execute body — see {@link TOOL_USE_CANCELLED_MESSAGE}.
1714
+ */
1715
+ async function runSingleToolDispatch(ctx, call, turnId, fixed) {
1716
+ const { toolDef, callId, displayName, runToolCounts, perCallAbort } = fixed;
1605
1717
  const gateCtx = {
1606
1718
  ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, call.input),
1607
1719
  block: false,
@@ -1727,12 +1839,14 @@ async function executeSingleTool(ctx, call, turnId) {
1727
1839
  runToolCounts,
1728
1840
  ...coercions ? { coercions } : {}
1729
1841
  });
1730
- let output;
1842
+ let output = "";
1731
1843
  let isError = false;
1844
+ let cancelledByUser = false;
1845
+ const childSignal = typeof AbortSignal.any === "function" ? AbortSignal.any([ctx.signal, perCallAbort.signal]) : ctx.signal;
1732
1846
  try {
1733
1847
  const toolCtx = {
1734
1848
  provider: ctx.provider,
1735
- signal: ctx.signal,
1849
+ signal: childSignal,
1736
1850
  execution: ctx.execution,
1737
1851
  handle: ctx.handle,
1738
1852
  hooks: ctx.hooks,
@@ -1751,16 +1865,48 @@ async function executeSingleTool(ctx, call, turnId) {
1751
1865
  ...ctx.readState ? { readState: ctx.readState } : {},
1752
1866
  ...typeof ctx.depth === "number" ? { depth: ctx.depth } : {}
1753
1867
  };
1754
- output = await toolDef.execute(effectiveInput, toolCtx);
1868
+ const bodyPromise = toolDef.execute(effectiveInput, toolCtx);
1869
+ bodyPromise.catch(() => {});
1870
+ let removeAbortListener;
1871
+ const cancellationPromise = new Promise((_, reject) => {
1872
+ if (perCallAbort.signal.aborted) {
1873
+ reject(new Error(CANCELLED_BY_USER_SENTINEL));
1874
+ return;
1875
+ }
1876
+ const onAbort = () => reject(new Error(CANCELLED_BY_USER_SENTINEL));
1877
+ perCallAbort.signal.addEventListener("abort", onAbort, { once: true });
1878
+ removeAbortListener = () => perCallAbort.signal.removeEventListener("abort", onAbort);
1879
+ });
1880
+ try {
1881
+ output = await Promise.race([bodyPromise, cancellationPromise]);
1882
+ } finally {
1883
+ removeAbortListener?.();
1884
+ }
1755
1885
  } catch (err) {
1756
- const error = err instanceof Error ? err : new Error(String(err));
1757
- const errorCtx = {
1886
+ if (perCallAbort.signal.aborted && !ctx.signal.aborted) cancelledByUser = true;
1887
+ else {
1888
+ const error = err instanceof Error ? err : new Error(String(err));
1889
+ const errorCtx = {
1890
+ ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),
1891
+ error
1892
+ };
1893
+ await ctx.hooks.callHook("tool:error", errorCtx);
1894
+ output = errorCtx.result ?? `Tool error: ${error.message}`;
1895
+ isError = true;
1896
+ }
1897
+ }
1898
+ if (cancelledByUser) {
1899
+ const reason = typeof perCallAbort.signal.reason === "string" ? perCallAbort.signal.reason : "cancelled-by-user";
1900
+ await ctx.hooks.callHook("tool:cancelled", {
1758
1901
  ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),
1759
- error
1760
- };
1761
- await ctx.hooks.callHook("tool:error", errorCtx);
1762
- output = errorCtx.result ?? `Tool error: ${error.message}`;
1763
- isError = true;
1902
+ reason,
1903
+ runToolCounts
1904
+ });
1905
+ return { result: {
1906
+ id: callId,
1907
+ content: TOOL_USE_CANCELLED_MESSAGE,
1908
+ isError: true
1909
+ } };
1764
1910
  }
1765
1911
  const emitted = await emitToolResult(ctx, {
1766
1912
  turnId,
@@ -2412,14 +2558,21 @@ function createSkillsUseTool(options) {
2412
2558
  return {
2413
2559
  spec: {
2414
2560
  name: "skills_use",
2415
- description: "Activate a specialized skill and load its full instructions. Call this when a task matches a skill's description from the catalog. After calling, follow the returned instructions; use skills_read to load referenced files and skills_run_script to execute bundled scripts.",
2561
+ description: "Activate or deactivate a specialized skill. Call with `mode: \"activate\"` (default) to load a skill's full instructions when a task matches its catalog description. Call with `mode: \"deactivate\"` to release a skill whose allowed-tools restrictions are now in the way — e.g. when the skill's work is done or the active skill is blocking unrelated tool calls. After activating, follow the returned instructions; use skills_read to load referenced files and skills_run_script to execute bundled scripts.",
2416
2562
  inputSchema: {
2417
2563
  type: "object",
2418
- properties: { name: {
2419
- type: "string",
2420
- enum: options.catalog.map((s) => s.name),
2421
- description: "The name of the skill to activate (must be in the available skills catalog)."
2422
- } },
2564
+ properties: {
2565
+ name: {
2566
+ type: "string",
2567
+ enum: options.catalog.map((s) => s.name),
2568
+ description: "The name of the skill to activate or deactivate (must be in the available skills catalog)."
2569
+ },
2570
+ mode: {
2571
+ type: "string",
2572
+ enum: ["activate", "deactivate"],
2573
+ description: "Whether to activate (load + apply the skill) or deactivate (release an active skill). Default: \"activate\"."
2574
+ }
2575
+ },
2423
2576
  required: ["name"],
2424
2577
  additionalProperties: false
2425
2578
  }
@@ -2428,8 +2581,18 @@ function createSkillsUseTool(options) {
2428
2581
  const skillName = input.name;
2429
2582
  const skill = byName.get(skillName);
2430
2583
  if (!skill) return `Error: unknown skill "${skillName}". Available skills: ${[...byName.keys()].join(", ") || "<none>"}.`;
2584
+ if ((input.mode ?? "activate") === "deactivate") {
2585
+ const removed = options.state.deactivate(skillName);
2586
+ if (!removed) return `Skill "${skillName}" was not active — nothing to deactivate.`;
2587
+ await options.hooks.callHook("skills:deactivate", {
2588
+ skill: removed.skill,
2589
+ reason: "model"
2590
+ });
2591
+ const remaining = options.state.active().map((a) => a.skill.name);
2592
+ return `Skill "${skillName}" deactivated — its allowed-tools restrictions no longer apply.${remaining.length > 0 ? ` Remaining active skills: ${remaining.join(", ")}.` : " No skills are currently active."}`;
2593
+ }
2431
2594
  if (!options.state.isActive(skillName)) {
2432
- if (options.state.activate(skill, "model") === "cap-reached") return `Error: cannot activate "${skillName}" — the maxActive skill cap has been reached. Currently active: ${options.state.active().map((a) => a.skill.name).join(", ")}. Deactivate an existing skill first.`;
2595
+ if (options.state.activate(skill, "model") === "cap-reached") return `Error: cannot activate "${skillName}" — the maxActive skill cap has been reached. Currently active: ${options.state.active().map((a) => a.skill.name).join(", ")}. Call \`skills_use\` with \`mode: "deactivate"\` and one of those names first.`;
2433
2596
  await options.hooks.callHook("skills:activate", {
2434
2597
  skill,
2435
2598
  via: "model"
@@ -2590,9 +2753,11 @@ function createToolSearchTool(options) {
2590
2753
  //#region src/agent.ts
2591
2754
  const HOOK_EVENT_SET = new Set([
2592
2755
  "system:before",
2756
+ "agent:start",
2593
2757
  "turn:before",
2594
2758
  "turn:after",
2595
2759
  "tool-results:after",
2760
+ "stream:start",
2596
2761
  "stream:text",
2597
2762
  "stream:end",
2598
2763
  "stream:thinking",
@@ -2603,6 +2768,7 @@ const HOOK_EVENT_SET = new Set([
2603
2768
  "tool:before",
2604
2769
  "tool:after",
2605
2770
  "tool:error",
2771
+ "tool:cancelled",
2606
2772
  "tool:transform",
2607
2773
  "tool:unknown",
2608
2774
  "validation:reject",
@@ -2624,6 +2790,7 @@ const HOOK_EVENT_SET = new Set([
2624
2790
  "child:tool:after",
2625
2791
  "child:tool:transform",
2626
2792
  "child:tool:error",
2793
+ "child:tool:cancelled",
2627
2794
  "child:turn:after",
2628
2795
  "mcp:connect",
2629
2796
  "mcp:error",
@@ -2649,6 +2816,7 @@ const HOOK_EVENT_SET = new Set([
2649
2816
  "budget:exceeded",
2650
2817
  "tool-budget:exceeded",
2651
2818
  "pairing:repair",
2819
+ "tracing:redact",
2652
2820
  "agent:abort",
2653
2821
  "agent:done",
2654
2822
  "session:start",
@@ -2902,6 +3070,7 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
2902
3070
  let running = false;
2903
3071
  let idleResolve;
2904
3072
  let idlePromise;
3073
+ const pendingToolCancels = /* @__PURE__ */ new Map();
2905
3074
  let executionHandle = null;
2906
3075
  let mcpConnection = null;
2907
3076
  let mcpWarmupPromise = null;
@@ -2983,6 +3152,16 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
2983
3152
  prompt: promptLabel
2984
3153
  });
2985
3154
  }
3155
+ const runStartedAt = Date.now();
3156
+ await hooks.callHook("agent:start", {
3157
+ runId,
3158
+ ...options.parentRunId ? { parentRunId: options.parentRunId } : {},
3159
+ depth: typeof options.depth === "number" ? options.depth : 0,
3160
+ ...agentName ? { agentName } : {},
3161
+ ...provider.name ? { providerName: provider.name } : {},
3162
+ startedAt: runStartedAt,
3163
+ ...options.tracingContext ? { tracingContext: Object.freeze({ ...options.tracingContext }) } : {}
3164
+ });
2986
3165
  if (externalSignal) if (externalSignal.aborted) abortController.abort();
2987
3166
  else {
2988
3167
  externalAbortListener = () => abortController?.abort();
@@ -3009,20 +3188,27 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
3009
3188
  await ensureSkillsResolved();
3010
3189
  if (resolvedSkills && session && session.turns.length > 0 && skillActivationState.active().length === 0) {
3011
3190
  const skillsByName = new Map(resolvedSkills.map((s) => [s.name, s]));
3191
+ const lastModeBySkill = /* @__PURE__ */ new Map();
3012
3192
  for (const turn of session.turns) {
3013
3193
  if (turn.role !== "assistant") continue;
3014
3194
  for (const block of turn.content) {
3015
3195
  if (block.type !== "tool_call" || block.name !== "skills_use") continue;
3016
- const skillName = block.input?.name;
3196
+ const input = block.input;
3197
+ const skillName = input?.name;
3017
3198
  if (!skillName) continue;
3018
- const skill = skillsByName.get(skillName);
3019
- if (!skill) continue;
3020
- if (skillActivationState.activate(skill, "resume") === "ok") await hooks.callHook("skills:activate", {
3021
- skill,
3022
- via: "resume"
3023
- });
3199
+ const mode = input?.mode === "deactivate" ? "deactivate" : "activate";
3200
+ lastModeBySkill.set(skillName, mode);
3024
3201
  }
3025
3202
  }
3203
+ for (const [skillName, mode] of lastModeBySkill) {
3204
+ if (mode !== "activate") continue;
3205
+ const skill = skillsByName.get(skillName);
3206
+ if (!skill) continue;
3207
+ if (skillActivationState.activate(skill, "resume") === "ok") await hooks.callHook("skills:activate", {
3208
+ skill,
3209
+ via: "resume"
3210
+ });
3211
+ }
3026
3212
  }
3027
3213
  const thinking = options.thinking ?? "off";
3028
3214
  const model = options.model ?? provider.meta.defaultModel;
@@ -3220,7 +3406,8 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
3220
3406
  ...strictToolPairing ? { strictToolPairing: true } : {},
3221
3407
  providerName: provider.name,
3222
3408
  runStartMs,
3223
- runToolCounts: {}
3409
+ runToolCounts: {},
3410
+ pendingToolCancels
3224
3411
  });
3225
3412
  const parentTurnCost = stats.turnUsage?.reduce((sum, t) => sum + (t.cost ?? 0), 0) ?? 0;
3226
3413
  let childrenIn = 0;
@@ -3317,6 +3504,12 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
3317
3504
  function abort() {
3318
3505
  abortController?.abort();
3319
3506
  }
3507
+ function cancelTool(callId, reason) {
3508
+ const controller = pendingToolCancels.get(callId);
3509
+ if (!controller || controller.signal.aborted) return false;
3510
+ controller.abort(reason ?? "user-cancelled-tool");
3511
+ return true;
3512
+ }
3320
3513
  function steer(message) {
3321
3514
  steeringQueue.push(message);
3322
3515
  }
@@ -3410,6 +3603,8 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
3410
3603
  async function destroy() {
3411
3604
  if (destroyed) return;
3412
3605
  destroyed = true;
3606
+ for (const controller of pendingToolCancels.values()) if (!controller.signal.aborted) controller.abort("agent-destroyed");
3607
+ pendingToolCancels.clear();
3413
3608
  if (mcpWarmupPromise) try {
3414
3609
  await mcpWarmupPromise;
3415
3610
  } catch {}
@@ -3430,6 +3625,7 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
3430
3625
  hooks,
3431
3626
  run,
3432
3627
  abort,
3628
+ cancelTool,
3433
3629
  steer,
3434
3630
  followUp: followUpFn,
3435
3631
  waitForIdle,
@@ -4821,6 +5017,7 @@ const BUBBLED_EVENTS = [
4821
5017
  "tool:before",
4822
5018
  "tool:after",
4823
5019
  "tool:error",
5020
+ "tool:cancelled",
4824
5021
  "turn:after"
4825
5022
  ];
4826
5023
  const BUBBLED_MUTABLE_EVENTS = [
@@ -4837,6 +5034,7 @@ const CHILD_EVENT_NAME = {
4837
5034
  "tool:before": "child:tool:before",
4838
5035
  "tool:after": "child:tool:after",
4839
5036
  "tool:error": "child:tool:error",
5037
+ "tool:cancelled": "child:tool:cancelled",
4840
5038
  "turn:after": "child:turn:after"
4841
5039
  };
4842
5040
  const CHILD_MUTABLE_EVENT_NAME = {
@@ -5065,7 +5263,7 @@ function createSpawnTool(options = {}) {
5065
5263
  });
5066
5264
  if (forwardHooks) {
5067
5265
  const unregisterEnricher = agent.hooks.hook("tool:before", async (toolCtx) => {
5068
- if (toolCtx.name !== "write_file") return;
5266
+ if (toolCtx.name !== "write_file" && toolCtx.name !== "edit" && toolCtx.name !== "multi_edit") return;
5069
5267
  if (!agent.handle) return;
5070
5268
  const inputPath = toolCtx.input?.path;
5071
5269
  if (typeof inputPath !== "string") return;
@@ -5080,11 +5278,14 @@ function createSpawnTool(options = {}) {
5080
5278
  };
5081
5279
  }
5082
5280
  options.onSpawn?.(child);
5083
- await ctx.hooks.callHook("spawn:before", {
5281
+ const spawnHookCtx = {
5084
5282
  id,
5085
5283
  task,
5086
- depth: childDepth
5087
- });
5284
+ depth: childDepth,
5285
+ tracingContext: {}
5286
+ };
5287
+ await ctx.hooks.callHook("spawn:before", spawnHookCtx);
5288
+ const propagatedTracing = Object.keys(spawnHookCtx.tracingContext).length > 0 ? Object.freeze({ ...spawnHookCtx.tracingContext }) : void 0;
5088
5289
  const runPromise = agent.run({
5089
5290
  prompt: task,
5090
5291
  model: options.model,
@@ -5092,7 +5293,8 @@ function createSpawnTool(options = {}) {
5092
5293
  thinking: options.thinking,
5093
5294
  signal: ctx.signal,
5094
5295
  depth: childDepth,
5095
- ...options.persist && ctx.runId ? { parentRunId: ctx.runId } : {}
5296
+ ...options.persist && ctx.runId ? { parentRunId: ctx.runId } : {},
5297
+ ...propagatedTracing ? { tracingContext: propagatedTracing } : {}
5096
5298
  });
5097
5299
  try {
5098
5300
  finalStats = await raceWithTimeout(runPromise, options.timeoutMs);
@@ -5236,6 +5438,6 @@ const writeFile$1 = {
5236
5438
  }
5237
5439
  };
5238
5440
  //#endregion
5239
- 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, SHELL_CASCADE_CANCEL_MESSAGE as y };
5441
+ export { hashContent as A, PERSISTED_STUB_PREFIX as C, maybePersistToolResult as D, cleanupPersistedSession as E, resolveReadStateMap as M, resolvePersistDir as O, validateToolArgs as S, buildPersistedStub as T, createSkillsReadTool as _, multiEdit as a, TOOL_USE_CANCELLED_MESSAGE as b, grep as c, resolveOldString as d, styleReplacementForVia as f, createSkillsRunScriptTool as g, createSkillsUseTool as h, readFile$1 as i, readStateKey as j, getReadState 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, PERSISTENCE_PREVIEW_BYTES as w, TOOL_USE_SKIPPED_MESSAGE as x, SHELL_CASCADE_CANCEL_MESSAGE as y };
5240
5442
 
5241
- //# sourceMappingURL=tools-PQH1Ge4M.js.map
5443
+ //# sourceMappingURL=tools-BK2vG9UX.js.map