agents 0.15.0 → 0.16.1

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 (53) hide show
  1. package/dist/{agent-tool-types-VPsjVYL0.d.ts → agent-tool-types-NofdbL9X.d.ts} +57 -4
  2. package/dist/agent-tool-types.d.ts +1 -1
  3. package/dist/{agent-tools-BGpgfpJT.d.ts → agent-tools-DLquv-dp.d.ts} +2 -2
  4. package/dist/agent-tools.d.ts +1 -1
  5. package/dist/browser/ai.d.ts +211 -7
  6. package/dist/browser/ai.js +236 -25
  7. package/dist/browser/ai.js.map +1 -1
  8. package/dist/browser/index.d.ts +133 -69
  9. package/dist/browser/index.js +3 -2
  10. package/dist/browser/tanstack-ai.d.ts +17 -7
  11. package/dist/browser/tanstack-ai.js +25 -19
  12. package/dist/browser/tanstack-ai.js.map +1 -1
  13. package/dist/chat/index.d.ts +111 -5
  14. package/dist/chat/index.js +207 -35
  15. package/dist/chat/index.js.map +1 -1
  16. package/dist/chat-sdk/index.d.ts +1 -1
  17. package/dist/{classPrivateFieldGet2-Beqsfu2Z.js → classPrivateFieldGet2-CZ7QjTXN.js} +5 -5
  18. package/dist/{classPrivateMethodInitSpec-B5ko1s2R.js → classPrivateMethodInitSpec-D-0__zd9.js} +2 -2
  19. package/dist/client.d.ts +19 -2
  20. package/dist/client.js +31 -11
  21. package/dist/client.js.map +1 -1
  22. package/dist/{compaction-helpers-BEUILPss.d.ts → compaction-helpers-DVcu5lPN.d.ts} +91 -12
  23. package/dist/connector-CrKhowfD.js +1291 -0
  24. package/dist/connector-CrKhowfD.js.map +1 -0
  25. package/dist/connector-v2M1zlZp.d.ts +659 -0
  26. package/dist/experimental/memory/session/index.d.ts +75 -12
  27. package/dist/experimental/memory/session/index.js +226 -21
  28. package/dist/experimental/memory/session/index.js.map +1 -1
  29. package/dist/experimental/memory/utils/index.d.ts +2 -2
  30. package/dist/{index-CPe1OtI0.d.ts → index-B7IbEeze.d.ts} +32 -1
  31. package/dist/index.d.ts +8 -2
  32. package/dist/index.js +119 -45
  33. package/dist/index.js.map +1 -1
  34. package/dist/mcp/client.d.ts +1 -1
  35. package/dist/mcp/index.d.ts +1 -1
  36. package/dist/mcp/index.js +1 -1
  37. package/dist/mcp/index.js.map +1 -1
  38. package/dist/observability/index.d.ts +1 -1
  39. package/dist/react.d.ts +12 -1
  40. package/dist/react.js +101 -30
  41. package/dist/react.js.map +1 -1
  42. package/dist/{retries-CF_HKSlJ.d.ts → retries-CwlpAGet.d.ts} +35 -5
  43. package/dist/retries.d.ts +9 -5
  44. package/dist/retries.js +87 -1
  45. package/dist/retries.js.map +1 -1
  46. package/dist/serializable.d.ts +1 -1
  47. package/dist/skills/index.js +2 -2
  48. package/dist/sub-routing.d.ts +1 -1
  49. package/dist/workflows.d.ts +1 -1
  50. package/package.json +11 -11
  51. package/dist/shared-4CAYLCTO.d.ts +0 -34
  52. package/dist/shared-wyII629d.js +0 -432
  53. package/dist/shared-wyII629d.js.map +0 -1
@@ -1,10 +1,10 @@
1
1
  import { n as AgentEmail } from "./internal_context-Dg4Cgjcu.js";
2
- import { t as RetryOptions } from "./retries-CF_HKSlJ.js";
2
+ import { t as RetryOptions } from "./retries-CwlpAGet.js";
3
3
  import {
4
4
  n as Observability,
5
5
  r as ObservabilityEvent,
6
6
  s as MCPObservabilityEvent
7
- } from "./index-CPe1OtI0.js";
7
+ } from "./index-B7IbEeze.js";
8
8
  import { t as AgentMcpOAuthProvider } from "./do-oauth-client-provider-D4ZwyBDu.js";
