@foldrun/core 0.4.0 → 0.6.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 (170) hide show
  1. package/README.md +7 -4
  2. package/dist/index.d.ts +4 -0
  3. package/dist/index.js +4 -0
  4. package/dist/src/account.d.ts +45 -0
  5. package/dist/src/account.js +130 -0
  6. package/dist/src/api-tools.d.ts +8 -0
  7. package/dist/src/api-tools.js +22 -4
  8. package/dist/src/browser-pod.d.ts +148 -0
  9. package/dist/src/browser-pod.js +333 -0
  10. package/dist/src/catalog.js +2 -1
  11. package/dist/src/clock.d.ts +49 -0
  12. package/dist/src/clock.js +121 -0
  13. package/dist/src/completions.js +17 -8
  14. package/dist/src/confine.d.ts +22 -0
  15. package/dist/src/confine.js +218 -3
  16. package/dist/src/container.d.ts +9 -0
  17. package/dist/src/container.js +115 -5
  18. package/dist/src/context-tools.d.ts +1 -1
  19. package/dist/src/deploy.d.ts +33 -0
  20. package/dist/src/deploy.js +145 -6
  21. package/dist/src/egress.d.ts +7 -0
  22. package/dist/src/egress.js +6 -0
  23. package/dist/src/eol.d.ts +2 -0
  24. package/dist/src/eol.js +7 -0
  25. package/dist/src/evals.d.ts +5 -0
  26. package/dist/src/evals.js +9 -2
  27. package/dist/src/flow-lint.d.ts +8 -0
  28. package/dist/src/flow-lint.js +223 -1
  29. package/dist/src/flow-patterns.d.ts +141 -0
  30. package/dist/src/flow-patterns.js +568 -0
  31. package/dist/src/gitrepo.d.ts +18 -0
  32. package/dist/src/gitrepo.js +49 -0
  33. package/dist/src/host-env.js +5 -0
  34. package/dist/src/kinds.js +1 -1
  35. package/dist/src/language.d.ts +17 -0
  36. package/dist/src/language.js +55 -0
  37. package/dist/src/layout.d.ts +69 -0
  38. package/dist/src/layout.js +200 -0
  39. package/dist/src/library.d.ts +9 -0
  40. package/dist/src/library.js +30 -3
  41. package/dist/src/limits.d.ts +84 -0
  42. package/dist/src/limits.js +287 -0
  43. package/dist/src/locale.d.ts +47 -0
  44. package/dist/src/locale.js +177 -0
  45. package/dist/src/mail.d.ts +88 -0
  46. package/dist/src/mail.js +247 -0
  47. package/dist/src/model-credential.d.ts +14 -0
  48. package/dist/src/model-credential.js +58 -0
  49. package/dist/src/model-probe.d.ts +3 -0
  50. package/dist/src/model-probe.js +1 -0
  51. package/dist/src/notify.d.ts +53 -7
  52. package/dist/src/notify.js +194 -126
  53. package/dist/src/openapi.js +5 -3
  54. package/dist/src/operator.d.ts +201 -0
  55. package/dist/src/operator.js +251 -0
  56. package/dist/src/paths.d.ts +28 -0
  57. package/dist/src/paths.js +55 -0
  58. package/dist/src/platform.d.ts +43 -3
  59. package/dist/src/platform.js +9 -1
  60. package/dist/src/provider-health.js +3 -2
  61. package/dist/src/providers.d.ts +481 -0
  62. package/dist/src/providers.js +1105 -6
  63. package/dist/src/refusal.d.ts +83 -0
  64. package/dist/src/refusal.js +281 -0
  65. package/dist/src/run-container.d.ts +149 -13
  66. package/dist/src/run-container.js +424 -43
  67. package/dist/src/runner.d.ts +177 -9
  68. package/dist/src/runner.js +1146 -154
  69. package/dist/src/runtime.d.ts +52 -0
  70. package/dist/src/runtime.js +493 -58
  71. package/dist/src/script-tools.d.ts +17 -2
  72. package/dist/src/script-tools.js +82 -10
  73. package/dist/src/secret-files.d.ts +6 -5
  74. package/dist/src/secret-files.js +24 -11
  75. package/dist/src/secret-health.d.ts +30 -6
  76. package/dist/src/secret-health.js +42 -20
  77. package/dist/src/secrets.d.ts +19 -0
  78. package/dist/src/secrets.js +155 -86
  79. package/dist/src/starter.d.ts +9 -0
  80. package/dist/src/starter.js +92 -11
  81. package/dist/src/step-exec.d.ts +72 -2
  82. package/dist/src/step-exec.js +431 -37
  83. package/dist/src/step-removal.d.ts +15 -0
  84. package/dist/src/step-removal.js +49 -0
  85. package/dist/src/storage.d.ts +23 -4
  86. package/dist/src/storage.js +125 -23
  87. package/dist/src/store.d.ts +222 -37
  88. package/dist/src/store.js +349 -62
  89. package/dist/src/subagents.d.ts +75 -0
  90. package/dist/src/subagents.js +149 -0
  91. package/dist/src/test-mode.d.ts +17 -2
  92. package/dist/src/test-mode.js +53 -13
  93. package/dist/src/tool-names.d.ts +13 -1
  94. package/dist/src/tool-names.js +38 -11
  95. package/dist/src/tool-programs.d.ts +26 -0
  96. package/dist/src/tool-programs.js +172 -0
  97. package/dist/src/tool-test.js +104 -12
  98. package/dist/src/translator.d.ts +8 -2
  99. package/dist/src/translator.js +69 -23
  100. package/dist/src/trigger-log.d.ts +1 -1
  101. package/dist/src/triggers.d.ts +3 -0
  102. package/dist/src/triggers.js +10 -2
  103. package/dist/src/untrusted-fetch.d.ts +11 -0
  104. package/dist/src/untrusted-fetch.js +77 -0
  105. package/dist/src/web-actions.d.ts +91 -0
  106. package/dist/src/web-actions.js +199 -0
  107. package/dist/src/webhook.d.ts +4 -0
  108. package/dist/src/webhook.js +39 -0
  109. package/index.ts +4 -0
  110. package/package.json +3 -3
  111. package/src/account.ts +149 -0
  112. package/src/api-tools.ts +32 -4
  113. package/src/browser-pod.ts +350 -0
  114. package/src/catalog.ts +2 -1
  115. package/src/clock.ts +149 -0
  116. package/src/completions.ts +17 -8
  117. package/src/confine.ts +218 -3
  118. package/src/container.ts +112 -5
  119. package/src/context-tools.ts +1 -1
  120. package/src/deploy.ts +137 -6
  121. package/src/egress.ts +13 -1
  122. package/src/eol.ts +7 -0
  123. package/src/evals.ts +14 -2
  124. package/src/flow-lint.ts +253 -1
  125. package/src/flow-patterns.ts +597 -0
  126. package/src/gitrepo.ts +48 -0
  127. package/src/host-env.ts +5 -0
  128. package/src/kinds.ts +1 -1
  129. package/src/language.ts +62 -0
  130. package/src/layout.ts +228 -0
  131. package/src/library.ts +37 -3
  132. package/src/limits.ts +290 -0
  133. package/src/locale.ts +217 -0
  134. package/src/mail.ts +318 -0
  135. package/src/model-credential.ts +71 -0
  136. package/src/model-probe.ts +4 -0
  137. package/src/notify.ts +189 -103
  138. package/src/openapi.ts +5 -3
  139. package/src/operator.ts +300 -0
  140. package/src/paths.ts +54 -0
  141. package/src/platform.ts +52 -3
  142. package/src/provider-health.ts +3 -2
  143. package/src/providers.ts +1368 -6
  144. package/src/refusal.ts +320 -0
  145. package/src/run-container.ts +460 -44
  146. package/src/runner.ts +1168 -155
  147. package/src/runtime.ts +487 -55
  148. package/src/script-tools.ts +81 -10
  149. package/src/secret-files.ts +22 -10
  150. package/src/secret-health.ts +64 -19
  151. package/src/secrets.ts +149 -87
  152. package/src/starter.ts +93 -11
  153. package/src/step-exec.ts +447 -38
  154. package/src/step-removal.ts +59 -0
  155. package/src/storage.ts +141 -22
  156. package/src/store.ts +506 -70
  157. package/src/subagents.ts +189 -0
  158. package/src/test-mode.ts +51 -11
  159. package/src/tool-names.ts +40 -11
  160. package/src/tool-programs.ts +179 -0
  161. package/src/tool-test.ts +110 -12
  162. package/src/translator.ts +72 -22
  163. package/src/trigger-log.ts +1 -0
  164. package/src/triggers.ts +10 -2
  165. package/src/untrusted-fetch.ts +88 -0
  166. package/src/web-actions.ts +237 -0
  167. package/src/webhook.ts +41 -0
  168. package/templates/blog-desk/agents/writer/agent.md +1 -1
  169. package/templates/hello/agents/curator/agent.md +4 -4
  170. package/templates/hello/agents/notetaker/agent.md +3 -3
