@namzu/sdk 51.1.0 → 52.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/dist/agents/QueryAgent.d.ts.map +1 -1
  3. package/dist/agents/QueryAgent.js +2 -0
  4. package/dist/agents/QueryAgent.js.map +1 -1
  5. package/dist/agents/SupervisorAgent.d.ts +1 -1
  6. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  7. package/dist/agents/SupervisorAgent.js.map +1 -1
  8. package/dist/agents/examples/SupervisorAgent.d.ts +42 -0
  9. package/dist/agents/examples/SupervisorAgent.d.ts.map +1 -1
  10. package/dist/agents/examples/SupervisorAgent.js +80 -4
  11. package/dist/agents/examples/SupervisorAgent.js.map +1 -1
  12. package/dist/agents/index.d.ts +1 -1
  13. package/dist/agents/index.d.ts.map +1 -1
  14. package/dist/agents/index.js.map +1 -1
  15. package/dist/authorization/protected-paths.d.ts +36 -0
  16. package/dist/authorization/protected-paths.d.ts.map +1 -0
  17. package/dist/authorization/protected-paths.js +107 -0
  18. package/dist/authorization/protected-paths.js.map +1 -0
  19. package/dist/authorization/readonly-commands.d.ts +39 -0
  20. package/dist/authorization/readonly-commands.d.ts.map +1 -0
  21. package/dist/authorization/readonly-commands.js +797 -0
  22. package/dist/authorization/readonly-commands.js.map +1 -0
  23. package/dist/authorization/readonly-flags.d.ts +19 -0
  24. package/dist/authorization/readonly-flags.d.ts.map +1 -0
  25. package/dist/authorization/readonly-flags.js +178 -0
  26. package/dist/authorization/readonly-flags.js.map +1 -0
  27. package/dist/authorization/removal.d.ts +52 -0
  28. package/dist/authorization/removal.d.ts.map +1 -0
  29. package/dist/authorization/removal.js +283 -0
  30. package/dist/authorization/removal.js.map +1 -0
  31. package/dist/authorization/shell-paths.d.ts +66 -0
  32. package/dist/authorization/shell-paths.d.ts.map +1 -0
  33. package/dist/authorization/shell-paths.js +697 -0
  34. package/dist/authorization/shell-paths.js.map +1 -0
  35. package/dist/bridge/acp/update.d.ts.map +1 -1
  36. package/dist/bridge/acp/update.js +1 -0
  37. package/dist/bridge/acp/update.js.map +1 -1
  38. package/dist/compaction/context-window.d.ts +24 -0
  39. package/dist/compaction/context-window.d.ts.map +1 -1
  40. package/dist/compaction/context-window.js +34 -0
  41. package/dist/compaction/context-window.js.map +1 -1
  42. package/dist/compaction/manual.d.ts.map +1 -1
  43. package/dist/compaction/manual.js +10 -2
  44. package/dist/compaction/manual.js.map +1 -1
  45. package/dist/compaction/plan.d.ts +4 -0
  46. package/dist/compaction/plan.d.ts.map +1 -1
  47. package/dist/compaction/plan.js +6 -2
  48. package/dist/compaction/plan.js.map +1 -1
  49. package/dist/compaction/verifier.d.ts +11 -1
  50. package/dist/compaction/verifier.d.ts.map +1 -1
  51. package/dist/compaction/verifier.js +19 -7
  52. package/dist/compaction/verifier.js.map +1 -1
  53. package/dist/config/runtime.d.ts +55 -7
  54. package/dist/config/runtime.d.ts.map +1 -1
  55. package/dist/config/runtime.js +23 -2
  56. package/dist/config/runtime.js.map +1 -1
  57. package/dist/connector/web/guarded-fetch.d.ts.map +1 -1
  58. package/dist/connector/web/guarded-fetch.js +24 -0
  59. package/dist/connector/web/guarded-fetch.js.map +1 -1
  60. package/dist/connector/web/index.d.ts +1 -1
  61. package/dist/connector/web/index.d.ts.map +1 -1
  62. package/dist/connector/web/page.d.ts +59 -0
  63. package/dist/connector/web/page.d.ts.map +1 -0
  64. package/dist/connector/web/page.js +112 -0
  65. package/dist/connector/web/page.js.map +1 -0
  66. package/dist/connector/web/types.d.ts +47 -0
  67. package/dist/connector/web/types.d.ts.map +1 -1
  68. package/dist/connector/web/types.js.map +1 -1
  69. package/dist/constants/continuation.d.ts +8 -0
  70. package/dist/constants/continuation.d.ts.map +1 -1
  71. package/dist/constants/continuation.js +8 -0
  72. package/dist/constants/continuation.js.map +1 -1
  73. package/dist/constants/plugin/index.d.ts +1 -1
  74. package/dist/constants/plugin/index.d.ts.map +1 -1
  75. package/dist/constants/plugin/index.js +1 -1
  76. package/dist/constants/plugin/index.js.map +1 -1
  77. package/dist/manager/agent/lifecycle.d.ts +7 -0
  78. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  79. package/dist/manager/agent/lifecycle.js +11 -2
  80. package/dist/manager/agent/lifecycle.js.map +1 -1
  81. package/dist/manager/session/turn-recorder.d.ts.map +1 -1
  82. package/dist/manager/session/turn-recorder.js +4 -2
  83. package/dist/manager/session/turn-recorder.js.map +1 -1
  84. package/dist/plugin/index.d.ts +1 -1
  85. package/dist/plugin/index.d.ts.map +1 -1
  86. package/dist/plugin/index.js +1 -1
  87. package/dist/plugin/index.js.map +1 -1
  88. package/dist/plugin/lifecycle.d.ts +6 -0
  89. package/dist/plugin/lifecycle.d.ts.map +1 -1
  90. package/dist/plugin/lifecycle.js +98 -114
  91. package/dist/plugin/lifecycle.js.map +1 -1
  92. package/dist/plugin/shell-hook.d.ts +90 -22
  93. package/dist/plugin/shell-hook.d.ts.map +1 -1
  94. package/dist/plugin/shell-hook.js +434 -41
  95. package/dist/plugin/shell-hook.js.map +1 -1
  96. package/dist/public-runtime.d.ts +4 -1
  97. package/dist/public-runtime.d.ts.map +1 -1
  98. package/dist/public-runtime.js +4 -1
  99. package/dist/public-runtime.js.map +1 -1
  100. package/dist/public-types.d.ts +2 -2
  101. package/dist/public-types.d.ts.map +1 -1
  102. package/dist/runtime/jobs/registry.d.ts +21 -0
  103. package/dist/runtime/jobs/registry.d.ts.map +1 -1
  104. package/dist/runtime/jobs/registry.js +15 -2
  105. package/dist/runtime/jobs/registry.js.map +1 -1
  106. package/dist/runtime/query/executor/tool-call-admission.d.ts +10 -0
  107. package/dist/runtime/query/executor/tool-call-admission.d.ts.map +1 -1
  108. package/dist/runtime/query/executor/tool-call-admission.js +74 -13
  109. package/dist/runtime/query/executor/tool-call-admission.js.map +1 -1
  110. package/dist/runtime/query/executor.d.ts +41 -2
  111. package/dist/runtime/query/executor.d.ts.map +1 -1
  112. package/dist/runtime/query/executor.js +190 -6
  113. package/dist/runtime/query/executor.js.map +1 -1
  114. package/dist/runtime/query/finalize-turn.d.ts +6 -0
  115. package/dist/runtime/query/finalize-turn.d.ts.map +1 -1
  116. package/dist/runtime/query/finalize-turn.js +1 -1
  117. package/dist/runtime/query/finalize-turn.js.map +1 -1
  118. package/dist/runtime/query/index.d.ts +29 -0
  119. package/dist/runtime/query/index.d.ts.map +1 -1
  120. package/dist/runtime/query/index.js +15 -3
  121. package/dist/runtime/query/index.js.map +1 -1
  122. package/dist/runtime/query/iteration/index.d.ts +31 -0
  123. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  124. package/dist/runtime/query/iteration/index.js +216 -7
  125. package/dist/runtime/query/iteration/index.js.map +1 -1
  126. package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts +58 -0
  127. package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts.map +1 -0
  128. package/dist/runtime/query/iteration/phases/compaction-breaker.js +91 -0
  129. package/dist/runtime/query/iteration/phases/compaction-breaker.js.map +1 -0
  130. package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
  131. package/dist/runtime/query/iteration/phases/compaction.js +89 -7
  132. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  133. package/dist/runtime/query/iteration/phases/context.d.ts +10 -0
  134. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  135. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  136. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  137. package/dist/runtime/query/iteration/phases/tool-review.js +165 -2
  138. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  139. package/dist/runtime/query/path-escalation.d.ts +36 -0
  140. package/dist/runtime/query/path-escalation.d.ts.map +1 -0
  141. package/dist/runtime/query/path-escalation.js +184 -0
  142. package/dist/runtime/query/path-escalation.js.map +1 -0
  143. package/dist/runtime/query/plugin-hooks.d.ts.map +1 -1
  144. package/dist/runtime/query/plugin-hooks.js +8 -0
  145. package/dist/runtime/query/plugin-hooks.js.map +1 -1
  146. package/dist/runtime/query/reminders.d.ts +35 -0
  147. package/dist/runtime/query/reminders.d.ts.map +1 -0
  148. package/dist/runtime/query/reminders.js +7 -0
  149. package/dist/runtime/query/reminders.js.map +1 -0
  150. package/dist/runtime/query/resume-pending.d.ts.map +1 -1
  151. package/dist/runtime/query/resume-pending.js +26 -0
  152. package/dist/runtime/query/resume-pending.js.map +1 -1
  153. package/dist/runtime/query/review-policy.d.ts +26 -0
  154. package/dist/runtime/query/review-policy.d.ts.map +1 -1
  155. package/dist/runtime/query/review-policy.js +79 -5
  156. package/dist/runtime/query/review-policy.js.map +1 -1
  157. package/dist/runtime/query/tool-output-budget.d.ts +46 -0
  158. package/dist/runtime/query/tool-output-budget.d.ts.map +1 -1
  159. package/dist/runtime/query/tool-output-budget.js +85 -1
  160. package/dist/runtime/query/tool-output-budget.js.map +1 -1
  161. package/dist/runtime/query/tooling.d.ts +1 -0
  162. package/dist/runtime/query/tooling.d.ts.map +1 -1
  163. package/dist/runtime/query/tooling.js +5 -0
  164. package/dist/runtime/query/tooling.js.map +1 -1
  165. package/dist/session/paths.d.ts +5 -1
  166. package/dist/session/paths.d.ts.map +1 -1
  167. package/dist/session/paths.js +8 -0
  168. package/dist/session/paths.js.map +1 -1
  169. package/dist/store/memory/disk.d.ts.map +1 -1
  170. package/dist/store/memory/disk.js +2 -2
  171. package/dist/store/memory/disk.js.map +1 -1
  172. package/dist/store/memory/markdown.d.ts.map +1 -1
  173. package/dist/store/memory/markdown.js +2 -2
  174. package/dist/store/memory/markdown.js.map +1 -1
  175. package/dist/store/memory/naming.d.ts +9 -0
  176. package/dist/store/memory/naming.d.ts.map +1 -1
  177. package/dist/store/memory/naming.js +12 -0
  178. package/dist/store/memory/naming.js.map +1 -1
  179. package/dist/tools/builtins/bash.d.ts +30 -1
  180. package/dist/tools/builtins/bash.d.ts.map +1 -1
  181. package/dist/tools/builtins/bash.js +170 -26
  182. package/dist/tools/builtins/bash.js.map +1 -1
  183. package/dist/tools/builtins/content-fingerprint.d.ts +2 -0
  184. package/dist/tools/builtins/content-fingerprint.d.ts.map +1 -1
  185. package/dist/tools/builtins/content-fingerprint.js +4 -0
  186. package/dist/tools/builtins/content-fingerprint.js.map +1 -1
  187. package/dist/tools/builtins/edit.d.ts.map +1 -1
  188. package/dist/tools/builtins/edit.js +60 -18
  189. package/dist/tools/builtins/edit.js.map +1 -1
  190. package/dist/tools/builtins/grep.d.ts +8 -1
  191. package/dist/tools/builtins/grep.d.ts.map +1 -1
  192. package/dist/tools/builtins/grep.js +152 -33
  193. package/dist/tools/builtins/grep.js.map +1 -1
  194. package/dist/tools/builtins/read-file.d.ts +1 -0
  195. package/dist/tools/builtins/read-file.d.ts.map +1 -1
  196. package/dist/tools/builtins/read-file.js +44 -5
  197. package/dist/tools/builtins/read-file.js.map +1 -1
  198. package/dist/tools/builtins/read-render.d.ts +52 -0
  199. package/dist/tools/builtins/read-render.d.ts.map +1 -1
  200. package/dist/tools/builtins/read-render.js +143 -0
  201. package/dist/tools/builtins/read-render.js.map +1 -1
  202. package/dist/tools/builtins/web.d.ts +13 -2
  203. package/dist/tools/builtins/web.d.ts.map +1 -1
  204. package/dist/tools/builtins/web.js +225 -32
  205. package/dist/tools/builtins/web.js.map +1 -1
  206. package/dist/tools/coordinator/ask-user-question.d.ts +2 -0
  207. package/dist/tools/coordinator/ask-user-question.d.ts.map +1 -1
  208. package/dist/tools/coordinator/ask-user-question.js +256 -92
  209. package/dist/tools/coordinator/ask-user-question.js.map +1 -1
  210. package/dist/tools/coordinator/question-options.d.ts +1 -0
  211. package/dist/tools/coordinator/question-options.d.ts.map +1 -1
  212. package/dist/tools/coordinator/question-options.js +6 -1
  213. package/dist/tools/coordinator/question-options.js.map +1 -1
  214. package/dist/tools/defineTool.d.ts +4 -1
  215. package/dist/tools/defineTool.d.ts.map +1 -1
  216. package/dist/tools/defineTool.js +6 -1
  217. package/dist/tools/defineTool.js.map +1 -1
  218. package/dist/tools/paths.d.ts +6 -0
  219. package/dist/tools/paths.d.ts.map +1 -1
  220. package/dist/tools/paths.js +1 -1
  221. package/dist/tools/paths.js.map +1 -1
  222. package/dist/types/agent/query.d.ts +3 -0
  223. package/dist/types/agent/query.d.ts.map +1 -1
  224. package/dist/types/errors/catalog.js +1 -1
  225. package/dist/types/errors/catalog.js.map +1 -1
  226. package/dist/types/hitl/index.d.ts +104 -0
  227. package/dist/types/hitl/index.d.ts.map +1 -1
  228. package/dist/types/hitl/index.js.map +1 -1
  229. package/dist/types/message/index.d.ts +1 -1
  230. package/dist/types/message/index.d.ts.map +1 -1
  231. package/dist/types/message/index.js +2 -0
  232. package/dist/types/message/index.js.map +1 -1
  233. package/dist/types/plugin/index.d.ts +93 -19
  234. package/dist/types/plugin/index.d.ts.map +1 -1
  235. package/dist/types/plugin/index.js +3 -0
  236. package/dist/types/plugin/index.js.map +1 -1
  237. package/dist/types/provider/errors.d.ts +19 -0
  238. package/dist/types/provider/errors.d.ts.map +1 -1
  239. package/dist/types/provider/errors.js +24 -0
  240. package/dist/types/provider/errors.js.map +1 -1
  241. package/dist/types/session/events.d.ts +10 -2
  242. package/dist/types/session/events.d.ts.map +1 -1
  243. package/dist/types/session/events.js.map +1 -1
  244. package/dist/types/session/records.d.ts +9 -9
  245. package/dist/types/session/records.d.ts.map +1 -1
  246. package/dist/types/session/records.js +1 -0
  247. package/dist/types/session/records.js.map +1 -1
  248. package/dist/types/session/stop-reason.d.ts +5 -0
  249. package/dist/types/session/stop-reason.d.ts.map +1 -1
  250. package/dist/types/tool/index.d.ts +52 -0
  251. package/dist/types/tool/index.d.ts.map +1 -1
  252. package/dist/types/tool/index.js.map +1 -1
  253. package/package.json +4 -2
  254. package/src/agents/QueryAgent.ts +5 -1
  255. package/src/agents/SupervisorAgent.ts +1 -1
  256. package/src/agents/examples/SupervisorAgent.ts +217 -106
  257. package/src/agents/index.ts +1 -1
  258. package/src/authorization/protected-paths.ts +110 -0
  259. package/src/authorization/readonly-commands.ts +771 -0
  260. package/src/authorization/readonly-flags.ts +196 -0
  261. package/src/authorization/removal.ts +326 -0
  262. package/src/authorization/shell-paths.ts +927 -0
  263. package/src/bridge/acp/update.ts +1 -0
  264. package/src/compaction/context-window.ts +41 -0
  265. package/src/compaction/manual.ts +10 -2
  266. package/src/compaction/plan.ts +11 -2
  267. package/src/compaction/verifier.ts +28 -3
  268. package/src/config/runtime.ts +23 -2
  269. package/src/connector/web/guarded-fetch.ts +26 -0
  270. package/src/connector/web/index.ts +3 -0
  271. package/src/connector/web/page.ts +130 -0
  272. package/src/connector/web/types.ts +47 -0
  273. package/src/constants/continuation.ts +10 -0
  274. package/src/constants/plugin/index.ts +1 -1
  275. package/src/manager/agent/lifecycle.ts +12 -4
  276. package/src/manager/session/turn-recorder.ts +10 -2
  277. package/src/plugin/index.ts +1 -0
  278. package/src/plugin/lifecycle.ts +107 -119
  279. package/src/plugin/shell-hook.ts +477 -43
  280. package/src/public-runtime.ts +8 -0
  281. package/src/public-types.ts +4 -0
  282. package/src/runtime/jobs/registry.ts +39 -2
  283. package/src/runtime/query/executor/tool-call-admission.ts +79 -13
  284. package/src/runtime/query/executor.ts +242 -8
  285. package/src/runtime/query/finalize-turn.ts +7 -1
  286. package/src/runtime/query/index.ts +45 -3
  287. package/src/runtime/query/iteration/index.ts +251 -12
  288. package/src/runtime/query/iteration/phases/compaction-breaker.ts +106 -0
  289. package/src/runtime/query/iteration/phases/compaction.ts +99 -12
  290. package/src/runtime/query/iteration/phases/context.ts +11 -0
  291. package/src/runtime/query/iteration/phases/tool-review.ts +177 -2
  292. package/src/runtime/query/path-escalation.ts +204 -0
  293. package/src/runtime/query/plugin-hooks.ts +8 -0
  294. package/src/runtime/query/reminders.ts +45 -0
  295. package/src/runtime/query/resume-pending.ts +27 -0
  296. package/src/runtime/query/review-policy.ts +106 -5
  297. package/src/runtime/query/tool-output-budget.ts +115 -1
  298. package/src/runtime/query/tooling.ts +6 -0
  299. package/src/session/paths.ts +11 -1
  300. package/src/store/memory/disk.ts +2 -1
  301. package/src/store/memory/markdown.ts +2 -1
  302. package/src/store/memory/naming.ts +13 -0
  303. package/src/tools/builtins/bash.ts +203 -34
  304. package/src/tools/builtins/content-fingerprint.ts +5 -0
  305. package/src/tools/builtins/edit.ts +69 -19
  306. package/src/tools/builtins/grep.ts +159 -38
  307. package/src/tools/builtins/read-file.ts +48 -5
  308. package/src/tools/builtins/read-render.ts +193 -0
  309. package/src/tools/builtins/web.ts +258 -33
  310. package/src/tools/coordinator/ask-user-question.ts +309 -103
  311. package/src/tools/coordinator/question-options.ts +8 -1
  312. package/src/tools/defineTool.ts +11 -2
  313. package/src/tools/paths.ts +1 -1
  314. package/src/types/agent/query.ts +3 -0
  315. package/src/types/errors/catalog.ts +1 -1
  316. package/src/types/hitl/index.ts +104 -0
  317. package/src/types/message/index.ts +2 -0
  318. package/src/types/plugin/index.ts +101 -18
  319. package/src/types/provider/errors.ts +33 -0
  320. package/src/types/session/events.ts +10 -2
  321. package/src/types/session/records.ts +1 -0
  322. package/src/types/session/stop-reason.ts +5 -0
  323. package/src/types/tool/index.ts +53 -2
