@namzu/sdk 6.2.0 → 8.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 (251) hide show
  1. package/CHANGELOG.md +677 -0
  2. package/dist/agents/ReactiveAgent.d.ts.map +1 -1
  3. package/dist/agents/ReactiveAgent.js +5 -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 +172 -158
  7. package/dist/agents/SupervisorAgent.js.map +1 -1
  8. package/dist/agents/__tests__/supervisor-inbox-scope.test.d.ts +2 -0
  9. package/dist/agents/__tests__/supervisor-inbox-scope.test.d.ts.map +1 -0
  10. package/dist/agents/__tests__/supervisor-inbox-scope.test.js +125 -0
  11. package/dist/agents/__tests__/supervisor-inbox-scope.test.js.map +1 -0
  12. package/dist/agents/runAgent.d.ts +19 -1
  13. package/dist/agents/runAgent.d.ts.map +1 -1
  14. package/dist/agents/runAgent.js +2 -0
  15. package/dist/agents/runAgent.js.map +1 -1
  16. package/dist/bridge/a2a/mapper.d.ts.map +1 -1
  17. package/dist/bridge/a2a/mapper.js +4 -0
  18. package/dist/bridge/a2a/mapper.js.map +1 -1
  19. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  20. package/dist/bridge/sse/mapper.js +24 -0
  21. package/dist/bridge/sse/mapper.js.map +1 -1
  22. package/dist/connector/mcp/__tests__/positional-arrays.test.d.ts +2 -0
  23. package/dist/connector/mcp/__tests__/positional-arrays.test.d.ts.map +1 -0
  24. package/dist/connector/mcp/__tests__/positional-arrays.test.js +142 -0
  25. package/dist/connector/mcp/__tests__/positional-arrays.test.js.map +1 -0
  26. package/dist/connector/mcp/adapter.d.ts.map +1 -1
  27. package/dist/connector/mcp/adapter.js +123 -8
  28. package/dist/connector/mcp/adapter.js.map +1 -1
  29. package/dist/constants/agent/index.d.ts +5 -0
  30. package/dist/constants/agent/index.d.ts.map +1 -1
  31. package/dist/constants/agent/index.js +5 -0
  32. package/dist/constants/agent/index.js.map +1 -1
  33. package/dist/constants/plugin/index.d.ts +15 -0
  34. package/dist/constants/plugin/index.d.ts.map +1 -1
  35. package/dist/constants/plugin/index.js +15 -0
  36. package/dist/constants/plugin/index.js.map +1 -1
  37. package/dist/contracts/api.d.ts +1 -1
  38. package/dist/contracts/api.d.ts.map +1 -1
  39. package/dist/gateway/__tests__/completion-inbox.test.js +292 -2
  40. package/dist/gateway/__tests__/completion-inbox.test.js.map +1 -1
  41. package/dist/gateway/completion-inbox.d.ts +94 -6
  42. package/dist/gateway/completion-inbox.d.ts.map +1 -1
  43. package/dist/gateway/completion-inbox.js +235 -15
  44. package/dist/gateway/completion-inbox.js.map +1 -1
  45. package/dist/gateway/local.d.ts +11 -0
  46. package/dist/gateway/local.d.ts.map +1 -1
  47. package/dist/gateway/local.js +27 -1
  48. package/dist/gateway/local.js.map +1 -1
  49. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  50. package/dist/manager/agent/lifecycle.js +6 -0
  51. package/dist/manager/agent/lifecycle.js.map +1 -1
  52. package/dist/manager/run/persistence.d.ts +8 -0
  53. package/dist/manager/run/persistence.d.ts.map +1 -1
  54. package/dist/manager/run/persistence.js +12 -0
  55. package/dist/manager/run/persistence.js.map +1 -1
  56. package/dist/provider/thinking-support.d.ts +2 -1
  57. package/dist/provider/thinking-support.d.ts.map +1 -1
  58. package/dist/provider/thinking-support.js +14 -0
  59. package/dist/provider/thinking-support.js.map +1 -1
  60. package/dist/public-runtime.d.ts +1 -1
  61. package/dist/public-runtime.d.ts.map +1 -1
  62. package/dist/public-runtime.js +9 -1
  63. package/dist/public-runtime.js.map +1 -1
  64. package/dist/run/reporter.d.ts.map +1 -1
  65. package/dist/run/reporter.js +11 -0
  66. package/dist/run/reporter.js.map +1 -1
  67. package/dist/runtime/query/__tests__/completion-does-not-erase-the-answer.test.d.ts +2 -0
  68. package/dist/runtime/query/__tests__/completion-does-not-erase-the-answer.test.d.ts.map +1 -0
  69. package/dist/runtime/query/__tests__/completion-does-not-erase-the-answer.test.js +142 -0
  70. package/dist/runtime/query/__tests__/completion-does-not-erase-the-answer.test.js.map +1 -0
  71. package/dist/runtime/query/__tests__/completion-notification.test.js +414 -32
  72. package/dist/runtime/query/__tests__/completion-notification.test.js.map +1 -1
  73. package/dist/runtime/query/__tests__/context-size-on-the-wire.test.d.ts +2 -0
  74. package/dist/runtime/query/__tests__/context-size-on-the-wire.test.d.ts.map +1 -0
  75. package/dist/runtime/query/__tests__/context-size-on-the-wire.test.js +100 -0
  76. package/dist/runtime/query/__tests__/context-size-on-the-wire.test.js.map +1 -0
  77. package/dist/runtime/query/__tests__/context.test.js +18 -0
  78. package/dist/runtime/query/__tests__/context.test.js.map +1 -1
  79. package/dist/runtime/query/__tests__/effort-reaches-the-wire.test.d.ts +2 -0
  80. package/dist/runtime/query/__tests__/effort-reaches-the-wire.test.d.ts.map +1 -0
  81. package/dist/runtime/query/__tests__/effort-reaches-the-wire.test.js +118 -0
  82. package/dist/runtime/query/__tests__/effort-reaches-the-wire.test.js.map +1 -0
  83. package/dist/runtime/query/__tests__/tool-timeout.test.js +34 -0
  84. package/dist/runtime/query/__tests__/tool-timeout.test.js.map +1 -1
  85. package/dist/runtime/query/context.d.ts.map +1 -1
  86. package/dist/runtime/query/context.js +16 -1
  87. package/dist/runtime/query/context.js.map +1 -1
  88. package/dist/runtime/query/executor.d.ts.map +1 -1
  89. package/dist/runtime/query/executor.js +11 -1
  90. package/dist/runtime/query/executor.js.map +1 -1
  91. package/dist/runtime/query/guard.d.ts +28 -0
  92. package/dist/runtime/query/guard.d.ts.map +1 -1
  93. package/dist/runtime/query/guard.js +31 -0
  94. package/dist/runtime/query/guard.js.map +1 -1
  95. package/dist/runtime/query/iteration/__tests__/settle-grace.test.d.ts +2 -0
  96. package/dist/runtime/query/iteration/__tests__/settle-grace.test.d.ts.map +1 -0
  97. package/dist/runtime/query/iteration/__tests__/settle-grace.test.js +226 -0
  98. package/dist/runtime/query/iteration/__tests__/settle-grace.test.js.map +1 -0
  99. package/dist/runtime/query/iteration/index.d.ts +92 -0
  100. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  101. package/dist/runtime/query/iteration/index.js +818 -565
  102. package/dist/runtime/query/iteration/index.js.map +1 -1
  103. package/dist/runtime/query/iteration/phases/__tests__/compaction-declined.test.d.ts +2 -0
  104. package/dist/runtime/query/iteration/phases/__tests__/compaction-declined.test.d.ts.map +1 -0
  105. package/dist/runtime/query/iteration/phases/__tests__/compaction-declined.test.js +95 -0
  106. package/dist/runtime/query/iteration/phases/__tests__/compaction-declined.test.js.map +1 -0
  107. package/dist/runtime/query/iteration/phases/compaction.d.ts +34 -0
  108. package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
  109. package/dist/runtime/query/iteration/phases/compaction.js +61 -4
  110. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  111. package/dist/telemetry/__tests__/model-call-span.test.js +22 -4
  112. package/dist/telemetry/__tests__/model-call-span.test.js.map +1 -1
  113. package/dist/telemetry/__tests__/span-closure.test.js +12 -5
  114. package/dist/telemetry/__tests__/span-closure.test.js.map +1 -1
  115. package/dist/tools/__tests__/untrusted-envelope.test.js +16 -0
  116. package/dist/tools/__tests__/untrusted-envelope.test.js.map +1 -1
  117. package/dist/tools/coordinator/__tests__/completion-delivery.test.js +117 -0
  118. package/dist/tools/coordinator/__tests__/completion-delivery.test.js.map +1 -1
  119. package/dist/tools/coordinator/__tests__/task-list.test.js +57 -0
  120. package/dist/tools/coordinator/__tests__/task-list.test.js.map +1 -1
  121. package/dist/tools/coordinator/__tests__/wait-with-idle-bound.test.d.ts +2 -0
  122. package/dist/tools/coordinator/__tests__/wait-with-idle-bound.test.d.ts.map +1 -0
  123. package/dist/tools/coordinator/__tests__/wait-with-idle-bound.test.js +193 -0
  124. package/dist/tools/coordinator/__tests__/wait-with-idle-bound.test.js.map +1 -0
  125. package/dist/tools/coordinator/index.d.ts +19 -0
  126. package/dist/tools/coordinator/index.d.ts.map +1 -1
  127. package/dist/tools/coordinator/index.js +191 -71
  128. package/dist/tools/coordinator/index.js.map +1 -1
  129. package/dist/tools/coordinator/wait-with-idle-bound.d.ts +66 -0
  130. package/dist/tools/coordinator/wait-with-idle-bound.d.ts.map +1 -0
  131. package/dist/tools/coordinator/wait-with-idle-bound.js +78 -0
  132. package/dist/tools/coordinator/wait-with-idle-bound.js.map +1 -0
  133. package/dist/tools/untrusted-envelope.d.ts.map +1 -1
  134. package/dist/tools/untrusted-envelope.js +9 -1
  135. package/dist/tools/untrusted-envelope.js.map +1 -1
  136. package/dist/types/agent/base.d.ts +16 -0
  137. package/dist/types/agent/base.d.ts.map +1 -1
  138. package/dist/types/agent/gateway.d.ts +41 -0
  139. package/dist/types/agent/gateway.d.ts.map +1 -1
  140. package/dist/types/agent/lifecycle-event.d.ts +9 -1
  141. package/dist/types/agent/lifecycle-event.d.ts.map +1 -1
  142. package/dist/types/agent/task.d.ts +5 -0
  143. package/dist/types/agent/task.d.ts.map +1 -1
  144. package/dist/types/hitl/index.d.ts +10 -0
  145. package/dist/types/hitl/index.d.ts.map +1 -1
  146. package/dist/types/hitl/index.js.map +1 -1
  147. package/dist/types/probe/registry.d.ts +6 -0
  148. package/dist/types/probe/registry.d.ts.map +1 -1
  149. package/dist/types/provider/interface.d.ts +35 -0
  150. package/dist/types/provider/interface.d.ts.map +1 -1
  151. package/dist/types/run/config.d.ts +25 -0
  152. package/dist/types/run/config.d.ts.map +1 -1
  153. package/dist/types/run/entity.d.ts +16 -0
  154. package/dist/types/run/entity.d.ts.map +1 -1
  155. package/dist/types/run/events.d.ts +75 -0
  156. package/dist/types/run/events.d.ts.map +1 -1
  157. package/dist/types/run/events.js.map +1 -1
  158. package/dist/types/run/prepare-step.d.ts +17 -2
  159. package/dist/types/run/prepare-step.d.ts.map +1 -1
  160. package/dist/types/verification/index.d.ts +98 -0
  161. package/dist/types/verification/index.d.ts.map +1 -1
  162. package/dist/types/verification/index.js +10 -0
  163. package/dist/types/verification/index.js.map +1 -1
  164. package/dist/utils/__tests__/abort-reason.test.d.ts +2 -0
  165. package/dist/utils/__tests__/abort-reason.test.d.ts.map +1 -0
  166. package/dist/utils/__tests__/abort-reason.test.js +48 -0
  167. package/dist/utils/__tests__/abort-reason.test.js.map +1 -0
  168. package/dist/utils/abort.d.ts +26 -0
  169. package/dist/utils/abort.d.ts.map +1 -1
  170. package/dist/utils/abort.js +34 -0
  171. package/dist/utils/abort.js.map +1 -1
  172. package/dist/verification/__tests__/argument-pattern.test.d.ts +2 -0
  173. package/dist/verification/__tests__/argument-pattern.test.d.ts.map +1 -0
  174. package/dist/verification/__tests__/argument-pattern.test.js +122 -0
  175. package/dist/verification/__tests__/argument-pattern.test.js.map +1 -0
  176. package/dist/verification/__tests__/rule-order-and-reason.test.d.ts +2 -0
  177. package/dist/verification/__tests__/rule-order-and-reason.test.d.ts.map +1 -0
  178. package/dist/verification/__tests__/rule-order-and-reason.test.js +126 -0
  179. package/dist/verification/__tests__/rule-order-and-reason.test.js.map +1 -0
  180. package/dist/verification/gate.d.ts +17 -1
  181. package/dist/verification/gate.d.ts.map +1 -1
  182. package/dist/verification/gate.js +102 -2
  183. package/dist/verification/gate.js.map +1 -1
  184. package/dist/verification/index.d.ts +1 -1
  185. package/dist/verification/index.d.ts.map +1 -1
  186. package/dist/verification/index.js +1 -1
  187. package/dist/verification/index.js.map +1 -1
  188. package/dist/verification/rules.d.ts.map +1 -1
  189. package/dist/verification/rules.js +27 -0
  190. package/dist/verification/rules.js.map +1 -1
  191. package/package.json +1 -1
  192. package/src/agents/ReactiveAgent.ts +5 -0
  193. package/src/agents/SupervisorAgent.ts +175 -162
  194. package/src/agents/__tests__/supervisor-inbox-scope.test.ts +149 -0
  195. package/src/agents/runAgent.ts +22 -1
  196. package/src/bridge/a2a/mapper.ts +4 -0
  197. package/src/bridge/sse/mapper.ts +25 -0
  198. package/src/connector/mcp/__tests__/positional-arrays.test.ts +183 -0
  199. package/src/connector/mcp/adapter.ts +131 -7
  200. package/src/constants/agent/index.ts +5 -0
  201. package/src/constants/plugin/index.ts +15 -0
  202. package/src/contracts/api.ts +1 -0
  203. package/src/gateway/__tests__/completion-inbox.test.ts +348 -2
  204. package/src/gateway/completion-inbox.ts +248 -16
  205. package/src/gateway/local.ts +26 -1
  206. package/src/manager/agent/lifecycle.ts +6 -0
  207. package/src/manager/run/persistence.ts +12 -0
  208. package/src/provider/thinking-support.ts +19 -2
  209. package/src/public-runtime.ts +9 -0
  210. package/src/run/reporter.ts +12 -0
  211. package/src/runtime/query/__tests__/completion-does-not-erase-the-answer.test.ts +163 -0
  212. package/src/runtime/query/__tests__/completion-notification.test.ts +486 -34
  213. package/src/runtime/query/__tests__/context-size-on-the-wire.test.ts +122 -0
  214. package/src/runtime/query/__tests__/context.test.ts +24 -0
  215. package/src/runtime/query/__tests__/effort-reaches-the-wire.test.ts +135 -0
  216. package/src/runtime/query/__tests__/tool-timeout.test.ts +38 -0
  217. package/src/runtime/query/context.ts +16 -1
  218. package/src/runtime/query/executor.ts +11 -1
  219. package/src/runtime/query/guard.ts +32 -0
  220. package/src/runtime/query/iteration/__tests__/settle-grace.test.ts +265 -0
  221. package/src/runtime/query/iteration/index.ts +906 -635
  222. package/src/runtime/query/iteration/phases/__tests__/compaction-declined.test.ts +124 -0
  223. package/src/runtime/query/iteration/phases/compaction.ts +83 -10
  224. package/src/telemetry/__tests__/model-call-span.test.ts +22 -5
  225. package/src/telemetry/__tests__/span-closure.test.ts +12 -5
  226. package/src/tools/__tests__/untrusted-envelope.test.ts +23 -0
  227. package/src/tools/coordinator/__tests__/completion-delivery.test.ts +147 -0
  228. package/src/tools/coordinator/__tests__/task-list.test.ts +72 -0
  229. package/src/tools/coordinator/__tests__/wait-with-idle-bound.test.ts +247 -0
  230. package/src/tools/coordinator/index.ts +205 -78
  231. package/src/tools/coordinator/wait-with-idle-bound.ts +142 -0
  232. package/src/tools/untrusted-envelope.ts +9 -1
  233. package/src/types/agent/base.ts +17 -0
  234. package/src/types/agent/gateway.ts +42 -0
  235. package/src/types/agent/lifecycle-event.ts +7 -0
  236. package/src/types/agent/task.ts +5 -0
  237. package/src/types/hitl/index.ts +10 -0
  238. package/src/types/probe/registry.ts +6 -0
  239. package/src/types/provider/interface.ts +39 -0
  240. package/src/types/run/config.ts +26 -0
  241. package/src/types/run/entity.ts +17 -0
  242. package/src/types/run/events.ts +75 -0
  243. package/src/types/run/prepare-step.ts +17 -2
  244. package/src/types/verification/index.ts +61 -0
  245. package/src/utils/__tests__/abort-reason.test.ts +56 -0
  246. package/src/utils/abort.ts +34 -0
  247. package/src/verification/__tests__/argument-pattern.test.ts +158 -0
  248. package/src/verification/__tests__/rule-order-and-reason.test.ts +149 -0
  249. package/src/verification/gate.ts +106 -3
  250. package/src/verification/index.ts +1 -1
  251. package/src/verification/rules.ts +28 -0
