@namzu/sdk 32.0.0 → 33.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 (260) hide show
  1. package/CHANGELOG.md +127 -0
  2. package/dist/authorization/gate.d.ts +1 -1
  3. package/dist/authorization/gate.js +1 -1
  4. package/dist/connector/index.d.ts +1 -1
  5. package/dist/connector/index.d.ts.map +1 -1
  6. package/dist/connector/index.js +1 -1
  7. package/dist/connector/index.js.map +1 -1
  8. package/dist/connector/mcp/adapter.d.ts.map +1 -1
  9. package/dist/connector/mcp/adapter.js +71 -16
  10. package/dist/connector/mcp/adapter.js.map +1 -1
  11. package/dist/connector/mcp/client.d.ts.map +1 -1
  12. package/dist/connector/mcp/client.js +3 -2
  13. package/dist/connector/mcp/client.js.map +1 -1
  14. package/dist/connector/mcp/http-redirect.d.ts +17 -0
  15. package/dist/connector/mcp/http-redirect.d.ts.map +1 -0
  16. package/dist/connector/mcp/http-redirect.js +31 -0
  17. package/dist/connector/mcp/http-redirect.js.map +1 -0
  18. package/dist/connector/mcp/http-sse.d.ts.map +1 -1
  19. package/dist/connector/mcp/http-sse.js +5 -0
  20. package/dist/connector/mcp/http-sse.js.map +1 -1
  21. package/dist/connector/mcp/image-admission.d.ts +16 -0
  22. package/dist/connector/mcp/image-admission.d.ts.map +1 -0
  23. package/dist/connector/mcp/image-admission.js +288 -0
  24. package/dist/connector/mcp/image-admission.js.map +1 -0
  25. package/dist/connector/mcp/stdio.d.ts +13 -0
  26. package/dist/connector/mcp/stdio.d.ts.map +1 -1
  27. package/dist/connector/mcp/stdio.js +66 -23
  28. package/dist/connector/mcp/stdio.js.map +1 -1
  29. package/dist/connector/mcp/streamable-http.d.ts.map +1 -1
  30. package/dist/connector/mcp/streamable-http.js +3 -0
  31. package/dist/connector/mcp/streamable-http.js.map +1 -1
  32. package/dist/constants/tools/index.js +1 -1
  33. package/dist/constants/tools/index.js.map +1 -1
  34. package/dist/execution/base.d.ts +13 -0
  35. package/dist/execution/base.d.ts.map +1 -1
  36. package/dist/execution/base.js +103 -8
  37. package/dist/execution/base.js.map +1 -1
  38. package/dist/execution/errors.d.ts +12 -0
  39. package/dist/execution/errors.d.ts.map +1 -0
  40. package/dist/execution/errors.js +21 -0
  41. package/dist/execution/errors.js.map +1 -0
  42. package/dist/execution/factory.d.ts.map +1 -1
  43. package/dist/execution/factory.js +1 -0
  44. package/dist/execution/factory.js.map +1 -1
  45. package/dist/execution/hybrid.d.ts +5 -0
  46. package/dist/execution/hybrid.d.ts.map +1 -1
  47. package/dist/execution/hybrid.js +36 -16
  48. package/dist/execution/hybrid.js.map +1 -1
  49. package/dist/execution/index.d.ts +1 -0
  50. package/dist/execution/index.d.ts.map +1 -1
  51. package/dist/execution/index.js +1 -0
  52. package/dist/execution/index.js.map +1 -1
  53. package/dist/execution/local.d.ts +13 -0
  54. package/dist/execution/local.d.ts.map +1 -1
  55. package/dist/execution/local.js +145 -19
  56. package/dist/execution/local.js.map +1 -1
  57. package/dist/execution/output.d.ts +27 -0
  58. package/dist/execution/output.d.ts.map +1 -0
  59. package/dist/execution/output.js +105 -0
  60. package/dist/execution/output.js.map +1 -0
  61. package/dist/execution/remote.d.ts +26 -2
  62. package/dist/execution/remote.d.ts.map +1 -1
  63. package/dist/execution/remote.js +86 -15
  64. package/dist/execution/remote.js.map +1 -1
  65. package/dist/plugin/lifecycle.d.ts.map +1 -1
  66. package/dist/plugin/lifecycle.js +9 -3
  67. package/dist/plugin/lifecycle.js.map +1 -1
  68. package/dist/process/kill-tree.d.ts +11 -13
  69. package/dist/process/kill-tree.d.ts.map +1 -1
  70. package/dist/process/kill-tree.js +11 -13
  71. package/dist/process/kill-tree.js.map +1 -1
  72. package/dist/provider/fallback.d.ts.map +1 -1
  73. package/dist/provider/fallback.js +31 -0
  74. package/dist/provider/fallback.js.map +1 -1
  75. package/dist/provider/idle-timeout.d.ts.map +1 -1
  76. package/dist/provider/idle-timeout.js +5 -0
  77. package/dist/provider/idle-timeout.js.map +1 -1
  78. package/dist/provider/retry.d.ts.map +1 -1
  79. package/dist/provider/retry.js +5 -0
  80. package/dist/provider/retry.js.map +1 -1
  81. package/dist/public-runtime.d.ts +2 -2
  82. package/dist/public-runtime.d.ts.map +1 -1
  83. package/dist/public-runtime.js +2 -2
  84. package/dist/public-runtime.js.map +1 -1
  85. package/dist/registry/tool/execute.d.ts +7 -1
  86. package/dist/registry/tool/execute.d.ts.map +1 -1
  87. package/dist/registry/tool/execute.js +201 -54
  88. package/dist/registry/tool/execute.js.map +1 -1
  89. package/dist/run/command-gate.d.ts.map +1 -1
  90. package/dist/run/command-gate.js +11 -0
  91. package/dist/run/command-gate.js.map +1 -1
  92. package/dist/run/workspace-fingerprint.d.ts +4 -4
  93. package/dist/run/workspace-fingerprint.d.ts.map +1 -1
  94. package/dist/run/workspace-fingerprint.js +6 -4
  95. package/dist/run/workspace-fingerprint.js.map +1 -1
  96. package/dist/runtime/bidi/index.d.ts +1 -1
  97. package/dist/runtime/bidi/index.d.ts.map +1 -1
  98. package/dist/runtime/bidi/index.js +1 -1
  99. package/dist/runtime/bidi/index.js.map +1 -1
  100. package/dist/runtime/bidi/session.d.ts +19 -0
  101. package/dist/runtime/bidi/session.d.ts.map +1 -1
  102. package/dist/runtime/bidi/session.js +150 -26
  103. package/dist/runtime/bidi/session.js.map +1 -1
  104. package/dist/runtime/query/executor.d.ts +43 -1
  105. package/dist/runtime/query/executor.d.ts.map +1 -1
  106. package/dist/runtime/query/executor.js +626 -161
  107. package/dist/runtime/query/executor.js.map +1 -1
  108. package/dist/runtime/query/index.d.ts.map +1 -1
  109. package/dist/runtime/query/index.js +46 -4
  110. package/dist/runtime/query/index.js.map +1 -1
  111. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  112. package/dist/runtime/query/iteration/phases/tool-review.js +84 -22
  113. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  114. package/dist/runtime/query/request-rich-content.d.ts.map +1 -1
  115. package/dist/runtime/query/request-rich-content.js +33 -13
  116. package/dist/runtime/query/request-rich-content.js.map +1 -1
  117. package/dist/runtime/query/resume-pending.d.ts +5 -1
  118. package/dist/runtime/query/resume-pending.d.ts.map +1 -1
  119. package/dist/runtime/query/resume-pending.js +66 -1
  120. package/dist/runtime/query/resume-pending.js.map +1 -1
  121. package/dist/runtime/query/tool-output-budget.d.ts.map +1 -1
  122. package/dist/runtime/query/tool-output-budget.js +45 -16
  123. package/dist/runtime/query/tool-output-budget.js.map +1 -1
  124. package/dist/runtime/query/tooling.d.ts +6 -0
  125. package/dist/runtime/query/tooling.d.ts.map +1 -1
  126. package/dist/runtime/query/tooling.js +4 -0
  127. package/dist/runtime/query/tooling.js.map +1 -1
  128. package/dist/sandbox/provider/local.d.ts.map +1 -1
  129. package/dist/sandbox/provider/local.js +193 -41
  130. package/dist/sandbox/provider/local.js.map +1 -1
  131. package/dist/scheduler/delegating.d.ts +2 -1
  132. package/dist/scheduler/delegating.d.ts.map +1 -1
  133. package/dist/scheduler/delegating.js +6 -5
  134. package/dist/scheduler/delegating.js.map +1 -1
  135. package/dist/scheduler/local.d.ts +2 -1
  136. package/dist/scheduler/local.d.ts.map +1 -1
  137. package/dist/scheduler/local.js +2 -2
  138. package/dist/scheduler/local.js.map +1 -1
  139. package/dist/session/workspace/driver.d.ts +12 -4
  140. package/dist/session/workspace/driver.d.ts.map +1 -1
  141. package/dist/session/workspace/git-worktree.d.ts +17 -3
  142. package/dist/session/workspace/git-worktree.d.ts.map +1 -1
  143. package/dist/session/workspace/git-worktree.js +153 -34
  144. package/dist/session/workspace/git-worktree.js.map +1 -1
  145. package/dist/skills/registry.d.ts +15 -0
  146. package/dist/skills/registry.d.ts.map +1 -1
  147. package/dist/skills/registry.js +33 -0
  148. package/dist/skills/registry.js.map +1 -1
  149. package/dist/tools/builtins/computer-use.d.ts.map +1 -1
  150. package/dist/tools/builtins/computer-use.js +39 -2
  151. package/dist/tools/builtins/computer-use.js.map +1 -1
  152. package/dist/tools/builtins/run-code.d.ts.map +1 -1
  153. package/dist/tools/builtins/run-code.js +9 -11
  154. package/dist/tools/builtins/run-code.js.map +1 -1
  155. package/dist/tools/builtins/skill.d.ts +2 -1
  156. package/dist/tools/builtins/skill.d.ts.map +1 -1
  157. package/dist/tools/builtins/skill.js +250 -6
  158. package/dist/tools/builtins/skill.js.map +1 -1
  159. package/dist/tools/coordinator/agent.d.ts.map +1 -1
  160. package/dist/tools/coordinator/agent.js +100 -30
  161. package/dist/tools/coordinator/agent.js.map +1 -1
  162. package/dist/types/agent/manager.d.ts +1 -1
  163. package/dist/types/agent/manager.d.ts.map +1 -1
  164. package/dist/types/agent/scheduler.d.ts +10 -1
  165. package/dist/types/agent/scheduler.d.ts.map +1 -1
  166. package/dist/types/authorization/index.d.ts +12 -12
  167. package/dist/types/bidi/index.d.ts +19 -2
  168. package/dist/types/bidi/index.d.ts.map +1 -1
  169. package/dist/types/computer-use/index.d.ts +18 -0
  170. package/dist/types/computer-use/index.d.ts.map +1 -1
  171. package/dist/types/connector/execution-context.d.ts +3 -1
  172. package/dist/types/connector/execution-context.d.ts.map +1 -1
  173. package/dist/types/execution/index.d.ts +43 -1
  174. package/dist/types/execution/index.d.ts.map +1 -1
  175. package/dist/types/hitl/index.d.ts +11 -0
  176. package/dist/types/hitl/index.d.ts.map +1 -1
  177. package/dist/types/hitl/index.js.map +1 -1
  178. package/dist/types/message/index.d.ts +1 -1
  179. package/dist/types/message/index.d.ts.map +1 -1
  180. package/dist/types/message/index.js +2 -1
  181. package/dist/types/message/index.js.map +1 -1
  182. package/dist/types/plugin/index.d.ts +14 -1
  183. package/dist/types/plugin/index.d.ts.map +1 -1
  184. package/dist/types/plugin/index.js +1 -0
  185. package/dist/types/plugin/index.js.map +1 -1
  186. package/dist/types/provider/interface.d.ts +14 -0
  187. package/dist/types/provider/interface.d.ts.map +1 -1
  188. package/dist/types/sandbox/index.d.ts +15 -10
  189. package/dist/types/sandbox/index.d.ts.map +1 -1
  190. package/dist/types/sandbox/index.js.map +1 -1
  191. package/dist/types/tool/index.d.ts +77 -6
  192. package/dist/types/tool/index.d.ts.map +1 -1
  193. package/dist/types/tool/index.js.map +1 -1
  194. package/dist/types/workspace/ref.d.ts +5 -0
  195. package/dist/types/workspace/ref.d.ts.map +1 -1
  196. package/dist/utils/abort.d.ts +8 -0
  197. package/dist/utils/abort.d.ts.map +1 -1
  198. package/dist/utils/abort.js +16 -0
  199. package/dist/utils/abort.js.map +1 -1
  200. package/package.json +4 -4
  201. package/src/authorization/gate.ts +1 -1
  202. package/src/connector/index.ts +2 -0
  203. package/src/connector/mcp/adapter.ts +82 -21
  204. package/src/connector/mcp/client.ts +2 -1
  205. package/src/connector/mcp/http-redirect.ts +32 -0
  206. package/src/connector/mcp/http-sse.ts +5 -0
  207. package/src/connector/mcp/image-admission.ts +272 -0
  208. package/src/connector/mcp/stdio.ts +63 -21
  209. package/src/connector/mcp/streamable-http.ts +3 -0
  210. package/src/constants/tools/index.ts +1 -1
  211. package/src/execution/base.ts +124 -7
  212. package/src/execution/errors.ts +24 -0
  213. package/src/execution/factory.ts +1 -0
  214. package/src/execution/hybrid.ts +47 -16
  215. package/src/execution/index.ts +1 -0
  216. package/src/execution/local.ts +164 -19
  217. package/src/execution/output.ts +104 -0
  218. package/src/execution/remote.ts +122 -17
  219. package/src/plugin/lifecycle.ts +9 -3
  220. package/src/process/kill-tree.ts +11 -13
  221. package/src/provider/fallback.ts +39 -0
  222. package/src/provider/idle-timeout.ts +8 -0
  223. package/src/provider/retry.ts +8 -0
  224. package/src/public-runtime.ts +7 -1
  225. package/src/registry/tool/execute.ts +230 -64
  226. package/src/run/command-gate.ts +12 -0
  227. package/src/run/workspace-fingerprint.ts +5 -4
  228. package/src/runtime/bidi/index.ts +1 -1
  229. package/src/runtime/bidi/session.ts +177 -28
  230. package/src/runtime/query/executor.ts +769 -163
  231. package/src/runtime/query/index.ts +53 -6
  232. package/src/runtime/query/iteration/phases/tool-review.ts +87 -23
  233. package/src/runtime/query/request-rich-content.ts +39 -14
  234. package/src/runtime/query/resume-pending.ts +83 -1
  235. package/src/runtime/query/tool-output-budget.ts +52 -17
  236. package/src/runtime/query/tooling.ts +10 -0
  237. package/src/sandbox/provider/local.ts +213 -45
  238. package/src/scheduler/delegating.ts +6 -5
  239. package/src/scheduler/local.ts +3 -2
  240. package/src/session/workspace/driver.ts +12 -4
  241. package/src/session/workspace/git-worktree.ts +173 -34
  242. package/src/skills/registry.ts +47 -0
  243. package/src/tools/builtins/computer-use.ts +45 -2
  244. package/src/tools/builtins/run-code.ts +9 -11
  245. package/src/tools/builtins/skill.ts +325 -7
  246. package/src/tools/coordinator/agent.ts +108 -33
  247. package/src/types/agent/manager.ts +1 -1
  248. package/src/types/agent/scheduler.ts +10 -1
  249. package/src/types/bidi/index.ts +19 -2
  250. package/src/types/computer-use/index.ts +19 -0
  251. package/src/types/connector/execution-context.ts +3 -0
  252. package/src/types/execution/index.ts +46 -1
  253. package/src/types/hitl/index.ts +11 -0
  254. package/src/types/message/index.ts +3 -2
  255. package/src/types/plugin/index.ts +15 -0
  256. package/src/types/provider/interface.ts +18 -0
  257. package/src/types/sandbox/index.ts +15 -10
  258. package/src/types/tool/index.ts +85 -6
  259. package/src/types/workspace/ref.ts +5 -0
  260. package/src/utils/abort.ts +18 -0
