@namzu/sdk 41.0.0 → 42.0.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.
Files changed (141) hide show
  1. package/CHANGELOG.md +233 -0
  2. package/dist/agents/ReactiveAgent.d.ts.map +1 -1
  3. package/dist/agents/ReactiveAgent.js +3 -0
  4. package/dist/agents/ReactiveAgent.js.map +1 -1
  5. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  6. package/dist/agents/SupervisorAgent.js +11 -0
  7. package/dist/agents/SupervisorAgent.js.map +1 -1
  8. package/dist/agents/runAgent.d.ts +14 -0
  9. package/dist/agents/runAgent.d.ts.map +1 -1
  10. package/dist/agents/runAgent.js +3 -0
  11. package/dist/agents/runAgent.js.map +1 -1
  12. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  13. package/dist/manager/agent/lifecycle.js +20 -0
  14. package/dist/manager/agent/lifecycle.js.map +1 -1
  15. package/dist/manager/resident/outbox.d.ts +8 -8
  16. package/dist/manager/resident/store.d.ts +4 -4
  17. package/dist/public-runtime.d.ts +4 -1
  18. package/dist/public-runtime.d.ts.map +1 -1
  19. package/dist/public-runtime.js +13 -1
  20. package/dist/public-runtime.js.map +1 -1
  21. package/dist/public-tools.d.ts +1 -1
  22. package/dist/public-tools.d.ts.map +1 -1
  23. package/dist/public-tools.js +4 -2
  24. package/dist/public-tools.js.map +1 -1
  25. package/dist/registry/tool/execute.d.ts.map +1 -1
  26. package/dist/registry/tool/execute.js +10 -1
  27. package/dist/registry/tool/execute.js.map +1 -1
  28. package/dist/runtime/bidi/session.d.ts +11 -0
  29. package/dist/runtime/bidi/session.d.ts.map +1 -1
  30. package/dist/runtime/bidi/session.js +2 -0
  31. package/dist/runtime/bidi/session.js.map +1 -1
  32. package/dist/runtime/query/cancelled-before-start.d.ts +34 -0
  33. package/dist/runtime/query/cancelled-before-start.d.ts.map +1 -0
  34. package/dist/runtime/query/cancelled-before-start.js +152 -0
  35. package/dist/runtime/query/cancelled-before-start.js.map +1 -0
  36. package/dist/runtime/query/checkpoint.d.ts +21 -0
  37. package/dist/runtime/query/checkpoint.d.ts.map +1 -1
  38. package/dist/runtime/query/checkpoint.js +23 -0
  39. package/dist/runtime/query/checkpoint.js.map +1 -1
  40. package/dist/runtime/query/executor/tool-call-admission.d.ts +57 -0
  41. package/dist/runtime/query/executor/tool-call-admission.d.ts.map +1 -0
  42. package/dist/runtime/query/executor/tool-call-admission.js +373 -0
  43. package/dist/runtime/query/executor/tool-call-admission.js.map +1 -0
  44. package/dist/runtime/query/executor.d.ts +76 -35
  45. package/dist/runtime/query/executor.d.ts.map +1 -1
  46. package/dist/runtime/query/executor.js +52 -380
  47. package/dist/runtime/query/executor.js.map +1 -1
  48. package/dist/runtime/query/finalize-run.d.ts +55 -0
  49. package/dist/runtime/query/finalize-run.d.ts.map +1 -0
  50. package/dist/runtime/query/finalize-run.js +113 -0
  51. package/dist/runtime/query/finalize-run.js.map +1 -0
  52. package/dist/runtime/query/guardrail-presets.d.ts +187 -1
  53. package/dist/runtime/query/guardrail-presets.d.ts.map +1 -1
  54. package/dist/runtime/query/guardrail-presets.js +298 -0
  55. package/dist/runtime/query/guardrail-presets.js.map +1 -1
  56. package/dist/runtime/query/index.d.ts +18 -9
  57. package/dist/runtime/query/index.d.ts.map +1 -1
  58. package/dist/runtime/query/index.js +241 -893
  59. package/dist/runtime/query/index.js.map +1 -1
  60. package/dist/runtime/query/iteration/index.d.ts +6 -161
  61. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  62. package/dist/runtime/query/iteration/index.js +23 -523
  63. package/dist/runtime/query/iteration/index.js.map +1 -1
  64. package/dist/runtime/query/iteration/outstanding-work.d.ts +158 -0
  65. package/dist/runtime/query/iteration/outstanding-work.d.ts.map +1 -0
  66. package/dist/runtime/query/iteration/outstanding-work.js +365 -0
  67. package/dist/runtime/query/iteration/outstanding-work.js.map +1 -0
  68. package/dist/runtime/query/iteration/phases/plan.d.ts.map +1 -1
  69. package/dist/runtime/query/iteration/phases/plan.js +13 -2
  70. package/dist/runtime/query/iteration/phases/plan.js.map +1 -1
  71. package/dist/runtime/query/iteration/step-shaping.d.ts +41 -0
  72. package/dist/runtime/query/iteration/step-shaping.d.ts.map +1 -0
  73. package/dist/runtime/query/iteration/step-shaping.js +184 -0
  74. package/dist/runtime/query/iteration/step-shaping.js.map +1 -0
  75. package/dist/runtime/query/prepare-run.d.ts +94 -0
  76. package/dist/runtime/query/prepare-run.d.ts.map +1 -0
  77. package/dist/runtime/query/prepare-run.js +589 -0
  78. package/dist/runtime/query/prepare-run.js.map +1 -0
  79. package/dist/runtime/query/release-run.d.ts +56 -0
  80. package/dist/runtime/query/release-run.d.ts.map +1 -0
  81. package/dist/runtime/query/release-run.js +101 -0
  82. package/dist/runtime/query/release-run.js.map +1 -0
  83. package/dist/runtime/query/resume-pending.d.ts +112 -1
  84. package/dist/runtime/query/resume-pending.d.ts.map +1 -1
  85. package/dist/runtime/query/resume-pending.js +133 -0
  86. package/dist/runtime/query/resume-pending.js.map +1 -1
  87. package/dist/runtime/query/tooling.d.ts +2 -0
  88. package/dist/runtime/query/tooling.d.ts.map +1 -1
  89. package/dist/runtime/query/tooling.js +3 -0
  90. package/dist/runtime/query/tooling.js.map +1 -1
  91. package/dist/store/evidence/compaction-archive.d.ts +2 -2
  92. package/dist/tools/coordinator/agent.d.ts.map +1 -1
  93. package/dist/tools/coordinator/agent.js +17 -2
  94. package/dist/tools/coordinator/agent.js.map +1 -1
  95. package/dist/tools/coordinator/index.d.ts.map +1 -1
  96. package/dist/tools/coordinator/index.js +17 -3
  97. package/dist/tools/coordinator/index.js.map +1 -1
  98. package/dist/tools/untrusted-envelope.d.ts +35 -0
  99. package/dist/tools/untrusted-envelope.d.ts.map +1 -1
  100. package/dist/tools/untrusted-envelope.js +91 -3
  101. package/dist/tools/untrusted-envelope.js.map +1 -1
  102. package/dist/types/agent/base.d.ts +23 -0
  103. package/dist/types/agent/base.d.ts.map +1 -1
  104. package/dist/types/agent/task.d.ts +19 -0
  105. package/dist/types/agent/task.d.ts.map +1 -1
  106. package/dist/types/run/config.d.ts +12 -5
  107. package/dist/types/run/config.d.ts.map +1 -1
  108. package/dist/types/tool/index.d.ts +19 -0
  109. package/dist/types/tool/index.d.ts.map +1 -1
  110. package/dist/types/tool/index.js.map +1 -1
  111. package/package.json +1 -1
  112. package/src/agents/ReactiveAgent.ts +3 -0
  113. package/src/agents/SupervisorAgent.ts +11 -0
  114. package/src/agents/runAgent.ts +18 -0
  115. package/src/manager/agent/lifecycle.ts +22 -0
  116. package/src/public-runtime.ts +14 -0
  117. package/src/public-tools.ts +8 -2
  118. package/src/registry/tool/execute.ts +9 -1
  119. package/src/runtime/bidi/session.ts +13 -0
  120. package/src/runtime/query/cancelled-before-start.ts +189 -0
  121. package/src/runtime/query/checkpoint.ts +22 -0
  122. package/src/runtime/query/executor/tool-call-admission.ts +473 -0
  123. package/src/runtime/query/executor.ts +76 -442
  124. package/src/runtime/query/finalize-run.ts +192 -0
  125. package/src/runtime/query/guardrail-presets.ts +356 -0
  126. package/src/runtime/query/index.ts +287 -1011
  127. package/src/runtime/query/iteration/index.ts +40 -586
  128. package/src/runtime/query/iteration/outstanding-work.ts +386 -0
  129. package/src/runtime/query/iteration/phases/plan.ts +18 -2
  130. package/src/runtime/query/iteration/step-shaping.ts +271 -0
  131. package/src/runtime/query/prepare-run.ts +718 -0
  132. package/src/runtime/query/release-run.ts +168 -0
  133. package/src/runtime/query/resume-pending.ts +158 -0
  134. package/src/runtime/query/tooling.ts +5 -0
  135. package/src/tools/coordinator/agent.ts +17 -2
  136. package/src/tools/coordinator/index.ts +17 -3
  137. package/src/tools/untrusted-envelope.ts +94 -3
  138. package/src/types/agent/base.ts +24 -0
  139. package/src/types/agent/task.ts +20 -0
  140. package/src/types/run/config.ts +12 -5
  141. package/src/types/tool/index.ts +20 -0