9
9
  import {
10
10
  _ as WorkflowPage,
@@ -2343,7 +2343,15 @@ interface AgentStaticOptions {
2343
2343
  /**
2344
2344
  * Maximum age in milliseconds of an unmanaged interrupted-fiber row before
2345
2345
  * recovery stops retrying a repeatedly-throwing `onFiberRecovered()` hook
2346
- * and discards the row. Set to `0` to retain rows indefinitely.
2346
+ * and discards the row (emitting `fiber:recovery:skipped` with reason
2347
+ * `max_age_exceeded`). Defaults to 24h.
2348
+ *
2349
+ * Set to `0` to retain rows indefinitely. NOTE: with `0`, a hook that keeps
2350
+ * throwing is retried forever — the recovery alarm backs off exponentially
2351
+ * (capped at 5 minutes) so it is not a busy-loop, but the Durable Object
2352
+ * stays warm (never idle-evicts) for as long as the un-recoverable row
2353
+ * exists. Prefer a finite age unless you intend to inspect/clear such rows
2354
+ * yourself.
2347
2355
  */
2348
2356
  fiberRecoveryMaxAgeMs?: number;
2349
2357
  /**
@@ -2467,6 +2475,13 @@ declare class Agent<
2467
2475
  private _managedFiberTerminalWaiters;
2468
2476
  /** @internal Prevents re-entrant recovery from overlapping alarm ticks. */
2469
2477
  private _runFiberRecoveryInProgress;
2478
+ /**
2479
+ * @internal Consecutive runFiber-recovery scans that made NO forward progress
2480
+ * while work was still pending. Drives the exponential backoff of the
2481
+ * recovery follow-up alarm so a repeatedly-throwing recovery hook does not
2482
+ * busy-loop the DO. Reset to 0 whenever a scan recovers anything.
2483
+ */
2484
+ private _recoveryNoProgressScans;
2470
2485
  /** @internal Single-flight background recovery for parent agent-tool rows. */
2471
2486
  private _agentToolRunRecoveryPromise;
2472
2487
  private _ParentClass;
@@ -3296,6 +3311,18 @@ declare class Agent<
3296
3311
  targetPath: ReadonlyArray<AgentPathStep>
3297
3312
  ): Promise<void>;
3298
3313
  private _executeScheduleCallback;
3314
+ /**
3315
+ * Whether any runFiber recovery work is still outstanding: orphaned
3316
+ * `cf_agents_runs` rows left by a dead process (excluding fibers currently
3317
+ * executing in memory, which already hold a keepAlive ref) or managed
3318
+ * ledger fibers stuck in a non-terminal state with no live run row.
3319
+ *
3320
+ * Used by `_scheduleNextAlarm` to arm a follow-up alarm so multi-pass
3321
+ * recovery (e.g. after a scan-deadline yield, or while retrying a throwing
3322
+ * recovery hook) resumes instead of starving.
3323
+ * @internal
3324
+ */
3325
+ private _hasPendingFiberRecovery;
3299
3326
  private _scheduleNextAlarm;
3300
3327
  /**
3301
3328
  * Override PartyServer's onAlarm hook as a no-op.
@@ -3861,6 +3888,32 @@ declare class Agent<
3861
3888
  * callers should treat it as fire-and-forget.
3862
3889
  */
3863
3890
  destroy(): Promise<void>;
3891
+ /**
3892
+ * @internal Defer this agent's destruction to its own alarm invocation
3893
+ * instead of running it inline (#1625).
3894
+ *
3895
+ * `destroy()` is a multi-step I/O sequence (drop tables, delete alarm,
3896
+ * delete all storage, dispose connections). Running it on the `waitUntil`
3897
+ * of a request whose client has already disconnected — the MCP
3898
+ * Streamable-HTTP session-DELETE path — gives it little to no
3899
+ * post-invocation grace, so the runtime routinely cancels it mid-flight.
3900
+ * This method instead performs two fast storage writes (a durable
3901
+ * "condemned" marker and an immediate alarm) that the caller can await
3902
+ * before responding; the alarm then fires as a fresh invocation with its
3903
+ * own full execution budget and runs `destroy()` there. If even that
3904
+ * invocation is interrupted, the marker survives and the next wake
3905
+ * finishes teardown — see the `alarm()` preamble.
3906
+ *
3907
+ * Unlike `destroy()`, this method does not abort the isolate, so RPC
3908
+ * callers don't need to swallow an abort error.
3909
+ */
3910
+ _cf_scheduleDestroy(): Promise<void>;
3911
+ /**
3912
+ * Whether a (deferred or interrupted) destroy is pending. Reads the
3913
+ * durable marker directly — the in-memory `_isFacet` flag may not be
3914
+ * hydrated yet at the call sites, but facets never write the marker.
3915
+ */
3916
+ private _hasPendingDestroy;
3864
3917
  /** @internal Drop every internal Agents SDK table during top-level destroy. */
3865
3918
  protected _dropInternalTablesForDestroy(): void;
3866
3919
  /**
@@ -4794,4 +4847,4 @@ export {
4794
4847
  MCPServer as z,
4795
4848
  WorkerTransport as zt
4796
4849
  };
4797
- //# sourceMappingURL=agent-tool-types-VPsjVYL0.d.ts.map
4850
+ //# sourceMappingURL=agent-tool-types-NofdbL9X.d.ts.map
@@ -16,7 +16,7 @@ import {
16
16
  s as AgentToolInterruptedReason,
17
17
  t as AgentToolChildAdapter,
18
18
  u as AgentToolRunInspection
19
- } from "./agent-tool-types-VPsjVYL0.js";
19
+ } from "./agent-tool-types-NofdbL9X.js";
20
20
  export {
21
21
  AgentToolChildAdapter,
22
22
  AgentToolDisplayMetadata,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  a as AgentToolEventState,
3
3
  i as AgentToolEventMessage
4
- } from "./agent-tool-types-VPsjVYL0.js";
4
+ } from "./agent-tool-types-NofdbL9X.js";
5
5
 
6
6
  //#region src/chat/agent-tools.d.ts
7
7
  declare function createAgentToolEventState(): AgentToolEventState;
@@ -11,4 +11,4 @@ declare function applyAgentToolEvent(
11
11
  ): AgentToolEventState;
12
12
  //#endregion
13
13
  export { createAgentToolEventState as n, applyAgentToolEvent as t };
14
- //# sourceMappingURL=agent-tools-BGpgfpJT.d.ts.map
14
+ //# sourceMappingURL=agent-tools-DLquv-dp.d.ts.map
@@ -16,7 +16,7 @@ import {
16
16
  s as AgentToolInterruptedReason,
17
17
  t as AgentToolChildAdapter,
18
18
  u as AgentToolRunInspection
19
- } from "./agent-tool-types-VPsjVYL0.js";
19
+ } from "./agent-tool-types-NofdbL9X.js";
20
20
  import { Tool } from "ai";
21
21
 
22
22
  //#region src/agent-tools.d.ts
@@ -1,21 +1,159 @@
1
- import { t as BrowserToolsOptions } from "../shared-4CAYLCTO.js";
1
+ import {
2
+ I as BrowserBinding,
3
+ f as BrowserSessionStore,
4
+ r as BrowserConnectorSessionOptions,
5
+ t as BrowserConnector,
6
+ v as QuickActionBinding,
7
+ y as QuickActionCommonOptions
8
+ } from "../connector-v2M1zlZp.js";
2
9
  import { ToolSet } from "ai";
10
+ import { CodemodeRuntimeHandle } from "@cloudflare/codemode";
3
11
 
4
12
  //#region src/browser/ai.d.ts
13
+ interface CreateBrowserToolsOptions {
14
+ /**
15
+ * Durable Object state. The codemode runtime that backs the browser tool
16
+ * lives in a facet of this DO, and browser session ids are stored in its
17
+ * storage — so the tool must be created from inside a Durable Object
18
+ * (e.g. an Agent).
19
+ *
20
+ * Optional: when omitted, it is resolved from the current Agent via
21
+ * `getCurrentAgent()`, so inside an Agent method you can just pass `browser`
22
+ * and `loader`. Pass it explicitly outside an Agent context.
23
+ *
24
+ * The worker must export the `CodemodeRuntime` class (the
25
+ * `@cloudflare/codemode/vite` plugin does this automatically, or add
26
+ * `export { CodemodeRuntime } from "@cloudflare/codemode"` to your entry).
27
+ */
28
+ ctx?: DurableObjectState;
29
+ /**
30
+ * WorkerLoader binding for sandboxed code execution.
31
+ *
32
+ * Requires `"worker_loaders": [{ "binding": "LOADER" }]` in wrangler.jsonc.
33
+ */
34
+ loader: WorkerLoader;
35
+ /**
36
+ * Browser Rendering binding (Fetcher).
37
+ *
38
+ * This is the primary way to connect — works both locally in
39
+ * `wrangler dev` and when deployed to Cloudflare Workers.
40
+ *
41
+ * Requires `"browser": { "binding": "BROWSER" }` in wrangler.jsonc.
42
+ */
43
+ browser?: BrowserBinding;
44
+ /**
45
+ * Optional CDP base URL override (e.g. `http://localhost:9222`).
46
+ *
47
+ * Use when connecting to a manually managed Chrome instance or
48
+ * a remote CDP endpoint behind a tunnel.
49
+ */
50
+ cdpUrl?: string;
51
+ /**
52
+ * Headers to send with CDP URL discovery requests.
53
+ * Useful when the CDP endpoint requires authentication
54
+ * (e.g. Cloudflare Access headers).
55
+ */
56
+ cdpHeaders?: Record<string, string>;
57
+ /**
58
+ * Browser session lifecycle (binding-backed only). Defaults to one fresh
59
+ * session per codemode execution (`one-shot`).
60
+ */
61
+ session?: BrowserConnectorSessionOptions;
62
+ /**
63
+ * Durable store for Browser Run session ids. Defaults to a
64
+ * {@link DurableBrowserSessionStore} over `ctx.storage`.
65
+ */
66
+ store?: BrowserSessionStore;
67
+ /**
68
+ * Sandbox execution timeout in milliseconds. Defaults to 30000 (30s).
69
+ * Also used as the per-CDP-command timeout.
70
+ */
71
+ timeout?: number;
72
+ /**
73
+ * Codemode runtime name — the durable identity of the tool's executions
74
+ * and snippets. Defaults to `"browser"`.
75
+ */
76
+ name?: string;
77
+ /**
78
+ * Also expose stateless {@link createQuickActionTools | Quick Action} tools
79
+ * (`browser_markdown`, `browser_extract`, …) alongside the durable
80
+ * `browser_execute` tool.
81
+ *
82
+ * Enabled by default whenever a Browser Run `browser` binding is available
83
+ * (they share it). Pass an object to configure them, or `false` to disable.
84
+ * The Quick Action binding defaults to `browser`; override it via
85
+ * `quickActions.browser`. When only `cdpUrl` is set (no binding), the
86
+ * defaults are skipped silently — pass `quickActions: { browser }` to force
87
+ * them.
88
+ */
89
+ quickActions?:
90
+ | boolean
91
+ | {
92
+ browser?: QuickActionBinding;
93
+ actions?: QuickActionToolName[];
94
+ maxChars?: number;
95
+ options?: QuickActionCommonOptions;
96
+ };
97
+ }
98
+ /**
99
+ * The browser tool's moving parts, for hosts that need more than the tools:
100
+ *
101
+ * - `runtime` — the codemode runtime handle (approve/reject paused runs,
102
+ * `expirePaused`, audit via `executions()`, snippets).
103
+ * - `connector` — host-side session helpers: `sessionInfo()`,
104
+ * `closeSession()`, and `sweep()` for a recurring cleanup task.
105
+ * - `tools` — what `createBrowserTools` returns.
106
+ */
107
+ interface BrowserRuntime {
108
+ runtime: CodemodeRuntimeHandle;
109
+ connector: BrowserConnector;
110
+ tools: ToolSet;
111
+ }
112
+ /**
113
+ * Create the browser codemode runtime: the `browser_execute` tool plus the
114
+ * runtime handle and connector for host-side wiring (approvals, session info,
115
+ * sweeps).
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * export class MyAgent extends Agent<Env> {
120
+ * get browser() {
121
+ * return createBrowserRuntime({
122
+ * ctx: this.ctx,
123
+ * browser: this.env.BROWSER,
124
+ * loader: this.env.LOADER,
125
+ * session: { mode: "dynamic" }
126
+ * });
127
+ * }
128
+ *
129
+ * @callable()
130
+ * async closeBrowserSession() {
131
+ * await this.browser.connector.closeSession();
132
+ * }
133
+ * }
134
+ * ```
135
+ */
136
+ declare function createBrowserRuntime(
137
+ options: CreateBrowserToolsOptions
138
+ ): BrowserRuntime;
5
139
  /**
6
140
  * Create AI SDK tools for browser automation via CDP code mode.
7
141
  *
8
- * Returns a `ToolSet` with `search` (query the CDP spec) and
9
- * `execute` (run CDP commands against a live browser).
142
+ * Returns a `ToolSet` with a single durable `browser_execute` tool backed by
143
+ * a codemode runtime: the model writes TypeScript against the `cdp` connector
144
+ * (`cdp.send`, `cdp.attachToTarget`, `cdp.spec`, …), executions are recorded
145
+ * for abort-and-replay, and browser sessions survive pauses.
10
146
  *
11
147
  * @example
12
148
  * ```ts
13
149
  * import { createBrowserTools } from "agents/browser/ai";
14
150
  * import { generateText } from "ai";
15
151
  *
152
+ * // inside a Durable Object / Agent:
16
153
  * const browserTools = createBrowserTools({
17
- * browser: env.BROWSER,
18
- * loader: env.LOADER,
154
+ * ctx: this.ctx,
155
+ * browser: this.env.BROWSER,
156
+ * loader: this.env.LOADER,
19
157
  * });
20
158
  *
21
159
  * const result = await generateText({
@@ -25,7 +163,73 @@ import { ToolSet } from "ai";
25
163
  * });
26
164
  * ```
27
165
  */