@@ -20,13 +20,24 @@ import { generateToolCallId } from '../../utils/id.js';
20
20
  import { compressShellOutput } from '../../utils/shell-compress.js';
21
21
  import { parseStructuredResultJson } from '../../utils/structured-result-json.js';
22
22
  import { bindOwner } from '../jobs/registry.js';
23
- import { formatFailedToolOutput, isUnregistered, prepareDirectCall, runPreToolHook, unknownToolMessage, } from './executor/tool-call-admission.js';
23
+ import { formatFailedToolOutput, hookNote, isUnregistered, prepareDirectCall, runPreToolHook, unknownToolMessage, } from './executor/tool-call-admission.js';
24
24
  import { describeVisibleFileEvidence } from './file-evidence-context.js';
25
25
  import { seedObservationLedger } from './file-evidence-seed.js';
26
26
  import { DEFAULT_TOOL_RESULT_GUARDRAILS } from './guardrail-presets.js';
27
+ import { declaredWriteEscalation, shellEscalations } from './path-escalation.js';
27
28
  import { savedPresentation } from './saved-presentation.js';
28
29
  import { ToolCallBudget, assertMaxToolCalls } from './tool-call-budget.js';
29
- import { DEFAULT_MAX_TOOL_OUTPUT_CHARS, applyToolOutputBudget, describeDroppedContent, measureContentBytes, } from './tool-output-budget.js';
30
+ import { DEFAULT_MAX_TOOL_OUTPUT_CHARS, DEFAULT_MAX_TOOL_OUTPUT_CHARS_PER_MESSAGE, applyToolOutputBudget, describeDroppedContent, fitResultsToMessageBudget, measureContentBytes, } from './tool-output-budget.js';
31
+ /**
32
+ * A nested call cannot open a review from inside the tool that issued it, so
33
+ * a hook that asked for a person to approve it can only be answered with a
34
+ * refusal. Silence would run the call the hook wanted a person to see.
35
+ */
36
+ function askedNestedMessage(toolName, outcome) {
37
+ if (outcome.kind !== 'continue' || outcome.permission?.decision !== 'ask')
38
+ return undefined;
39
+ return `Blocked: a hook asked that a person approve "${toolName}", and an approval cannot be requested from inside another tool. Call it directly.`;
40
+ }
30
41
  function assertUniqueToolCallIds(toolCalls) {
31
42
  const seen = new Set();
32
43
  for (const [index, toolCall] of toolCalls.entries()) {
@@ -548,12 +559,67 @@ export class ToolExecutor {
548
559
  tool.sandboxEscapeArgument !== undefined &&
549
560
  input[tool.sandboxEscapeArgument] === true;
550
561
  const unknownProgram = this.unknownProgramOf(tool, input, sandboxed);
551
- if (!outsidePaths && !sandboxEscape && unknownProgram === undefined)
562
+ const reaches = await this.pathEscalationsOf(tool, input, sandboxed);
563
+ if (reaches.outsidePaths.length > 0) {
564
+ outsidePaths = [...new Set([...(outsidePaths ?? []), ...reaches.outsidePaths])];
565
+ }
566
+ const protectedPaths = reaches.protectedPaths;
567
+ const removal = reaches.dangerousRemoval;
568
+ if (!outsidePaths &&
569
+ !sandboxEscape &&
570
+ unknownProgram === undefined &&
571
+ protectedPaths.length === 0 &&
572
+ removal === undefined) {
552
573
  return undefined;
574
+ }
553
575
  return {
554
576
  ...(outsidePaths ? { outsidePaths } : {}),
555
577
  ...(sandboxEscape ? { sandboxEscape: true } : {}),
556
578
  ...(unknownProgram !== undefined ? { unknownProgram } : {}),
579
+ ...(protectedPaths.length > 0 ? { protectedPaths } : {}),
580
+ ...(removal !== undefined ? { dangerousRemoval: removal } : {}),
581
+ };
582
+ }
583
+ /**
584
+ * What the call's own arguments write or remove that no mode may approve in
585
+ * advance, and, for a shell line, the paths outside the roots it names.
586
+ *
587
+ * The protected-path and removal floors hold whether or not the turn reviews
588
+ * outside paths and whether or not it is sandboxed: they are about what the
589
+ * write plants and what the removal destroys, not about where the turn is
590
+ * allowed to look. The outside-path half follows `outsideRootAccess`, as the
591
+ * file tools' own does.
592
+ */
593
+ async pathEscalationsOf(tool, input, sandboxed) {
594
+ const roots = toolRoots(this.config);
595
+ const protectedPaths = [];
596
+ let outsidePaths = [];
597
+ let removal;
598
+ if (tool.pathArgument !== undefined && tool.isReadOnly?.(input) !== true) {
599
+ const value = input[tool.pathArgument];
600
+ if (typeof value === 'string') {
601
+ const hit = await declaredWriteEscalation(roots, value);
602
+ if (hit !== undefined)
603
+ protectedPaths.push(hit);
604
+ }
605
+ }
606
+ if (tool.commandArgument !== undefined) {
607
+ const line = input[tool.commandArgument];
608
+ if (typeof line === 'string') {
609
+ const dialect = tool.commandDialect?.({ sandboxed }) ?? 'sh';
610
+ const found = await shellEscalations(line, dialect, {
611
+ roots,
612
+ reviewOutside: this.config.outsideRootAccess === 'review' && !sandboxed,
613
+ });
614
+ protectedPaths.push(...found.protectedPaths);
615
+ outsidePaths = found.outsidePaths;
616
+ removal = found.dangerousRemoval;
617
+ }
618
+ }
619
+ return {
620
+ outsidePaths,
621
+ protectedPaths: [...new Set(protectedPaths)],
622
+ ...(removal !== undefined ? { dangerousRemoval: removal } : {}),
557
623
  };
558
624
  }
559
625
  /**
@@ -602,11 +668,13 @@ export class ToolExecutor {
602
668
  .filter((call) => call.kind !== 'synthetic')
603
669
  .map((call) => {
604
670
  const escalation = escalations.get(call.toolCall.id);
671
+ const hookPermission = call.hook?.permission;
605
672
  return {
606
673
  id: call.toolCall.id,
607
674
  name: call.toolName,
608
675
  input: call.input,
609
676
  ...(escalation ? { escalation } : {}),
677
+ ...(hookPermission ? { hookPermission } : {}),
610
678
  };
611
679
  });
612
680
  const batch = Object.freeze({
@@ -832,6 +900,7 @@ export class ToolExecutor {
832
900
  isError: true,
833
901
  });
834
902
  }
903
+ this.fitBatchToMessageBudget(results);
835
904
  // isError and rich content were computed and then discarded here: the
836
905
  // tuple narrowed to {toolCallId, output} BEFORE the message was built,
837
906
  // so the failure signal and any image block were structurally lost at
@@ -839,6 +908,46 @@ export class ToolExecutor {
839
908
  const messages = results.map((r) => createToolMessage(r.content ?? r.output, r.toolCallId, r.isError, r.revealedTools));
840
909
  return { messages, results, observations };
841
910
  }
911
+ /**
912
+ * Hold the batch's combined result text under the per-message cap.
913
+ *
914
+ * Runs once, on the settled batch and before the results become history
915
+ * messages, so what it writes is what every later request replays.
916
+ */
917
+ fitBatchToMessageBudget(results) {
918
+ const maxChars = this.config.maxToolOutputCharsPerMessage ?? DEFAULT_MAX_TOOL_OUTPUT_CHARS_PER_MESSAGE;
919
+ const replacements = fitResultsToMessageBudget(results.map((r) => ({
920
+ toolCallId: r.toolCallId,
921
+ toolName: r.toolName,
922
+ output: r.output,
923
+ // A result carrying rich content or a structured-output copy is
924
+ // not text the model reads alone, and a self-limiting tool has
925
+ // already decided its own size.
926
+ replaceable: r.content === undefined &&
927
+ r.structuredResultJson === undefined &&
928
+ this.config.tools.get(r.toolName)?.selfLimitingOutput !== true,
929
+ })), {
930
+ maxChars,
931
+ spillDir: this.outputDirectory(),
932
+ onError: (message) => this.log.warn('Failed to retain a tool output replaced for the message budget', {
933
+ [NAMZU.TURN_ID]: this.config.turnId,
934
+ 'exception.message': message,
935
+ }),
936
+ });
937
+ if (replacements.size === 0)
938
+ return;
939
+ for (const [i, r] of results.entries()) {
940
+ const replacement = replacements.get(r.toolCallId);
941
+ if (replacement === undefined)
942
+ continue;
943
+ results[i] = { ...r, output: replacement.output };
944
+ }
945
+ this.log.warn("Tool results exceeded the message's combined budget", {
946
+ [NAMZU.TURN_ID]: this.config.turnId,
947
+ 'namzu.runtime.replaced_count': replacements.size,
948
+ 'namzu.runtime.message_budget_chars': maxChars,
949
+ });
950
+ }
842
951
  /**
843
952
  * Run a tool on behalf of another tool, and put it on the record.
844
953
  *
@@ -1236,7 +1345,9 @@ export class ToolExecutor {
1236
1345
  let input;
1237
1346
  let prepared;
1238
1347
  let inputFailure;
1348
+ let preparedHookContext;
1239
1349
  if (preparedCall?.kind === 'ready' || preparedCall?.kind === 'legacy') {
1350
+ preparedHookContext = preparedCall.hook?.context;
1240
1351
  input = preparedCall.input;
1241
1352
  prepared = preparedCall.kind === 'ready' ? preparedCall.prepared : undefined;
1242
1353
  if (preparedCall.kind === 'legacy')
@@ -1262,6 +1373,7 @@ export class ToolExecutor {
1262
1373
  return this.recordSyntheticPreparation(admitted);
1263
1374
  toolName = admitted.toolName;
1264
1375
  input = admitted.input;
1376
+ preparedHookContext = admitted.hook?.context;
1265
1377
  prepared = admitted.kind === 'ready' ? admitted.prepared : undefined;
1266
1378
  if (admitted.kind === 'legacy')
1267
1379
  inputFailure = admitted.inputFailure;
@@ -1460,7 +1572,10 @@ export class ToolExecutor {
1460
1572
  ];
1461
1573
  }
1462
1574
  }
1463
- const maxToolOutputChars = this.config.maxToolOutputChars ?? DEFAULT_MAX_TOOL_OUTPUT_CHARS;
1575
+ // A tool that bounds its own output is exempt from the DEFAULT per-result
1576
+ // cap only; a host that set the cap explicitly still gets it.
1577
+ const maxToolOutputChars = this.config.maxToolOutputChars ??
1578
+ (executedDefinition?.selfLimitingOutput === true ? 0 : DEFAULT_MAX_TOOL_OUTPUT_CHARS);
1464
1579
  const sourceOutput = output;
1465
1580
  // This owner-bound channel follows the selected receipt after both result
1466
1581
  // screening and post-tool replacement. ToolResult.data and raw input can
@@ -1511,12 +1626,25 @@ export class ToolExecutor {
1511
1626
  });
1512
1627
  }
1513
1628
  output = budgeted.output;
1514
- const modelContent = selectedContent === undefined
1629
+ let modelContent = selectedContent === undefined
1515
1630
  ? undefined
1516
1631
  : this.budgetContent(selectedContent, toolName, toolCall.id, {
1517
1632
  sourceOutput,
1518
1633
  budgeted,
1519
1634
  });
1635
+ // What hooks said about this call rides with its result, in the model's
1636
+ // receipt and the host's alike, framed as a hook's words. Added after
1637
+ // the output budget so a bounded result is never trimmed to make room
1638
+ // for it; the note has its own bound (`hookNote`).
1639
+ const hookTexts = [preparedHookContext, post.context].filter((text) => text !== undefined && text.length > 0);
1640
+ if (hookTexts.length > 0) {
1641
+ const note = hookNote(hookTexts);
1642
+ output = `${output}\n\n${note}`;
1643
+ if (typeof modelContent === 'string')
1644
+ modelContent = `${modelContent}\n\n${note}`;
1645
+ else if (modelContent)
1646
+ modelContent = [...modelContent, { type: 'text', text: note }];
1647
+ }
1520
1648
  // A failed call, or an override that says the call failed. A `replace`
1521
1649
  // says the opposite, and reading it as a failure is what made redaction
1522
1650
  // unusable: the model was told a successful call had gone wrong, and
@@ -1820,8 +1948,21 @@ export class ToolExecutor {
1820
1948
  emitEvent: this.emitEvent,
1821
1949
  log: this.log,
1822
1950
  allowedTools: this.effectiveAllowedTools(),
1951
+ requestHalt: (reason) => this.requestHalt(reason),
1823
1952
  };
1824
1953
  }
1954
+ /** Why a hook asked for the turn to stop, until the iteration loop reads it. */
1955
+ haltReason;
1956
+ /** The first request wins: a later hook does not rewrite why the turn stopped. */
1957
+ requestHalt(reason) {
1958
+ this.haltReason ??= reason;
1959
+ }
1960
+ /** Read and clear a hook's request to stop the turn. */
1961
+ takeHalt() {
1962
+ const reason = this.haltReason;
1963
+ this.haltReason = undefined;
1964
+ return reason;
1965
+ }
1825
1966
  async prepareNestedCall(toolName, input, signal) {
1826
1967
  signal.throwIfAborted();
1827
1968
  const prepare = this.config.tools.prepareExecutionAsync ?? this.config.tools.prepareExecution;
@@ -1845,6 +1986,14 @@ export class ToolExecutor {
1845
1986
  ...(preOutcome.kind === 'skip' ? { skipped: true } : {}),
1846
1987
  };
1847
1988
  }
1989
+ const nestedAsk = askedNestedMessage(toolName, preOutcome);
1990
+ if (nestedAsk)
1991
+ return {
1992
+ kind: 'synthetic',
1993
+ input: preOutcome.input,
1994
+ message: nestedAsk,
1995
+ isError: true,
1996
+ };
1848
1997
  return { kind: 'legacy', input: preOutcome.input };
1849
1998
  }
1850
1999
  let preparation;
@@ -1885,6 +2034,15 @@ export class ToolExecutor {
1885
2034
  ...(preOutcome.kind === 'skip' ? { skipped: true } : {}),
1886
2035
  };
1887
2036
  }
