@namzu/sdk 45.0.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 (230) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/dist/authorization/gate.d.ts.map +1 -1
  3. package/dist/authorization/gate.js +12 -2
  4. package/dist/authorization/gate.js.map +1 -1
  5. package/dist/authorization/rules.d.ts.map +1 -1
  6. package/dist/authorization/rules.js +37 -6
  7. package/dist/authorization/rules.js.map +1 -1
  8. package/dist/authorization/shell-lexer.d.ts +14 -0
  9. package/dist/authorization/shell-lexer.d.ts.map +1 -1
  10. package/dist/authorization/shell-lexer.js +19 -6
  11. package/dist/authorization/shell-lexer.js.map +1 -1
  12. package/dist/bridge/a2a/mapper.d.ts.map +1 -1
  13. package/dist/bridge/a2a/mapper.js +2 -0
  14. package/dist/bridge/a2a/mapper.js.map +1 -1
  15. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  16. package/dist/bridge/sse/mapper.js +1 -0
  17. package/dist/bridge/sse/mapper.js.map +1 -1
  18. package/dist/directory/types.d.ts +2 -0
  19. package/dist/directory/types.d.ts.map +1 -1
  20. package/dist/directory/types.js.map +1 -1
  21. package/dist/manager/resident/outbox.d.ts +4 -4
  22. package/dist/prompt/coding-agent-doctrine.d.ts +1 -1
  23. package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
  24. package/dist/prompt/coding-agent-doctrine.js +1 -0
  25. package/dist/prompt/coding-agent-doctrine.js.map +1 -1
  26. package/dist/public-runtime.d.ts +4 -1
  27. package/dist/public-runtime.d.ts.map +1 -1
  28. package/dist/public-runtime.js +11 -1
  29. package/dist/public-runtime.js.map +1 -1
  30. package/dist/public-tools.d.ts +4 -0
  31. package/dist/public-tools.d.ts.map +1 -1
  32. package/dist/public-tools.js +10 -0
  33. package/dist/public-tools.js.map +1 -1
  34. package/dist/public-types.d.ts +7 -1
  35. package/dist/public-types.d.ts.map +1 -1
  36. package/dist/runtime/query/declined.d.ts +12 -0
  37. package/dist/runtime/query/declined.d.ts.map +1 -0
  38. package/dist/runtime/query/declined.js +12 -0
  39. package/dist/runtime/query/declined.js.map +1 -0
  40. package/dist/runtime/query/executor.d.ts +3 -1
  41. package/dist/runtime/query/executor.d.ts.map +1 -1
  42. package/dist/runtime/query/executor.js +14 -2
  43. package/dist/runtime/query/executor.js.map +1 -1
  44. package/dist/runtime/query/index.d.ts.map +1 -1
  45. package/dist/runtime/query/index.js +1 -0
  46. package/dist/runtime/query/index.js.map +1 -1
  47. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  48. package/dist/runtime/query/iteration/index.js +7 -0
  49. package/dist/runtime/query/iteration/index.js.map +1 -1
  50. package/dist/runtime/query/iteration/phases/context.d.ts +6 -0
  51. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  52. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  53. package/dist/runtime/query/iteration/phases/handoff.d.ts +22 -0
  54. package/dist/runtime/query/iteration/phases/handoff.d.ts.map +1 -0
  55. package/dist/runtime/query/iteration/phases/handoff.js +65 -0
  56. package/dist/runtime/query/iteration/phases/handoff.js.map +1 -0
  57. package/dist/runtime/query/iteration/phases/index.d.ts +1 -0
  58. package/dist/runtime/query/iteration/phases/index.d.ts.map +1 -1
  59. package/dist/runtime/query/iteration/phases/index.js +1 -0
  60. package/dist/runtime/query/iteration/phases/index.js.map +1 -1
  61. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  62. package/dist/runtime/query/iteration/phases/tool-review.js +8 -3
  63. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  64. package/dist/runtime/query/resume-pending.d.ts.map +1 -1
  65. package/dist/runtime/query/resume-pending.js +3 -2
  66. package/dist/runtime/query/resume-pending.js.map +1 -1
  67. package/dist/runtime/query/review-policy.d.ts.map +1 -1
  68. package/dist/runtime/query/review-policy.js +4 -3
  69. package/dist/runtime/query/review-policy.js.map +1 -1
  70. package/dist/schedules/cron.d.ts +21 -0
  71. package/dist/schedules/cron.d.ts.map +1 -0
  72. package/dist/schedules/cron.js +167 -0
  73. package/dist/schedules/cron.js.map +1 -0
  74. package/dist/schedules/describe.d.ts +14 -0
  75. package/dist/schedules/describe.d.ts.map +1 -0
  76. package/dist/schedules/describe.js +133 -0
  77. package/dist/schedules/describe.js.map +1 -0
  78. package/dist/schedules/errors.d.ts +11 -0
  79. package/dist/schedules/errors.d.ts.map +1 -0
  80. package/dist/schedules/errors.js +15 -0
  81. package/dist/schedules/errors.js.map +1 -0
  82. package/dist/schedules/evaluate.d.ts +35 -0
  83. package/dist/schedules/evaluate.d.ts.map +1 -0
  84. package/dist/schedules/evaluate.js +158 -0
  85. package/dist/schedules/evaluate.js.map +1 -0
  86. package/dist/schedules/index.d.ts +11 -0
  87. package/dist/schedules/index.d.ts.map +1 -0
  88. package/dist/schedules/index.js +8 -0
  89. package/dist/schedules/index.js.map +1 -0
  90. package/dist/schedules/next-fire.d.ts +50 -0
  91. package/dist/schedules/next-fire.d.ts.map +1 -0
  92. package/dist/schedules/next-fire.js +250 -0
  93. package/dist/schedules/next-fire.js.map +1 -0
  94. package/dist/schedules/spec.d.ts +30 -0
  95. package/dist/schedules/spec.d.ts.map +1 -0
  96. package/dist/schedules/spec.js +169 -0
  97. package/dist/schedules/spec.js.map +1 -0
  98. package/dist/schedules/types.d.ts +144 -0
  99. package/dist/schedules/types.d.ts.map +1 -0
  100. package/dist/schedules/types.js +11 -0
  101. package/dist/schedules/types.js.map +1 -0
  102. package/dist/schedules/tz.d.ts +44 -0
  103. package/dist/schedules/tz.d.ts.map +1 -0
  104. package/dist/schedules/tz.js +141 -0
  105. package/dist/schedules/tz.js.map +1 -0
  106. package/dist/skills/index.d.ts +1 -1
  107. package/dist/skills/index.d.ts.map +1 -1
  108. package/dist/skills/index.js +1 -1
  109. package/dist/skills/index.js.map +1 -1
  110. package/dist/skills/loader.d.ts +16 -3
  111. package/dist/skills/loader.d.ts.map +1 -1
  112. package/dist/skills/loader.js +48 -4
  113. package/dist/skills/loader.js.map +1 -1
  114. package/dist/tools/builtins/browser-url.d.ts +83 -0
  115. package/dist/tools/builtins/browser-url.d.ts.map +1 -0
  116. package/dist/tools/builtins/browser-url.js +240 -0
  117. package/dist/tools/builtins/browser-url.js.map +1 -0
  118. package/dist/tools/builtins/browser.d.ts +367 -0
  119. package/dist/tools/builtins/browser.d.ts.map +1 -0
  120. package/dist/tools/builtins/browser.js +704 -0
  121. package/dist/tools/builtins/browser.js.map +1 -0
  122. package/dist/tools/builtins/skill.d.ts.map +1 -1
  123. package/dist/tools/builtins/skill.js +15 -0
  124. package/dist/tools/builtins/skill.js.map +1 -1
  125. package/dist/tools/defineTool.d.ts +2 -0
  126. package/dist/tools/defineTool.d.ts.map +1 -1
  127. package/dist/tools/defineTool.js +1 -0
  128. package/dist/tools/defineTool.js.map +1 -1
  129. package/dist/tools/schedules/index.d.ts +5 -0
  130. package/dist/tools/schedules/index.d.ts.map +1 -0
  131. package/dist/tools/schedules/index.js +4 -0
  132. package/dist/tools/schedules/index.js.map +1 -0
  133. package/dist/tools/schedules/loop-tool.d.ts +14 -0
  134. package/dist/tools/schedules/loop-tool.d.ts.map +1 -0
  135. package/dist/tools/schedules/loop-tool.js +81 -0
  136. package/dist/tools/schedules/loop-tool.js.map +1 -0
  137. package/dist/tools/schedules/present.d.ts +16 -0
  138. package/dist/tools/schedules/present.d.ts.map +1 -0
  139. package/dist/tools/schedules/present.js +69 -0
  140. package/dist/tools/schedules/present.js.map +1 -0
  141. package/dist/tools/schedules/prompt-scan.d.ts +17 -0
  142. package/dist/tools/schedules/prompt-scan.d.ts.map +1 -0
  143. package/dist/tools/schedules/prompt-scan.js +92 -0
  144. package/dist/tools/schedules/prompt-scan.js.map +1 -0
  145. package/dist/tools/schedules/schedule-tool.d.ts +16 -0
  146. package/dist/tools/schedules/schedule-tool.d.ts.map +1 -0
  147. package/dist/tools/schedules/schedule-tool.js +327 -0
  148. package/dist/tools/schedules/schedule-tool.js.map +1 -0
  149. package/dist/tools/schedules/types.d.ts +184 -0
  150. package/dist/tools/schedules/types.d.ts.map +1 -0
  151. package/dist/tools/schedules/types.js +11 -0
  152. package/dist/tools/schedules/types.js.map +1 -0
  153. package/dist/types/authorization/index.d.ts +86 -9
  154. package/dist/types/authorization/index.d.ts.map +1 -1
  155. package/dist/types/authorization/index.js +11 -1
  156. package/dist/types/authorization/index.js.map +1 -1
  157. package/dist/types/browser/index.d.ts +280 -0
  158. package/dist/types/browser/index.d.ts.map +1 -0
  159. package/dist/types/browser/index.js +12 -0
  160. package/dist/types/browser/index.js.map +1 -0
  161. package/dist/types/session/events.d.ts +6 -0
  162. package/dist/types/session/events.d.ts.map +1 -1
  163. package/dist/types/session/events.js.map +1 -1
  164. package/dist/types/session/records.d.ts +23 -0
  165. package/dist/types/session/records.d.ts.map +1 -1
  166. package/dist/types/session/records.js +9 -0
  167. package/dist/types/session/records.js.map +1 -1
  168. package/dist/types/tool/index.d.ts +41 -0
  169. package/dist/types/tool/index.d.ts.map +1 -1
  170. package/dist/types/tool/index.js.map +1 -1
  171. package/dist/types/tool/presentation.d.ts +7 -0
  172. package/dist/types/tool/presentation.d.ts.map +1 -1
  173. package/dist/utils/frontmatter.d.ts +18 -2
  174. package/dist/utils/frontmatter.d.ts.map +1 -1
  175. package/dist/utils/frontmatter.js +13 -3
  176. package/dist/utils/frontmatter.js.map +1 -1
  177. package/dist/utils/id.d.ts +8 -0
  178. package/dist/utils/id.d.ts.map +1 -1
  179. package/dist/utils/id.js +12 -0
  180. package/dist/utils/id.js.map +1 -1
  181. package/package.json +1 -1
  182. package/src/authorization/gate.ts +14 -2
  183. package/src/authorization/rules.ts +37 -7
  184. package/src/authorization/shell-lexer.ts +36 -6
  185. package/src/bridge/a2a/mapper.ts +2 -0
  186. package/src/bridge/sse/mapper.ts +1 -0
  187. package/src/directory/types.ts +2 -0
  188. package/src/prompt/coding-agent-doctrine.ts +1 -0
  189. package/src/public-runtime.ts +28 -0
  190. package/src/public-tools.ts +35 -0
  191. package/src/public-types.ts +50 -0
  192. package/src/runtime/query/declined.ts +12 -0
  193. package/src/runtime/query/executor.ts +17 -1
  194. package/src/runtime/query/index.ts +1 -0
  195. package/src/runtime/query/iteration/index.ts +8 -0
  196. package/src/runtime/query/iteration/phases/context.ts +6 -0
  197. package/src/runtime/query/iteration/phases/handoff.ts +74 -0
  198. package/src/runtime/query/iteration/phases/index.ts +1 -0
  199. package/src/runtime/query/iteration/phases/tool-review.ts +11 -4
  200. package/src/runtime/query/resume-pending.ts +3 -2
  201. package/src/runtime/query/review-policy.ts +4 -3
  202. package/src/schedules/cron.ts +202 -0
  203. package/src/schedules/describe.ts +138 -0
  204. package/src/schedules/errors.ts +15 -0
  205. package/src/schedules/evaluate.ts +178 -0
  206. package/src/schedules/index.ts +18 -0
  207. package/src/schedules/next-fire.ts +257 -0
  208. package/src/schedules/spec.ts +210 -0
  209. package/src/schedules/types.ts +163 -0
  210. package/src/schedules/tz.ts +155 -0
  211. package/src/skills/index.ts +1 -1
  212. package/src/skills/loader.ts +55 -4
  213. package/src/tools/builtins/browser-url.ts +251 -0
  214. package/src/tools/builtins/browser.ts +817 -0
  215. package/src/tools/builtins/skill.ts +18 -0
  216. package/src/tools/defineTool.ts +3 -0
  217. package/src/tools/schedules/index.ts +4 -0
  218. package/src/tools/schedules/loop-tool.ts +85 -0
  219. package/src/tools/schedules/present.ts +86 -0
  220. package/src/tools/schedules/prompt-scan.ts +96 -0
  221. package/src/tools/schedules/schedule-tool.ts +376 -0
  222. package/src/tools/schedules/types.ts +197 -0
  223. package/src/types/authorization/index.ts +60 -2
  224. package/src/types/browser/index.ts +341 -0
  225. package/src/types/session/events.ts +6 -0
  226. package/src/types/session/records.ts +10 -0
  227. package/src/types/tool/index.ts +42 -0
  228. package/src/types/tool/presentation.ts +7 -0
  229. package/src/utils/frontmatter.ts +29 -3
  230. package/src/utils/id.ts +14 -0