28
- declare function createBrowserTools(options: BrowserToolsOptions): ToolSet;
166
+ declare function createBrowserTools(
167
+ options: CreateBrowserToolsOptions
168
+ ): ToolSet;
169
+ /** A Quick Action exposed as an AI SDK tool. */
170
+ type QuickActionToolName =
171
+ | "markdown"
172
+ | "extract"
173
+ | "links"
174
+ | "scrape"
175
+ | "content";
176
+ interface CreateQuickActionToolsOptions {
177
+ /**
178
+ * Browser Run binding with Quick Actions support (`env.BROWSER`). Requires a
179
+ * Worker `compatibility_date` of `2026-03-24`+ and `remote: true` for local
180
+ * `wrangler dev`.
181
+ */
182
+ browser: QuickActionBinding;
183
+ /**
184
+ * Which tools to expose. Defaults to the text-returning, model-friendly set
185
+ * (`markdown`, `extract`, `links`, `scrape`). `content` (raw HTML) is opt-in
186
+ * since it is large and rarely what a model wants.
187
+ */
188
+ actions?: QuickActionToolName[];
189
+ /**
190
+ * Bound every result to roughly this many characters before returning it to
191
+ * the model, to protect the context window, preserving each result's shape:
192
+ * text (markdown/content) is truncated to a string, oversized arrays
193
+ * (links/scrape) are trimmed but stay arrays, and only an opaque oversized
194
+ * object degrades to a truncated-preview summary. Set to `0` to disable.
195
+ * Defaults to 50000.
196
+ */
197
+ maxChars?: number;
198
+ /**
199
+ * Common Browser Run options merged into every request — e.g. `cookies`,
200
+ * `authenticate`, or `setExtraHTTPHeaders` for authenticated pages, and
201
+ * `gotoOptions` / `viewport` for JavaScript-heavy pages. The model only ever
202
+ * supplies the page (`url`/`html`) and action-specific fields; these
203
+ * host-supplied options are never exposed to it.
204
+ */
205
+ options?: QuickActionCommonOptions;
206
+ }
207
+ /**
208
+ * Create AI SDK tools for Browser Run [Quick Actions](https://developers.cloudflare.com/browser-run/quick-actions/):
209
+ * stateless one-shot browsing (read a page as Markdown, extract structured
210
+ * data with AI, list links, scrape elements). Unlike `createBrowserTools`,
211
+ * these need only the `browser` binding — no Durable Object, loader, or
212
+ * sandbox — so they work from any Worker.
213
+ *
214
+ * @example
215
+ * ```ts
216
+ * import { createQuickActionTools } from "agents/browser/ai";
217
+ *
218
+ * const tools = createQuickActionTools({ browser: this.env.BROWSER });
219
+ * const result = await generateText({ model, tools, messages });
220
+ * ```
221
+ */
222
+ declare function createQuickActionTools(
223
+ options: CreateQuickActionToolsOptions
224
+ ): ToolSet;
29
225
  //#endregion