@@ -5,22 +5,67 @@ export type ExecutionCapability = 'filesystem' | 'process' | 'network' | 'shell'
5
5
  export interface CommandOptions {
6
6
  cwd?: string
7
7
  env?: Record<string, string>
8
+ /**
9
+ * Caller-owned cancellation for this command.
10
+ *
11
+ * A compliant executor may accept this only when it can prove that command
12
+ * admission never happened or that admitted work reached quiescence before
13
+ * settling. `LocalExecutionContext` owns that guarantee for its process
14
+ * group. `RemoteExecutionContext` refuses this option because its generic
15
+ * executor seam has no reservation and terminal-acknowledgement protocol;
16
+ * use the `Sandbox.exec()` contract for cancellable remote execution.
17
+ */
18
+ signal?: AbortSignal
19
+ /**
20
+ * Command deadline in milliseconds; `0` disables the deadline.
21
+ *
22
+ * `LocalExecutionContext` owns its spawned process group through stdio
23
+ * close, first requesting termination and then forcing it after a bounded
24
+ * grace period. Remote handlers define how this option is enforced at their
25
+ * own execution boundary.
26
+ */
8
27
  timeoutMs?: number
9
28
  shell?: string | boolean
10
29
  }
11
30
 
31
+ export type CommandTermination =
32
+ | {
33
+ /** Caller cancellation was already present, so no process was admitted. */
34
+ origin: 'caller'
35
+ admitted: false
36
+ }
37
+ | {
38
+ /** The first Namzu-owned cause that requested termination. */
39
+ origin: 'caller' | 'timeout' | 'teardown'
40
+ admitted: true
41
+ /** The direct child's actual close signal, when Node reported one. */
42
+ signal?: string
43
+ }
44
+
12
45
  export interface CommandResult {
13
- exitCode: number
46
+ /** Numeric process exit, or `null` when no numeric exit exists. */
47
+ exitCode: number | null
14
48
  stdout: string
15
49
  stderr: string
50
+ /** Whether the executor retained only part of stdout. Absent means unknown. */
51
+ stdoutTruncated?: boolean
52
+ /** Whether the executor retained only part of stderr. Absent means unknown. */
53
+ stderrTruncated?: boolean
16
54
  durationMs: number
55
+ /** Present only when Namzu requested termination or refused pre-aborted admission. */
56
+ termination?: CommandTermination
17
57
  }