@@ -4,7 +4,6 @@ import { GENAI, NAMZU } from '../../constants/telemetry/index.js';
4
4
  import { buildProbeContext } from '../../probe/context.js';
5
5
  import { ProbeVetoError } from '../../probe/errors.js';
6
6
  import { probe as defaultProbeRegistry } from '../../probe/registry.js';
7
- import { renderToolSchema } from '../../registry/tool/schema.js';
8
7
  import { SKILL_TOOL_NAME } from '../../tools/builtins/skill.js';
9
8
  import { createFileReadTracker } from '../../tools/file-read-tracker.js';
10
9
  import { createToolMessage, } from '../../types/message/index.js';
@@ -15,9 +14,10 @@ import { toErrorMessage } from '../../utils/error.js';
15
14
  import { generateToolCallId } from '../../utils/id.js';
16
15
  import { compressShellOutput } from '../../utils/shell-compress.js';
17
16
  import { bindOwner } from '../jobs/registry.js';
17
+ import { formatFailedToolOutput, prepareDirectCall, repairTruncatedCall, resolveCall, runPreToolHook, truncatedToolInputMessage, } from './executor/tool-call-admission.js';
18
18
  import { describeVisibleFileEvidence } from './file-evidence-context.js';
19
19
  import { seedObservationLedger } from './file-evidence-seed.js';