30
- export { type BrowserToolsOptions, createBrowserTools };
226
+ export {
227
+ BrowserRuntime,
228
+ CreateBrowserToolsOptions,
229
+ CreateQuickActionToolsOptions,
230
+ QuickActionToolName,
231
+ createBrowserRuntime,
232
+ createBrowserTools,
233
+ createQuickActionTools
234
+ };
31
235
  //# sourceMappingURL=ai.d.ts.map
@@ -1,21 +1,117 @@
1
- import { n as SEARCH_DESCRIPTION, r as createBrowserToolHandlers, t as EXECUTE_DESCRIPTION } from "../shared-wyII629d.js";
1
+ import { __DO_NOT_USE_WILL_BREAK__agentContext } from "../internal_context.js";
2
+ import { c as browserLinks, d as browserScrape, i as DurableBrowserSessionStore, l as browserMarkdown, o as browserContent, s as browserExtract, t as BrowserConnector } from "../connector-CrKhowfD.js";
2
3
  import { tool } from "ai";
3
4
  import { z } from "zod";
5
+ import { DynamicWorkerExecutor, createCodemodeRuntime, truncateResult } from "@cloudflare/codemode";
4
6
  //#region src/browser/ai.ts
7
+ let didWarnExperimental = false;
8
+ let didDebugQuickActionSkip = false;
9
+ /**
10
+ * The Durable Object state to build the runtime in: the explicit `ctx` if
11
+ * given, otherwise the current Agent's `ctx` (via `getCurrentAgent()`), so
12
+ * `createBrowserRuntime` can be called from an Agent method without threading
13
+ * `this.ctx` through.
14
+ */
15
+ function resolveCtx(options) {
16
+ if (options.ctx) return options.ctx;
17
+ return (__DO_NOT_USE_WILL_BREAK__agentContext.getStore()?.agent)?.ctx;
18
+ }
19
+ function connectorOptions(options, ctx) {
20
+ if (options.cdpUrl) return {
21
+ cdpUrl: options.cdpUrl,
22
+ cdpHeaders: options.cdpHeaders,
23
+ timeout: options.timeout
24
+ };
25
+ if (!options.browser) throw new Error("Either 'browser' (Fetcher binding) or 'cdpUrl' must be provided");
26
+ return {
27
+ browser: options.browser,
28
+ store: options.store ?? new DurableBrowserSessionStore(ctx.storage),
29
+ session: options.session,
30
+ timeout: options.timeout
31
+ };
32
+ }
33
+ /**
34
+ * Create the browser codemode runtime: the `browser_execute` tool plus the
35
+ * runtime handle and connector for host-side wiring (approvals, session info,
36
+ * sweeps).
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * export class MyAgent extends Agent<Env> {
41
+ * get browser() {
42
+ * return createBrowserRuntime({
43
+ * ctx: this.ctx,
44
+ * browser: this.env.BROWSER,
45
+ * loader: this.env.LOADER,
46
+ * session: { mode: "dynamic" }
47
+ * });
48
+ * }
49
+ *
50
+ * @callable()
51
+ * async closeBrowserSession() {
52
+ * await this.browser.connector.closeSession();
53
+ * }
54
+ * }
55
+ * ```
56
+ */
57
+ function createBrowserRuntime(options) {
58
+ if (!didWarnExperimental) {
59
+ didWarnExperimental = true;
60
+ console.warn("[agents/browser] Browser tools are experimental and may change in a future release.");
61
+ }
62
+ const ctx = resolveCtx(options);
63
+ if (!ctx) throw new Error("createBrowserRuntime requires a Durable Object 'ctx' — pass it explicitly, or call from within an Agent so it can be resolved via getCurrentAgent()");
64
+ const connector = new BrowserConnector(ctx, connectorOptions(options, ctx));
65
+ const runtime = createCodemodeRuntime({
66
+ ctx,
67
+ executor: new DynamicWorkerExecutor({
68
+ loader: options.loader,
69
+ timeout: options.timeout
70
+ }),
71
+ connectors: [connector],
72
+ name: options.name ?? "browser",
73
+ transformResult: truncateResult
74
+ });
75
+ const tools = { browser_execute: runtime.tool() };
76
+ if (options.quickActions !== false) {
77
+ const qa = options.quickActions == null || options.quickActions === true ? {} : options.quickActions;
78
+ const quickActionBrowser = qa.browser ?? options.browser;
79
+ if (quickActionBrowser) Object.assign(tools, createQuickActionTools({
80
+ browser: quickActionBrowser,
81
+ actions: qa.actions,
82
+ maxChars: qa.maxChars,
83
+ options: qa.options
84
+ }));
85
+ else if (options.quickActions) throw new Error("quickActions requires a Browser Run binding — set 'browser' (env.BROWSER) or 'quickActions.browser'");
86
+ else if (!didDebugQuickActionSkip) {
87
+ didDebugQuickActionSkip = true;
88
+ console.debug("[agents/browser] Quick Action tools skipped — no Browser Run binding (only 'cdpUrl' is set). Pass 'browser' or 'quickActions.browser' to enable them.");
89
+ }
90
+ }
91
+ return {
92
+ runtime,
93
+ connector,
94
+ tools
95
+ };
96
+ }
5
97
  /**
6
98
  * Create AI SDK tools for browser automation via CDP code mode.
7
99
  *
8
- * Returns a `ToolSet` with `search` (query the CDP spec) and
9
- * `execute` (run CDP commands against a live browser).
100
+ * Returns a `ToolSet` with a single durable `browser_execute` tool backed by
101
+ * a codemode runtime: the model writes TypeScript against the `cdp` connector
102
+ * (`cdp.send`, `cdp.attachToTarget`, `cdp.spec`, …), executions are recorded
103
+ * for abort-and-replay, and browser sessions survive pauses.
10
104
  *
11
105
  * @example
12
106
  * ```ts
13
107
  * import { createBrowserTools } from "agents/browser/ai";
14
108
  * import { generateText } from "ai";
15
109
  *
110
+ * // inside a Durable Object / Agent:
16
111
  * const browserTools = createBrowserTools({
17
- * browser: env.BROWSER,
18
- * loader: env.LOADER,
112
+ * ctx: this.ctx,
113
+ * browser: this.env.BROWSER,
114
+ * loader: this.env.LOADER,
19
115
  * });
20
116
  *
21
117
  * const result = await generateText({
@@ -26,29 +122,144 @@ import { z } from "zod";
26
122
  * ```
27
123
  */
