@springbrand/agent-runtime 0.1.3-alpha.1 → 0.1.3-alpha.2

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.
@@ -1,13 +1,8 @@
1
- import {
2
- createCodemodeRuntime,
3
- DynamicWorkerExecutor,
4
- truncateResult,
5
- } from "@cloudflare/codemode";
6
1
  import {
7
2
  createWorkspaceStateBackend,
8
3
  type WorkspaceFsLike,
9
4
  } from "@cloudflare/shell";
10
- import { StateConnector } from "@cloudflare/shell/workers";
5
+ import { createExecuteRuntime } from "@cloudflare/think/tools/execute";
11
6
  import type {
12
7
  RuntimeCodeExecutionPort,
13
8
  WorkspacePort,
@@ -22,7 +17,7 @@ const CODEMODE_SANDBOX_TIMEOUT_MS = 55_000;
22
17
  *
23
18
  * Worker 宿主在具备 Durable Object state 和完整平台绑定时调用,然后把返回值交给 Runtime 工具组装。
24
19
  *
25
- * Cloudflare Codemode 把持久化 Runtime 和无状态 Dynamic Worker executor 分开;这里必须用 StateConnector 显式注入已限定范围的 Workspace,并在返回模型前用官方 `truncateResult` 限制结果。
20
+ * Think 的独立 execute factory 接受显式宿主参数,不要求 Agent 继承 Think;这里只借它组装 Codemode Runtime、Dynamic Worker executor 和已限定范围的 Workspace state connector。
26
21
  */
27
22
  export function createWorkspaceCodeExecutionPort(options: {
28
23
  readonly ctx: DurableObjectState;
@@ -30,26 +25,26 @@ export function createWorkspaceCodeExecutionPort(options: {
30
25
  readonly outbound: Fetcher;
31
26
  readonly workspace: WorkspacePort;
32
27
  }): RuntimeCodeExecutionPort {
33
- const runtime = createCodemodeRuntime({
28
+ const { tool } = createExecuteRuntime({
34
29
  ctx: options.ctx,
35
- executor: new DynamicWorkerExecutor({
36
- loader: options.loader,
37
- globalOutbound: options.outbound,
38
- // 先于外层 60s 截止结束,给 Runtime RPC 结算和 Worker 释放留出时间。
39
- timeout: CODEMODE_SANDBOX_TIMEOUT_MS,
40
- }),
41
- connectors: [
42
- new StateConnector(
43
- options.ctx,
44
- createWorkspaceStateBackend(
45
- options.workspace as unknown as WorkspaceFsLike,
46
- ),
47
- ),
48
- ],
30
+ loader: options.loader,
31
+ globalOutbound: options.outbound,
32
+ // 先于外层 60s 截止结束,给 Runtime RPC 结算和 Worker 释放留出时间。
33
+ timeout: CODEMODE_SANDBOX_TIMEOUT_MS,
34
+ state: createWorkspaceStateBackend(
35
+ options.workspace as unknown as WorkspaceFsLike,
36
+ ),
49
37
  name: "execute",
50
- transformResult: truncateResult,
51
38
  });
39
+ const execute = tool.execute;
40
+ if (typeof execute !== "function") {
41
+ throw new Error("Think createExecuteRuntime returned a non-executable tool");
42
+ }
52
43
  return {
53
- execute: (input) => runtime.execute(input),
44
+ execute: (input) => Promise.resolve(execute(input, {
45
+ toolCallId: "execute",
46
+ messages: [],
47
+ context: undefined,
48
+ })),
54
49
  };
55
50
  }
@@ -1,21 +1,16 @@
1
+ import { createQuickActionTools } from "@cloudflare/think/tools/browser";
1
2
  import type {
2
3
  AgentTool,
3
4
  AgentToolResult,
4
5
  } from "@earendil-works/pi-agent-core";
5
6
  import { Type } from "@earendil-works/pi-ai";
6
- import {
7
- browserExtract,
8
- browserLinks,
9
- browserMarkdown,
10
- browserScrape,
11
- type QuickActionPage,
12
- } from "agents/browser";
13
7
  import type {
14
8
  RuntimeBrowserPort,
15
9
  RuntimeCodeExecutionPort,
16
10
  } from "../../kernel/bindings";
17
11
  import { serializeOutput } from "../../lib/artifacts";
18
12
  import type { PiLoadedExtension } from "../assembly/extensions";
13
+ import { aiToolToPi } from "./ai-adapter";
19
14
  import type { PiToolCandidate } from "./compiler";
20
15
 
21
16
  // 本文件沿用 `../../index.ts` 入口定义的 Extension、Port 和 Tool Candidate 术语。
@@ -32,128 +27,33 @@ function result(details: unknown): AgentToolResult<unknown> {
32
27
 
33
28
  // #region Browser Quick Actions
34
29
 
35
- const pageParameters = {
36
- url: Type.Optional(Type.String({ format: "uri" })),
37
- html: Type.Optional(Type.String({ minLength: 1 })),
30
+ const BROWSER_TOOL_LABELS: Readonly<Record<string, string>> = {
31
+ browser_markdown: "Read web page",
32
+ browser_extract: "Extract web data",
33
+ browser_links: "List web links",
34
+ browser_scrape: "Scrape web elements",
38
35
  };
39
- const browserPageParameters = Type.Object(pageParameters);
40
- const browserExtractParameters = Type.Object({
41
- ...pageParameters,
42
- prompt: Type.Optional(Type.String({ minLength: 1 })),
43
- schema: Type.Optional(Type.Unknown()),
44
- });
45
- const browserScrapeParameters = Type.Object({
46
- ...pageParameters,
47
- selectors: Type.Array(Type.String({ minLength: 1 }), {
48
- minItems: 1,
49
- }),
50
- });
51
-
52
- // 把工具输入收窄为 Browser Quick Action 接受的页面来源。
53
- // 四个 Browser Quick Action execute 方法在调用 Agents SDK 前共用它。
54
- // 实际运行时再拒绝空输入,而同时给出 url 和 html 时优先 url,不能把不完整来源传给宿主绑定。
55
- function page(input: {
56
- url?: string;
57
- html?: string;
58
- }): QuickActionPage {
59
- if (input.url) return { url: input.url };
60
- if (input.html) return { html: input.html };
61
- throw new Error("Provide either 'url' or 'html'");
62
- }
63
36
 
64
37
  /**
65
38
  * 为 Cloudflare Browser Run 的四个一次性 Quick Action 创建 Pi 工具候选项。
66
39
  *
67
40
  * Runtime 在宿主提供 Browser port 时调用,模型分别用它读取 Markdown、抽取数据、列出链接或按选择器抓取。
68
41
  *
69
- * Cloudflare 官方将 Quick Actions 定位为只需 browser binding 的无状态单次操作,所以这里直接调用四个宿主函数,不引入额外会话或重试状态。
42
+ * Cloudflare 官方将 Quick Actions 定位为只需 browser binding 的无状态单次操作,这里直接复用官方工厂。
70
43
  */
71
44
  export function browserQuickActionPiToolCandidates(
72
45
  browser: RuntimeBrowserPort,
73
46
  ): PiToolCandidate[] {
74
- const markdown: AgentTool<typeof browserPageParameters> = {
75
- name: "browser_markdown",
76
- label: "Read web page",
77
- description:
78
- "Load a web page (or render raw HTML) and return its content as Markdown. Best for reading articles, docs, or any page as text.",
79
- parameters: browserPageParameters,
80
- // URL 或 HTML 读成 Markdown。
81
- // Pi 工具循环在模型选择 `browser_markdown` 时调用,调用前允许 Turn 取消。
82
- // 取消检查必须放在 Browser binding 调用前,避免已终止 Turn 继续发起页面请求。
83
- async execute(_toolCallId, input, signal) {
84
- signal?.throwIfAborted();
85
- return result(await browserMarkdown(browser, page(input)));
86
- },
87
- };
88
- const extract: AgentTool<typeof browserExtractParameters> = {
89
- name: "browser_extract",
90
- label: "Extract web data",
91
- description:
92
- "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.",
93
- parameters: browserExtractParameters,
94
- // 按自然语言提示或 JSON Schema 从页面抽取数据。
95
- // Pi 工具循环在模型选择 `browser_extract` 时调用,至少需要 prompt 或 schema 之一。
96
- // schema 必须转成 Browser Run 期望的 `json_schema` response_format,而空抽取要求必须在发起请求前失败。
97
- async execute(
98
- _toolCallId,
99
- { prompt, schema, ...input },
100
- signal,
101
- ) {
102
- signal?.throwIfAborted();
103
- if (!prompt && schema === undefined) {
104
- throw new Error("Provide either 'prompt' or 'schema'");
105
- }
106
- return result(await browserExtract(browser, {
107
- ...page(input),
108
- prompt,
109
- response_format: schema === undefined
110
- ? undefined
111
- : { type: "json_schema", schema },
112
- }));
113
- },
114
- };
115
- const links: AgentTool<typeof browserPageParameters> = {
116
- name: "browser_links",
117
- label: "List web links",
118
- description:
119
- "Return every link found on a web page (including ones not visible). Useful for discovering pages to follow.",
120
- parameters: browserPageParameters,
121
- // 列出 URL 或 HTML 页面中的链接。
122
- // Pi 工具循环在模型选择 `browser_links` 时调用,调用前允许 Turn 取消。
123
- // 保留 Agents SDK 的原始结果形状,让统一 result 边界负责文本投影而不在此处二次整形。
124
- async execute(_toolCallId, input, signal) {
125
- signal?.throwIfAborted();
126
- return result(await browserLinks(browser, page(input)));
127
- },
128
- };
129
- const scrape: AgentTool<typeof browserScrapeParameters> = {
130
- name: "browser_scrape",
131
- label: "Scrape web elements",
132
- description:
133
- "Scrape specific elements from a web page by CSS selector. Returns the matched elements' text, HTML, and attributes.",
134
- parameters: browserScrapeParameters,
135
- // 按 CSS 选择器抓取 URL 或 HTML 页面的指定元素。
136
- // Pi 工具循环在模型选择 `browser_scrape` 时调用,并把简化的字符串列表传入。
137
- // Browser Run 需要 `{ selector }` 对象列表,因此这个适配只在边界做一次形状转换。
138
- async execute(
139
- _toolCallId,
140
- { selectors, ...input },
141
- signal,
142
- ) {
143
- signal?.throwIfAborted();
144
- return result(await browserScrape(browser, {
145
- ...page(input),
146
- elements: selectors.map((selector) => ({ selector })),
147
- }));
148
- },
149
- };
150
-
151
- return [markdown, extract, links, scrape].map((tool) => ({
152
- owner: "core:browser",
153
- authorized: true,
154
- requiredExecutionLevel: "low",
155
- tool,
156
- }));
47
+ return Object.entries(createQuickActionTools({ browser })).map(
48
+ ([name, tool]) => ({
49
+ owner: "core:browser",
50
+ authorized: true,
51
+ requiredExecutionLevel: "low",
52
+ tool: aiToolToPi(name, tool, {
53
+ label: BROWSER_TOOL_LABELS[name] ?? name,
54
+ }),
55
+ }),
56
+ );
157
57
  }
158
58
 
159
59
  // #endregion