@@ -19,10 +19,60 @@ export type VerificationRule = {
19
19
  type: 'deny_by_name';
20
20
  toolNames: string[];
21
21
  } | {
22
+ /**
23
+ * Match a regular expression against the tool's NAME, the
24
+ * serialised arguments, or both concatenated.
25
+ *
26
+ * Read `target: 'args'` carefully before writing one: it tests
27
+ * `JSON.stringify(toolInput)`, so the subject is the JSON TEXT of
28
+ * the whole argument object — `{"command":"git push origin main"}`
29
+ * — and not any single argument. The name suggests otherwise, and
30
+ * that is what makes it a trap: an anchored pattern like
31
+ * `^git push.*$` is a natural thing to write and can never match,
32
+ * so the rule silently decides nothing. `'both'` PREFIXES the tool
33
+ * name to that text rather than requiring it, so it is not a scope
34
+ * either — a rule written with `bash` in mind still sees every
35
+ * other tool's arguments.
36
+ *
37
+ * When you mean "this tool, this argument", use
38
+ * {@link VerificationRule} `argument_pattern` instead. This one
39
+ * stays for the case it is actually good at: matching anywhere in
40
+ * the serialised input without caring where.
41
+ */
22
42
  type: 'custom_pattern';
23
43
  pattern: string;
24
44
  target: 'name' | 'args' | 'both';
25
45
  decision: 'allow' | 'deny';
46
+ } | {
47
+ /**
48
+ * Match a regular expression against ONE named argument of ONE
49
+ * named set of tools.
50
+ *
51
+ * This exists because `custom_pattern` could express neither half.
52
+ * It carries no tool scope, so a rule an operator wrote about
53
+ * `bash` decided `edit` calls too; and its argument target tests
54
+ * the serialised object, so pinning the tool cost the ability to
55
+ * anchor and anchoring cost the tool scope. Every pattern rule was
56
+ * therefore one of those two wrong things.
57
+ *
58
+ * The subject here is the argument's own VALUE, so `^git push`
59
+ * means what it looks like it means.
60
+ *
61
+ * A rule whose tool is not called, or whose argument is absent,
62
+ * decides nothing — the rule's precondition simply is not met. So
63
+ * does one whose argument holds an object or an array: a pattern
64
+ * cannot say anything true about a structured value, and pretending
65
+ * otherwise by matching its serialisation would reintroduce exactly
66
+ * the confusion this rule was added to remove. If you need to
67
+ * refuse a tool over the SHAPE of its input rather than a string in
68
+ * it, deny it by name.
69
+ */
70
+ type: 'argument_pattern';
71
+ toolNames: string[];
72
+ /** The argument key, at the top level of the tool's input. */
73
+ argument: string;
74
+ pattern: string;
75
+ decision: 'allow' | 'deny';
26
76
  } | {
27
77
  type: 'allow_by_tier';
28
78
  tiers: string[];
@@ -81,6 +131,24 @@ export declare const VerificationRuleSchema: z.ZodDiscriminatedUnion<"type", [z.
81
131
  target: "name" | "args" | "both";
82
132
  pattern: string;
83
133
  decision: "deny" | "allow";
134
+ }>, z.ZodObject<{
135
+ type: z.ZodLiteral<"argument_pattern">;
136
+ toolNames: z.ZodArray<z.ZodString, "many">;
137
+ argument: z.ZodString;
138
+ pattern: z.ZodString;
139
+ decision: z.ZodEnum<["allow", "deny"]>;
140
+ }, "strip", z.ZodTypeAny, {
141
+ type: "argument_pattern";
142
+ pattern: string;
143
+ toolNames: string[];
144
+ decision: "deny" | "allow";
145
+ argument: string;
146
+ }, {
147
+ type: "argument_pattern";
148
+ pattern: string;
149
+ toolNames: string[];
150
+ decision: "deny" | "allow";
151
+ argument: string;
84
152
  }>, z.ZodObject<{
85
153
  type: z.ZodLiteral<"allow_by_tier">;
86
154
  tiers: z.ZodArray<z.ZodString, "many">;
@@ -147,6 +215,24 @@ export declare const VerificationGateConfigSchema: z.ZodObject<{
147
215
  target: "name" | "args" | "both";
148
216
  pattern: string;
149
217
  decision: "deny" | "allow";
218
+ }>, z.ZodObject<{
219
+ type: z.ZodLiteral<"argument_pattern">;
220
+ toolNames: z.ZodArray<z.ZodString, "many">;
221
+ argument: z.ZodString;
222
+ pattern: z.ZodString;
223
+ decision: z.ZodEnum<["allow", "deny"]>;
224
+ }, "strip", z.ZodTypeAny, {
225
+ type: "argument_pattern";
226
+ pattern: string;
227
+ toolNames: string[];
228
+ decision: "deny" | "allow";
229
+ argument: string;
230
+ }, {
231
+ type: "argument_pattern";
232
+ pattern: string;
233
+ toolNames: string[];
234
+ decision: "deny" | "allow";
235
+ argument: string;
150
236
  }>, z.ZodObject<{
151
237
  type: z.ZodLiteral<"allow_by_tier">;
152
238
  tiers: z.ZodArray<z.ZodString, "many">;
@@ -180,6 +266,12 @@ export declare const VerificationGateConfigSchema: z.ZodObject<{
180
266
  target: "name" | "args" | "both";
181
267
  pattern: string;
182
268
  decision: "deny" | "allow";
269
+ } | {
270
+ type: "argument_pattern";
271
+ pattern: string;
272
+ toolNames: string[];
273
+ decision: "deny" | "allow";
274
+ argument: string;
183
275
  } | {
184
276
  type: "allow_by_tier";
185
277
  tiers: string[];
@@ -207,6 +299,12 @@ export declare const VerificationGateConfigSchema: z.ZodObject<{
207
299
  target: "name" | "args" | "both";
208
300
  pattern: string;
209
301
  decision: "deny" | "allow";
302
+ } | {
303
+ type: "argument_pattern";
304
+ pattern: string;
305
+ toolNames: string[];
306
+ decision: "deny" | "allow";
307
+ argument: string;
210
308
  } | {
211
309
  type: "allow_by_tier";
212
310
  tiers: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/verification/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;AAEtD,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAA;IAC/B,QAAQ,CAAC,WAAW,EAAE,gBAAgB,GAAG,IAAI,CAAA;IAC7C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,MAAM,gBAAgB,GACzB;IAAE,IAAI,EAAE,iBAAiB,CAAA;CAAE,GAC3B;IAAE,IAAI,EAAE,yBAAyB,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,GAC7C;IACA,IAAI,EAAE,gBAAgB,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;IAChC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAA;CACzB,GACD;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAA;AA2B7C,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAQjC,CAAA;AAEF,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAMvC,CAAA;AAEF,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/verification/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;AAEtD,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAA;IAC/B,QAAQ,CAAC,WAAW,EAAE,gBAAgB,GAAG,IAAI,CAAA;IAC7C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,MAAM,gBAAgB,GACzB;IAAE,IAAI,EAAE,iBAAiB,CAAA;CAAE,GAC3B;IAAE,IAAI,EAAE,yBAAyB,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,GAC7C;IACA;;;;;;;;;;;;;;;;;;;OAmBG;IACH,IAAI,EAAE,gBAAgB,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;IAChC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAA;CACzB,GACD;IACA;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,IAAI,EAAE,kBAAkB,CAAA;IACxB,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAA;CACzB,GACD;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAA;AAoC7C,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IASjC,CAAA;AAEF,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAMvC,CAAA;AAEF,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAA"}
@@ -20,6 +20,15 @@ const CustomPatternSchema = z.object({
20
20
  target: z.enum(['name', 'args', 'both']),
21
21
  decision: z.enum(['allow', 'deny']),
22
22
  });
23
+ const ArgumentPatternSchema = z.object({
24
+ type: z.literal('argument_pattern'),
25
+ toolNames: z.array(z.string()).min(1),
26
+ // A rule that names no argument would silently apply to none, which is the
27
+ // fail-open shape this rule type exists to remove.
28
+ argument: z.string().min(1),
29
+ pattern: z.string().max(MAX_CUSTOM_PATTERN_LENGTH),
30
+ decision: z.enum(['allow', 'deny']),
31
+ });
23
32
  const AllowByTierSchema = z.object({
24
33
  type: z.literal('allow_by_tier'),
25
34
  tiers: z.array(z.string()),
@@ -31,6 +40,7 @@ export const VerificationRuleSchema = z.discriminatedUnion('type', [
31
40
  AllowByNameSchema,
32
41
  DenyByNameSchema,
33
42
  CustomPatternSchema,
43
+ ArgumentPatternSchema,
34
44
  AllowByTierSchema,
35
45
  ]);
36
46
  export const VerificationGateConfigSchema = z.object({
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/types/verification/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,yBAAyB,EAAE,MAAM,uCAAuC,CAAA;AAwBjF,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAA;AAC5E,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,yBAAyB,CAAC,EAAE,CAAC,CAAA;AAC5F,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAAC;IACpC,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CAC/B,CAAC,CAAA;AACF,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;IAChC,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CAC9B,CAAC,CAAA;AACF,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC;IAC/B,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CAC9B,CAAC,CAAA;AACF,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC;IACjC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,yBAAyB,CAAC;IAClD,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;CACnC,CAAC,CAAA;AACF,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;IAChC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CAC1B,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE;IAClE,mBAAmB;IACnB,2BAA2B;IAC3B,qBAAqB;IACrB,iBAAiB;IACjB,gBAAgB;IAChB,mBAAmB;IACnB,iBAAiB;CACjB,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC,MAAM,CAAC;IACpD,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IACnC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAClD,kBAAkB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAC9C,qBAAqB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IACjD,YAAY,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;CACvC,CAAC,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/types/verification/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=abort-reason.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abort-reason.test.d.ts","sourceRoot":"","sources":["../../../src/utils/__tests__/abort-reason.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,48 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { abortReasonText } from '../abort.js';
3
+ /**
4
+ * A cancellation and a deadline arrive by the same mechanism and mean opposite
5
+ * things to whoever reads the result. The distinction only survives if the
6
+ * words a caller attached travel with the abort — and only if the words the
7
+ * platform invents on a caller's behalf do NOT, because a fabricated
8
+ * explanation is worse than an honest silence.
9
+ */
10
+ describe('a stop carries the words its caller gave it', () => {
11
+ it('reports a reason someone wrote', () => {
12
+ expect(abortReasonText(new Error('deployment window closed'))).toBe('deployment window closed');
13
+ });
14
+ it('reports a named deadline, which is the case this exists for', () => {
15
+ const reason = new Error('run budget of 30000ms exhausted');
16
+ expect(abortReasonText(reason)).toBe('run budget of 30000ms exhausted');
17
+ });
18
+ });
19
+ describe('a stop with nothing to say stays silent', () => {
20
+ it('says nothing for a bare abort()', () => {
21
+ // `abort()` with no argument fills `reason` with a DOMException named
22
+ // AbortError. Nobody wrote that word; it is the platform's way of
23
+ // saying the caller gave no reason, and rendering it would turn "we
24
+ // do not know" into what looks like an answer.
25
+ const controller = new AbortController();
26
+ controller.abort();
27
+ expect(controller.signal.reason).toBeInstanceOf(Error);
28
+ expect(controller.signal.reason.name).toBe('AbortError');
29
+ expect(abortReasonText(controller.signal.reason)).toBeUndefined();
30
+ });
31
+ it('says nothing for a platform timeout', () => {
32
+ const timeout = new Error('The operation was aborted due to timeout');
33
+ timeout.name = 'TimeoutError';
34
+ expect(abortReasonText(timeout)).toBeUndefined();
35
+ });
36
+ it('says nothing for a non-Error reason', () => {
37
+ // The agent manager aborts a child with the bare string 'canceled'.
38
+ // Rendering it produces "was cancelled: canceled" — noise wearing the
39
+ // shape of information.
40
+ expect(abortReasonText('canceled')).toBeUndefined();
41
+ expect(abortReasonText(undefined)).toBeUndefined();
42
+ expect(abortReasonText({ message: 'not an Error' })).toBeUndefined();
43
+ });
44
+ it('says nothing for an Error whose message is empty', () => {
45
+ expect(abortReasonText(new Error(' '))).toBeUndefined();
46
+ });
47
+ });
48
+ //# sourceMappingURL=abort-reason.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abort-reason.test.js","sourceRoot":"","sources":["../../../src/utils/__tests__/abort-reason.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE7C;;;;;;GAMG;AAEH,QAAQ,CAAC,6CAA6C,EAAE,GAAG,EAAE;IAC5D,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACzC,MAAM,CAAC,eAAe,CAAC,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAA;IAChG,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACtE,MAAM,MAAM,GAAG,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;QAC3D,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAA;IACxE,CAAC,CAAC,CAAA;AACH,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,yCAAyC,EAAE,GAAG,EAAE;IACxD,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QAC1C,sEAAsE;QACtE,kEAAkE;QAClE,oEAAoE;QACpE,+CAA+C;QAC/C,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;QACxC,UAAU,CAAC,KAAK,EAAE,CAAA;QAElB,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAA;QACtD,MAAM,CAAE,UAAU,CAAC,MAAM,CAAC,MAAgB,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;QACnE,MAAM,CAAC,eAAe,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,EAAE,CAAA;IAClE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC9C,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;QACrE,OAAO,CAAC,IAAI,GAAG,cAAc,CAAA;QAC7B,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,EAAE,CAAA;IACjD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC9C,oEAAoE;QACpE,sEAAsE;QACtE,wBAAwB;QACxB,MAAM,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,EAAE,CAAA;QACnD,MAAM,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAA;QAClD,MAAM,CAAC,eAAe,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,CAAA;IACrE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC3D,MAAM,CAAC,eAAe,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAA;IAC1D,CAAC,CAAC,CAAA;AACH,CAAC,CAAC,CAAA"}
@@ -1,2 +1,28 @@
1
+ /**
2
+ * The words a caller attached to a stop, or nothing when there were none.
3
+ *
4
+ * A cancellation and a deadline arrive by the same mechanism and mean opposite
5
+ * things to whoever reads the result. "Was cancelled" tells a model that
6
+ * something outside it decided, and nothing about what — so a tool result
7
+ * could not distinguish an operator pressing stop from a budget running out
8
+ * from a parent abandoning a child, and every one of those wants a different
9
+ * next move.
10
+ *
11
+ * Two kinds of reason are deliberately reported as no reason:
12
+ *
13
+ * - `AbortError` and `TimeoutError`. `abort()` with no argument fills `reason`
14
+ * with a DOMException named `AbortError`, so it is not a message anybody
15
+ * wrote; it is the platform's word for "someone stopped and said nothing".
16
+ * Rendering it would turn today's honest silence into a fake explanation.
17
+ * - Anything that is not an `Error`. The agent manager aborts a child with the
18
+ * bare string `'canceled'`, which would otherwise render as "was cancelled:
19
+ * canceled" — noise wearing the shape of information.
20
+ *
21
+ * Name-checked rather than checked by class, because a reason can cross a
22
+ * package boundary where `instanceof` stops holding across duplicate copies.
23
+ * That is the same answer this codebase already settled on for provider
24
+ * errors.
25
+ */
26
+ export declare function abortReasonText(reason: unknown): string | undefined;
1
27
  export declare function createChildAbortController(parent: AbortController): AbortController;
2
28
  //# sourceMappingURL=abort.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"abort.d.ts","sourceRoot":"","sources":["../../src/utils/abort.ts"],"names":[],"mappings":"AAAA,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,eAAe,GAAG,eAAe,CA4BnF"}
1
+ {"version":3,"file":"abort.d.ts","sourceRoot":"","sources":["../../src/utils/abort.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAKnE;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,eAAe,GAAG,eAAe,CA4BnF"}
@@ -1,3 +1,37 @@
1
+ import { toErrorMessage } from './error.js';
2
+ /**
3
+ * The words a caller attached to a stop, or nothing when there were none.
4
+ *
5
+ * A cancellation and a deadline arrive by the same mechanism and mean opposite
6
+ * things to whoever reads the result. "Was cancelled" tells a model that
7
+ * something outside it decided, and nothing about what — so a tool result
8
+ * could not distinguish an operator pressing stop from a budget running out
9
+ * from a parent abandoning a child, and every one of those wants a different
10
+ * next move.
11
+ *
12
+ * Two kinds of reason are deliberately reported as no reason:
13
+ *
14
+ * - `AbortError` and `TimeoutError`. `abort()` with no argument fills `reason`
15
+ * with a DOMException named `AbortError`, so it is not a message anybody
16
+ * wrote; it is the platform's word for "someone stopped and said nothing".
17
+ * Rendering it would turn today's honest silence into a fake explanation.
18
+ * - Anything that is not an `Error`. The agent manager aborts a child with the
19
+ * bare string `'canceled'`, which would otherwise render as "was cancelled:
20
+ * canceled" — noise wearing the shape of information.
21
+ *
22
+ * Name-checked rather than checked by class, because a reason can cross a
23
+ * package boundary where `instanceof` stops holding across duplicate copies.
24
+ * That is the same answer this codebase already settled on for provider
25
+ * errors.
26
+ */
27
+ export function abortReasonText(reason) {
28
+ if (!(reason instanceof Error))
29
+ return undefined;
30
+ if (reason.name === 'AbortError' || reason.name === 'TimeoutError')
31
+ return undefined;
32
+ const text = toErrorMessage(reason).trim();
33
+ return text.length > 0 ? text : undefined;
34
+ }
1
35
  export function createChildAbortController(parent) {
2
36
  const child = new AbortController();
3
37
  if (parent.signal.aborted) {
@@ -1 +1 @@
1
- {"version":3,"file":"abort.js","sourceRoot":"","sources":["../../src/utils/abort.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,0BAA0B,CAAC,MAAuB;IACjE,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAA;IAEnC,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QAC3B,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QACjC,OAAO,KAAK,CAAA;IACb,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,CAAA;IAEpC,MAAM,aAAa,GAAG,GAAS,EAAE;QAChC,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,EAAE,CAAA;QAC7B,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YAChC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAChC,CAAC;IACF,CAAC,CAAA;IAED,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;IAEtE,KAAK,CAAC,MAAM,CAAC,gBAAgB,CAC5B,OAAO,EACP,GAAG,EAAE;QACJ,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAA;IAC1D,CAAC,EACD,EAAE,IAAI,EAAE,IAAI,EAAE,CACd,CAAA;IAED,OAAO,KAAK,CAAA;AACb,CAAC"}
1
+ {"version":3,"file":"abort.js","sourceRoot":"","sources":["../../src/utils/abort.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,eAAe,CAAC,MAAe;IAC9C,IAAI,CAAC,CAAC,MAAM,YAAY,KAAK,CAAC;QAAE,OAAO,SAAS,CAAA;IAChD,IAAI,MAAM,CAAC,IAAI,KAAK,YAAY,IAAI,MAAM,CAAC,IAAI,KAAK,cAAc;QAAE,OAAO,SAAS,CAAA;IACpF,MAAM,IAAI,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAA;IAC1C,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAA;AAC1C,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,MAAuB;IACjE,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAA;IAEnC,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QAC3B,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QACjC,OAAO,KAAK,CAAA;IACb,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,CAAA;IAEpC,MAAM,aAAa,GAAG,GAAS,EAAE;QAChC,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,EAAE,CAAA;QAC7B,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YAChC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAChC,CAAC;IACF,CAAC,CAAA;IAED,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;IAEtE,KAAK,CAAC,MAAM,CAAC,gBAAgB,CAC5B,OAAO,EACP,GAAG,EAAE;QACJ,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAA;IAC1D,CAAC,EACD,EAAE,IAAI,EAAE,IAAI,EAAE,CACd,CAAA;IAED,OAAO,KAAK,CAAA;AACb,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=argument-pattern.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"argument-pattern.test.d.ts","sourceRoot":"","sources":["../../../src/verification/__tests__/argument-pattern.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,122 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { getRootLogger } from '../../utils/logger.js';
3
+ import { VerificationGate } from '../gate.js';
4
+ /**
5
+ * Every pattern rule an operator could write was one of two wrong things.
6
+ *
7
+ * `custom_pattern` carries no tool scope, so a rule written about `bash`
8
+ * decided `edit` calls as well. And `target: 'args'` tests
9
+ * `JSON.stringify(toolInput)`, so the subject is the JSON TEXT of the whole
10
+ * argument object — meaning the natural, anchored thing to write
11
+ * (`^git push.*$`) can never match, and the rule silently decides nothing.
12
+ * Pinning the tool cost the anchor; anchoring cost the tool scope.
13
+ */
14
+ function gate(rules) {
15
+ return new VerificationGate({
16
+ enabled: true,
17
+ rules,
18
+ allowReadOnlyTools: false,
19
+ denyDangerousPatterns: false,
20
+ logDecisions: false,
21
+ }, getRootLogger());
22
+ }
23
+ /** Only the fields the gate reads. */
24
+ function toolDef(name) {
25
+ return { name, isReadOnly: () => false };
26
+ }
27
+ const PUSH_RULE = {
28
+ type: 'argument_pattern',
29
+ toolNames: ['bash'],
30
+ argument: 'command',
31
+ pattern: '^git push',
32
+ decision: 'deny',
33
+ };
34
+ function evaluate(rules, toolName, toolInput) {
35
+ return gate(rules).evaluate({ toolName, toolInput, toolDef: toolDef(toolName) });
36
+ }
37
+ describe('a rule can name one tool and one argument at once', () => {
38
+ it('denies the call it was written about', () => {
39
+ const result = evaluate([PUSH_RULE], 'bash', { command: 'git push origin main' });
40
+ expect(result.decision).toBe('deny');
41
+ });
42
+ it('anchors against the value, which is what the old rule could not do', () => {
43
+ // The whole defect in one assertion: this same pattern under
44
+ // `custom_pattern` with `target: 'args'` is tested against
45
+ // `{"command":"git push origin main"}`, where `^git push` cannot match
46
+ // because the string does not start there.
47
+ const viaOldRule = evaluate([{ type: 'custom_pattern', pattern: '^git push', target: 'args', decision: 'deny' }], 'bash', { command: 'git push origin main' });
48
+ expect(viaOldRule.decision, 'the old rule silently decided nothing').not.toBe('deny');
49
+ expect(evaluate([PUSH_RULE], 'bash', { command: 'git push origin main' }).decision).toBe('deny');
50
+ });
51
+ it('leaves a different tool alone', () => {
52
+ // The other half: a rule about `bash` used to decide `edit` too,
53
+ // because the pattern was matched against a serialisation that has no
54
+ // idea which tool produced it.
55
+ const result = evaluate([PUSH_RULE], 'edit', { command: 'git push origin main' });
56
+ expect(result.decision).not.toBe('deny');
57
+ });
58
+ it('leaves a different argument alone', () => {
59
+ const result = evaluate([PUSH_RULE], 'bash', { description: 'git push origin main' });
60
+ expect(result.decision).not.toBe('deny');
61
+ });
62
+ it('says which argument decided, so a model knows whether to reword', () => {
63
+ const result = evaluate([PUSH_RULE], 'bash', { command: 'git push origin main' });
64
+ expect(result.reason).toContain('command');
65
+ expect(result.reason).toContain('^git push');
66
+ expect(result.reason).toContain('bash');
67
+ });
68
+ });
69
+ describe('what it deliberately does not decide', () => {
70
+ it('decides nothing when the argument is absent', () => {
71
+ const result = evaluate([PUSH_RULE], 'bash', {});
72
+ expect(result.decision).not.toBe('deny');
73
+ });
74
+ it('decides nothing about a structured argument', () => {
75
+ // No string a pattern could match says anything true about an object,
76
+ // and serialising it to try would put this rule back where
77
+ // `custom_pattern` already is. An operator who needs to refuse a tool
78
+ // over the SHAPE of its input wants deny_by_name.
79
+ const rule = { ...PUSH_RULE, argument: 'env', pattern: 'PROD' };
80
+ const result = evaluate([rule], 'bash', { env: { NODE_ENV: 'PROD' } });
81
+ expect(result.decision).not.toBe('deny');
82
+ });
83
+ it('reads a number or a boolean rather than skipping it', () => {
84
+ // These render unambiguously, so skipping them would be a fail-open
85
+ // with no upside: a rule about a numeric argument is a rule someone
86
+ // can reasonably write.
87
+ const rule = {
88
+ type: 'argument_pattern',
89
+ toolNames: ['sleep'],
90
+ argument: 'seconds',
91
+ pattern: '^[0-9]{4,}$',
92
+ decision: 'deny',
93
+ };
94
+ expect(evaluate([rule], 'sleep', { seconds: 86400 }).decision).toBe('deny');
95
+ expect(evaluate([rule], 'sleep', { seconds: 5 }).decision).not.toBe('deny');
96
+ });
97
+ });
98
+ describe('a rule that cannot be compiled decides nothing at all', () => {
99
+ it('does not widen into a rule about the whole tool', () => {
100
+ // The failure this forbids: a typo'd regex turning "deny bash when its
101
+ // command matches X" into "deny bash" — a far larger authorization than
102
+ // anybody wrote, granted by a mistake nobody would notice.
103
+ //
104
+ // What actually secures it is the missing-pattern check at the top of
105
+ // `evaluateRule`, which returns before the tool name is consulted. The
106
+ // gate's construction order (compile first, only then record the names)
107
+ // is defence in depth and NOT the mechanism: reversing those two lines
108
+ // fails nothing, which was measured rather than assumed. So this test
109
+ // pins the OUTCOME and the comment in the gate says which line to keep.
110
+ const broken = { ...PUSH_RULE, pattern: '([unclosed' };
111
+ const result = evaluate([broken], 'bash', { command: 'ls' });
112
+ expect(result.decision).not.toBe('deny');
113
+ });
114
+ it('is secured by the pattern check, not by the construction order', () => {
115
+ // The honest version of the mutation: remove the check that actually
116
+ // holds and this fails. A rule whose pattern never compiled has no
117
+ // pattern to test, so it must decide nothing even for a tool it names.
118
+ const broken = { ...PUSH_RULE, pattern: '([unclosed' };
119
+ expect(evaluate([broken], 'bash', { command: 'git push origin main' }).decision).not.toBe('deny');
120
+ });
121
+ });
122
+ //# sourceMappingURL=argument-pattern.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"argument-pattern.test.js","sourceRoot":"","sources":["../../../src/verification/__tests__/argument-pattern.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rule-order-and-reason.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-order-and-reason.test.d.ts","sourceRoot":"","sources":["../../../src/verification/__tests__/rule-order-and-reason.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,126 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { getRootLogger } from '../../utils/logger.js';
3
+ import { VerificationGate, describeRule } from '../gate.js';
4
+ /**
5
+ * Two things an operator has to be able to trust about a policy gate: that a
6
+ * rule they wrote is consulted, and that a refusal says enough to act on.
7
+ *
8
+ * Neither held. `allowReadOnlyTools` was expanded into a rule AHEAD of the
9
+ * operator's own, and the gate stops at the first match — so "prompt me before
10
+ * every read" was not rejected, not warned about, just never reached. And a
11
+ * denial arrived as the rule's TYPE NAME, which tells a model that a rule
12
+ * matched and nothing whatever about what it said.
13
+ */
14
+ function gate(config) {
15
+ return new VerificationGate({
16
+ enabled: true,
17
+ rules: [],
18
+ allowReadOnlyTools: false,
19
+ denyDangerousPatterns: false,
20
+ logDecisions: false,
21
+ ...config,
22
+ }, getRootLogger());
23
+ }
24
+ /** Only the fields the gate reads. */
25
+ function toolDef(name, readOnly) {
26
+ return { name, isReadOnly: () => readOnly };
27
+ }
28
+ describe('an operator rule outranks the read-only convenience', () => {
29
+ it('lets a rule make a read-only tool prompt', () => {
30
+ // The case that was silently unreachable: `read` observes only, the
31
+ // convenience is on, and the operator has asked to be consulted anyway.
32
+ const result = gate({
33
+ allowReadOnlyTools: true,
34
+ rules: [{ type: 'deny_by_name', toolNames: ['read'] }],
35
+ }).evaluate({
36
+ toolName: 'read',
37
+ toolInput: { path: 'notes.txt' },
38
+ toolDef: toolDef('read', true),
39
+ });
40
+ expect(result.decision, 'the operator rule was never consulted').toBe('deny');
41
+ });
42
+ it('still allows a read-only tool nobody wrote a rule about', () => {
43
+ // The convenience keeps doing its job as a DEFAULT, which is what it
44
+ // always was in substance.
45
+ const result = gate({
46
+ allowReadOnlyTools: true,
47
+ rules: [{ type: 'deny_by_name', toolNames: ['write'] }],
48
+ }).evaluate({
49
+ toolName: 'grep',
50
+ toolInput: { pattern: 'x' },
51
+ toolDef: toolDef('grep', true),
52
+ });
53
+ expect(result.decision).toBe('allow');
54
+ });
55
+ });
56
+ describe('the safety floor still outranks everything', () => {
57
+ it('refuses a dangerous pattern even when a rule would allow the tool', () => {
58
+ // The denial goes first and stays there. An operator rule must not be
59
+ // able to open what it closes.
60
+ const result = gate({
61
+ denyDangerousPatterns: true,
62
+ rules: [{ type: 'allow_by_name', toolNames: ['bash'] }],
63
+ }).evaluate({
64
+ toolName: 'bash',
65
+ toolInput: { command: 'rm -rf /' },
66
+ toolDef: toolDef('bash', false),
67
+ });
68
+ expect(result.decision).toBe('deny');
69
+ });
70
+ });
71
+ describe('a refusal says what the rule said', () => {
72
+ it('names the pattern rather than the rule type', () => {
73
+ // "Matched rule: custom_pattern" tells a model a rule matched and
74
+ // nothing about it, so it rewords the same call and tries again. The
75
+ // pattern is what makes the retry visibly pointless.
76
+ const reason = describeRule({
77
+ type: 'custom_pattern',
78
+ pattern: 'git push*',
79
+ target: 'args',
80
+ decision: 'deny',
81
+ });
82
+ expect(reason).toContain('git push*');
83
+ expect(reason).toContain('denied');
84
+ expect(reason).not.toContain('custom_pattern');
85
+ });
86
+ it('says a by-name denial is about the tool, not the input', () => {
87
+ // The distinction that decides the next move: reworded arguments will
88
+ // not help, so the model should stop rather than iterate.
89
+ const reason = describeRule({ type: 'deny_by_name', toolNames: ['bash', 'write'] });
90
+ expect(reason).toContain('bash');
91
+ expect(reason).toContain('a different input will not change it');
92
+ });
93
+ it('says a dangerous-pattern refusal is not worth rewording', () => {
94
+ expect(describeRule({ type: 'deny_dangerous_patterns' })).toContain('will not help');
95
+ });
96
+ it('reaches the model through the gate result, not just the helper', () => {
97
+ const result = gate({ rules: [{ type: 'deny_by_name', toolNames: ['bash'] }] }).evaluate({
98
+ toolName: 'bash',
99
+ toolInput: { command: 'ls' },
100
+ toolDef: toolDef('bash', false),
101
+ });
102
+ expect(result.reason).toContain('bash');
103
+ expect(result.reason).not.toMatch(/^Matched rule:/);
104
+ });
105
+ it('is reachable by a caller driving the rules without the gate', async () => {
106
+ // `evaluateRule` is public and answers only WHETHER a rule matched. A
107
+ // caller holding that verdict and no describer is back to switching on
108
+ // the rule's type for its wording — the same defect the gate carried,
109
+ // one layer up. So the two travel together, and this asserts the
110
+ // PAIRING rather than either one alone.
111
+ //
112
+ // Asserted at this barrel rather than the package barrel on purpose.
113
+ // Importing `../../index.js` costs ~4s of module-graph load — measured,
114
+ // on an idle machine, against a default 5s test budget — so the version
115
+ // of this test that looked stronger was one busy CI runner away from
116
+ // failing for a reason that has nothing to do with verification. The
117
+ // package half is gated better elsewhere anyway: the public-surface
118
+ // baseline resolves `dist/index.d.ts` through the checker and reports a
119
+ // REGRESSION naming any symbol that leaves the surface.
120
+ const barrel = await import('../index.js');
121
+ expect(typeof barrel.evaluateRule).toBe('function');
122
+ expect(typeof barrel.describeRule).toBe('function');
123
+ expect(barrel.describeRule({ type: 'deny_by_name', toolNames: ['bash'] })).toContain('bash');
124
+ });
125
+ });
126
+ //# sourceMappingURL=rule-order-and-reason.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-order-and-reason.test.js","sourceRoot":"","sources":["../../../src/verification/__tests__/rule-order-and-reason.test.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,27 @@
1
1
  import type { ToolDefinition } from '../types/tool/index.js';
2
- import type { GateEvaluationResult, VerificationGateConfig } from '../types/verification/index.js';
2
+ import type { GateEvaluationResult, VerificationGateConfig, VerificationRule } from '../types/verification/index.js';
3
3
  import type { Logger } from '../utils/logger.js';
4
4
  export interface ToolCallContext {
5
5
  readonly toolName: string;
6
6
  readonly toolInput: unknown;
7
7
  readonly toolDef: ToolDefinition | undefined;
8
8
  }
9
+ /**
10
+ * What the rule actually said, in words a model can act on.
11
+ *
12
+ * This used to be the rule TYPE and nothing else, so a denial reached the
13
+ * model as "Blocked by the verification gate: Matched rule: deny_by_name" —
14
+ * naming the kind of rule and nothing about it. Not which tool, not which
15
+ * pattern, not whether a different input would fare better.
16
+ *
17
+ * The difference is behavioural rather than cosmetic. Told only that it was
18
+ * denied, a model rewords the same call and tries again, because nothing in
19
+ * the message says a retry is pointless. Told that a pattern rule denies
20
+ * `git push*`, it can stop, say so, and do something else. A refusal that
21
+ * cannot be reasoned about produces thrashing; one that can produces a route
22
+ * around it.
23
+ */
24
+ export declare function describeRule(rule: VerificationRule): string;
9
25
  export declare class VerificationGate {
10
26
  private readonly rules;
11
27
  private readonly compiledPatterns;