28
124
  function createBrowserTools(options) {
29
- const handlers = createBrowserToolHandlers(options);
125
+ return createBrowserRuntime(options).tools;
126
+ }
127
+ const DEFAULT_QUICK_ACTION_TOOLS = [
128
+ "markdown",
129
+ "extract",
130
+ "links",
131
+ "scrape"
132
+ ];
133
+ const DEFAULT_QUICK_ACTION_MAX_CHARS = 5e4;
134
+ const pageInputSchema = z.object({
135
+ url: z.string().url().optional().describe("URL of the page to load"),
136
+ html: z.string().optional().describe("Raw HTML to render instead of loading a URL")
137
+ }).refine((value) => Boolean(value.url) || Boolean(value.html), { message: "Provide either 'url' or 'html'" });
138
+ function toPage(input, options) {
139
+ const page = input.url ? { url: input.url } : { html: input.html };
30
140
  return {
31
- browser_search: tool({
32
- description: SEARCH_DESCRIPTION,
33
- inputSchema: z.object({ code: z.string().describe("JavaScript async arrow function that queries the CDP spec") }),
34
- execute: async ({ code }) => {
35
- const result = await handlers.search(code);
36
- if (result.isError) throw new Error(result.text);
37
- return result.text;
38
- }
39
- }),
40
- browser_execute: tool({
41
- description: EXECUTE_DESCRIPTION,
42
- inputSchema: z.object({ code: z.string().describe("JavaScript async arrow function that uses the cdp helper") }),
43
- execute: async ({ code }) => {
44
- const result = await handlers.execute(code);
45
- if (result.isError) throw new Error(result.text);
46
- return result.text;
47
- }
48
- })
141
+ ...options,
142
+ ...page
143
+ };
144
+ }
145
+ function truncate(text, maxChars) {
146
+ if (maxChars <= 0 || text.length <= maxChars) return text;
147
+ return `${text.slice(0, maxChars)}\n\n[truncated ${text.length - maxChars} characters]`;
148
+ }
149
+ /**
150
+ * Keep a tool result within a rough character budget so a single browse cannot
151
+ * blow the model's context window — while preserving the result's shape so the
152
+ * model sees a consistent type across calls:
153
+ *
154
+ * - strings (markdown/content) are truncated to a string;
155
+ * - arrays (links/scrape) are trimmed from the end but stay arrays;
156
+ * - only an opaque oversized object (e.g. a sprawling `extract`) degrades to a
157
+ * `{ truncated, note, preview }` summary, since it cannot be trimmed safely.
158
+ */
159
+ function boundResult(value, maxChars) {
160
+ if (maxChars <= 0) return value;
161
+ if (typeof value === "string") return truncate(value, maxChars);
162
+ let json;
163
+ try {
164
+ json = JSON.stringify(value);
165
+ } catch {
166
+ return value;
167
+ }
168
+ if (json.length <= maxChars) return value;
169
+ if (Array.isArray(value)) {
170
+ const trimmed = boundArray(value, maxChars);
171
+ if (trimmed.length > 0) return trimmed;
172
+ }
173
+ return {
174
+ truncated: true,
175
+ note: `Result is too large (${json.length} characters); narrow the request.`,
176
+ preview: `${json.slice(0, maxChars)}…`
49
177
  };
50
178
  }
