@springbrand/agent-runtime 0.2.0-alpha.41 → 0.2.0-alpha.43
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.
- package/package.json +1 -1
- package/src/adapter/cloudflare/resources/r2-skill-source.ts +215 -0
- package/src/adapter/cloudflare/resources/runtime-resources.ts +1 -18
- package/src/adapter/cloudflare/subagent/tools.ts +2 -5
- package/src/adapter/cloudflare/universal-agent/preparation.ts +21 -9
- package/src/adapter/cloudflare/universal-agent/tools.ts +3 -2
- package/src/adapter/cloudflare/workspace/scoped-workspace.ts +15 -0
- package/src/index.ts +3 -0
- package/src/kernel/bindings.ts +38 -6
- package/src/layers/context/budget/gate.ts +3 -3
- package/src/layers/orchestration/temporary-agent/workspace.ts +2 -0
- package/src/lib/prompt.ts +30 -14
- package/src/pi/assembly/snapshot.ts +7 -1
- package/src/pi/runtime-adapter/assembly.ts +8 -3
- package/src/pi/runtime-adapter/execution.ts +108 -13
- package/src/pi/runtime-adapter/models.ts +9 -6
- package/src/pi/runtime-adapter/openrouter-messages.ts +10 -3
- package/src/pi/tool/base.ts +42 -14
- package/src/pi/tool/compiler.ts +15 -2
- package/src/pi/tool/core-host.ts +228 -1
- package/src/pi/tool/core.ts +37 -5
- package/src/pi/tool/declared.ts +3 -0
- package/src/pi/tool/nested-tools.ts +5 -1
- package/src/pi/tool/schedule.ts +12 -10
- package/src/pi/tool/skill.ts +240 -86
- package/src/pi/tool/subagent.ts +2 -0
- package/src/pi/tool/time.ts +1 -1
- package/src/pi/tool/web-fetch.ts +1 -1
- package/src/pi/tool/web-search/web-search.ts +2 -1
- package/src/pi/tool/workspace-revision.ts +2 -1
- package/src/pi/tool/workspace-sandbox.ts +10 -21
- package/src/runtime-agent.ts +3 -0
- package/src/runtime-assembler.ts +105 -7
- package/src/runtime-definition.ts +1 -0
- package/src/runtime.ts +50 -0
package/src/pi/tool/core-host.ts
CHANGED
|
@@ -1,15 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createWorkspaceStateBackend,
|
|
3
|
+
type StateBackend,
|
|
4
|
+
type WorkspaceFsLike,
|
|
5
|
+
} from "@cloudflare/shell";
|
|
6
|
+
import {
|
|
7
|
+
truncateResponse,
|
|
8
|
+
truncateResult,
|
|
9
|
+
type ProxyToolOutput,
|
|
10
|
+
} from "@cloudflare/codemode";
|
|
1
11
|
import { createBrowserTools } from "@cloudflare/think/tools/browser";
|
|
12
|
+
import { createExecuteRuntime } from "@cloudflare/think/tools/execute";
|
|
2
13
|
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
14
|
+
import type { Usage } from "@earendil-works/pi-ai";
|
|
3
15
|
import type { ToolSet } from "ai";
|
|
4
16
|
import type {
|
|
5
17
|
RuntimeBrowserPort,
|
|
6
18
|
RuntimeCodeExecutionPort,
|
|
19
|
+
WorkspacePort,
|
|
7
20
|
} from "../../kernel/bindings";
|
|
21
|
+
import type { RuntimeCodeExecutionFactory } from "../../kernel/bindings";
|
|
8
22
|
import { serializeOutput } from "../../lib/artifacts";
|
|
23
|
+
import type { PiToolCandidate } from "./compiler";
|
|
24
|
+
import { piCandidatesToAiTools } from "./nested-tools";
|
|
9
25
|
|
|
10
|
-
// 本文件沿用 `../../index.ts` 入口定义的
|
|
26
|
+
// 本文件沿用 `../../index.ts` 入口定义的 Workspace、Port 和 Tool Candidate 术语。
|
|
11
27
|
|
|
12
28
|
const CODEMODE_SANDBOX_TIMEOUT_MS = 55_000;
|
|
29
|
+
export type WorkspaceAccessMode = "none" | "read" | "write";
|
|
30
|
+
const WRITE_STATE_METHODS = new Set([
|
|
31
|
+
"writeFile",
|
|
32
|
+
"writeFileBytes",
|
|
33
|
+
"appendFile",
|
|
34
|
+
"writeJson",
|
|
35
|
+
"updateJson",
|
|
36
|
+
"mkdir",
|
|
37
|
+
"replaceInFile",
|
|
38
|
+
"replaceInFiles",
|
|
39
|
+
"rm",
|
|
40
|
+
"cp",
|
|
41
|
+
"mv",
|
|
42
|
+
"symlink",
|
|
43
|
+
"createArchive",
|
|
44
|
+
"extractArchive",
|
|
45
|
+
"compressFile",
|
|
46
|
+
"decompressFile",
|
|
47
|
+
"removeTree",
|
|
48
|
+
"copyTree",
|
|
49
|
+
"moveTree",
|
|
50
|
+
"planEdits",
|
|
51
|
+
"applyEditPlan",
|
|
52
|
+
"applyEdits",
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
export function workspaceStateBackend(
|
|
56
|
+
workspace: WorkspacePort,
|
|
57
|
+
mode: WorkspaceAccessMode,
|
|
58
|
+
): StateBackend | undefined {
|
|
59
|
+
if (mode === "none") return undefined;
|
|
60
|
+
const backend = createWorkspaceStateBackend(
|
|
61
|
+
workspace as unknown as WorkspaceFsLike,
|
|
62
|
+
);
|
|
63
|
+
return new Proxy(backend, {
|
|
64
|
+
get(target, property, receiver) {
|
|
65
|
+
if (property === "writeFileBytes") return undefined;
|
|
66
|
+
if (
|
|
67
|
+
mode === "read" &&
|
|
68
|
+
typeof property === "string" &&
|
|
69
|
+
WRITE_STATE_METHODS.has(property)
|
|
70
|
+
) {
|
|
71
|
+
return async () => {
|
|
72
|
+
throw new Error("Workspace state is read-only for this execution");
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const value = Reflect.get(target, property, receiver);
|
|
76
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
77
|
+
},
|
|
78
|
+
}) as StateBackend;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const SPRINGBRAND_CODE_MODE_POLICY = `## SpringBrand Workspace and Asset Policy
|
|
82
|
+
|
|
83
|
+
Use state.* for every Workspace filesystem operation. Workspace file methods do not exist under tools.*.
|
|
84
|
+
|
|
85
|
+
state.writeFileBytes is not available. Use state.writeFile or state.appendFile for sandbox-created text.
|
|
86
|
+
|
|
87
|
+
Bundled Skill resources are not Workspace files. Read text with tools.read_skill_resource, run bundled scripts with tools.run_skill_script, and copy templates or assets with tools.materialize_skill_resource.
|
|
88
|
+
|
|
89
|
+
User Attachments are not Workspace files until copied with tools.materialize_attachment.
|
|
90
|
+
|
|
91
|
+
Never create or write data URLs, base64 image strings, or embedded binary payloads in Code Mode.
|
|
92
|
+
|
|
93
|
+
Copy a remote asset into Workspace with tools.materialize_url({ url, destination }). Raw fetch remains available. Do not carry large responses, binary payloads, data URLs, or base64 assets through Code Mode.
|
|
94
|
+
|
|
95
|
+
After materialization, reference the Workspace file by a relative path. Website HTML and CSS must reference relative Workspace assets, never temporary external URLs or data URLs.
|
|
96
|
+
|
|
97
|
+
Keep large content out of the execute result. Write it to Workspace and return only path, mediaType, bytes, summary, and warnings.
|
|
98
|
+
|
|
99
|
+
This execute tool cannot ask the user, inspect a rendered page, capture a screenshot, save or publish a Creation, or authorize an external action. Use the corresponding top-level Tool for those operations.`;
|
|
13
100
|
|
|
14
101
|
/**
|
|
15
102
|
* 宿主的 Browser Rendering 绑定,只取本仓真正用到的那一面。
|
|
@@ -21,6 +108,13 @@ export interface RuntimeBrowserBinding {
|
|
|
21
108
|
fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
|
|
22
109
|
}
|
|
23
110
|
|
|
111
|
+
/**
|
|
112
|
+
* 把宿主的 Worker Loader、出站网络和 Workspace 组装成代码执行 Port。
|
|
113
|
+
*
|
|
114
|
+
* Worker 宿主在具备 Durable Object state 和完整平台绑定时调用,然后把返回值交给 Runtime 工具组装。
|
|
115
|
+
*
|
|
116
|
+
* Think 的独立 execute factory 接受显式宿主参数,不要求 Agent 继承 Think;这里只借它组装 Codemode Runtime、Dynamic Worker executor 和已限定范围的 Workspace state connector。
|
|
117
|
+
*/
|
|
24
118
|
function result(details: unknown): AgentToolResult<unknown> {
|
|
25
119
|
return {
|
|
26
120
|
content: [{ type: "text", text: serializeOutput(details).text }],
|
|
@@ -28,6 +122,92 @@ function result(details: unknown): AgentToolResult<unknown> {
|
|
|
28
122
|
};
|
|
29
123
|
}
|
|
30
124
|
|
|
125
|
+
function sumUsage(results: readonly AgentToolResult<unknown>[]): Usage | undefined {
|
|
126
|
+
const usages = results.flatMap(({ usage }) => usage ? [usage] : []);
|
|
127
|
+
if (usages.length === 0) return undefined;
|
|
128
|
+
return usages.reduce<Usage>((total, usage) => ({
|
|
129
|
+
input: total.input + usage.input,
|
|
130
|
+
output: total.output + usage.output,
|
|
131
|
+
cacheRead: total.cacheRead + usage.cacheRead,
|
|
132
|
+
cacheWrite: total.cacheWrite + usage.cacheWrite,
|
|
133
|
+
...(total.cacheWrite1h === undefined && usage.cacheWrite1h === undefined
|
|
134
|
+
? {}
|
|
135
|
+
: { cacheWrite1h: (total.cacheWrite1h ?? 0) + (usage.cacheWrite1h ?? 0) }),
|
|
136
|
+
...(total.reasoning === undefined && usage.reasoning === undefined
|
|
137
|
+
? {}
|
|
138
|
+
: { reasoning: (total.reasoning ?? 0) + (usage.reasoning ?? 0) }),
|
|
139
|
+
totalTokens: total.totalTokens + usage.totalTokens,
|
|
140
|
+
cost: {
|
|
141
|
+
input: total.cost.input + usage.cost.input,
|
|
142
|
+
output: total.cost.output + usage.cost.output,
|
|
143
|
+
cacheRead: total.cost.cacheRead + usage.cost.cacheRead,
|
|
144
|
+
cacheWrite: total.cost.cacheWrite + usage.cost.cacheWrite,
|
|
145
|
+
total: total.cost.total + usage.cost.total,
|
|
146
|
+
},
|
|
147
|
+
}), {
|
|
148
|
+
input: 0,
|
|
149
|
+
output: 0,
|
|
150
|
+
cacheRead: 0,
|
|
151
|
+
cacheWrite: 0,
|
|
152
|
+
totalTokens: 0,
|
|
153
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function codeExecutionResult(
|
|
158
|
+
details: unknown,
|
|
159
|
+
innerResults: readonly AgentToolResult<unknown>[],
|
|
160
|
+
): AgentToolResult<unknown> {
|
|
161
|
+
const addedToolNames = [...new Set(
|
|
162
|
+
innerResults.flatMap(({ addedToolNames }) => addedToolNames ?? []),
|
|
163
|
+
)].sort();
|
|
164
|
+
const usage = sumUsage(innerResults);
|
|
165
|
+
return {
|
|
166
|
+
...result(details),
|
|
167
|
+
...(addedToolNames.length > 0 ? { addedToolNames } : {}),
|
|
168
|
+
...(usage ? { usage } : {}),
|
|
169
|
+
...(innerResults.length > 0 && innerResults.every(({ terminate }) => terminate === true)
|
|
170
|
+
? { terminate: true }
|
|
171
|
+
: {}),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function projectCodeModeResult(details: unknown): unknown {
|
|
176
|
+
if (typeof details !== "object" || details === null) {
|
|
177
|
+
return { status: "error", executionId: "unknown", error: truncateResponse(String(details)) };
|
|
178
|
+
}
|
|
179
|
+
const output = details as ProxyToolOutput;
|
|
180
|
+
const calls = Array.isArray(output.calls) ? output.calls : [];
|
|
181
|
+
const callSummary = calls.length > 0
|
|
182
|
+
? {
|
|
183
|
+
total: calls.length,
|
|
184
|
+
failed: calls.filter(({ state }) => state === "error").length,
|
|
185
|
+
methods: [...new Set(calls.map(({ method }) => method))]
|
|
186
|
+
.map((method) => truncateResponse(method, { maxChars: 256 }))
|
|
187
|
+
.slice(0, 100),
|
|
188
|
+
}
|
|
189
|
+
: undefined;
|
|
190
|
+
const common = {
|
|
191
|
+
status: output.status,
|
|
192
|
+
executionId: output.executionId,
|
|
193
|
+
...(callSummary ? { callSummary } : {}),
|
|
194
|
+
};
|
|
195
|
+
if (output.status === "completed") {
|
|
196
|
+
return { ...common, result: truncateResult(output.result) };
|
|
197
|
+
}
|
|
198
|
+
if (output.status === "paused") {
|
|
199
|
+
return {
|
|
200
|
+
...common,
|
|
201
|
+
pending: output.pending.slice(0, 100).map(({ connector, method, seq }) => ({
|
|
202
|
+
connector: truncateResponse(connector, { maxChars: 256 }),
|
|
203
|
+
method: truncateResponse(method, { maxChars: 256 }),
|
|
204
|
+
seq,
|
|
205
|
+
})),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
return { ...common, error: truncateResponse(output.error) };
|
|
209
|
+
}
|
|
210
|
+
|
|
31
211
|
// 上游把 Code Mode 类工具交付为 AI SDK tool;本仓只取 description 和 execute 两件,
|
|
32
212
|
// 并在这里就地校验,使装配期缺件立刻失败,而不是等模型调用时才炸。
|
|
33
213
|
function toCodeExecutionPort(
|
|
@@ -93,6 +273,7 @@ function browserToolDescription(): string {
|
|
|
93
273
|
*
|
|
94
274
|
* Worker 宿主在具备 DO state 与 Browser 绑定时调用,返回值经 Platform Port 交给 Runtime 工具组装。
|
|
95
275
|
*
|
|
276
|
+
* 形状与 `createWorkspaceCodeExecutionFactory` 同构:宿主提供 DO state 与平台绑定,Runtime 只拿到一个可选装配输入。
|
|
96
277
|
* `create()` 延迟到 Tool Surface 真的要注册时才调,被 deny 的装配不会白建连接器。
|
|
97
278
|
*/
|
|
98
279
|
export function createBrowserExecutionFactory(options: {
|
|
@@ -137,3 +318,49 @@ export function createBrowserExecutionFactory(options: {
|
|
|
137
318
|
},
|
|
138
319
|
};
|
|
139
320
|
}
|
|
321
|
+
|
|
322
|
+
export function createWorkspaceCodeExecutionFactory(options: {
|
|
323
|
+
readonly ctx: DurableObjectState;
|
|
324
|
+
readonly loader: WorkerLoader;
|
|
325
|
+
readonly outbound: Fetcher;
|
|
326
|
+
readonly workspace: WorkspacePort;
|
|
327
|
+
readonly workspaceAccessMode: WorkspaceAccessMode;
|
|
328
|
+
}): RuntimeCodeExecutionFactory {
|
|
329
|
+
return {
|
|
330
|
+
create(candidates) {
|
|
331
|
+
const state = workspaceStateBackend(
|
|
332
|
+
options.workspace,
|
|
333
|
+
options.workspaceAccessMode,
|
|
334
|
+
);
|
|
335
|
+
const createPort = (innerResults: AgentToolResult<unknown>[]) => {
|
|
336
|
+
let nestedCallOrdinal = 0;
|
|
337
|
+
const { tool } = createExecuteRuntime({
|
|
338
|
+
ctx: options.ctx,
|
|
339
|
+
loader: options.loader,
|
|
340
|
+
globalOutbound: options.outbound,
|
|
341
|
+
tools: piCandidatesToAiTools(candidates, {
|
|
342
|
+
fallbackToolCallId: (name) =>
|
|
343
|
+
`codemode:${++nestedCallOrdinal}:${name}`,
|
|
344
|
+
onResult: (value) => innerResults.push(value),
|
|
345
|
+
}),
|
|
346
|
+
timeout: CODEMODE_SANDBOX_TIMEOUT_MS,
|
|
347
|
+
...(state ? { state } : {}),
|
|
348
|
+
name: "execute",
|
|
349
|
+
});
|
|
350
|
+
return toCodeExecutionPort(
|
|
351
|
+
tool,
|
|
352
|
+
"execute",
|
|
353
|
+
(details) => codeExecutionResult(
|
|
354
|
+
projectCodeModeResult(details),
|
|
355
|
+
innerResults,
|
|
356
|
+
),
|
|
357
|
+
);
|
|
358
|
+
};
|
|
359
|
+
const description = createPort([]).description;
|
|
360
|
+
return {
|
|
361
|
+
description: `${description}\n\n${SPRINGBRAND_CODE_MODE_POLICY}`,
|
|
362
|
+
execute: (input) => createPort([]).execute(input),
|
|
363
|
+
};
|
|
364
|
+
},
|
|
365
|
+
};
|
|
366
|
+
}
|
package/src/pi/tool/core.ts
CHANGED
|
@@ -165,7 +165,7 @@ function browserResult(details: unknown): AgentToolResult<unknown> {
|
|
|
165
165
|
|
|
166
166
|
// #region Extension discovery
|
|
167
167
|
|
|
168
|
-
const noParameters = Type.Object({});
|
|
168
|
+
const noParameters = Type.Object({}, { additionalProperties: false });
|
|
169
169
|
|
|
170
170
|
/**
|
|
171
171
|
* 为当前已加载 Extension 列表创建一个 Pi 工具候选项。
|
|
@@ -202,19 +202,21 @@ export function listExtensionsPiToolCandidate(
|
|
|
202
202
|
|
|
203
203
|
// #endregion
|
|
204
204
|
|
|
205
|
-
// #region
|
|
205
|
+
// #region Code Mode
|
|
206
206
|
|
|
207
207
|
const executeParameters = Type.Object({
|
|
208
208
|
code: Type.String({
|
|
209
209
|
minLength: 1,
|
|
210
|
+
maxLength: 131_072,
|
|
210
211
|
description:
|
|
211
212
|
"Plain JavaScript async function. TypeScript annotations are not supported.",
|
|
212
213
|
}),
|
|
213
|
-
});
|
|
214
|
+
}, { additionalProperties: false });
|
|
214
215
|
const CODEMODE_EXECUTE_TIMEOUT_MS = 60_000;
|
|
215
216
|
|
|
216
|
-
//
|
|
217
|
-
// `
|
|
217
|
+
// 把模型提供的代码交给 Codemode Runtime 执行,并在外层再压一道截止。
|
|
218
|
+
// 两个 Code Mode 类工具(`execute` 与 `browser_execute`)共用同一段时序,
|
|
219
|
+
// 避免两套心智模型;`label` 只用于超时文案,因为模型看到的名字由候选项决定。
|
|
218
220
|
function runCodemode(
|
|
219
221
|
runtime: RuntimeCodeExecutionPort,
|
|
220
222
|
label: string,
|
|
@@ -253,6 +255,36 @@ function runCodemode(
|
|
|
253
255
|
};
|
|
254
256
|
}
|
|
255
257
|
|
|
258
|
+
/**
|
|
259
|
+
* 把 Cloudflare Codemode Runtime handle 包装为 Pi 代码执行工具候选项。
|
|
260
|
+
*
|
|
261
|
+
* Tool Surface 收到 Host 已组装的 Code Execution Port 后调用,
|
|
262
|
+
* 模型再通过 `execute` 运行代码。
|
|
263
|
+
*
|
|
264
|
+
* Code Mode 作为一个完整的 safe 工具对外暴露,内部能力不再单独提权。
|
|
265
|
+
*/
|
|
266
|
+
export function codeExecutionPiToolCandidate(
|
|
267
|
+
runtime: RuntimeCodeExecutionPort,
|
|
268
|
+
): PiToolCandidate {
|
|
269
|
+
const tool: AgentTool<typeof executeParameters> = {
|
|
270
|
+
name: "execute",
|
|
271
|
+
label: "Execute JavaScript",
|
|
272
|
+
description: runtime.description,
|
|
273
|
+
parameters: executeParameters,
|
|
274
|
+
// Pi 工具循环在模型选择 `execute` 时调用,调用前允许 Turn 取消。
|
|
275
|
+
// 必须通过 Runtime handle 而不是直接调用 executor,因为 Cloudflare Codemode 把重放、审批和执行日志放在持久化 Runtime 层。
|
|
276
|
+
execute: runCodemode(runtime, "Code Mode execute"),
|
|
277
|
+
};
|
|
278
|
+
return {
|
|
279
|
+
owner: "core:codemode",
|
|
280
|
+
requiredExecutionLevel: "safe",
|
|
281
|
+
outputBudget: { kind: "structure" },
|
|
282
|
+
source: "codemode",
|
|
283
|
+
summary: "Run JavaScript with network and configured connector access",
|
|
284
|
+
tool,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
256
288
|
/** 模型可见的浏览器工具名;属外部契约,改名是破坏性变更。 */
|
|
257
289
|
export const BROWSER_EXECUTE_TOOL_NAME = "browser_execute";
|
|
258
290
|
|
package/src/pi/tool/declared.ts
CHANGED
|
@@ -49,6 +49,7 @@ export interface PiDeclaredToolPolicy<Reply = unknown> {
|
|
|
49
49
|
readonly modelName: string;
|
|
50
50
|
readonly requiredExecutionLevel: PiToolCandidate["requiredExecutionLevel"];
|
|
51
51
|
readonly requiredExecutionLevelForInput?: PiToolCandidate["requiredExecutionLevelForInput"];
|
|
52
|
+
readonly exposureMode?: PiToolCandidate["exposureMode"];
|
|
52
53
|
readonly retry?: PiToolCandidate["retry"];
|
|
53
54
|
readonly source?: PiToolCandidate["source"];
|
|
54
55
|
readonly summary?: string;
|
|
@@ -104,6 +105,7 @@ export function createPiDeclaredToolCandidate<Reply = unknown>(
|
|
|
104
105
|
parameters: structuredClone(
|
|
105
106
|
declared.inputSchema ?? { type: "object", additionalProperties: true },
|
|
106
107
|
) as AgentTool["parameters"],
|
|
108
|
+
constrainedSampling: false,
|
|
107
109
|
execute: async (toolCallId, input, signal) =>
|
|
108
110
|
project(await call(input as Record<string, unknown>, { toolCallId, signal })),
|
|
109
111
|
};
|
|
@@ -115,6 +117,7 @@ export function createPiDeclaredToolCandidate<Reply = unknown>(
|
|
|
115
117
|
...(policy.requiredExecutionLevelForInput
|
|
116
118
|
? { requiredExecutionLevelForInput: policy.requiredExecutionLevelForInput }
|
|
117
119
|
: {}),
|
|
120
|
+
...(policy.exposureMode ? { exposureMode: policy.exposureMode } : {}),
|
|
118
121
|
...(policy.retry ? { retry: policy.retry } : {}),
|
|
119
122
|
...(policy.source ? { source: policy.source } : {}),
|
|
120
123
|
};
|
|
@@ -35,7 +35,11 @@ export function piCandidatesToAiTools(
|
|
|
35
35
|
call?.abortSignal ?? new AbortController().signal,
|
|
36
36
|
);
|
|
37
37
|
options.onResult?.(result);
|
|
38
|
-
return result;
|
|
38
|
+
if (result.details !== undefined) return result.details;
|
|
39
|
+
const [only] = result.content;
|
|
40
|
+
return result.content.length === 1 && only?.type === "text"
|
|
41
|
+
? only.text
|
|
42
|
+
: result.content;
|
|
39
43
|
},
|
|
40
44
|
}),
|
|
41
45
|
]));
|
package/src/pi/tool/schedule.ts
CHANGED
|
@@ -18,28 +18,28 @@ const scheduleTriggerParameters = Type.Union([
|
|
|
18
18
|
minimum: 1,
|
|
19
19
|
description: "Fire once, this many seconds from now.",
|
|
20
20
|
}),
|
|
21
|
-
}),
|
|
21
|
+
}, { additionalProperties: false }),
|
|
22
22
|
Type.Object({
|
|
23
23
|
kind: Type.Literal("once-at"),
|
|
24
24
|
at: Type.String({
|
|
25
25
|
description:
|
|
26
26
|
"Fire once at this absolute UTC ISO time, e.g. 2026-07-20T09:00:00Z.",
|
|
27
27
|
}),
|
|
28
|
-
}),
|
|
28
|
+
}, { additionalProperties: false }),
|
|
29
29
|
Type.Object({
|
|
30
30
|
kind: Type.Literal("cron"),
|
|
31
31
|
cron: Type.String({
|
|
32
32
|
description:
|
|
33
33
|
"Repeat on a standard UTC cron, e.g. '0 9 * * *' = daily 09:00 UTC.",
|
|
34
34
|
}),
|
|
35
|
-
}),
|
|
35
|
+
}, { additionalProperties: false }),
|
|
36
36
|
Type.Object({
|
|
37
37
|
kind: Type.Literal("interval"),
|
|
38
38
|
seconds: Type.Integer({
|
|
39
39
|
minimum: 1,
|
|
40
40
|
description: "Repeat every this many seconds.",
|
|
41
41
|
}),
|
|
42
|
-
}),
|
|
42
|
+
}, { additionalProperties: false }),
|
|
43
43
|
], { description: "When to fire." });
|
|
44
44
|
|
|
45
45
|
const scheduleParameters = Type.Object({
|
|
@@ -56,21 +56,21 @@ const scheduleParameters = Type.Object({
|
|
|
56
56
|
description:
|
|
57
57
|
"IANA timezone the clock times in `trigger` should be interpreted in, e.g. 'Asia/Shanghai'. Pass the user's own timezone whenever they state a wall-clock time. Omit only for delays and intervals. Defaults to UTC.",
|
|
58
58
|
})),
|
|
59
|
-
});
|
|
59
|
+
}, { additionalProperties: false });
|
|
60
60
|
|
|
61
|
-
const noParameters = Type.Object({});
|
|
61
|
+
const noParameters = Type.Object({}, { additionalProperties: false });
|
|
62
62
|
const scheduleIdParameters = Type.Object({
|
|
63
63
|
id: Type.String({
|
|
64
64
|
description: "The schedule id, as returned by `list_schedules`.",
|
|
65
65
|
}),
|
|
66
|
-
});
|
|
66
|
+
}, { additionalProperties: false });
|
|
67
67
|
const updateScheduleParameters = Type.Object({
|
|
68
68
|
id: Type.String({ description: "The exact id returned by `list_schedules`." }),
|
|
69
69
|
prompt: Type.Optional(Type.String()),
|
|
70
70
|
trigger: Type.Optional(scheduleTriggerParameters),
|
|
71
71
|
label: Type.Optional(Type.String()),
|
|
72
72
|
timezone: Type.Optional(Type.String()),
|
|
73
|
-
});
|
|
73
|
+
}, { additionalProperties: false });
|
|
74
74
|
const changeScheduleAgentParameters = Type.Object({
|
|
75
75
|
id: Type.String({
|
|
76
76
|
description: "The exact schedule id returned by `list_schedules`.",
|
|
@@ -78,7 +78,7 @@ const changeScheduleAgentParameters = Type.Object({
|
|
|
78
78
|
userAgentId: Type.String({
|
|
79
79
|
description: "The exact User Agent id selected by the user.",
|
|
80
80
|
}),
|
|
81
|
-
});
|
|
81
|
+
}, { additionalProperties: false });
|
|
82
82
|
|
|
83
83
|
function result<T>(details: T): AgentToolResult<T> {
|
|
84
84
|
return {
|
|
@@ -92,12 +92,13 @@ function candidate<T extends TSchema>(
|
|
|
92
92
|
options: Partial<
|
|
93
93
|
Pick<
|
|
94
94
|
PiToolCandidate,
|
|
95
|
-
"alwaysRequiresApproval" | "owner" | "requiredExecutionLevel" | "summary"
|
|
95
|
+
"alwaysRequiresApproval" | "exposureMode" | "owner" | "requiredExecutionLevel" | "summary"
|
|
96
96
|
>
|
|
97
97
|
> = {},
|
|
98
98
|
): PiToolCandidate {
|
|
99
99
|
return {
|
|
100
100
|
owner: options.owner ?? "runtime-base",
|
|
101
|
+
exposureMode: options.exposureMode ?? "direct",
|
|
101
102
|
requiredExecutionLevel: options.requiredExecutionLevel ?? "safe",
|
|
102
103
|
source: "action",
|
|
103
104
|
tool,
|
|
@@ -133,6 +134,7 @@ export function schedulePiToolCandidates(
|
|
|
133
134
|
},
|
|
134
135
|
{
|
|
135
136
|
alwaysRequiresApproval: true,
|
|
137
|
+
exposureMode: "direct",
|
|
136
138
|
summary: "Create a scheduled task",
|
|
137
139
|
},
|
|
138
140
|
),
|