@@ -24,7 +24,197 @@ export interface ProviderPreset {
24
24
  note?: string;
25
25
  /** A tool loop was driven through this endpoint from this runtime. */
26
26
  verified?: boolean;
27
+ /** Whether the endpoint runs a server-side web search, and in whose shape.
28
+ * Absent means it does not — `web.search: <that name>` is then an error a
29
+ * person should see from `check`, not an empty result at 3am. */
30
+ search?: SearchShape;
27
31
  }
32
+ /** Whether this endpoint will execute a server-side web search, and in whose
33
+ * shape. Separate from `format` on purpose: speaking a wire is not the same
34
+ * as running a tool on it. DeepSeek's endpoint is Anthropic-shaped and still
35
+ * has no search at all, which is exactly the mistake this field exists to
36
+ * stop `check` from letting through.
37
+ *
38
+ * "anthropic" executes the `web_search_20250305` server tool as Anthropic
39
+ * defines it, so the runtime grants it and the endpoint answers
40
+ * "plugin" has its own switch rather than a tool (OpenRouter's web
41
+ * plugin / `:online`), fulfilled natively where the underlying
42
+ * model supports it and by Exa everywhere else
43
+ * "openai" the Responses-shaped `web_search` tool
44
+ * "builtin_fn" Moonshot's `$web_search`, a Chat-Completions builtin_function
45
+ * the client has to echo back — billed per successful call
46
+ * undefined no server-side search. Ours is the only way to the web. */
47
+ export type SearchShape = "anthropic" | "plugin" | "openai" | "builtin_fn" | "direct";
48
+ /** A search API the runtime calls itself, with the customer's own key.
49
+ *
50
+ * These are not model providers — none of them serves a model — so they
51
+ * live apart from PROVIDERS. `web.search:` accepts either kind of name, and
52
+ * the difference decides where the search runs: a provider's server-side
53
+ * tool runs on the provider's machines and is off the run record, while a
54
+ * direct API is called from the run's own sandbox through the egress proxy
55
+ * — the key never enters the pod, the call is on the record with its
56
+ * arguments, and it works whichever model is driving. Same switch, better
57
+ * audit trail. Shapes were read from each vendor's own API reference on
58
+ * 2026-09-16; the gallery's web tool carries the
59
+ * matching request and response mapping. */
60
+ export interface SearchApi {
61
+ /** The vendor's API reference the adapter was built from, the day it was
62
+ * last matched to it, and what that page could not confirm. Read by
63
+ * web-actions.ts; a check older than 90 days fails its test. */
64
+ docs?: string;
65
+ checked?: string;
66
+ gaps?: string;
67
+ name: string;
68
+ aliases?: string[];
69
+ title: string;
70
+ /** The one host the key may be sent to — the egress grant is for this. */
71
+ host: string;
72
+ endpoint: string;
73
+ method: "GET" | "POST";
74
+ /** The header the key travels in, and any prefix on the value. */
75
+ auth: {
76
+ header: string;
77
+ prefix?: string;
78
+ };
79
+ /** The vault name a customer stores their key under. */
80
+ secret: string;
81
+ index: string;
82
+ note?: string;
83
+ /** The API answers without a key (at a lower rate). The runner declares
84
+ * the secret only when the vault has it, so a missing key is not an
85
+ * error the way a declared-and-absent secret is. */
86
+ secretOptional?: boolean;
87
+ /** What the value in the vault must be, when it is not the bare key. */
88
+ secretFormat?: string;
89
+ }
90
+ export declare const SEARCH_APIS: readonly SearchApi[];
91
+ /** Names people will try that cannot work, and why — said at `check`
92
+ * rather than discovered at 3am. */
93
+ export declare const REFUSED_WEB: Record<string, string>;
94
+ /** Fetch APIs the runtime calls itself: a URL in, the page out, with the
95
+ * customer's own key. Same seam as the search APIs — `web.fetch: jina` —
96
+ * and the same trade: our own fetch is free and on the record; these are
97
+ * for the failure modes ours cannot cover, chiefly a page that refuses a
98
+ * plain request. Three tiers, priced accordingly: a reader (Jina,
99
+ * Firecrawl) turns a page into clean markdown; a search vendor's extract
100
+ * (Exa, Tavily, Parallel) reads many at once; an unblocker (Zyte, ScrapingBee) renders
101
+ * behind the anti-bot walls a reader cannot pass. */
102
+ export interface FetchApi extends Omit<SearchApi, "index"> {
103
+ tier: "reader" | "extract" | "unblocker";
104
+ /** How many URLs one call may carry. 1 means one call per page. */
105
+ batch: number;
106
+ what: string;
107
+ }
108
+ export declare const FETCH_APIS: readonly FetchApi[];
109
+ /** Remote browsers: a CDP endpoint our web browse connects to instead of
110
+ * the account's pod — the same tool, the same modes and actions, rendered
111
+ * on the vendor's machines. `web.browse: browserbase`. The one capability
112
+ * where the key cannot ride the egress proxy: CDP is a websocket, so the
113
+ * wrapper holds the real value the way it already holds a cookie secret,
114
+ * and creates the session itself where the vendor wants one. */
115
+ /** What a vendor's own session can be asked for, per its docs — the keys of
116
+ * `web.browse.vendor_session:` it accepts. Absent means it has no such option;
117
+ * naming one is an error in `check` that says which vendors do. */
118
+ export interface SessionSupport {
119
+ /** on: a proxy can be switched on; always: it is always on (true is a
120
+ * no-op, false an error); the rest say which parts of a location, and
121
+ * whether a proxy of your own is taken. */
122
+ proxy?: {
123
+ on?: boolean;
124
+ always?: boolean;
125
+ country?: boolean;
126
+ state?: boolean;
127
+ city?: boolean;
128
+ own?: boolean;
129
+ needsCountry?: boolean;
130
+ stateOrCity?: boolean;
131
+ };
132
+ captcha?: boolean;
133
+ stealth?: boolean;
134
+ region?: readonly string[];
135
+ /** The session's life, in seconds: [least, most]. */
136
+ timeout?: readonly [number, number];
137
+ /** Saved logins across runs: the vendor's contexts or profiles. */
138
+ keep?: boolean;
139
+ record?: boolean;
140
+ block?: readonly ("ads" | "trackers" | "cookies")[];
141
+ /** Where `options:` goes, as written: the session request's body, or the
142
+ * connection URL's query. Absent: the vendor takes none. */
143
+ options?: "body" | "query";
144
+ /** web.browse settings this vendor's browser will not take. */
145
+ refuses?: readonly string[];
146
+ }
147
+ /** `web.browse.vendor_session:` — what the vendor's session is asked for.
148
+ * Called `session:` until 2026-10-01, which a call's `session=` (a named
149
+ * saved login) also is; the old key is still read, and check warns. */
150
+ export interface BrowseSession {
151
+ proxy?: boolean | {
152
+ country?: string;
153
+ state?: string;
154
+ city?: string;
155
+ own?: string;
156
+ };
157
+ captcha?: boolean;
158
+ stealth?: boolean;
159
+ region?: string;
160
+ /** seconds */
161
+ timeout?: number;
162
+ keep?: string;
163
+ record?: boolean;
164
+ block?: ("ads" | "trackers" | "cookies")[];
165
+ options?: Record<string, unknown>;
166
+ }
167
+ export interface BrowserApi {
168
+ session?: SessionSupport;
169
+ /** Steps of web browse this browser cannot do, per the vendor's own docs.
170
+ * Kept equal to the tool's VENDORS table by the platform's gallery test. */
171
+ lacks?: string[];
172
+ /** The vendor's API reference the adapter was built from, the day it was
173
+ * last matched to it, and what that page could not confirm. Read by
174
+ * web-actions.ts; a check older than 90 days fails its test. */
175
+ docs?: string;
176
+ checked?: string;
177
+ gaps?: string;
178
+ name: string;
179
+ aliases?: string[];
180
+ title: string;
181
+ /** How a session is reached: a REST call that returns the endpoint, or a
182
+ * websocket URL the key goes straight into. */
183
+ how: "session" | "direct";
184
+ host: string;
185
+ secret: string;
186
+ secretFormat?: string;
187
+ what: string;
188
+ note?: string;
189
+ }
190
+ export declare const BROWSER_APIS: readonly BrowserApi[];
191
+ /** The web's actions, in the order the docs list them. */
192
+ export declare const WEB_ACTIONS: readonly ["search", "fetch", "browse", "crawl", "map", "extract", "answer", "monitor"];
193
+ export type WebAction = (typeof WEB_ACTIONS)[number];
194
+ /** A provider for one of the actions that came after search, fetch and
195
+ * browse. Only what the runtime needs — the vault name of the key and the
196
+ * one host it may go to — plus the docs the adapter in the `web` tool was
197
+ * built from. None has been called live from here: no account holds these
198
+ * keys yet, so each is as good as its docs page. */
199
+ export interface ActionApi {
200
+ name: string;
201
+ title: string;
202
+ host: string;
203
+ secret: string;
204
+ docs: string;
205
+ checked: string;
206
+ gaps?: string;
207
+ }
208
+ export declare const ACTION_APIS: Record<"crawl" | "map" | "extract" | "answer" | "monitor", readonly ActionApi[]>;
209
+ export declare function findActionApi(action: keyof typeof ACTION_APIS, name: string): ActionApi | undefined;
210
+ export declare function findBrowserApi(name: string): BrowserApi | undefined;
211
+ export declare function findFetchApi(name: string): FetchApi | undefined;
212
+ export declare function findSearchApi(name: string): SearchApi | undefined;
213
+ /** Whose index answers, for the ones that will answer at all. Checked against
214
+ * each vendor's own documentation on 2026-09-16. Worth stating out loud
215
+ * because it is the whole reason to prefer a provider's search over ours:
216
+ * you are buying an index we cannot crawl, not a faster endpoint. */
217
+ export declare const SEARCH_INDEX: Record<string, string>;
28
218
  export declare const PROVIDERS: readonly ProviderPreset[];