20
- import { skippedToolResultText } from './plugin-hooks.js';
20
+ import { DEFAULT_TOOL_RESULT_GUARDRAILS } from './guardrail-presets.js';
21
21
  import { ToolCallBudget, assertMaxToolCalls } from './tool-call-budget.js';
22
22
  import { DEFAULT_MAX_TOOL_OUTPUT_CHARS, applyToolOutputBudget, describeDroppedContent, measureContentBytes, } from './tool-output-budget.js';
23
23
  function assertUniqueToolCallIds(toolCalls) {
@@ -171,13 +171,6 @@ export const DEFAULT_TOOL_RETRY_BACKOFF = {
171
171
  initialDelayMs: 500,
172
172
  maxDelayMs: 16_000,
173
173
  };
174
- /**
175
- * An empty arguments string means "no arguments", not "malformed" — the
176
- * shape a no-parameter tool arrives in.
177
- */
178
- function parseArguments(raw) {
179
- return JSON.parse(raw || '{}');
180
- }
181
174
  /**
182
175
  * Model-visible text for a tool call that was never executed.
183
176
  *
@@ -293,10 +286,28 @@ export class ToolExecutor {
293
286
  *
294
287
  * `denials` marks ids that must NOT run: each is answered with a
295
288
  * synthetic error result carrying the caller's reason instead of being
296
- * executed. This is what makes the invariant hold by construction —
297
- * a gate denial, a human rejection and a partial approval all leave
298
- * the history valid, because there is exactly one place that turns a
299
- * batch of tool calls into messages and it always covers all of them.
289
+ * executed. A gate denial, a human rejection and a partial approval all
290
+ * leave the history valid, because there is exactly one place that turns
291
+ * a batch of tool calls into messages and it covers all of them.
292
+ *
293
+ * **That is a property of every path that RETURNS, not of the batch as
294
+ * a whole.** A per-call throw rejects the batch before the fill-the-holes
295
+ * loop below can run: `serial = serial.then(run)` means one rejection
296
+ * skips every LATER serial call, and `Promise.all([...parallel, serial])`
297
+ * then rejects — so this method produces no messages at all and the
298
+ * assistant turn keeps its `tool_use` blocks unanswered. A resume is what
299
+ * repairs that turn; see the `unfinished` step `iteration/index.ts`
300
+ * records for it.
301
+ *
302
+ * Reachable, not hypothetical, and demonstrated end to end by
303
+ * `a-throwing-batch-answers-nothing.test.ts`: `executeSingle` rethrows a
304
+ * retry's budget-admission error, and a `runPreToolHook` failure on a
305
+ * call whose preparation did not already run the hook.
306
+ *
307
+ * So do not read the guarantee below as covering a throw. The invariant
308
+ * holds for denials, for approvals, for a rejected batch and for a
309
+ * generation that partially failed while still returning: each of those
310
+ * leaves a hole that the fill-the-holes loop closes.
300
311
  *
301
312
  * Answering with `is_error` semantics rather than dropping the call is
302
313
  * the universal contract across providers: an unanswered `tool_use`
@@ -388,7 +399,7 @@ export class ToolExecutor {
388
399
  assertUniqueToolCallIds(response.message.toolCalls ?? []);
389
400
  const calls = new Map();
390
401
  for (const toolCall of response.message.toolCalls ?? []) {
391
- calls.set(toolCall.id, await this.prepareDirectCall(toolCall));
402
+ calls.set(toolCall.id, await prepareDirectCall(this.admissionHost(), toolCall));
392
403
  }
393
404
  return this.publishPreparedBatch(calls);
394
405
  }
@@ -401,7 +412,7 @@ export class ToolExecutor {
401
412
  const calls = new Map(previous.calls);
402
413
  for (const toolCall of response.message.toolCalls ?? []) {
403
414
  if (changedCallIds.has(toolCall.id)) {
404
- calls.set(toolCall.id, await this.prepareDirectCall(toolCall));
415
+ calls.set(toolCall.id, await prepareDirectCall(this.admissionHost(), toolCall));
405
416
  }
406
417
  }
407
418
  return this.publishPreparedBatch(calls);
@@ -914,6 +925,11 @@ export class ToolExecutor {
914
925
  this.skillScope = { ...scope, adoptedInBatch: this.batchCounter };
915
926
  },
916
927
  maxToolOutputChars: this.config.maxToolOutputChars ?? DEFAULT_MAX_TOOL_OUTPUT_CHARS,
928
+ // The run's screens, defaulted HERE rather than on the registry: a
929
+ // host builds the registry and hands it over, so a registry-side
930
+ // default is the host's to write and the shipped one reaches
931
+ // nobody. `[]` survives the `??` and is how a run says "none".
932
+ toolResultGuardrails: this.config.toolResultGuardrails ?? DEFAULT_TOOL_RESULT_GUARDRAILS,
917
933
  ...(this.config.skills ? { skills: this.config.skills } : {}),
918
934
  ...(this.config.web ? { web: this.config.web } : {}),
919
935
  // The SAME registry and the SAME context a model-issued call
@@ -984,7 +1000,7 @@ export class ToolExecutor {
984
1000
  // was answered with a generic hint while the configured repairer sat
985
1001
  // unused. Offer it the partial buffer first.
986
1002
  const truncationRepair = toolCall.metadata?.inputTruncated === true
987
- ? await this.repairTruncatedCall(toolCall, toolName)
1003
+ ? await repairTruncatedCall(this.admissionHost(), toolCall, toolName)
988
1004
  : null;
989
1005
  if (toolCall.metadata?.inputTruncated === true && !truncationRepair) {
990
1006
  const message = truncatedToolInputMessage(toolName);
@@ -1014,7 +1030,7 @@ export class ToolExecutor {
1014
1030
  // error went back as a `tool_result`, the model re-read the whole
1015
1031
  // context and tried again. A host that can repair it locally turns
1016
1032
  // that into nothing. No-op when no repairer is configured.
1017
- const resolved = await this.resolveCall(truncationRepair
1033
+ const resolved = await resolveCall(this.admissionHost(), truncationRepair
1018
1034
  ? {
1019
1035
  ...toolCall,
1020
1036
  function: {
@@ -1057,7 +1073,7 @@ export class ToolExecutor {
1057
1073
  input = resolved.input;
1058
1074
  let preOutcome;
1059
1075
  try {
1060
- preOutcome = await this.runPreToolHook(toolName, input);
1076
+ preOutcome = await runPreToolHook(this.admissionHost(), toolName, input);
1061
1077
  }
1062
1078
  catch (error) {
1063
1079
  if (!this.config.abortSignal.aborted)
@@ -1531,16 +1547,22 @@ export class ToolExecutor {
1531
1547
  parentSignal.removeEventListener('abort', onParentAbort);
1532
1548
  }
1533
1549
  }
1534
- async runPreToolHook(toolName, input, signal = this.config.abortSignal) {
1535
- if (!this.config.pluginManager)
1536
- return { kind: 'continue', input, modified: false };
1537
- const results = await this.config.pluginManager.executeHooks('pre_tool_use', {
1538
- runId: this.config.runId,
1539
- toolName,
1540
- toolInput: input,
1541
- signal,
1542
- }, this.emitEvent);
1543
- return this.interpretPreToolResults(toolName, input, results);
1550
+ /**
1551
+ * The three things the admission family reads off this executor.
1552
+ *
1553
+ * Built per call rather than held: `setSandbox` REPLACES `config`, so a
1554
+ * host captured once would hand the next admission a stale sandbox.
1555
+ *
1556
+ * The one way this differs from the inline code it replaced, which
1557
+ * re-read `this.config` at every use: an admission that spans a
1558
+ * `setSandbox()` now finishes against the config it STARTED with rather
1559
+ * than against the new one. Distinguishing the two readings needs
1560
+ * `setSandbox` to be called from a hook awaited in the middle of one
1561
+ * admission — its only call site is the run's sandbox acquisition,
1562
+ * before the loop, so nothing in this tree can tell them apart.
1563
+ */
1564
+ admissionHost() {
1565
+ return { config: this.config, emitEvent: this.emitEvent, log: this.log };
1544
1566
  }
1545
1567
  async prepareNestedCall(toolName, input, signal) {
1546
1568
  const prepare = this.config.tools.prepareExecution;
@@ -1554,7 +1576,7 @@ export class ToolExecutor {
1554
1576
  isError: true,
1555
1577
  };
1556
1578
  }
1557
- const preOutcome = await this.runPreToolHook(toolName, input, signal);
1579
+ const preOutcome = await runPreToolHook(this.admissionHost(), toolName, input, signal);
1558
1580
  if (preOutcome.kind === 'skip' || preOutcome.kind === 'error') {
1559
1581
  return {
1560
1582
  kind: 'synthetic',
@@ -1585,7 +1607,7 @@ export class ToolExecutor {
1585
1607
  isError: true,
1586
1608
  };
1587
1609
  }
1588
- const preOutcome = await this.runPreToolHook(toolName, preparation.prepared.input, signal);
1610
+ const preOutcome = await runPreToolHook(this.admissionHost(), toolName, preparation.prepared.input, signal);
1589
1611
  if (preOutcome.kind === 'skip' || preOutcome.kind === 'error') {
1590
1612
  return {
1591
1613
  kind: 'synthetic',
@@ -1612,347 +1634,6 @@ export class ToolExecutor {
1612
1634
  }
1613
1635
  return { kind: 'ready', input: modified.prepared.input, prepared: modified.prepared };
1614
1636
  }
1615
- async prepareDirectCall(toolCall) {
1616
- let toolName = toolCall.function.name;
1617
- const truncationRepair = toolCall.metadata?.inputTruncated === true
1618
- ? await this.repairTruncatedCall(toolCall, toolName)
1619
- : null;
1620
- if (toolCall.metadata?.inputTruncated === true && !truncationRepair) {
1621
- return {
1622
- kind: 'synthetic',
1623
- toolCall,
1624
- toolName,
1625
- input: {},
1626
- message: truncatedToolInputMessage(toolName),
1627
- isError: true,
1628
- };
1629
- }
1630
- const prepare = this.config.tools.prepareExecution;
1631
- const executePrepared = this.config.tools.executePrepared;
1632
- if (typeof prepare !== 'function' || typeof executePrepared !== 'function') {
1633
- const resolved = await this.resolveCall(truncationRepair
1634
- ? {
1635
- ...toolCall,
1636
- function: {
1637
- ...toolCall.function,
1638
- name: truncationRepair.toolName ?? toolName,
1639
- arguments: truncationRepair.arguments,
1640
- },
1641
- metadata: {},
1642
- }
1643
- : toolCall);
1644
- toolName = resolved.toolName;
1645
- if (!resolved.ok) {
1646
- return {
1647
- kind: 'synthetic',
1648
- toolCall,
1649
- toolName,
1650
- input: {},
1651
- message: resolved.message,
1652
- isError: true,
1653
- };
1654
- }
1655
- const preOutcome = await this.runPreToolHook(toolName, resolved.input);
1656
- if (preOutcome.kind === 'skip' || preOutcome.kind === 'error') {
1657
- return {
1658
- kind: 'synthetic',
1659
- toolCall,
1660
- toolName,
1661
- input: preOutcome.input,
1662
- message: preOutcome.output,
1663
- isError: preOutcome.kind === 'error',
1664
- };
1665
- }
1666
- if (!this.config.authorizationGate) {
1667
- return {
1668
- kind: 'legacy',
1669
- toolCall,
1670
- toolName,
1671
- input: preOutcome.input,
1672
- };
1673
- }
1674
- return {
1675
- kind: 'synthetic',
1676
- toolCall,
1677
- toolName,
1678
- input: preOutcome.input,
1679
- message: `Tool "${toolName}" was not executed because its registry cannot bind authorization to one prepared input.`,
1680
- isError: true,
1681
- };
1682
- }
1683
- let raw = truncationRepair?.arguments ?? toolCall.function.arguments;
1684
- toolName = truncationRepair?.toolName ?? toolName;
1685
- let repairUsed = truncationRepair !== null;
1686
- let preparation;
1687
- for (;;) {
1688
- let parsed;
1689
- try {
1690
- parsed = parseArguments(raw);
1691
- }
1692
- catch {
1693
- const message = `Error: Invalid JSON in tool arguments for "${toolName}"`;
1694
- const repair = !repairUsed && this.config.repairToolCall
1695
- ? await this.requestRepair(toolCall, toolName, {
1696
- reason: 'invalid_json',
1697
- message,
1698
- })
1699
- : null;
1700
- if (repair) {
1701
- repairUsed = true;
1702
- toolName = repair.toolName ?? toolName;
1703
- raw = repair.arguments;
1704
- continue;
1705
- }
1706
- return { kind: 'synthetic', toolCall, toolName, input: {}, message, isError: true };
1707
- }
1708
- try {
1709
- preparation = prepare.call(this.config.tools, toolName, parsed);
1710
- }
1711
- catch (err) {
1712
- const message = `Error: Unknown or unavailable tool "${toolName}": ${toErrorMessage(err)}`;
1713
- const repair = !repairUsed && this.config.repairToolCall
1714
- ? await this.requestRepair(toolCall, toolName, {
1715
- reason: 'unknown_tool',
1716
- message,
1717
- })
1718
- : null;
1719
- if (repair) {
1720
- repairUsed = true;
1721
- toolName = repair.toolName ?? toolName;
1722
- raw = repair.arguments;
1723
- continue;
1724
- }
1725
- return { kind: 'synthetic', toolCall, toolName, input: parsed, message, isError: true };
1726
- }
1727
- if (preparation.success)
1728
- break;
1729
- const message = formatFailedToolOutput(preparation.result.output, preparation.result.error);
1730
- const repair = !repairUsed && this.config.repairToolCall
1731
- ? await this.requestRepair(toolCall, toolName, {
1732
- reason: 'schema_validation',
1733
- message,
1734
- })
1735
- : null;
1736
- if (repair) {
1737
- repairUsed = true;
1738
- toolName = repair.toolName ?? toolName;
1739
- raw = repair.arguments;
1740
- continue;
1741
- }
1742
- return {
1743
- kind: 'synthetic',
1744
- toolCall,
1745
- toolName,
1746
- input: parsed,
1747
- message,
1748
- isError: true,
1749
- };
1750
- }
1751
- const preOutcome = await this.runPreToolHook(toolName, preparation.prepared.input);
1752
- if (preOutcome.kind === 'skip' || preOutcome.kind === 'error') {
1753
- return {
1754
- kind: 'synthetic',
1755
- toolCall,
1756
- toolName,
1757
- input: preOutcome.input,
1758
- message: preOutcome.output,
1759
- isError: preOutcome.kind === 'error',
1760
- };
1761
- }
1762
- if (preOutcome.modified) {
1763
- const modified = prepare.call(this.config.tools, toolName, preOutcome.input);
1764
- if (!modified.success) {
1765
- return {
1766
- kind: 'synthetic',
1767
- toolCall,
1768
- toolName,
1769
- input: preOutcome.input,
1770
- message: formatFailedToolOutput(modified.result.output, modified.result.error),
1771
- isError: true,
1772
- };
1773
- }
1774
- preparation = modified;
1775
- }
1776
- return {
1777
- kind: 'ready',
1778
- toolCall,
1779
- toolName,
1780
- input: preparation.prepared.input,
1781
- prepared: preparation.prepared,
1782
- };
1783
- }
1784
- interpretPreToolResults(toolName, initialInput, results) {
1785
- let currentInput = initialInput;
1786
- let modified = false;
1787
- for (const result of results) {
1788
- switch (result.action) {
1789
- case 'continue':
1790
- continue;
1791
- case 'modify':
1792
- currentInput = result.input;
1793
- modified = true;
1794
- continue;
1795
- case 'skip':
1796
- return {
1797
- kind: 'skip',
1798
- input: currentInput,
1799
- output: skippedToolResultText(toolName, result.reason),
1800
- };
1801
- case 'error':
1802
- return {
1803
- kind: 'error',
1804
- input: currentInput,
1805
- output: `Error: ${result.message}`,
1806
- };
1807
- case 'retry':
1808
- case 'annotate':
1809
- // There is no result to replace yet. Rejecting loudly beats
1810
- // silently ignoring it: a hook author who returned this here
1811
- // meant to redact something and would otherwise watch the secret
1812
- // go through.
1813
- case 'replace':
1814
- throw new Error(`Plugin hook pre_tool_use returned unsupported action '${result.action}' for tool ${toolName}`);
1815
- default: {
1816
- const _exhaustive = result;
1817
- throw new Error(`Unknown PluginHookResult: ${JSON.stringify(_exhaustive)}`);
1818
- }
1819
- }
1820
- }
1821
- return { kind: 'continue', input: currentInput, modified };
1822
- }
1823
- /**
1824
- * Turn the call the model issued into a name and a parsed input, giving
1825
- * a configured repairer one chance to fix it first.
1826
- *
1827
- * Exactly one chance: a repairer that produces a call which is still
1828
- * broken will not do better on a second look, and an unbounded loop
1829
- * here is a hang rather than a degradation.
1830
- *
1831
- * `invalid_json` is the ONLY failure that stops the call here, and it
1832
- * stopped it before this function existed too. `unknown_tool` and
1833
- * `schema_validation` merely OFFER the repair and otherwise fall
1834
- * through to the registry, which reports both with better messages —
1835
- * its schema error already ships a "Required: <field>: <type>" hint the
1836
- * model can self-correct from. So with no repairer configured this is
1837
- * behaviorally identical to the bare `JSON.parse` it replaced.
1838
- */
1839
- async resolveCall(toolCall) {
1840
- let toolName = toolCall.function.name;
1841
- let raw = toolCall.function.arguments;
1842
- for (let attempt = 0;; attempt++) {
1843
- const failure = this.inspectCall(toolName, raw);
1844
- if (!failure)
1845
- return { ok: true, toolName, input: parseArguments(raw) };
1846
- const repair = attempt === 0 && this.config.repairToolCall
1847
- ? await this.requestRepair(toolCall, toolName, failure)
1848
- : null;
1849
- if (!repair) {
1850
- if (failure.reason === 'invalid_json') {
1851
- return { ok: false, toolName, message: failure.message };
1852
- }
1853
- return { ok: true, toolName, input: parseArguments(raw) };
1854
- }
1855
- this.log.info('Repaired a malformed tool call', {
1856
- [NAMZU.RUN_ID]: this.config.runId,
1857
- [GENAI.TOOL_NAME]: toolName,
1858
- 'namzu.runtime.reason': failure.reason,
1859
- ...(repair.toolName && repair.toolName !== toolName
1860
- ? { 'namzu.runtime.repaired_to': repair.toolName }
1861
- : {}),
1862
- });
1863
- toolName = repair.toolName ?? toolName;
1864
- raw = repair.arguments;
1865
- }
1866
- }
1867
- /**
1868
- * What is wrong with this call, or `null` if nothing is.
1869
- *
1870
- * JSON is checked before the tool is looked up: an unparseable argument
1871
- * string is broken regardless of which tool it was aimed at, and it is
1872
- * the one problem the executor itself has to answer.
1873
- */
1874
- async repairTruncatedCall(toolCall, toolName) {
1875
- if (!this.config.repairToolCall)
1876
- return null;
1877
- // Present the PARTIAL buffer, not the normalized `"{}"` — a repairer
1878
- // handed an empty object has nothing to work from.
1879
- const partial = toolCall.metadata?.partialArguments ?? '';
1880
- const repair = await this.requestRepair({ ...toolCall, function: { ...toolCall.function, arguments: partial } }, toolName, { reason: 'invalid_json', message: truncatedToolInputMessage(toolName) });
1881
- if (repair) {
1882
- this.log.info('Repaired a tool call whose input stream was truncated', {
1883
- [NAMZU.RUN_ID]: this.config.runId,
1884
- [GENAI.TOOL_NAME]: toolName,
1885
- 'namzu.runtime.partial_length': partial.length,
1886
- });
1887
- }
1888
- return repair;
1889
- }
1890
- inspectCall(toolName, raw) {
1891
- let parsed;
1892
- try {
1893
- parsed = parseArguments(raw);
1894
- }
1895
- catch {
1896
- return {
1897
- reason: 'invalid_json',
1898
- message: `Error: Invalid JSON in tool arguments for "${toolName}"`,
1899
- };
1900
- }
1901
- const tool = this.config.tools.get?.(toolName);
1902
- if (!tool) {
1903
- // Either the model named a tool that does not exist, or this
1904
- // registry does not implement `get`. Both are the registry's to
1905
- // answer; a repairer still gets offered the `unknown_tool` case.
1906
- return {
1907
- reason: 'unknown_tool',
1908
- message: `Error: Unknown tool "${toolName}"`,
1909
- };
1910
- }
1911
- // A registry that hands back a tool with no schema has nothing to
1912
- // validate against; that is not a repairable condition, just an
1913
- // unvalidatable one.
1914
- const validation = tool.inputSchema?.safeParse(parsed);
1915
- if (validation && !validation.success) {
1916
- return {
1917
- reason: 'schema_validation',
1918
- message: `Error: Invalid arguments for "${toolName}": ${validation.error.issues
1919
- .map((issue) => `${issue.path.join('.') || '(root)'}: ${issue.message}`)
1920
- .join('; ')}`,
1921
- };
1922
- }
1923
- return null;
1924
- }
1925
- async requestRepair(toolCall, toolName, failure) {
1926
- const repairToolCall = this.config.repairToolCall;
1927
- if (!repairToolCall)
1928
- return null;
1929
- const tool = this.config.tools.get(toolName);
1930
- try {
1931
- return await repairToolCall({
1932
- toolCall,
1933
- reason: failure.reason,
1934
- message: failure.message,
1935
- ...(tool
1936
- ? {
1937
- tool,
1938
- jsonSchema: tool.modelInputSchema ?? renderToolSchema(tool.inputSchema),
1939
- }
1940
- : {}),
1941
- availableTools: this.config.tools.listNames(),
1942
- });
1943
- }
1944
- catch (err) {
1945
- // A broken repairer must not turn a recoverable tool error into a
1946
- // failed run: the original error is still a perfectly good answer
1947
- // to give the model.
1948
- this.log.error('repairToolCall threw — falling back to the original error', {
1949
- [NAMZU.RUN_ID]: this.config.runId,
1950
- [GENAI.TOOL_NAME]: toolName,
1951
- 'exception.message': toErrorMessage(err),
1952
- });
1953
- return null;
1954
- }
1955
- }
1956
1637
  /**
1957
1638
  * One execution attempt, with a throw materialized as an error result.
1958
1639
  *
@@ -2255,13 +1936,4 @@ class Semaphore {
2255
1936
  this.available++;
2256
1937
  }
2257
1938
  }
2258
- function formatFailedToolOutput(output, error) {
2259
- const errorText = `Error: ${error ?? 'Tool execution failed'}`;
2260
- if (!output || output.trim().length === 0)
2261
- return errorText;
2262
- return `${output}\n\n${errorText}`;
2263
- }
2264
- function truncatedToolInputMessage(toolName) {
2265
- return `Error: Tool "${toolName}" call was cut off while the model was streaming JSON arguments. The tool was NOT executed. Retry with a much shorter input. Self-budget content/new_string under 12000 characters before calling file tools. For long files, create a short opening with write and a deterministic marker, then advance that marker with bounded exact edit calls; for delegated work, pass a shared workspace filename/reference instead of embedding the content in the tool call.`;
2266
- }
2267
1939
  //# sourceMappingURL=executor.js.map