179
+ /**
180
+ * Take as many leading items as fit within `maxChars` (measured against their
181
+ * JSON length), returning a trimmed array of the same element type. Silent by
182
+ * design: the model gets fewer, valid items rather than a reshaped result.
183
+ */
184
+ function boundArray(value, maxChars) {
185
+ const out = [];
186
+ let size = 2;
187
+ for (const item of value) {
188
+ const itemSize = JSON.stringify(item).length + 1;
189
+ if (size + itemSize > maxChars) break;
190
+ out.push(item);
191
+ size += itemSize;
192
+ }
193
+ return out;
194
+ }
195
+ /**
196
+ * Create AI SDK tools for Browser Run [Quick Actions](https://developers.cloudflare.com/browser-run/quick-actions/):
197
+ * stateless one-shot browsing (read a page as Markdown, extract structured
198
+ * data with AI, list links, scrape elements). Unlike `createBrowserTools`,
199
+ * these need only the `browser` binding — no Durable Object, loader, or
200
+ * sandbox — so they work from any Worker.
201
+ *
202
+ * @example
203
+ * ```ts
204
+ * import { createQuickActionTools } from "agents/browser/ai";
205
+ *
206
+ * const tools = createQuickActionTools({ browser: this.env.BROWSER });
207
+ * const result = await generateText({ model, tools, messages });
208
+ * ```
209
+ */
210
+ function createQuickActionTools(options) {
211
+ const { browser } = options;
212
+ const requestOptions = options.options;
213
+ const enabled = new Set(options.actions ?? DEFAULT_QUICK_ACTION_TOOLS);
214
+ const maxChars = options.maxChars ?? DEFAULT_QUICK_ACTION_MAX_CHARS;
215
+ const tools = {};
216
+ if (enabled.has("markdown")) tools.browser_markdown = tool({
217
+ description: "Load a web page (or render raw HTML) and return its content as Markdown. Best for reading articles, docs, or any page as text.",
218
+ inputSchema: pageInputSchema,
219
+ execute: async (input) => boundResult(await browserMarkdown(browser, toPage(input, requestOptions)), maxChars)
220
+ });
221
+ if (enabled.has("extract")) tools.browser_extract = tool({
222
+ description: "Extract structured data from a web page using AI. Describe what you want in 'prompt'. Passing a JSON Schema in 'schema' is strongly recommended — without one the extractor often fails to produce JSON.",
223
+ inputSchema: z.object({
224
+ url: z.string().url().optional().describe("URL of the page to load"),
225
+ html: z.string().optional().describe("Raw HTML to render instead of loading a URL"),
226
+ prompt: z.string().optional().describe("What to extract, in natural language"),
227
+ schema: z.unknown().optional().describe("Optional JSON Schema describing the desired output")
228
+ }).refine((value) => Boolean(value.url) || Boolean(value.html), { message: "Provide either 'url' or 'html'" }).refine((value) => Boolean(value.prompt) || Boolean(value.schema), { message: "Provide either 'prompt' or 'schema'" }),
229
+ execute: async (input) => boundResult(await browserExtract(browser, {
230
+ ...toPage(input, requestOptions),
231
+ prompt: input.prompt,
232
+ response_format: input.schema ? {
233
+ type: "json_schema",
234
+ schema: input.schema
235
+ } : void 0
236
+ }), maxChars)
237
+ });
238
+ if (enabled.has("links")) tools.browser_links = tool({
239
+ description: "Return every link found on a web page (including ones not visible). Useful for discovering pages to follow.",
240
+ inputSchema: pageInputSchema,
241
+ execute: async (input) => boundResult(await browserLinks(browser, toPage(input, requestOptions)), maxChars)
242
+ });
243
+ if (enabled.has("scrape")) tools.browser_scrape = tool({
244
+ description: "Scrape specific elements from a web page by CSS selector. Returns the matched elements' text, HTML, and attributes.",
245
+ inputSchema: z.object({
246
+ url: z.string().url().optional().describe("URL of the page to load"),
247
+ html: z.string().optional().describe("Raw HTML to render instead of loading a URL"),
248
+ selectors: z.array(z.string()).min(1).describe("CSS selectors to extract")
249
+ }).refine((value) => Boolean(value.url) || Boolean(value.html), { message: "Provide either 'url' or 'html'" }),
250
+ execute: async (input) => boundResult(await browserScrape(browser, {
251
+ ...toPage(input, requestOptions),
252
+ elements: input.selectors.map((selector) => ({ selector }))
253
+ }), maxChars)
254
+ });
255
+ if (enabled.has("content")) tools.browser_content = tool({
256
+ description: "Load a web page and return its fully rendered HTML (after JavaScript runs). Prefer 'browser_markdown' unless you need the raw HTML.",
257
+ inputSchema: pageInputSchema,
258
+ execute: async (input) => boundResult(await browserContent(browser, toPage(input, requestOptions)), maxChars)
259
+ });
260
+ return tools;
261
+ }
51
262
  //#endregion
52
- export { createBrowserTools };
263
+ export { createBrowserRuntime, createBrowserTools, createQuickActionTools };
53
264
 
54
265
  //# sourceMappingURL=ai.js.map