29
219
  /**
30
220
  * Request fields `params:` may not set.
@@ -45,3 +235,294 @@ export declare function providerPreset(name: unknown): ProviderPreset | null;
45
235
  * without saying `format: openai`? The heuristic the checker uses to say
46
236
  * so before a run fails with a 404 from the wrong path. */
47
237
  export declare function looksOpenAiShaped(baseUrl: string): boolean;
238
+ /** What `web.search: <name>` in an agent's frontmatter resolves to.
239
+ *
240
+ * Unset means ours: the account's own search engine, free, on the run
241
+ * record. Naming a provider buys that provider's index instead — which is
242
+ * the only reason to do it, and the reason the failure modes below are
243
+ * errors rather than a quiet fallback. A desk that silently searched
244
+ * nothing for a month is worse than one that refused to deploy.
245
+ */
246
+ export interface SearchChoice {
247
+ /** null when ours answers. */
248
+ provider: string | null;
249
+ shape?: SearchShape;
250
+ /** Whose index answers, for the run trace and the help page. */
251
+ index?: string;
252
+ /** For a direct API: the vault name of the customer's key, and the one
253
+ * host the egress proxy may fill it in for. */
254
+ secret?: string;
255
+ secretOptional?: boolean;
256
+ host?: string;
257
+ /** Set when the name cannot work; `check` prints it and refuses. */
258
+ error?: string;
259
+ }
260
+ /** `web.search: exa`, or the long form with the customer's own vault name:
261
+ *
262
+ * web:
263
+ * search:
264
+ * name: exa
265
+ * key: ${MY_EXA_KEY}
266
+ *
267
+ * The same two spellings `provider:` takes. `key` is a reference, never a
268
+ * value — a credential written into a markdown file is refused here, the
269
+ * way it is refused everywhere else in foldrun. */
270
+ /**
271
+ * `web.browse:` settings — how the browser presents itself, not what one call
272
+ * does.
273
+ *
274
+ * web:
275
+ * browse:
276
+ * engine: firefox
277
+ * user_agent: "Mozilla/5.0 …"
278
+ *
279
+ * The engine and the user agent belong in the file, not in every call. They
280
+ * travel together: a Cloudflare clearance cookie is bound to the user agent
281
+ * that earned it, so a skill repeating the UA in each call is one edit away
282
+ * from a session that silently stops working (Medium, 2026-09-17).
283
+ *
284
+ * `via:` (or `name:`) names a remote browser vendor, which is what this key
285
+ * meant when it only took a string; that spelling still works. Anything a
286
+ * single call decides — mode, actions, wait_for, block — stays in the call.
287
+ */
288
+ export interface BrowseSettings {
289
+ engine?: "chrome" | "chromium" | "firefox" | "webkit" | "lightpanda" | "obscura";
290
+ user_agent?: string;
291
+ device?: string;
292
+ locale?: string;
293
+ timezone?: string;
294
+ /** The NAME of a vault secret holding this site's sign-in cookies, never
295
+ * the cookies themselves. It sits here beside `user_agent` because the two
296
+ * are one thing: a Cloudflare clearance cookie is bound to the user agent
297
+ * that earned it, and a file that carries one without the other is a
298
+ * session waiting to stop working. */
299
+ cookies?: string;
300
+ cookie_domain?: string;
301
+ /** The NAME of a vault secret holding this site's signed-in Web Storage and
302
+ * IndexedDB, as JSON: `{ localStorage, sessionStorage, indexedDB }`. The
303
+ * sibling of `cookies:` for the sites that keep their login in storage
304
+ * rather than a cookie — Firebase writes a record to IndexedDB, MSAL can
305
+ * keep tokens in sessionStorage, and a cookie jar alone opens those pages
306
+ * signed out. Seeded before the page's own scripts run, never read back. */
307
+ storage?: string;
308
+ storage_origin?: string;
309
+ /** Named bundles of the same settings plus the per-call ones that travel
310
+ * with an identity — proxy, headers, geolocation, permissions,
311
+ * color_scheme, block — chosen on a call as `identity=<name>`. "As an
312
+ * Australian phone" is six settings; a name says it once. Each value is a
313
+ * map of text (or JSON for headers), validated here the way the block is. */
314
+ identities?: Record<string, Record<string, string>>;
315
+ /** false runs the full browser with a window, on a virtual screen where
316
+ * the machine has none. Unset or true is the usual no-window browser. */
317
+ headless?: boolean;
318
+ /** Which build of the engine to run: a Chrome release channel
319
+ * (`stable`/`beta`/`dev`) or a label naming a binary installed in the
320
+ * image (`/opt/browser/<engine>/<version>/`). Unset is the pinned
321
+ * default. Resolved in the tool, which falls back to the default and
322
+ * says so when the named build is not installed. */
323
+ version?: string;
324
+ /** true keeps the browser and its open page alive between this agent's
325
+ * calls in a step, so a multi-step form can be driven one step per call.
326
+ * Chromium-only (chrome/chromium). Unset: each call is a fresh page, with
327
+ * only cookies carried by a session. */
328
+ live?: boolean;
329
+ /** The domains this agent's browser may reach at all — pages, scripts,
330
+ * fetches, sockets. A lock, not a default: a call may narrow it and never
331
+ * widen it, so no page's text can talk the model past it. */
332
+ allowed_domains?: string[];
333
+ /** Actions this agent may never take (eval, download, upload, …), checked
334
+ * before a browser opens. Names from WEB_BROWSE_ACTIONS, plus js. */
335
+ deny?: string[];
336
+ /** true marks the page's words in every reply with a nonce the page cannot
337
+ * know, so text written to look like instructions stays the page's. */
338
+ boundaries?: boolean;
339
+ /** Workspace paths of scripts run in every page before its own. */
340
+ init?: string[];
341
+ /** Workspace paths of unpacked Chrome extensions to load (Chromium, in the step). */
342
+ extensions?: string[];
343
+ /** true gives pages WebGPU, on a software GPU where there is none. */
344
+ webgpu?: boolean;
345
+ /** true opens pages whose certificate does not check out (staging, self-signed). */
346
+ ignore_https_errors?: boolean;
347
+ /** The NAME of a vault secret whose value encrypts state= logins at rest. */
348
+ state_key?: string;
349
+ /** true records every call, so the run page always has the recording to play. */
350
+ video?: boolean;
351
+ /** false stops the tool streaming its page to the run page while the step
352
+ * runs. Unset is on wherever the platform can take the frames. */
353
+ live_view?: boolean;
354
+ /** What the vendor's own session is asked for (only with a vendor).
355
+ * Written `vendor_session:`; `session:` is the deprecated spelling. */
356
+ vendor_session?: BrowseSession;
357
+ }
358
+ /** Every action web browse takes, which is what `deny:` may name. The
359
+ * gallery test holds this equal to the tool's own list, so a new action
360
+ * cannot be undeniable, and a misspelt denial — which would deny nothing —
361
+ * is refused at check time instead. */
362
+ export declare const WEB_BROWSE_ACTIONS: readonly ["click", "dblclick", "rightclick", "hover", "check", "uncheck", "drag", "fill", "type", "press", "select", "wait", "goto", "screenshot", "pdf", "scroll", "paste", "upload", "download", "extract", "frame", "tab", "back", "forward", "reload", "focus", "clear", "highlight", "keyboard", "keydown", "keyup", "mouse", "wheel", "tap", "swipe", "clipboard", "eval", "get", "expect", "if", "mock", "unmock", "offline", "cookie", "localstorage", "sessionstorage", "viewport", "dialog", "solve", "pushstate", "insert", "webmcp", "login"];
363
+ export declare const BROWSE_ENGINES: readonly ["chromium", "chrome", "firefox", "safari", "lightpanda", "obscura"];
364
+ /**
365
+ * What each engine cannot do, and the words a refusal uses. ONE table: the
366
+ * tool (web/browse.mjs in the platform gallery) carries a copy that refuses a
367
+ * call, and `foldrun check` reads this one to refuse a `web.browse:` block
368
+ * that pairs an engine with something it cannot do — so the mistake is found
369
+ * at check time, not at 3am. The gallery test holds the tool's copy equal to
370
+ * this one, so neither can learn an option the other does not.
371
+ *
372
+ * An entry is what the tool prints for the option: a call argument (video,
373
+ * trace, block…), `mode=<mode>`, or `headless: false`. A refusal reads
374
+ * `<entry> <why>`. chromium and chrome can do all of it, so they have no row.
375
+ */
376
+ export declare const BROWSE_ENGINE_LIMITS: Record<string, {
377
+ why: string;
378
+ cannot: readonly string[];
379
+ }>;
380
+ /** The refusal for one option on one engine, or null when the engine can. */
381
+ export declare function browseEngineRefusal(engine: string, what: string): string | null;
382
+ /** The shape of `web.browse.vendor_session:`, before anyone knows the vendor. What
383
+ * the vendor can do is browseSessionProblems' question. */
384
+ export declare function readBrowseSession(raw: unknown, key?: "vendor_session" | "session"): {
385
+ session?: BrowseSession;
386
+ error?: string;
387
+ };
388
+ /**
389
+ * Everything wrong with a session block for this vendor: a setting the
390
+ * vendor has no option for (naming the vendors that do), a value outside its
391
+ * range, and the browse settings its browser will not take. The rule the
392
+ * actions follow — the file is fixed, nothing is quietly dropped.
393
+ */
394
+ export declare function browseSessionProblems(settings: BrowseSettings, vendor: string | null): string[];
395
+ export declare function readBrowseSettings(raw: unknown): {
396
+ settings: BrowseSettings;
397
+ rest: unknown;
398
+ error?: string;
399
+ };
400
+ /**
401
+ * `web.search:` settings — what the account's own engine (SearXNG) is asked,
402
+ * not who answers.
403
+ *
404
+ * web:
405
+ * search:
406
+ * engines: [bing, google cse, duckduckgo]
407
+ * categories: [general, news]
408
+ * safesearch: moderate
409
+ *
410
+ * Every SearXNG request option that changes what comes back has a key here;
411
+ * the ones that only change its HTML page (theme, results_on_new_tab,
412
+ * image_proxy, url_formatting) do not, because in JSON they do nothing and a
413
+ * key that does nothing is a question someone asks later. Engine and
414
+ * category names are checked for shape here and against the running
415
+ * instance by the tool (its /config), because the instance decides which of
416
+ * SearXNG's engines are loaded. Plugin ids are a short list that changes
417
+ * rarely, so a misspelt one — which would enable nothing — is refused here.
418
+ *
419
+ * The settings speak SearXNG, so they are refused beside `name:` — with
420
+ * `web.search: exa` they would be silently ignored.
421
+ */
422
+ export interface SearchSettings {
423
+ /** Ask only these engines. A default: a call's `engines=` or
424
+ * `categories=` replaces it, and it beats `categories` beside it — the
425
+ * tool never sends SearXNG both, which would add the categories' engines
426
+ * to the list. */
427
+ engines?: string[];
428
+ /** Never ask these, whatever the call says. A lock, like
429
+ * web.browse.allowed_domains: a call may not widen it. */
430
+ exclude_engines?: string[];
431
+ /** The SearXNG categories to search (general, news, science, it, …),
432
+ * when no engine list is in force. A default a call replaces. */
433
+ categories?: string[];
434
+ /** 0 off, 1 moderate, 2 strict. */
435
+ safesearch?: 0 | 1 | 2;
436
+ /** The default recency when a call does not say. */
437
+ time_range?: "day" | "week" | "month" | "year";
438
+ /** Seconds SearXNG waits for its engines before answering with what it has. */
439
+ timeout?: number;
440
+ /** SearXNG plugins to switch on, and off, for this agent's searches. */
441
+ plugins?: string[];
442
+ exclude_plugins?: string[];
443
+ /** Where the open-access DOI rewrite plugin points a paper's link. */
444
+ doi_resolver?: string;
445
+ }
446
+ /** SearXNG's plugin ids (searx/plugins/*.py, 2026-09-29). The gallery test
447
+ * keeps this equal to the list the tool documents. */
448
+ export declare const WEB_SEARCH_PLUGINS: readonly ["ahmia_filter", "calculator", "hash_plugin", "hostnames", "infinite_scroll", "oa_doi_rewrite", "self_info", "time_zone", "tor_check", "tracker_url_remover", "unit_converter"];
449
+ /** The settings in a `web.search:` block, and the provider part with them
450
+ * removed — the shape readBrowseSettings has, for the same reason. */
451
+ export declare function readSearchSettings(raw: unknown): {
452
+ settings: SearchSettings;
453
+ rest: unknown;
454
+ error?: string;
455
+ };
456
+ /** The `web.search:` block's settings as the env the tool reads. Only what
457
+ * was said travels — an unset key is SearXNG's own default. */
458
+ export declare function searchSettingsEnv(s: SearchSettings): Record<string, string>;
459
+ export declare function resolveSearch(name: unknown, kind?: "search" | "fetch" | "browse"): SearchChoice;
460
+ /** A `web.browse:` block that pairs an engine with something it cannot do,
461
+ * in the tool's own words (BROWSE_ENGINE_LIMITS). The block's engine is
462
+ * checked against the block's settings; each identity is checked with its
463
+ * own engine (or the block's) against the block's settings and its own,
464
+ * because a call that picks the identity gets both. What only a call can
465
+ * ask for (a mode, a trace, mocks) is the tool's to refuse. */
466
+ export declare function browseEngineProblems(settings: BrowseSettings): string[];
467
+ /** Every `web:` value in a frontmatter that cannot work, each as the
468
+ * sentence resolveSearch gives. Computed once in core so `check`, the
469
+ * deploy gate and the run all refuse the same thing in the same words —
470
+ * the timezone rule's shape. Empty when all are unset or answerable. */
471
+ export declare function webProblems(front: Record<string, unknown>): string[];
472
+ /**
473
+ * The engines the runner image was built with, as the deploy wrote them into
474
+ * FOLDRUN_RUNNER_ENGINES (foldrun-infra dev/ci/runner-engines.sh): a comma
475
+ * list of names, each with `=<version>` where the image's manifest
476
+ * (/opt/browser/engines.json) has a dotted version —
477
+ * `chromium=153.0.8010.12,chrome=154.0.8037.58,firefox,lightpanda=0.4.1`.
478
+ * Null when unset.
479
+ */
480
+ export declare function runnerEngines(raw?: string | undefined): Record<string, string | null> | null;
481
+ /** The Chrome major a user-agent string claims — `Chrome/153.0.0.0` → 153 —
482
+ * or null for one that names no Chrome (Firefox, Safari, an iPhone). */
483
+ export declare function chromeMajorOf(ua: string): number | null;
484
+ /**
485
+ * A pinned `user_agent:` that claims another Chrome than the one that will
486
+ * send it. The tool's own default follows the installed Chrome (its
487
+ * chromiumVersion()), so only a pinned string can drift — and it drifts
488
+ * every time the image's Chrome moves (stable is unpinned, a new major every
489
+ * four weeks). A site then sees `Chrome/153` in the User-Agent header beside
490
+ * `"Google Chrome";v="154"` in the client hints the browser still sends, and
491
+ * a JavaScript engine that is 154's: the mismatch bot checks look for.
492
+ * `chrome` is the image's version of that browser (runnerEngines().chrome,
493
+ * or .chromium for engine chromium); null — an image that did not say —
494
+ * warns about nothing.
495
+ */
496
+ export declare function userAgentDrift(ua: string | undefined, chrome: string | null | undefined, where: string, browser?: string): string | null;
497
+ /**
498
+ * What in a `web:` block works today but should change — never a refusal.
499
+ * `check` prints these as warnings and the deploy reports them beside its
500
+ * outward-gate warnings. `opts.chrome` / `opts.chromium`: the runner image's
501
+ * Google Chrome and Playwright Chromium versions, when known
502
+ * (FOLDRUN_RUNNER_ENGINES on the platform, /api/version from the CLI); an
503
+ * engine whose version is unknown is not compared.
504
+ */
505
+ export declare function webWarnings(front: Record<string, unknown>, opts?: {
506
+ chrome?: string | null;
507
+ chromium?: string | null;
508
+ }): string[];
509
+ /**
510
+ * The agent's `web:` block, read once for everyone — the runner, `check`
511
+ * and the deploy gate:
512
+ *
513
+ * tools: [web]
514
+ * web:
515
+ * actions: [search, fetch, crawl] # what it may do; all when absent
516
+ * search: brave # who does each; absent is foldrun
517
+ * browse: { engine: firefox } # search and browse also take their settings
518
+ *
519
+ * `raw` is each action's value as written. Browse alone cascades: the
520
+ * workspace's (or account's) `web: {browse: …}` applies when the agent's
521
+ * says nothing. A per-action key outside the block (`web_search:`) is not
522
+ * read; it is a problem naming the block.
523
+ */
524
+ export declare function webConfig(front: Record<string, unknown>, workspaceFront?: Record<string, unknown>): {
525
+ actions: WebAction[] | null;
526
+ raw: Record<WebAction, unknown>;
527
+ problems: string[];
528
+ };