2037
+ const nestedAsk = askedNestedMessage(toolName, preOutcome);
2038
+ if (nestedAsk) {
2039
+ return {
2040
+ kind: 'synthetic',
2041
+ input: preOutcome.input,
2042
+ message: nestedAsk,
2043
+ isError: true,
2044
+ };
2045
+ }
1888
2046
  if (!preOutcome.modified) {
1889
2047
  return {
1890
2048
  kind: 'ready',
@@ -1937,6 +2095,7 @@ export class ToolExecutor {
1937
2095
  /**
1938
2096
  * @returns `override` — text replacing the tool's output, or `null`.
1939
2097
  * `retry` — the hook asked for the tool to run again.
2098
+ * `context` — what the hooks want the model to read beside the result.
1940
2099
  */
1941
2100
  async runPostToolHook(toolName, input, toolResult) {
1942
2101
  if (!this.config.pluginManager)
@@ -1951,6 +2110,22 @@ export class ToolExecutor {
1951
2110
  toolResult,
1952
2111
  signal: this.config.abortSignal,
1953
2112
  }, this.emitEvent);
2113
+ // A failed call is announced a second time, by name, so a hook that
2114
+ // only cares about failures need not read every result. After
2115
+ // `post_tool_use`, so the existing hooks see exactly what they saw.
2116
+ if (!toolResult.success) {
2117
+ results = [
2118
+ ...results,
2119
+ ...(await this.config.pluginManager.executeHooks('post_tool_use_failure', {
2120
+ sessionId: this.config.sessionId,
2121
+ turnId: this.config.turnId,
2122
+ toolName,
2123
+ toolInput: input,
2124
+ toolResult,
2125
+ signal: this.config.abortSignal,
2126
+ }, this.emitEvent)),
2127
+ ];
2128
+ }
1954
2129
  }
1955
2130
  catch (error) {
1956
2131
  if (!this.config.abortSignal.aborted)
@@ -1969,9 +2144,14 @@ export class ToolExecutor {
1969
2144
  }
1970
2145
  let override = null;
1971
2146
  let retry = false;
2147
+ const contexts = [];
1972
2148
  for (const result of results) {
1973
2149
  switch (result.action) {
1974
2150
  case 'continue':
2151
+ if (result.context)
2152
+ contexts.push(result.context);
2153
+ if (result.halt)
2154
+ this.requestHalt(result.halt.reason);
1975
2155
  continue;
1976
2156
  case 'error':
1977
2157
  override = { output: `Error: ${result.message}`, isError: true };
@@ -2005,7 +2185,11 @@ export class ToolExecutor {
2005
2185
  }
2006
2186
  }
2007
2187
  }
2008
- return { override, retry };
2188
+ return {
2189
+ override,
2190
+ retry,
2191
+ ...(contexts.length > 0 ? { context: contexts.join('\n') } : {}),
2192
+ };
2009
2193
  }
2010
2194
  /**
2011
2195
  * Answer a tool call that policy or a human refused, without executing