@namzu/sdk 44.3.0 → 45.1.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 (271) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/dist/authorization/command-line.d.ts +66 -19
  3. package/dist/authorization/command-line.d.ts.map +1 -1
  4. package/dist/authorization/command-line.js +130 -270
  5. package/dist/authorization/command-line.js.map +1 -1
  6. package/dist/authorization/gate.d.ts +7 -0
  7. package/dist/authorization/gate.d.ts.map +1 -1
  8. package/dist/authorization/gate.js +13 -3
  9. package/dist/authorization/gate.js.map +1 -1
  10. package/dist/authorization/rules.d.ts +10 -1
  11. package/dist/authorization/rules.d.ts.map +1 -1
  12. package/dist/authorization/rules.js +55 -8
  13. package/dist/authorization/rules.js.map +1 -1
  14. package/dist/authorization/shell-lexer.d.ts +152 -0
  15. package/dist/authorization/shell-lexer.d.ts.map +1 -0
  16. package/dist/authorization/shell-lexer.js +2156 -0
  17. package/dist/authorization/shell-lexer.js.map +1 -0
  18. package/dist/authorization/skill-grant.d.ts +182 -0
  19. package/dist/authorization/skill-grant.d.ts.map +1 -0
  20. package/dist/authorization/skill-grant.js +314 -0
  21. package/dist/authorization/skill-grant.js.map +1 -0
  22. package/dist/bridge/a2a/mapper.d.ts.map +1 -1
  23. package/dist/bridge/a2a/mapper.js +2 -0
  24. package/dist/bridge/a2a/mapper.js.map +1 -1
  25. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  26. package/dist/bridge/sse/mapper.js +1 -0
  27. package/dist/bridge/sse/mapper.js.map +1 -1
  28. package/dist/directory/types.d.ts +2 -0
  29. package/dist/directory/types.d.ts.map +1 -1
  30. package/dist/directory/types.js.map +1 -1
  31. package/dist/manager/resident/outbox.d.ts +4 -4
  32. package/dist/persona/assembler.d.ts.map +1 -1
  33. package/dist/persona/assembler.js +5 -2
  34. package/dist/persona/assembler.js.map +1 -1
  35. package/dist/prompt/coding-agent-doctrine.d.ts +1 -1
  36. package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
  37. package/dist/prompt/coding-agent-doctrine.js +1 -0
  38. package/dist/prompt/coding-agent-doctrine.js.map +1 -1
  39. package/dist/public-runtime.d.ts +5 -1
  40. package/dist/public-runtime.d.ts.map +1 -1
  41. package/dist/public-runtime.js +15 -1
  42. package/dist/public-runtime.js.map +1 -1
  43. package/dist/public-tools.d.ts +4 -0
  44. package/dist/public-tools.d.ts.map +1 -1
  45. package/dist/public-tools.js +12 -1
  46. package/dist/public-tools.js.map +1 -1
  47. package/dist/public-types.d.ts +9 -1
  48. package/dist/public-types.d.ts.map +1 -1
  49. package/dist/runtime/jobs/registry.d.ts +2 -2
  50. package/dist/runtime/jobs/registry.d.ts.map +1 -1
  51. package/dist/runtime/jobs/registry.js +6 -2
  52. package/dist/runtime/jobs/registry.js.map +1 -1
  53. package/dist/runtime/query/declined.d.ts +12 -0
  54. package/dist/runtime/query/declined.d.ts.map +1 -0
  55. package/dist/runtime/query/declined.js +12 -0
  56. package/dist/runtime/query/declined.js.map +1 -0
  57. package/dist/runtime/query/executor.d.ts +36 -40
  58. package/dist/runtime/query/executor.d.ts.map +1 -1
  59. package/dist/runtime/query/executor.js +95 -53
  60. package/dist/runtime/query/executor.js.map +1 -1
  61. package/dist/runtime/query/index.d.ts.map +1 -1
  62. package/dist/runtime/query/index.js +8 -0
  63. package/dist/runtime/query/index.js.map +1 -1
  64. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  65. package/dist/runtime/query/iteration/index.js +13 -0
  66. package/dist/runtime/query/iteration/index.js.map +1 -1
  67. package/dist/runtime/query/iteration/phases/context.d.ts +13 -0
  68. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  69. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  70. package/dist/runtime/query/iteration/phases/handoff.d.ts +22 -0
  71. package/dist/runtime/query/iteration/phases/handoff.d.ts.map +1 -0
  72. package/dist/runtime/query/iteration/phases/handoff.js +65 -0
  73. package/dist/runtime/query/iteration/phases/handoff.js.map +1 -0
  74. package/dist/runtime/query/iteration/phases/index.d.ts +1 -0
  75. package/dist/runtime/query/iteration/phases/index.d.ts.map +1 -1
  76. package/dist/runtime/query/iteration/phases/index.js +1 -0
  77. package/dist/runtime/query/iteration/phases/index.js.map +1 -1
  78. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  79. package/dist/runtime/query/iteration/phases/tool-review.js +56 -3
  80. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  81. package/dist/runtime/query/resume-pending.d.ts.map +1 -1
  82. package/dist/runtime/query/resume-pending.js +3 -2
  83. package/dist/runtime/query/resume-pending.js.map +1 -1
  84. package/dist/runtime/query/review-policy.d.ts +11 -0
  85. package/dist/runtime/query/review-policy.d.ts.map +1 -1
  86. package/dist/runtime/query/review-policy.js +36 -3
  87. package/dist/runtime/query/review-policy.js.map +1 -1
  88. package/dist/runtime/query/tooling.d.ts +3 -0
  89. package/dist/runtime/query/tooling.d.ts.map +1 -1
  90. package/dist/runtime/query/tooling.js +1 -0
  91. package/dist/runtime/query/tooling.js.map +1 -1
  92. package/dist/schedules/cron.d.ts +21 -0
  93. package/dist/schedules/cron.d.ts.map +1 -0
  94. package/dist/schedules/cron.js +167 -0
  95. package/dist/schedules/cron.js.map +1 -0
  96. package/dist/schedules/describe.d.ts +14 -0
  97. package/dist/schedules/describe.d.ts.map +1 -0
  98. package/dist/schedules/describe.js +133 -0
  99. package/dist/schedules/describe.js.map +1 -0
  100. package/dist/schedules/errors.d.ts +11 -0
  101. package/dist/schedules/errors.d.ts.map +1 -0
  102. package/dist/schedules/errors.js +15 -0
  103. package/dist/schedules/errors.js.map +1 -0
  104. package/dist/schedules/evaluate.d.ts +35 -0
  105. package/dist/schedules/evaluate.d.ts.map +1 -0
  106. package/dist/schedules/evaluate.js +158 -0
  107. package/dist/schedules/evaluate.js.map +1 -0
  108. package/dist/schedules/index.d.ts +11 -0
  109. package/dist/schedules/index.d.ts.map +1 -0
  110. package/dist/schedules/index.js +8 -0
  111. package/dist/schedules/index.js.map +1 -0
  112. package/dist/schedules/next-fire.d.ts +50 -0
  113. package/dist/schedules/next-fire.d.ts.map +1 -0
  114. package/dist/schedules/next-fire.js +250 -0
  115. package/dist/schedules/next-fire.js.map +1 -0
  116. package/dist/schedules/spec.d.ts +30 -0
  117. package/dist/schedules/spec.d.ts.map +1 -0
  118. package/dist/schedules/spec.js +169 -0
  119. package/dist/schedules/spec.js.map +1 -0
  120. package/dist/schedules/types.d.ts +144 -0
  121. package/dist/schedules/types.d.ts.map +1 -0
  122. package/dist/schedules/types.js +11 -0
  123. package/dist/schedules/types.js.map +1 -0
  124. package/dist/schedules/tz.d.ts +44 -0
  125. package/dist/schedules/tz.d.ts.map +1 -0
  126. package/dist/schedules/tz.js +141 -0
  127. package/dist/schedules/tz.js.map +1 -0
  128. package/dist/skills/index.d.ts +1 -1
  129. package/dist/skills/index.d.ts.map +1 -1
  130. package/dist/skills/index.js +1 -1
  131. package/dist/skills/index.js.map +1 -1
  132. package/dist/skills/loader.d.ts +21 -0
  133. package/dist/skills/loader.d.ts.map +1 -1
  134. package/dist/skills/loader.js +51 -1
  135. package/dist/skills/loader.js.map +1 -1
  136. package/dist/tools/builtins/bash.d.ts.map +1 -1
  137. package/dist/tools/builtins/bash.js +18 -6
  138. package/dist/tools/builtins/bash.js.map +1 -1
  139. package/dist/tools/builtins/browser-url.d.ts +83 -0
  140. package/dist/tools/builtins/browser-url.d.ts.map +1 -0
  141. package/dist/tools/builtins/browser-url.js +240 -0
  142. package/dist/tools/builtins/browser-url.js.map +1 -0
  143. package/dist/tools/builtins/browser.d.ts +367 -0
  144. package/dist/tools/builtins/browser.d.ts.map +1 -0
  145. package/dist/tools/builtins/browser.js +704 -0
  146. package/dist/tools/builtins/browser.js.map +1 -0
  147. package/dist/tools/builtins/skill.d.ts +2 -9
  148. package/dist/tools/builtins/skill.d.ts.map +1 -1
  149. package/dist/tools/builtins/skill.js +74 -51
  150. package/dist/tools/builtins/skill.js.map +1 -1
  151. package/dist/tools/command-shell.d.ts +90 -0
  152. package/dist/tools/command-shell.d.ts.map +1 -0
  153. package/dist/tools/command-shell.js +129 -0
  154. package/dist/tools/command-shell.js.map +1 -0
  155. package/dist/tools/defineTool.d.ts +13 -0
  156. package/dist/tools/defineTool.d.ts.map +1 -1
  157. package/dist/tools/defineTool.js +30 -1
  158. package/dist/tools/defineTool.js.map +1 -1
  159. package/dist/tools/schedules/index.d.ts +5 -0
  160. package/dist/tools/schedules/index.d.ts.map +1 -0
  161. package/dist/tools/schedules/index.js +4 -0
  162. package/dist/tools/schedules/index.js.map +1 -0
  163. package/dist/tools/schedules/loop-tool.d.ts +14 -0
  164. package/dist/tools/schedules/loop-tool.d.ts.map +1 -0
  165. package/dist/tools/schedules/loop-tool.js +81 -0
  166. package/dist/tools/schedules/loop-tool.js.map +1 -0
  167. package/dist/tools/schedules/present.d.ts +16 -0
  168. package/dist/tools/schedules/present.d.ts.map +1 -0
  169. package/dist/tools/schedules/present.js +69 -0
  170. package/dist/tools/schedules/present.js.map +1 -0
  171. package/dist/tools/schedules/prompt-scan.d.ts +17 -0
  172. package/dist/tools/schedules/prompt-scan.d.ts.map +1 -0
  173. package/dist/tools/schedules/prompt-scan.js +92 -0
  174. package/dist/tools/schedules/prompt-scan.js.map +1 -0
  175. package/dist/tools/schedules/schedule-tool.d.ts +16 -0
  176. package/dist/tools/schedules/schedule-tool.d.ts.map +1 -0
  177. package/dist/tools/schedules/schedule-tool.js +327 -0
  178. package/dist/tools/schedules/schedule-tool.js.map +1 -0
  179. package/dist/tools/schedules/types.d.ts +184 -0
  180. package/dist/tools/schedules/types.d.ts.map +1 -0
  181. package/dist/tools/schedules/types.js +11 -0
  182. package/dist/tools/schedules/types.js.map +1 -0
  183. package/dist/types/authorization/index.d.ts +86 -9
  184. package/dist/types/authorization/index.d.ts.map +1 -1
  185. package/dist/types/authorization/index.js +11 -1
  186. package/dist/types/authorization/index.js.map +1 -1
  187. package/dist/types/browser/index.d.ts +280 -0
  188. package/dist/types/browser/index.d.ts.map +1 -0
  189. package/dist/types/browser/index.js +12 -0
  190. package/dist/types/browser/index.js.map +1 -0
  191. package/dist/types/hitl/index.d.ts +23 -0
  192. package/dist/types/hitl/index.d.ts.map +1 -1
  193. package/dist/types/hitl/index.js.map +1 -1
  194. package/dist/types/session/events.d.ts +6 -0
  195. package/dist/types/session/events.d.ts.map +1 -1
  196. package/dist/types/session/events.js.map +1 -1
  197. package/dist/types/session/records.d.ts +23 -0
  198. package/dist/types/session/records.d.ts.map +1 -1
  199. package/dist/types/session/records.js +9 -0
  200. package/dist/types/session/records.js.map +1 -1
  201. package/dist/types/tool/index.d.ts +108 -5
  202. package/dist/types/tool/index.d.ts.map +1 -1
  203. package/dist/types/tool/index.js.map +1 -1
  204. package/dist/types/tool/presentation.d.ts +7 -0
  205. package/dist/types/tool/presentation.d.ts.map +1 -1
  206. package/dist/utils/frontmatter.d.ts +35 -3
  207. package/dist/utils/frontmatter.d.ts.map +1 -1
  208. package/dist/utils/frontmatter.js +45 -5
  209. package/dist/utils/frontmatter.js.map +1 -1
  210. package/dist/utils/id.d.ts +8 -0
  211. package/dist/utils/id.d.ts.map +1 -1
  212. package/dist/utils/id.js +12 -0
  213. package/dist/utils/id.js.map +1 -1
  214. package/package.json +1 -1
  215. package/src/authorization/command-line.ts +148 -293
  216. package/src/authorization/gate.ts +22 -2
  217. package/src/authorization/rules.ts +67 -8
  218. package/src/authorization/shell-lexer.ts +2349 -0
  219. package/src/authorization/skill-grant.ts +400 -0
  220. package/src/bridge/a2a/mapper.ts +2 -0
  221. package/src/bridge/sse/mapper.ts +1 -0
  222. package/src/directory/types.ts +2 -0
  223. package/src/persona/assembler.ts +5 -2
  224. package/src/prompt/coding-agent-doctrine.ts +1 -0
  225. package/src/public-runtime.ts +37 -0
  226. package/src/public-tools.ts +37 -1
  227. package/src/public-types.ts +57 -0
  228. package/src/runtime/jobs/registry.ts +19 -11
  229. package/src/runtime/query/declined.ts +12 -0
  230. package/src/runtime/query/executor.ts +116 -56
  231. package/src/runtime/query/index.ts +8 -0
  232. package/src/runtime/query/iteration/index.ts +13 -0
  233. package/src/runtime/query/iteration/phases/context.ts +13 -0
  234. package/src/runtime/query/iteration/phases/handoff.ts +74 -0
  235. package/src/runtime/query/iteration/phases/index.ts +1 -0
  236. package/src/runtime/query/iteration/phases/tool-review.ts +55 -3
  237. package/src/runtime/query/resume-pending.ts +3 -2
  238. package/src/runtime/query/review-policy.ts +57 -3
  239. package/src/runtime/query/tooling.ts +4 -0
  240. package/src/schedules/cron.ts +202 -0
  241. package/src/schedules/describe.ts +138 -0
  242. package/src/schedules/errors.ts +15 -0
  243. package/src/schedules/evaluate.ts +178 -0
  244. package/src/schedules/index.ts +18 -0
  245. package/src/schedules/next-fire.ts +257 -0
  246. package/src/schedules/spec.ts +210 -0
  247. package/src/schedules/types.ts +163 -0
  248. package/src/schedules/tz.ts +155 -0
  249. package/src/skills/index.ts +1 -1
  250. package/src/skills/loader.ts +59 -1
  251. package/src/tools/builtins/bash.ts +24 -6
  252. package/src/tools/builtins/browser-url.ts +251 -0
  253. package/src/tools/builtins/browser.ts +817 -0
  254. package/src/tools/builtins/skill.ts +92 -53
  255. package/src/tools/command-shell.ts +166 -0
  256. package/src/tools/defineTool.ts +36 -1
  257. package/src/tools/schedules/index.ts +4 -0
  258. package/src/tools/schedules/loop-tool.ts +85 -0
  259. package/src/tools/schedules/present.ts +86 -0
  260. package/src/tools/schedules/prompt-scan.ts +96 -0
  261. package/src/tools/schedules/schedule-tool.ts +376 -0
  262. package/src/tools/schedules/types.ts +197 -0
  263. package/src/types/authorization/index.ts +60 -2
  264. package/src/types/browser/index.ts +341 -0
  265. package/src/types/hitl/index.ts +21 -0
  266. package/src/types/session/events.ts +6 -0
  267. package/src/types/session/records.ts +10 -0
  268. package/src/types/tool/index.ts +109 -5
  269. package/src/types/tool/presentation.ts +7 -0
  270. package/src/utils/frontmatter.ts +81 -5
  271. package/src/utils/id.ts +14 -0
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { MAX_CUSTOM_PATTERN_LENGTH } from '../../constants/authorization/index.js'
3
+ import type { ShellDialect, ToolDefinition } from '../tool/index.js'
3
4
 