18
58
 
19
59
  export interface CommandExecutor {
20
60
  executeCommand(command: string, args?: string[], options?: CommandOptions): Promise<CommandResult>
21
61
  }
22
62
 
63
+ /**
64
+ * @deprecated Use `CommandExecutor` so command arguments retain their
65
+ * boundaries at the remote execution seam.
66
+ */
23
67
  export interface RemoteCommandHandler {
68
+ /** @deprecated Use `CommandExecutor.executeCommand()`. */
24
69
  executeRemote(command: string, options?: CommandOptions): Promise<CommandResult>
25
70
  }
26
71
 
@@ -71,6 +71,17 @@ export interface ToolCallSummary {
71
71
  name: string
72
72
  input: unknown
73
73
  isDestructive: boolean
74
+ /**
75
+ * Operator-policy verdict attached to the exact input shown for review.
76
+ *
77
+ * Persisted with a durable review so a later process cannot treat a call
78
+ * the gate denied as approved merely because the human approved a mixed
79
+ * batch. Absent only on checkpoints written before this field existed.
80
+ */
81
+ authorization?: {
82
+ decision: 'allow' | 'deny' | 'review'
83
+ reason?: string
84
+ }
74
85
  }
75
86
 
76
87
  export interface ToolModification {
@@ -6,7 +6,7 @@ export type CacheHint = 'cache' | 'ephemeral' | 'none'
6
6
 
7
7
  /** Why durable rich content must not be sent to a model again. */
8
8
  export interface ModelContentOmission {
9
- readonly reason: 'provider-rejected'
9
+ readonly reason: 'provider-rejected' | 'invalid-image'
10
10
  }
11
11
 
12
12
  /** Runtime validation for persisted model-delivery metadata. */
@@ -15,7 +15,8 @@ export function isModelContentOmission(value: unknown): value is ModelContentOmi
15
15
  typeof value === 'object' &&
16
16
  value !== null &&
17
17
  !Array.isArray(value) &&
18
- (value as { readonly reason?: unknown }).reason === 'provider-rejected'
18
+ ((value as { readonly reason?: unknown }).reason === 'provider-rejected' ||
19
+ (value as { readonly reason?: unknown }).reason === 'invalid-image')
19
20
  )