@@ -0,0 +1,197 @@
1
+ /**
2
+ * The host side of the `schedule` and `session_loop` tools.
3
+ *
4
+ * The SDK holds the tool's contract with the model — its schema, its refusals
5
+ * and its confirmation rule — and nothing about where jobs are stored or how
6
+ * a confirmation is drawn. The host supplies both, and it computes every
7
+ * field a person is asked to confirm: the model's own words are never what
8
+ * the confirmation shows as fact.
9
+ */
10
+
11
+ /** A rule effect, in the vocabulary of the CLI's `[permissions]` table. */
12
+ export type ScheduleRuleEffect = 'allow' | 'ask' | 'deny'
13
+
14
+ /** What the model proposes. Validated by the tool's schema, then by the host. */
15
+ export interface ScheduleJobDraft {
16
+ readonly name: string
17
+ readonly prompt: string
18
+ /** Schedule words: `every 30m`, `0 9 * * 1-5`, `at 09:00`, `in 2h`. */
19
+ readonly when: string
20
+ /** Folder the job runs in. Absent: the session's working directory. */
21
+ readonly folder?: string
22
+ /** IANA zone for a cron expression or a local time. Absent: the host's. */
23
+ readonly tz?: string
24
+ readonly permissions: {
25
+ readonly preset?: 'read-only' | 'edit-in-folder'
26
+ /**
27
+ * What happens to a call no rule covers. The model may choose `park`
28
+ * (wait for the operator) or `deny`; running uncovered calls without
29
+ * asking is an operator decision the tool cannot propose.
30
+ */
31
+ readonly unmatched: 'park' | 'deny'
32
+ readonly execution?: 'host' | 'sandbox'
33
+ readonly rules?: Readonly<
34
+ Record<string, ScheduleRuleEffect | Readonly<Record<string, ScheduleRuleEffect>>>
35
+ >
36
+ /**
37
+ * Browser access for the run. Absent: the `browser` and `browser_act`
38
+ * tools are denied. Present: only the listed sites are reachable, at
39
+ * the listed level, and every other site is denied — there is no
40
+ * catch-all key. The tool refuses this block unless the host sets
41
+ * {@link ScheduleToolHost.browserGrants}.
42
+ */
43
+ readonly browser?: ScheduleBrowserGrant
44
+ }
45
+ readonly budget?: {
46
+ readonly maxIterations?: number
47
+ readonly tokenBudget?: number
48
+ readonly timeoutMs?: number
49
+ }
50
+ }
51
+
52
+ /**
53
+ * How far a scheduled run may go on one site. `read`: open and read pages,
54
+ * never change them. `ask`: open and change pages, each change parked for the
55
+ * operator. `act`: open and change pages without asking.
56
+ */
57
+ export type ScheduleBrowserSiteLevel = 'read' | 'ask' | 'act'
58
+
59
+ /** A scheduled job's browser grant. */
60
+ export interface ScheduleBrowserGrant {
61
+ /** The browser profile the run uses; the operator's, signed in beforehand. */
62
+ readonly profile: string
63
+ /**
64
+ * Canonical site key (`https://github.com`, `https://*.example.com`,
65
+ * `http://localhost:*`) to level. The tool canonicalises the keys the
66
+ * model wrote (see `canonicalizeBrowserSitePattern`) before the host sees
67
+ * them. Unlisted sites are denied.
68
+ */
69
+ readonly sites: Readonly<Record<string, ScheduleBrowserSiteLevel>>
70
+ /** Show the browser window during the run. Default: no window. */
71
+ readonly headed?: boolean
72
+ }
73
+
74
+ /** What the person confirming is shown. Every field is the host's own computation. */
75
+ export interface ScheduleJobPreview {
76
+ readonly name: string
77
+ /** The canonical folder the job would run in. */
78
+ readonly folder: string
79
+ /** True when `folder` is outside the session's working directory and added directories. */
80
+ readonly outsideSessionRoots: boolean
81
+ readonly prompt: string
82
+ /** The schedule in words, with its zone. */
83
+ readonly schedule: string
84
+ /** The next fire times, ISO-8601 UTC. */
85
+ readonly nextFireTimes: readonly string[]
86
+ /** The permission set expanded to one line per rule, config denies included. */
87
+ readonly rules: readonly string[]
88
+ readonly unmatched: 'park' | 'deny' | 'allow'
89
+ readonly execution: 'host' | 'sandbox'
90
+ /** A rule lets the run reach the network. */
91
+ readonly networkAccess: boolean
92
+ readonly budget: {
93
+ readonly maxIterations: number
94
+ readonly tokenBudget: number
95
+ readonly timeoutMs: number
96
+ }
97
+ /** Runs per day at most, times the token budget. Absent for a one-shot. */
98
+ readonly dailyTokenCeiling?: number
99
+ /** Provider and model the job is pinned to. */
100
+ readonly model: string
101
+ /** Where the run's credential comes from, in words. */
102
+ readonly credentialSource?: string
103
+ /** Anything the host wants said in the warning colour. */
104
+ readonly warnings: readonly string[]
105
+ }
106
+
107
+ /** A job as the `list` action reports it. */
108
+ export interface ScheduleJobSummary {
109
+ readonly name: string
110
+ readonly folder: string
111
+ readonly state: string
112
+ readonly schedule: string
113
+ readonly nextFireAt?: string
114
+ readonly lastStatus?: string
115
+ /** Present only for jobs in the session's own folder. */
116
+ readonly prompt?: string
117
+ }
118
+
119
+ /** What the person answered to a proposed job. */
120
+ export type ScheduleConfirmAnswer = 'create' | 'create-paused' | 'cancel'
121
+
122
+ export interface ScheduleConfirmRequest {
123
+ readonly preview: ScheduleJobPreview
124
+ /** The prompt tripwire's findings over `preview.prompt`. */
125
+ readonly promptFindings: readonly string[]
126
+ /** Always `model` from this tool: the banner says it was not the operator. */
127
+ readonly proposedBy: 'model'
128
+ }
129
+
130
+ export interface ScheduleToolHost {
131
+ /**
132
+ * The host can store and enforce {@link ScheduleJobDraft.permissions}
133
+ * `.browser`. Absent or false: the tool refuses a draft carrying one,
134
+ * rather than let the host drop it and confirm a job the model believes
135
+ * can use the browser.
136
+ */
137
+ readonly browserGrants?: boolean
138
+ /** Validate the draft and compute what the person will be shown. Throws with a message on a refusal. */
139
+ preview(draft: ScheduleJobDraft): Promise<ScheduleJobPreview>
140
+ /**
141
+ * Ask the person. Anything but `create` or `create-paused` — `cancel`, a
142
+ * thrown error, a closed screen — means no job.
143
+ *
144
+ * `signal` fires when the tool's own confirmation deadline elapses or the
145
+ * turn is aborted; a host that draws a screen closes it then rather than
146
+ * leaving it live after the tool has given up on the answer.
147
+ */
148
+ confirm(request: ScheduleConfirmRequest, signal?: AbortSignal): Promise<ScheduleConfirmAnswer>
149
+ /**
150
+ * Create the job the person confirmed. `note`, when given, is appended to
151
+ * what the model is told, for what the person must still do before the
152
+ * job runs (install a scheduler, say): the model reports the job as set
153
+ * up otherwise.
154
+ */
155
+ create(
156
+ draft: ScheduleJobDraft,
157
+ preview: ScheduleJobPreview,
158
+ options: { readonly paused: boolean },
159
+ ): Promise<{ readonly name: string; readonly note?: string }>
160
+ /** Jobs, the session folder's in full, other folders' without their prompts. */
161
+ list(options: { readonly allFolders: boolean }): Promise<readonly ScheduleJobSummary[]>
162
+ /** A job by name or id prefix, or undefined. */
163
+ find(job: string): Promise<ScheduleJobSummary | undefined>
164
+ /** Ask the person to confirm resuming or deleting a job. See `confirm` on `signal`. */
165
+ confirmAction(
166
+ job: ScheduleJobSummary,
167
+ action: 'resume' | 'delete',
168
+ signal?: AbortSignal,
169
+ ): Promise<boolean>
170
+ pause(job: string): Promise<void>
171
+ resume(job: string): Promise<void>
172
+ delete(job: string): Promise<void>
173
+ }
174
+
175
+ /** A prompt the session re-sends to itself on an interval. */
176
+ export interface SessionLoop {
177
+ readonly id: string
178
+ /** The interval in words. */
179
+ readonly schedule: string
180
+ readonly prompt: string
181
+ readonly createdAt: string
182
+ readonly expiresAt?: string
183
+ readonly lastFiredAt?: string
184
+ readonly createdBy: 'operator' | 'model'
185
+ }
186
+
187
+ export interface SessionLoopHost {
188
+ /** Throws with a message when the interval is not usable or the loop limit is reached. */
189
+ create(request: {
190
+ readonly interval: string
191
+ readonly prompt: string
192
+ readonly createdBy: 'model'
193
+ }): Promise<SessionLoop>
194
+ list(): readonly SessionLoop[]
195
+ /** Stop one loop by id, or every loop with `all`. Returns how many stopped. */
196
+ delete(id: string): Promise<number>
197
+ }
@@ -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
+ }
@@ -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', {