4
5
  export type GateDecision = 'allow' | 'deny' | 'review'
5
6
 
@@ -81,9 +82,56 @@ export type AuthorizationRule =
81
82
  /** The argument key, at the top level of the tool's input. */
82
83
  argument: string
83
84
  pattern: string
84
- decision: 'allow' | 'deny'
85
+ /**
86
+ * `review` sends a matching call to the review policy — a person,
87
+ * or the turn's mode — instead of deciding it here. It matches as
88
+ * `deny` does (any segment of a command line), because it is a
89
+ * restriction: a rule that asks before `git push` must also ask
90
+ * before `true; git push`.
91
+ */
92
+ decision: 'allow' | 'deny' | 'review'
85
93
  }
86
94
  | { type: 'allow_by_tier'; tiers: string[] }
95
+ | {
96
+ /**
97
+ * A decision the host computes in code, in its place in the list.
98
+ *
99
+ * A pattern is the wrong tool for a rule about what a command line
100
+ * DOES: a regular expression over its text has to re-implement the
101
+ * shell's quoting, and every form it misses (`$'…'`, a line
102
+ * continuation, a quoted separator) is a way past the rule. A host
103
+ * that needs such a rule reads the line with `lexShellCommandLine`
104
+ * and decides on the words bash will pass, here.
105
+ *
106
+ * `decide` returns a decision, or `null` to let the next rule
107
+ * decide. A `decide` that throws is read as `deny`: a rule that
108
+ * could not reach a verdict must not let the call through.
109
+ */
110
+ type: 'predicate'
111
+ /**
112
+ * What the rule refuses or allows, in words. It is the reason the
113
+ * gate reports when this rule decides, so it should tell a model
114
+ * whether a different input could fare better.
115
+ */
116
+ description: string
117
+ decide: AuthorizationPredicate
118
+ }
119
+
120
+ /** The call an `AuthorizationRule` of type `predicate` is asked about. */
121
+ export interface AuthorizationPredicateCall {
122
+ readonly toolName: string
123
+ readonly toolInput: unknown
124
+ readonly toolDef: ToolDefinition | undefined
125
+ /**
126
+ * The shell a command line in this call runs in: the caller's
127
+ * (`ToolCallContext.commandDialect`), or `sh` when it did not say, which
128
+ * is the reading that holds whichever shell runs it.
129
+ */
130
+ readonly commandDialect: ShellDialect
131
+ }
132
+
133
+ /** The code behind an `AuthorizationRule` of type `predicate`. */
134
+ export type AuthorizationPredicate = (call: AuthorizationPredicateCall) => GateDecision | null
87
135
 