20
21
  }
21
22
 
@@ -10,6 +10,7 @@ import {
10
10
  } from '../../constants/plugin/index.js'
11
11
  import type { PluginId, RunId } from '../ids/index.js'
12
12
  import type { Message, ToolResultContent } from '../message/index.js'
13
+ import type { CancelCause } from '../run/cancel-cause.js'
13
14
  import type { ToolResult } from '../tool/index.js'
14
15
 
15
16
  // ---------------------------------------------------------------------------
@@ -85,6 +86,7 @@ export function assertPluginContributionType(type: PluginContributionType): void
85
86
  export type PluginHookEvent =
86
87
  | 'run_start'
87
88
  | 'run_end'
89
+ | 'run_interrupt'
88
90
  | 'pre_tool_use'
89
91
  | 'post_tool_use'
90
92
  | 'pre_llm_call'
@@ -96,6 +98,7 @@ export function assertPluginHookEvent(event: PluginHookEvent): void {
96
98
  switch (event) {
97
99
  case 'run_start':
98
100
  case 'run_end':
101
+ case 'run_interrupt':
99
102
  case 'pre_tool_use':
100
103
  case 'post_tool_use':
101
104
  case 'pre_llm_call':
@@ -147,6 +150,15 @@ export interface PluginHookContext {
147
150
  readonly toolInput?: unknown
148
151
  readonly toolResult?: ToolResult
149
152
  readonly iteration?: number
153
+ /**
154
+ * Why the run was stopped, on `run_interrupt`.
155
+ *
156
+ * That hook is emitted only for a root run carrying the explicit `user`
157
+ * cause. Keeping the field typed as the complete cause vocabulary lets a
158
+ * host narrow normally and leaves room for a future, deliberate expansion
159
+ * without overloading `event` or an error sentence.
160
+ */
161
+ readonly cancelCause?: CancelCause
150
162
 
151
163
  /**
152
164
  * The request about to be sent, on `pre_llm_call`.
@@ -176,6 +188,9 @@ export interface PluginHookContext {
176
188
  readonly response?: Readonly<PluginModelResponse>
177
189
  /**
178
190
  * Aborts when this hook's run is cancelled or its deadline expires.
191
+ * `run_interrupt` is the exception: the run is already cancelled, so its
192
+ * handler receives a fresh signal that represents only the bounded cleanup
193
+ * deadline. The original verdict is available as `cancelCause`.
179
194
  *
180
195
  * The runtime stops waiting on a slow hook either way, but in-process
181
196
  * JavaScript cannot be forcibly stopped. Without a signal the hook itself
@@ -163,6 +163,24 @@ export interface LLMProvider {
163
163
  thinking?: import('./chat.js').ThinkingConfig,
164
164
  ): readonly import('./chat.js').ReasoningEffort[] | undefined
165
165
 
166
+ /**
167
+ * The effort a model uses when a request omits an explicit level.
168
+ *
169
+ * A directional control cannot infer this from the menu: a model may start
170
+ * anywhere in its advertised order, so treating "default" as an item before
171
+ * the first level can make an increase lower the effective effort. The
172
+ * method is optional because many drivers cannot establish a model-owned
173
+ * default. A present method returning `undefined` means this particular
174
+ * model is unknown.
175
+ *
176
+ * When both this and {@link LLMProvider.reasoningEffortLevelsFor} return an
177
+ * exact value, the default must be a member of that menu.
178
+ */
179
+ reasoningEffortDefaultFor?(
180
+ model: string,
181
+ thinking?: import('./chat.js').ThinkingConfig,
182
+ ): import('./chat.js').ReasoningEffort | undefined
183
+
166
184
  /**
167
185
  * Legacy effort menu which cannot distinguish an unknown model from a model
168
186
  * with no effort support.
@@ -141,22 +141,27 @@ export interface SandboxExecOptions {
141
141
  readonly data: string
142
142
  }) => void
143
143
  /**
144
- * Cancellation for the command. A backend that honours it kills the
145
- * process; one that does not simply ignores it, so this is additive.
144
+ * Cancellation for the command. A backend that accepts it must terminate
145
+ * the owned process or prove that admission never happened. A backend that
146
+ * cannot make that guarantee must refuse before admission; silently
147
+ * ignoring the signal is not a compliant implementation.
146
148
  *
147
149
  * Without it a Stop (or a per-tool deadline) could only ever abandon
148
150
  * the *wait* — the sandboxed process kept running after the host
149
151
  * believed the run had been cancelled.
150
152
  *
151
- * **Who honours it.** The in-process local sandbox does: the signal is
152
- * merged with the call's own deadline and reaches `spawn`, so the child
153
- * dies. The remote backends do not, and deliberately: their wire has no
154
- * cancel op, so aborting the request would abandon the wait and leave the
155
- * command running the failure above, wearing the appearance of a fix.
156
- * They will honour it when their protocols carry a cancel.
153
+ * **Who honours it.** The in-process local sandbox does: the run owns a
154
+ * listener until the process group's shared stdio closes, and terminates
155
+ * that group directly. The HTTP-container backends in `@namzu/sandbox` also
156
+ * do: a
157
+ * current peer reserves an execution before admission and confirms its
158
+ * cancellation over a separate control request. Older worker and microVM
159
+ * images are refused when this option is present, because aborting only the HTTP
160
+ * or framed data request would abandon the wait and leave the command
161
+ * running.
157
162
  *
158
- * Passing it is therefore always safe and never harmful; whether it takes
159
- * effect depends on the backend.
163
+ * Every backend shipped by Namzu therefore either enforces the signal or
164
+ * returns an explicit unsupported error before running the command.
160
165
  */
161
166
  readonly signal?: AbortSignal
162
167
  }
@@ -26,6 +26,32 @@ export interface ToolRegistryRef {
26
26
  * filesystem imports into every consumer's type graph.
27
27
  */
28
28
  export interface SkillRegistryRef {
29
+ /**
30
+ * Enumerate the metadata the model may use to discover a skill.
31
+ *
32
+ * Optional so an older structural registry that only supports named loads
33
+ * remains a valid host. `SkillTool` refuses its list mode when this member is
34
+ * absent: `names()` cannot distinguish a model skill from an operator-only
35
+ * one, so treating it as a safe catalog would disclose an authority boundary.
36
+ */
37
+ catalog?():
38
+ | readonly {
39
+ /** The name the registry accepts, which may be namespaced by its host. */
40
+ registeredName: string
41
+ description: string
42
+ location: string
43
+ allowedTools?: string
44
+ invocation?: 'model' | 'operator' | 'both'
45
+ }[]
46
+ | Promise<
47
+ readonly {
48
+ registeredName: string
49
+ description: string
50
+ location: string
51
+ allowedTools?: string
52
+ invocation?: 'model' | 'operator' | 'both'
53
+ }[]
54
+ >
29
55
  /**
30
56
  * Load a skill's full body, or `undefined` for a name nobody registered.
31
57
  *
@@ -302,6 +328,16 @@ export interface ToolContext {
302
328
  allowedTools: readonly string[]
303
329
  }) => void
304
330
 
331
+ /**
332
+ * Effective model-visible character cap for this tool result.
333
+ *
334
+ * Present on executor-owned calls so a tool that can paginate does so
335
+ * before the generic head+tail fallback loses its middle. `0` means the
336
+ * host disabled the cap; absent means a direct host invocation did not
337
+ * declare one.
338
+ */
339
+ maxToolOutputChars?: number
340
+
305
341
  /**
306
342
  * Run another tool through the same dispatch this call came through.
307
343
  *
@@ -311,12 +347,17 @@ export interface ToolContext {
311
347
  * a property of holding a registry reference rather than a capability a
312
348
  * host wired deliberately.
313
349
  *
314
- * Available to every tool, which is worth being explicit about rather
315
- * than quietly true. Tools are host-installed code the model cannot add
316
- * one so the trust boundary this protects is the MODEL's reach, and
317
- * that is bounded where it has always been: `allowedTools` is enforced at
318
- * dispatch, so a tool calling through here reaches exactly what a
319
- * `tool_use` block would have.
350
+ * Available only for this invocation. The executor closes it when the
351
+ * visible call settles or is abandoned, aborts calls already started by
352
+ * it, and waits for their executor-owned terminal records before reporting
353
+ * the parent complete. Retaining the function does not retain authority.
354
+ *
355
+ * Tools are host-installed code — the model cannot add one — so the trust
356
+ * boundary this protects is the MODEL's reach. `allowedTools` is enforced
357
+ * again at dispatch. When the run has an operator authorization gate, a
358
+ * nested call must be explicitly allowed by that gate; a deny or an
359
+ * undecided call fails closed because another durable human review cannot
360
+ * be opened from inside the already-executing parent.
320
361
  */
321
362
  dispatchTool?: (
322
363
  name: string,
@@ -648,6 +689,27 @@ export interface ToolExecutionResult extends ToolResult {
648
689
  permissionMessage?: string
649
690
  }
650
691
 
692
+ /**
693
+ * An input decoded exactly once by its owning tool registry.
694
+ *
695
+ * `input` is the detached, deeply frozen JSON review projection:
696
+ * authorization, approval UI, probes and audit all inspect this value. The
697
+ * registry privately retains a separate detached copy that
698
+ * {@link ToolRegistryContract.executePrepared} gives the tool. Caller-owned
699
+ * aliases therefore cannot change either side after preparation. A
700
+ * preparation is registry-owned and cannot be executed by a different
701
+ * registry or after that tool registration is replaced.
702
+ */
703
+ export interface PreparedToolExecution {
704
+ readonly toolName: string
705
+ readonly input: unknown
706
+ }
707
+
708
+ /** Result of decoding a tool input at the execution boundary. */
709
+ export type ToolPreparationResult =
710
+ | { readonly success: true; readonly prepared: PreparedToolExecution }
711
+ | { readonly success: false; readonly result: ToolExecutionResult }
712
+
651
713
  /**
652
714
  * Full tool registry contract — registration, lookup, execution, prompt generation.
653
715
  * Concrete implementation: `ToolRegistry` in `registry/tool/execute.ts`.
@@ -675,6 +737,23 @@ export interface ToolRegistryContract {
675
737
  searchDeferred(query: string): ToolDefinition[]
676
738
  getCallableTools(toolNames?: string[]): ToolDefinition[]
677
739
 
740
+ /**
741
+ * Decode/transform an input once, before authorization or human review.
742
+ *
743
+ * The returned preparation is opaque registry authority. Implementations
744
+ * must not run tool code here. They must detach both the executable value
745
+ * and `prepared.input` from caller/schema aliases, and make the latter a
746
+ * deeply immutable JSON projection of the exact value retained for
747
+ * execution. Unsupported mutable/exotic graphs fail closed.
748
+ */
749
+ prepareExecution(toolName: string, rawInput: unknown): ToolPreparationResult
750
+
751
+ /** Execute the exact value retained by `prepareExecution`, without parsing again. */
752
+ executePrepared(
753
+ prepared: PreparedToolExecution,
754
+ context: ToolContext,
755
+ ): Promise<ToolExecutionResult>
756
+
678
757
  execute(toolName: string, rawInput: unknown, context: ToolContext): Promise<ToolExecutionResult>
679
758
 
680
759
  size(): number
@@ -49,6 +49,11 @@ export type WorkspaceBackendMeta = GitWorktreeBackendMeta
49
49
  * "which project is this directory" across sessions and process restarts.
50
50
  * The two words are close enough that the distinction has been rediscovered
51
51
  * more than once; it is written here so it does not have to be again.
52
+ *
53
+ * A persisted ref is recovery data for the backend configuration that created
54
+ * it, not authority for another driver instance to operate on an arbitrary
55
+ * resource. Reference drivers validate that affiliation before inspection,
56
+ * branching, or disposal.
52
57
  */
53
58
  export interface WorkspaceRef {
54
59
  id: WorkspaceId
@@ -1,3 +1,4 @@
1
+ import { EventEmitter } from 'node:events'
1
2
  import { toErrorMessage } from './error.js'
2
3
 
3
4
  /**
@@ -47,6 +48,23 @@ export function abortReasonText(reason: unknown): string | undefined {
47
48
  return text.length > 0 ? text : undefined
48
49
  }
49
50
 
51
+ /**
52
+ * Subscribe to caller cancellation without raising the Node 20.0 runtime floor.
53
+ *
54
+ * `addAbortListener` cannot be hidden by another listener calling
55
+ * `stopImmediatePropagation`. Both branches return an explicit disposer so a
56
+ * completed operation does not remain attached to a long-lived caller signal.
57
+ */
58
+ export function subscribeToAbort(signal: AbortSignal, listener: () => void): () => void {
59
+ if (typeof EventEmitter.addAbortListener === 'function') {
60
+ const subscription = EventEmitter.addAbortListener(signal, listener)
61
+ return () => subscription[Symbol.dispose]()
62
+ }
63
+
64
+ signal.addEventListener('abort', listener, { once: true })
65
+ return () => signal.removeEventListener('abort', listener)
66
+ }
67
+
50
68
  export function createChildAbortController(parent: AbortController): AbortController {
51
69
  const child = new AbortController()
52
70