88
136
  const AllowReadOnlySchema = z.object({
89
137
  type: z.literal('allow_read_only'),
@@ -121,12 +169,21 @@ const ArgumentPatternSchema = z.object({
121
169
  // fail-open shape this rule type exists to remove.
122
170
  argument: z.string().min(1),
123
171
  pattern: z.string().max(MAX_CUSTOM_PATTERN_LENGTH),
124
- decision: z.enum(['allow', 'deny']),
172
+ decision: z.enum(['allow', 'deny', 'review']),
125
173
  })
126
174
  const AllowByTierSchema = z.object({
127
175
  type: z.literal('allow_by_tier'),
128
176
  tiers: z.array(z.string()),
129
177
  })
178
+ const PredicateSchema = z.object({
179
+ type: z.literal('predicate'),
180
+ // A rule that says nothing about itself produces a refusal nobody can
181
+ // reason about.
182
+ description: z.string().min(1),
183
+ decide: z.custom<AuthorizationPredicate>((value) => typeof value === 'function', {
184
+ message: 'decide must be a function',
185
+ }),
186
+ })
130
187
 
131
188
  export const AuthorizationRuleSchema = z.discriminatedUnion('type', [
132
189
  AllowReadOnlySchema,
@@ -137,6 +194,7 @@ export const AuthorizationRuleSchema = z.discriminatedUnion('type', [
137
194
  CustomPatternSchema,
138
195
  ArgumentPatternSchema,
139
196
  AllowByTierSchema,
197
+ PredicateSchema,
140
198
  ])
141
199
 
142
200
  export const AuthorizationGateConfigSchema = z.object({
@@ -0,0 +1,341 @@
1
+ // ---------------------------------------------------------------------------
2
+ // The browser contract: what a browser host does for the `browser` and
3
+ // `browser_act` tools. The SDK owns the model-facing tools and this
4
+ // interface; a host package (for example `@namzu/browser`) owns the engine.
5
+ // ---------------------------------------------------------------------------
6
+
7
+ /** Every action the `browser` tool (observe and navigate) can ask for. */
8
+ export type BrowserObserveActionName =
9
+ | 'navigate'
10
+ | 'back'
11
+ | 'forward'
12
+ | 'reload'
13
+ | 'snapshot'
14
+ | 'screenshot'
15
+ | 'scroll'
16
+ | 'wait_for'
17
+ | 'tabs'
18
+
19
+ /** Every action the `browser_act` tool (change the page) can ask for. */
20
+ export type BrowserActActionName =
21
+ | 'click'
22
+ | 'type'
23
+ | 'fill_form'
24
+ | 'select'
25
+ | 'press'
26
+ | 'hover'
27
+ | 'upload'
28
+ | 'dialog'
29
+
30
+ export type BrowserActionName = BrowserObserveActionName | BrowserActActionName
31
+
32
+ export type BrowserScrollDirection = 'up' | 'down' | 'left' | 'right'
33
+
34
+ export type BrowserTabsOp = 'list' | 'select' | 'close' | 'new'
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // Capabilities — frozen at host construction; the model reads them through
38
+ // the tools' descriptions and schemas.
39
+ // ---------------------------------------------------------------------------
40
+
41
+ export interface BrowserCapabilities {
42
+ /** Which engine drives the browser, in words: `local-chromium`, `windows-cdp`. */
43
+ readonly engine: string
44
+ /** The browser window is not shown. */
45
+ readonly headless: boolean
46
+ /** `screenshot` can return an image. */
47
+ readonly screenshot: boolean
48
+ /** `upload` can attach a local file to a file input. */
49
+ readonly upload: boolean
50
+ /**
51
+ * Exact action subset when known. Absent: every action whose broad flag
52
+ * above allows it. Actions outside the subset are removed from the model
53
+ * schema and refused before the host is called.
54
+ */
55
+ readonly supportedActions?: readonly BrowserActionName[]
56
+ /**
57
+ * The most snapshot text one call returns. The tool cuts anything longer.
58
+ * Absent: {@link BROWSER_SNAPSHOT_MAX_CHARS}.
59
+ */
60
+ readonly snapshotMaxChars?: number
61
+ /**
62
+ * Why the browser cannot be used at all — the engine is not installed,
63
+ * the profile is missing. Present, both tools stay mounted, say this in
64
+ * their descriptions and refuse every call with it, so the model reads
65
+ * the reason once and tells the user instead of retrying.
66
+ */
67
+ readonly unavailableReason?: string
68
+ }
69
+
70
+ /** Default and ceiling of snapshot text per call, in characters. */
71
+ export const BROWSER_SNAPSHOT_MAX_CHARS = 20_000
72
+
73
+ /** Longest `wait_for` the tool accepts, in milliseconds. */
74
+ export const BROWSER_WAIT_MAX_MS = 30_000
75
+
76
+ /** Most fields one `fill_form` call may set. */
77
+ export const BROWSER_FILL_FORM_MAX_FIELDS = 20
78
+
79
+ // ---------------------------------------------------------------------------
80
+ // Actions
81
+ // ---------------------------------------------------------------------------
82
+
83
+ /**
84
+ * What the `browser` tool asks of the host. Every URL here has already been
85
+ * canonicalised by the tool (see `canonicalizeBrowserUrl`): only http, https
86
+ * or `about:blank`, no credentials, never a cloud metadata address.
87
+ */
88
+ export type BrowserObserveAction =
89
+ | { readonly action: 'navigate'; readonly url: string }
90
+ | { readonly action: 'back' }
91
+ | { readonly action: 'forward' }
92
+ | { readonly action: 'reload' }
93
+ | { readonly action: 'snapshot'; readonly ref?: string; readonly cursor?: string }
94
+ | { readonly action: 'screenshot'; readonly ref?: string; readonly fullPage?: boolean }
95
+ | {
96
+ readonly action: 'scroll'
97
+ readonly direction: BrowserScrollDirection
98
+ readonly ref?: string
99
+ /** Screens to scroll; the host's default when absent. */
100
+ readonly amount?: number
101
+ }
102
+ | {
103
+ readonly action: 'wait_for'
104
+ readonly text?: string
105
+ readonly textGone?: string
106
+ readonly timeMs?: number
107
+ }
108
+ | {
109
+ readonly action: 'tabs'
110
+ readonly op: BrowserTabsOp
111
+ /** `select` and `close`: the tab id from `list` or a page header. */
112
+ readonly tab?: string
113
+ /** `new`: the address to open; `about:blank` when absent. */
114
+ readonly url?: string
115
+ }
116
+
117
+ export interface BrowserFormField {
118
+ readonly ref: string
119
+ /** Text for a text box; `true`/`false` for a checkbox; the option's label for a select. */
120
+ readonly value: string
121
+ }
122
+
123
+ /** The change a `browser_act` call makes. */
124
+ export type BrowserActOperation =
125
+ | { readonly action: 'click'; readonly ref: string; readonly doubleClick?: boolean }
126
+ | {
127
+ readonly action: 'type'
128
+ readonly ref: string
129
+ readonly text: string
130
+ /** Press Enter after typing. */
131
+ readonly submit?: boolean
132
+ }
133
+ | { readonly action: 'fill_form'; readonly fields: readonly BrowserFormField[] }
134
+ | { readonly action: 'select'; readonly ref: string; readonly values: readonly string[] }
135
+ | { readonly action: 'press'; readonly key: string; readonly ref?: string }
136
+ | { readonly action: 'hover'; readonly ref: string }
137
+ | { readonly action: 'upload'; readonly ref: string; readonly path: string }
138
+ | { readonly action: 'dialog'; readonly accept: boolean; readonly promptText?: string }
139
+
140
+ /**
141
+ * What the `browser_act` tool asks of the host.
142
+ *
143
+ * `origin` is the page origin the model read from the snapshot header
144
+ * (`Page: <origin> — …`), canonicalised by the tool. **The host MUST compare
145
+ * it with the live origin of the page it is about to act on, immediately
146
+ * before acting, and throw a {@link BrowserOriginMismatch} without acting
147
+ * when they differ.** That comparison is what binds an approval of "click
148
+ * Place order on shop.example.com" to shop.example.com: a redirect between
149
+ * the snapshot and the click must not carry the click to another site.
150
+ */
151
+ export type BrowserActAction = BrowserActOperation & {
152
+ readonly origin: string
153
+ /** Return a snapshot of the page after the action. */
154
+ readonly snapshot?: boolean
155
+ }
156
+
157
+ // ---------------------------------------------------------------------------
158
+ // Results
159
+ // ---------------------------------------------------------------------------
160
+
161
+ /** The page an action left the browser on, as the host observed it. */
162
+ export interface BrowserPageInfo {
163
+ /** Canonical origin (`https://github.com`), or `null` for `about:blank`. */
164
+ readonly origin: string
165
+ readonly url: string
166
+ /** The document title. Page-controlled: the tool quotes and cuts it. */
167
+ readonly title: string
168
+ /** The tab's id, e.g. `t1`. */
169
+ readonly tab: string
170
+ }
171
+
172
+ export interface BrowserTabInfo extends BrowserPageInfo {
173
+ readonly active: boolean
174
+ }
175
+
176
+ export interface BrowserSnapshot {
177
+ readonly page: BrowserPageInfo
178
+ /**
179
+ * The accessibility tree as text, one element per line, each actionable
180
+ * element carrying `[ref=eN]`. Page-controlled: the tool wraps it as
181
+ * untrusted content.
182
+ */
183
+ readonly text: string
184
+ /** Present when more text follows; pass it back as `cursor`. */
185
+ readonly nextCursor?: string
186
+ }
187
+
188
+ export interface BrowserScreenshot {
189
+ readonly page: BrowserPageInfo
190
+ readonly data: Uint8Array
191
+ readonly mimeType: 'image/png' | 'image/jpeg'
192
+ readonly width: number
193
+ readonly height: number
194
+ }
195
+
196
+ /**
197
+ * What an action produced. Every field is optional because actions differ;
198
+ * the tool renders whichever are present.
199
+ */
200
+ export interface BrowserResult {
201
+ /** The page after the action. */
202
+ readonly page?: BrowserPageInfo
203
+ /**
204
+ * The host's own note, in the host's words — "download of report.pdf
205
+ * cancelled", "dialog accepted". Never page text: the tool shows it
206
+ * outside the untrusted envelope.
207
+ */
208
+ readonly message?: string
209
+ readonly snapshot?: BrowserSnapshot
210
+ readonly screenshot?: BrowserScreenshot
211
+ readonly tabs?: readonly BrowserTabInfo[]
212
+ }
213
+
214
+ /** What a snapshot said about one ref, for labelling a call before it runs. */
215
+ export interface BrowserRefDescription {
216
+ /** ARIA role: `button`, `link`, `textbox`. */
217
+ readonly role: string
218
+ /** Accessible name. Page-controlled. */
219
+ readonly name?: string
220
+ }
221
+
222
+ /** Who and where, for labels and reviews. May change between calls. */
223
+ export interface BrowserSessionInfo {
224
+ /** The profile the browser runs under. */
225
+ readonly profile?: string
226
+ /** Canonical origin of the active tab, when the host knows it. */
227
+ readonly origin?: string
228
+ }
229
+
230
+ // ---------------------------------------------------------------------------
231
+ // Structural errors. Hosts throw values carrying these shapes; the tools
232
+ // recognise them by shape, so a separately installed host and SDK need not
233
+ // share an error class.
234
+ // ---------------------------------------------------------------------------
235
+
236
+ /** The live page is not on the origin a `browser_act` call named. Nothing was done. */
237
+ export interface BrowserOriginMismatch {
238
+ readonly code: 'browser_origin_mismatch'
239
+ readonly expected: string
240
+ readonly actual: string
241
+ readonly message: string
242
+ }
243
+
244
+ /** The ref is not on the current page: it came from an older snapshot. Nothing was done. */
245
+ export interface BrowserStaleRef {
246
+ readonly code: 'browser_stale_ref'
247
+ readonly ref: string
248
+ readonly message: string
249
+ }
250
+
251
+ /** Why the page needs a person. */
252
+ export type BrowserHumanRequiredReason =
253
+ | 'sign-in'
254
+ | 'two-factor'
255
+ | 'captcha'
256
+ | 'bot-block'
257
+ | 'http-auth'
258
+ | 'credential-field'
259
+
260
+ /**
261
+ * The page needs a person: a sign-in, a second factor, a CAPTCHA, a bot
262
+ * wall, or a field that takes a password or one-time code. The agent must
263
+ * stop and hand over; it never signs in, solves or types a credential.
264
+ */
265
+ export interface BrowserHumanRequired {
266
+ readonly code: 'browser_human_required'
267
+ readonly reason: BrowserHumanRequiredReason
268
+ readonly origin: string
269
+ readonly message: string
270
+ readonly profile?: string
271
+ /** The command that opens a visible window on this profile, e.g. `namzu browser login work https://…`. */
272
+ readonly loginCommand?: string
273
+ }
274
+
275
+ /**
276
+ * A page-changing action started and did not report a clean completion. The
277
+ * page may already have changed, so replaying it is unsafe.
278
+ */
279
+ export interface BrowserOutcomeUnknown {
280
+ readonly code: 'browser_outcome_unknown'
281
+ readonly action: BrowserActionName
282
+ readonly outcome: 'unknown'
283
+ readonly retrySafety: 'unsafe'
284
+ readonly message: string
285
+ }
286
+
287
+ /** The site policy does not allow this origin at the level the call needs. */
288
+ export interface BrowserSiteDenied {
289
+ readonly code: 'browser_site_denied'
290
+ readonly origin: string
291
+ readonly message: string
292
+ }
293
+
294
+ export type BrowserHostError =
295
+ | BrowserOriginMismatch
296
+ | BrowserStaleRef
297
+ | BrowserHumanRequired
298
+ | BrowserOutcomeUnknown
299
+ | BrowserSiteDenied
300
+
301
+ // ---------------------------------------------------------------------------
302
+ // Host
303
+ // ---------------------------------------------------------------------------
304
+
305
+ export interface BrowserCallOptions {
306
+ /** Fires when the call is cancelled or times out. */
307
+ readonly signal?: AbortSignal
308
+ }
309
+
310
+ /**
311
+ * A browser the tools drive. Implementations live outside `@namzu/sdk`.
312
+ *
313
+ * The host is where the site policy is enforced after the fact: whatever the
314
+ * gate approved, the host re-checks the page a navigation, redirect or popup
315
+ * actually landed on, and the live origin before every `act`.
316
+ */
317
+ export interface BrowserHost {
318
+ readonly id: string
319
+ readonly capabilities: BrowserCapabilities
320
+
321
+ /** Observe or navigate. Throws a {@link BrowserHostError} shape to refuse. */
322
+ observe(action: BrowserObserveAction, options?: BrowserCallOptions): Promise<BrowserResult>
323
+ /**
324
+ * Change the page. MUST check `action.origin` against the live page first
325
+ * (see {@link BrowserActAction}). Throws a {@link BrowserHostError} shape
326
+ * to refuse.
327
+ */
328
+ act(action: BrowserActAction, options?: BrowserCallOptions): Promise<BrowserResult>
329
+
330
+ /**
331
+ * What the most recent snapshot said about `ref`, synchronously and
332
+ * without touching the page, for the label a person approves. Undefined
333
+ * when the ref is unknown.
334
+ */
335
+ describeRef?(ref: string): BrowserRefDescription | undefined
336
+ /** The current profile and page, synchronously, for labels. */
337
+ session?(): BrowserSessionInfo
338
+
339
+ initialize?(): Promise<void>
340
+ dispose?(): Promise<void>
341
+ }
@@ -40,6 +40,15 @@ export type HITLResumeDecision =
40
40
  * person said yes to a batch that showed the escape.
41
41
  */
42
42
  confirmedEscalations?: readonly string[]
43
+ /**
44
+ * Ids of calls approved on the strength of a skill's `allowed-tools`
45
+ * grant ({@link ToolCallSummary.skillGrant}), with nobody asked.
46
+ *
47
+ * The kernel writes each one to the session's audit trail naming the
48
+ * skill, and only for a call that actually carried the grant — an id
49
+ * listed here for an unmarked call is ignored rather than trusted.
50
+ */
51
+ skillGranted?: readonly string[]
43
52
  }
44
53
  | {
45
54
  action: 'modify_tools'
@@ -126,6 +135,18 @@ export interface ToolCallSummary {
126
135
  * approve it on their own. A `deny` rule still refuses it outright.
127
136
  */
128
137
  escalation?: ToolCallEscalation
138
+ /**
139
+ * Present when a skill loaded earlier in this turn pre-approved this call
140
+ * through its `allowed-tools`, and nothing stronger stands in the way.
141
+ *
142
+ * Marked only on a call the operator's policy left to review (no `deny`,
143
+ * no explicit `ask` rule), that is not destructive and that carries no
144
+ * {@link escalation}. The review policy decides what the mark is worth:
145
+ * `createReviewHandler` approves a batch without asking when every call
146
+ * it would have asked about carries one, and still refuses under `plan`
147
+ * and `strict`. A host's own handler may ignore it.
148
+ */
149
+ skillGrant?: { readonly skill: string }
129
150
  }
130
151
 
131
152
  /** See {@link ToolCallSummary.escalation}. */
@@ -534,6 +534,12 @@ type CoreSessionEvent =
534
534
  providerError?: import('../provider/error.js').ProviderErrorInfo
535
535
  /** Curated operator copy, absent when no catalog rule matched. */
536
536
  explanation?: { id: string; message: string; hint: string }
537
+ /**
538
+ * Present when a tool asked for a person: its `ToolResult.handoff`.
539
+ * The results of the batch are already committed; resuming the turn
540
+ * calls the model with them.
541
+ */
542
+ handoff?: import('../tool/index.js').ToolHandoff
537
543
  }
538
544
  /** A paused turn continues from its checkpoint, under the same `turnId`. */
539
545
  | {
@@ -201,6 +201,15 @@ const providerError = z
201
201
 
202
202
  const explanation = z.object({ id: text, message: text, hint: text }).strict()
203
203
 
204
+ /** A tool's request for a person, as `turn_paused` carries it. */
205
+ const toolHandoff = z
206
+ .object({
207
+ kind: z.literal('human-required'),
208
+ reason: text,
209
+ detail: z.record(text).optional(),
210
+ })
211
+ .strict()
212
+
204
213
  const stopReason = z.enum([
205
214
  'end_turn',
206
215
  'token_budget',
@@ -361,6 +370,7 @@ export const TurnPausedRecordSchema = inTurn('turn_paused', {
361
370
  providerError: providerError.optional(),
362
371
  explanation: explanation.optional(),
363
372
  budget: tokenBudgetSummary.optional(),
373
+ handoff: toolHandoff.optional(),
364
374
  })
365
375
 
366
376
  export const TurnResumingRecordSchema = inTurn('turn_resuming', {
@@ -77,6 +77,8 @@ export interface SkillRegistryRef {
77
77
  invocation?: 'model' | 'operator' | 'both'
78
78
  }
79
79
  body?: string
80
+ /** The skill's directory, which `${CLAUDE_SKILL_DIR}` in `allowed-tools` names. */
81
+ dirPath?: string
80
82
  }
81
83
  }
82
84
  | undefined
@@ -473,18 +475,56 @@ export interface ToolContext {
473
475
  }
474
476
 
475
477
  /**
476
- * Adopt the tool scope a skill declared.
478
+ * Formerly: narrow the turn's tools to what a skill's `allowed-tools`
479
+ * named. That reading was backwards — the field pre-approves, it never
480
+ * restricts — and the kernel no longer supplies this member, so a tool
481
+ * that calls it through `?.` does nothing.
477
482
  *
478
- * Called by the `skill` tool when a loaded skill names `allowed-tools`.
479
- * The scope INTERSECTS what the turn already allows and takes effect from
480
- * the next batch — a skill loaded alongside other calls must not
481
- * retroactively refuse them.
483
+ * @deprecated Never supplied by the kernel since `allowed-tools` became a
484
+ * pre-approval. Use {@link ToolContext.grantSkillTools}. Removed in the
485
+ * next major.
482
486
  */
483
487
  adoptSkillScope?: (scope: {
484
488
  skill: string
485
489
  allowedTools: readonly string[]
486
490
  }) => void
487
491
 
492
+ /**
493
+ * Pre-approve what a loaded skill's `allowed-tools` names, for the rest of
494
+ * this turn.
495
+ *
496
+ * Called by the `skill` tool. It never narrows anything: every tool the
497
+ * turn had stays callable, and a call the grant does not cover is reviewed
498
+ * exactly as before. A covered call skips the approval prompt, but not an
499
+ * operator `deny` or `ask` rule, plan mode, `strict` mode, a destructive
500
+ * call or one that reaches outside the turn's roots or sandbox. Each call
501
+ * approved this way is written to the session's audit trail naming the
502
+ * skill.
503
+ *
504
+ * Returns what would be granted and what was ignored (an unknown tool
505
+ * name, a pattern on a tool without a command line, a tool every call of
506
+ * which is destructive), so the tool can tell the model. Nothing is
507
+ * recorded until `commit()` is called: the caller commits only once the
508
+ * skill's instructions have actually been delivered, so a load that fails
509
+ * afterwards leaves no approval behind. Absent outside a turn, where
510
+ * there is nothing to grant into.
511
+ */
512
+ grantSkillTools?: (grant: {
513
+ readonly skill: string
514
+ /** The parsed entries, as `parseAllowedTools` returns them. */
515
+ readonly allowedTools: readonly string[]
516
+ /** The skill's directory, for `${CLAUDE_SKILL_DIR}` / `${NAMZU_SKILL_DIR}`. */
517
+ readonly skillDirectory?: string
518
+ }) => {
519
+ readonly granted: readonly string[]
520
+ readonly ignored: readonly {
521
+ readonly entry: string
522
+ readonly reason: string
523
+ }[]
524
+ /** Record the grant in the turn. Idempotent. */
525
+ readonly commit: () => void
526
+ }
527
+
488
528
  /**
489
529
  * Effective model-visible character cap for this tool result.
490
530
  *
@@ -675,8 +715,41 @@ export interface ToolResult {
675
715
  * must not lose: what its controls do, where things are, what failed.
676
716
  */
677
717
  workingState?: readonly import('../../compaction/types.js').WorkingStatePin[]
718
+ /**
719
+ * This result needs a person before the turn can go on — a sign-in page,
720
+ * a CAPTCHA, a second factor, anything the model must not try to answer
721
+ * itself.
722
+ *
723
+ * The result is committed like any other: it reaches the transcript and
724
+ * the session log with the rest of its batch. Then, instead of calling
725
+ * the model again, the kernel writes a checkpoint and ends the segment
726
+ * with `turn_paused` carrying this value. Resuming the turn continues
727
+ * from that checkpoint, and the next step is a model call that sees the
728
+ * results. Inside a delegated child there is no person to hand to, so
729
+ * the child's turn fails with {@link ToolHandoff.reason} instead.
730
+ */
731
+ handoff?: ToolHandoff
678
732
  }
679
733
 
734
+ /**
735
+ * A tool's request to stop the turn for a person. See {@link ToolResult.handoff}.
736
+ */
737
+ export interface ToolHandoff {
738
+ readonly kind: 'human-required'
739
+ /** Operator-facing text: what the person has to do before the turn resumes. */
740
+ readonly reason: string
741
+ /** Structured facts a host can render or act on, such as an origin or a command. */
742
+ readonly detail?: Readonly<Record<string, string>>
743
+ }
744
+
745
+ /**
746
+ * The shell a command line will run in, as the permission rules read it.
747
+ * `bash` reads it as bash does. `sh` reads it for a shell that may be bash or
748
+ * a POSIX shell such as `dash`: every construct the two read differently
749
+ * makes the line opaque, so a line that is not opaque means the same in both.
750
+ */
751
+ export type ShellDialect = 'bash' | 'sh'
752
+
680
753
  export interface ToolDefinition<TInput = unknown> extends ToolPresentation<TInput> {
681
754
  name: string
682
755
  description: string
@@ -737,6 +810,20 @@ export interface ToolDefinition<TInput = unknown> extends ToolPresentation<TInpu
737
810
  * chaining — has nothing to decompose and must not claim otherwise.
738
811
  */
739
812
  commandArgument?: string
813
+ /**
814
+ * The shell {@link commandArgument} runs in, as the dialect the permission
815
+ * rules must read it in.
816
+ *
817
+ * A rule reads a command line before it runs, and the reading is only
818
+ * right for the shell that runs it: `$'\x3b'`, `|&` and `&>` mean one
819
+ * thing to bash and another to `dash`. `bash` reads the line as bash
820
+ * does. `sh`, the default when this is absent, reads it for a shell that
821
+ * may be bash or a POSIX shell, and every construct the two read
822
+ * differently makes the line opaque, so no allow rule approves it. The
823
+ * shipped `bash` tool answers `bash` when it will spawn bash on the host
824
+ * and `sh` inside a sandbox, whose guest may not have bash.
825
+ */
826
+ commandDialect?: (context: { readonly sandboxed: boolean }) => ShellDialect
740
827
  /**
741
828
  * The argument that holds a filesystem path the tool resolves against the
742
829
  * turn's roots (the working directory and the added directories).
@@ -748,6 +835,23 @@ export interface ToolDefinition<TInput = unknown> extends ToolPresentation<TInpu
748
835
  * the safe direction to be wrong in.
749
836
  */
750
837
  pathArgument?: string
838
+ /**
839
+ * The argument that holds an absolute URL the tool has already
840
+ * canonicalised in its input schema — one spelling, no whitespace.
841
+ *
842
+ * Declared so an `argument_pattern` rule tests the URL whole. Without it
843
+ * the rule reads every argument as a possible command line, and a URL's
844
+ * query separators (`&`, `;`, `|`) cut it into "segments": an `allow` for
845
+ * `^https://github\.com(?:[/?#]|$)` then declined
846
+ * `https://github.com/search?q=a&type=code`, because `type=code` is not
847
+ * GitHub. A `deny` or `review` is unaffected either way; only an `allow`
848
+ * needed every segment to match.
849
+ *
850
+ * Declare it only for an argument the input schema itself canonicalises
851
+ * to a URL. A free-text argument that happens to hold one keeps the
852
+ * command-line reading, which is the safe direction to be wrong in.
853
+ */
854
+ urlArgument?: string
751
855
  /**
752
856
  * The boolean argument by which a call asks to run outside the turn's
753
857
  * sandbox, when the tool offers that at all (the shipped `bash` does).
@@ -26,6 +26,13 @@ export type ToolCallView =
26
26
  readonly activity?: 'exploration'
27
27
  /** A successful result may add no information beyond the completed call row. */
28
28
  readonly visibility?: 'hidden'
29
+ /**
30
+ * For a result: the person declined on the tool's own screen (a
31
+ * confirmation they cancelled). The call did not succeed, but
32
+ * nothing failed either, so a host draws it as neither: `label` says
33
+ * what did not happen ("Cancelled — nothing was saved").
34
+ */
35
+ readonly outcome?: 'cancelled'
29
36
  }
30
37
  /**
31
38
  * A change to a document. `path` is optional because not every diff is