@springbrand/agent-runtime 0.2.0-alpha.41 → 0.2.0-alpha.42
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/universal-agent/preparation.ts +20 -1
- package/src/adapter/cloudflare/universal-agent/tools.ts +3 -0
- package/src/index.ts +2 -0
- package/src/kernel/bindings.ts +28 -6
- package/src/lib/prompt.ts +31 -10
- package/src/pi/assembly/snapshot.ts +7 -1
- package/src/pi/runtime-adapter/assembly.ts +8 -3
- package/src/pi/runtime-adapter/execution.ts +106 -12
- package/src/pi/tool/base.ts +29 -5
- package/src/pi/tool/compiler.ts +4 -0
- package/src/pi/tool/core-host.ts +143 -1
- package/src/pi/tool/core.ts +34 -3
- package/src/pi/tool/declared.ts +2 -0
- package/src/pi/tool/schedule.ts +3 -1
- package/src/pi/tool/skill.ts +67 -78
- package/src/runtime-agent.ts +3 -0
- package/src/runtime-assembler.ts +97 -6
- package/src/runtime-definition.ts +1 -0
- package/src/runtime.ts +22 -0
package/src/pi/tool/skill.ts
CHANGED
|
@@ -46,6 +46,12 @@ const SKILL_RESOURCE_BUDGET_BYTES = 8 * 1024 * 1024;
|
|
|
46
46
|
|
|
47
47
|
type LoadedSkill = NonNullable<Awaited<ReturnType<SkillSource["load"]>>>;
|
|
48
48
|
|
|
49
|
+
const SKILL_RESOURCE_READ_GUIDANCE =
|
|
50
|
+
"Bundled Skill resources are not Workspace files. " +
|
|
51
|
+
"Use read_skill_resource for text instead of read, bash, or find on /skills.";
|
|
52
|
+
const SKILL_RESOURCE_MATERIALIZE_GUIDANCE =
|
|
53
|
+
"Use materialize_skill_resource for Workspace assets when that Tool is available.";
|
|
54
|
+
|
|
49
55
|
/**
|
|
50
56
|
* 按体积裁掉超预算的 Skill 资源,并把裁掉的事实写回 Skill 正文。
|
|
51
57
|
*
|
|
@@ -93,7 +99,7 @@ function budgetSkillResources(skill: LoadedSkill): LoadedSkill {
|
|
|
93
99
|
if (portable.length > 0) {
|
|
94
100
|
lines.push(
|
|
95
101
|
`> If any of these resources are needed, copy all required ones into the Workspace ` +
|
|
96
|
-
`in one
|
|
102
|
+
`in one execute with tools.materialize_skill_resource: ` +
|
|
97
103
|
`${portable.map((entry) => entry.path).join(", ")}.`,
|
|
98
104
|
);
|
|
99
105
|
}
|
|
@@ -121,7 +127,21 @@ function catalogSkillSource(binding: PiSkillBinding): SkillSource {
|
|
|
121
127
|
}],
|
|
122
128
|
load: async (name) => {
|
|
123
129
|
const skill = await source.load(name);
|
|
124
|
-
|
|
130
|
+
if (!skill) return null;
|
|
131
|
+
const loaded = budgetSkillResources(skill);
|
|
132
|
+
const hasOversizedResource = skill.resources?.some((resource) =>
|
|
133
|
+
typeof resource.size === "number" &&
|
|
134
|
+
resource.size > SKILL_RESOURCE_BUDGET_BYTES
|
|
135
|
+
);
|
|
136
|
+
const guidance = hasOversizedResource
|
|
137
|
+
? SKILL_RESOURCE_READ_GUIDANCE
|
|
138
|
+
: `${SKILL_RESOURCE_READ_GUIDANCE} ${SKILL_RESOURCE_MATERIALIZE_GUIDANCE}`;
|
|
139
|
+
return loaded.resources?.length
|
|
140
|
+
? {
|
|
141
|
+
...loaded,
|
|
142
|
+
body: `${loaded.body}\n\n${guidance}`,
|
|
143
|
+
}
|
|
144
|
+
: loaded;
|
|
125
145
|
},
|
|
126
146
|
...(source.readResource
|
|
127
147
|
? { readResource: (name: string, path: string) =>
|
|
@@ -193,6 +213,20 @@ const SKILL_TOOL_PARAMETER_DESCRIPTIONS: Readonly<Record<string, Readonly<Record
|
|
|
193
213
|
const SKILL_ENTRY_READ_GUIDANCE =
|
|
194
214
|
"SKILL.md contains the Skill instructions; use activate_skill instead.";
|
|
195
215
|
|
|
216
|
+
function normalizeRunSkillScriptArguments(input: unknown): unknown {
|
|
217
|
+
if (input === null || typeof input !== "object") return input;
|
|
218
|
+
const value = input as Record<string, unknown>;
|
|
219
|
+
if (typeof value.input !== "string") return input;
|
|
220
|
+
try {
|
|
221
|
+
const decoded = JSON.parse(value.input);
|
|
222
|
+
return decoded !== null && typeof decoded === "object"
|
|
223
|
+
? { ...value, input: decoded }
|
|
224
|
+
: input;
|
|
225
|
+
} catch {
|
|
226
|
+
return input;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
196
230
|
function resourceBytes(resource: SkillResource): Uint8Array {
|
|
197
231
|
if ((resource.encoding ?? "text") === "text") {
|
|
198
232
|
return new TextEncoder().encode(resource.content);
|
|
@@ -211,95 +245,44 @@ function materializeSkillResourceTool(
|
|
|
211
245
|
) {
|
|
212
246
|
const names = bindings.map(({ name }) => name) as [string, ...string[]];
|
|
213
247
|
const byName = new Map(bindings.map((binding) => [binding.name, binding]));
|
|
214
|
-
const resourceInput = z.object({
|
|
215
|
-
name: z.enum(names).describe("Activated Skill name"),
|
|
216
|
-
path: z.string().min(1).describe("Bundled resource path listed by activate_skill"),
|
|
217
|
-
destination: z.string().min(1).startsWith("/").describe(
|
|
218
|
-
"Absolute destination path in the Workspace",
|
|
219
|
-
),
|
|
220
|
-
});
|
|
221
248
|
return tool({
|
|
222
249
|
description:
|
|
223
|
-
"Copy complete bundled Skill
|
|
250
|
+
"Copy a complete bundled Skill resource directly into the Workspace without returning its contents to the model. " +
|
|
224
251
|
"Use this instead of read_skill_resource when a template, script, image, font, or other asset must become a Workspace file. " +
|
|
225
|
-
"
|
|
252
|
+
"When multiple resources are needed, copy them in one execute with a loop or Promise.all; never start one execute per resource. " +
|
|
226
253
|
"The destination is created or overwritten, including parent directories.",
|
|
227
254
|
inputSchema: z.object({
|
|
228
|
-
|
|
255
|
+
name: z.enum(names).describe("Activated Skill name"),
|
|
256
|
+
path: z.string().min(1).describe("Bundled resource path listed by activate_skill"),
|
|
257
|
+
destination: z.string().min(1).describe("Absolute destination path in the Workspace"),
|
|
229
258
|
}),
|
|
230
|
-
execute: async ({
|
|
259
|
+
execute: async ({ name, path, destination }, { abortSignal }) => {
|
|
231
260
|
abortSignal?.throwIfAborted();
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
throw new Error(`Duplicate materialize destination: ${destination}`);
|
|
236
|
-
}
|
|
237
|
-
destinations.add(destination);
|
|
261
|
+
const source = byName.get(name)?.source;
|
|
262
|
+
if (!source?.readResource) {
|
|
263
|
+
throw new Error(`Skill \"${name}\" has no readable resources.`);
|
|
238
264
|
}
|
|
265
|
+
const resource = await source.readResource(name, path);
|
|
266
|
+
if (!resource) throw new Error(`Resource not found: ${name}/${path}`);
|
|
267
|
+
abortSignal?.throwIfAborted();
|
|
268
|
+
const bytes = resourceBytes(resource);
|
|
239
269
|
|
|
240
|
-
|
|
241
|
-
for (const { name, path, destination } of resources) {
|
|
270
|
+
await serializeWorkspaceMutation(workspace, destination, async () => {
|
|
242
271
|
abortSignal?.throwIfAborted();
|
|
243
|
-
const
|
|
244
|
-
if (
|
|
245
|
-
|
|
246
|
-
status: "error",
|
|
247
|
-
name,
|
|
248
|
-
path,
|
|
249
|
-
destination,
|
|
250
|
-
error: `Skill \"${name}\" has no readable resources.`,
|
|
251
|
-
});
|
|
252
|
-
continue;
|
|
272
|
+
const parent = destination.replace(/\/[^/]+$/, "");
|
|
273
|
+
if (parent && parent !== "/") {
|
|
274
|
+
await workspace.mkdir(parent, { recursive: true });
|
|
253
275
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if (!resource) {
|
|
257
|
-
results.push({
|
|
258
|
-
status: "error",
|
|
259
|
-
name,
|
|
260
|
-
path,
|
|
261
|
-
destination,
|
|
262
|
-
error: `Resource not found: ${name}/${path}`,
|
|
263
|
-
});
|
|
264
|
-
continue;
|
|
265
|
-
}
|
|
266
|
-
abortSignal?.throwIfAborted();
|
|
267
|
-
const bytes = resourceBytes(resource);
|
|
268
|
-
|
|
269
|
-
await serializeWorkspaceMutation(workspace, destination, async () => {
|
|
270
|
-
abortSignal?.throwIfAborted();
|
|
271
|
-
const parent = destination.replace(/\/[^/]+$/, "");
|
|
272
|
-
if (parent && parent !== "/") {
|
|
273
|
-
await workspace.mkdir(parent, { recursive: true });
|
|
274
|
-
}
|
|
275
|
-
await workspace.writeFileBytes(destination, bytes, resource.mimeType);
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
results.push({
|
|
279
|
-
status: "written",
|
|
280
|
-
name,
|
|
281
|
-
path: resource.path,
|
|
282
|
-
destination,
|
|
283
|
-
bytesWritten: bytes.byteLength,
|
|
284
|
-
encoding: resource.encoding ?? "text",
|
|
285
|
-
...(resource.mimeType ? { mimeType: resource.mimeType } : {}),
|
|
286
|
-
});
|
|
287
|
-
} catch (error) {
|
|
288
|
-
abortSignal?.throwIfAborted();
|
|
289
|
-
results.push({
|
|
290
|
-
status: "error",
|
|
291
|
-
name,
|
|
292
|
-
path,
|
|
293
|
-
destination,
|
|
294
|
-
error: "Resource materialization failed",
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
}
|
|
276
|
+
await workspace.writeFileBytes(destination, bytes, resource.mimeType);
|
|
277
|
+
});
|
|
298
278
|
|
|
299
279
|
return {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
280
|
+
name,
|
|
281
|
+
path: resource.path,
|
|
282
|
+
destination,
|
|
283
|
+
bytesWritten: bytes.byteLength,
|
|
284
|
+
encoding: resource.encoding ?? "text",
|
|
285
|
+
...(resource.mimeType ? { mimeType: resource.mimeType } : {}),
|
|
303
286
|
};
|
|
304
287
|
},
|
|
305
288
|
});
|
|
@@ -368,6 +351,9 @@ export async function skillPiToolCandidates(
|
|
|
368
351
|
return execute(toolCallId, input, signal);
|
|
369
352
|
};
|
|
370
353
|
}
|
|
354
|
+
if (name === "run_skill_script") {
|
|
355
|
+
adapted.prepareArguments = normalizeRunSkillScriptArguments;
|
|
356
|
+
}
|
|
371
357
|
return {
|
|
372
358
|
owner: "runtime-skill",
|
|
373
359
|
requiredExecutionLevel:
|
|
@@ -380,6 +366,9 @@ export async function skillPiToolCandidates(
|
|
|
380
366
|
...(name === "activate_skill"
|
|
381
367
|
? { outputBudget: { kind: "structure" as const } }
|
|
382
368
|
: {}),
|
|
369
|
+
...(name === "materialize_skill_resource"
|
|
370
|
+
? { codeExecutionOnly: true as const }
|
|
371
|
+
: {}),
|
|
383
372
|
tool: adapted,
|
|
384
373
|
};
|
|
385
374
|
});
|
package/src/runtime-agent.ts
CHANGED
|
@@ -672,6 +672,9 @@ export function defineRuntimeAgent<
|
|
|
672
672
|
...(assembly.bindings?.workspace
|
|
673
673
|
? { workspace: assembly.bindings.workspace }
|
|
674
674
|
: {}),
|
|
675
|
+
...(assembly.bindings?.codeExecution
|
|
676
|
+
? { codeExecution: assembly.bindings.codeExecution }
|
|
677
|
+
: {}),
|
|
675
678
|
},
|
|
676
679
|
memoryProfile: {
|
|
677
680
|
enabled: false,
|
package/src/runtime-assembler.ts
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
} from "./lib/execution-level";
|
|
31
31
|
import {
|
|
32
32
|
createPiRuntimeAssembly,
|
|
33
|
+
type FinalizedPiToolSurface,
|
|
33
34
|
type PiToolSurface,
|
|
34
35
|
type PiRuntimeAssembly,
|
|
35
36
|
} from "./pi/assembly/snapshot";
|
|
@@ -43,6 +44,7 @@ import type {
|
|
|
43
44
|
RuntimeSkillContribution,
|
|
44
45
|
RuntimeToolSurfacePolicy,
|
|
45
46
|
} from "./runtime-definition";
|
|
47
|
+
import type { RuntimeCodeExecutionFactory } from "./kernel/bindings";
|
|
46
48
|
import {
|
|
47
49
|
basePiToolCandidates,
|
|
48
50
|
memoryPiToolCandidate,
|
|
@@ -50,6 +52,7 @@ import {
|
|
|
50
52
|
import {
|
|
51
53
|
BROWSER_EXECUTE_TOOL_NAME,
|
|
52
54
|
browserExecutionPiToolCandidate,
|
|
55
|
+
codeExecutionPiToolCandidate,
|
|
53
56
|
} from "./pi/tool/core";
|
|
54
57
|
import { skillPiToolCandidates } from "./pi/tool/skill";
|
|
55
58
|
import { createWebSearch } from "./pi/tool/web-search";
|
|
@@ -80,6 +83,7 @@ export interface RuntimeAssemblyInput {
|
|
|
80
83
|
readonly provider: RuntimeProviderPort;
|
|
81
84
|
readonly platform: RuntimePlatformPort;
|
|
82
85
|
readonly workspace?: WorkspacePort;
|
|
86
|
+
readonly codeExecution?: RuntimeCodeExecutionFactory;
|
|
83
87
|
readonly sandbox?: RuntimeSandboxPort;
|
|
84
88
|
readonly memory?: RuntimeMemoryPort;
|
|
85
89
|
readonly hostTools: readonly PiToolCandidate[];
|
|
@@ -153,6 +157,7 @@ function assertMemoryProfile(profile: RuntimeMemoryProfile): void {
|
|
|
153
157
|
}
|
|
154
158
|
|
|
155
159
|
interface ToolSurfaceInput {
|
|
160
|
+
readonly codeExecution?: RuntimeCodeExecutionFactory;
|
|
156
161
|
readonly platform: RuntimePlatformPort;
|
|
157
162
|
readonly workspace?: WorkspacePort;
|
|
158
163
|
readonly memory?: {
|
|
@@ -198,10 +203,15 @@ async function createToolSurface(
|
|
|
198
203
|
});
|
|
199
204
|
// 平台没有浏览器能力时注册空集:宁可没有这个 Tool,也不注册一个必然失败的 Tool 误导模型。
|
|
200
205
|
// `create()` 推迟到确认这个名字真的可见之后,被 deny 的装配不白建一个浏览器连接器。
|
|
206
|
+
// `direct` 使它只走 Direct 调用,不再被并进 `execute` 的工具集——两个 Code Mode 类工具互相嵌套
|
|
207
|
+
// 会让「哪次执行被记录、被重放」变得无法解释,而它们的分工本就由 System Prompt 划清。
|
|
201
208
|
const browserVisible = !deny.has(BROWSER_EXECUTE_TOOL_NAME) &&
|
|
202
209
|
allowsTool?.(BROWSER_EXECUTE_TOOL_NAME) !== false;
|
|
203
210
|
const browserCandidates = input.platform.browser && browserVisible
|
|
204
|
-
? [
|
|
211
|
+
? [{
|
|
212
|
+
...browserExecutionPiToolCandidate(input.platform.browser.create()),
|
|
213
|
+
direct: true as const,
|
|
214
|
+
}]
|
|
205
215
|
: [];
|
|
206
216
|
const baseCandidates = basePiToolCandidates(input.webSearch);
|
|
207
217
|
const memoryCandidates = input.memory
|
|
@@ -238,16 +248,87 @@ async function createToolSurface(
|
|
|
238
248
|
if (!EXECUTION_LEVELS.includes(candidate.requiredExecutionLevel)) {
|
|
239
249
|
throw new Error(`SpringBrand Tool ${name} execution level is invalid`);
|
|
240
250
|
}
|
|
241
|
-
if (name === "execute") {
|
|
242
|
-
throw new Error("SpringBrand reserved Runtime Tool name: execute");
|
|
243
|
-
}
|
|
244
251
|
if (tools.has(name)) {
|
|
245
252
|
throw new Error(`Duplicate Runtime SpringBrand Tool: ${name}`);
|
|
246
253
|
}
|
|
247
|
-
tools.set(name,
|
|
254
|
+
tools.set(name, Object.freeze({ ...candidate }));
|
|
248
255
|
}
|
|
249
256
|
|
|
250
|
-
|
|
257
|
+
const finalized = [...tools.values()];
|
|
258
|
+
const directVisible = finalized.filter(
|
|
259
|
+
(candidate) => !candidate.codeExecutionOnly,
|
|
260
|
+
);
|
|
261
|
+
if (tools.has("execute")) {
|
|
262
|
+
throw new Error("SpringBrand reserved Runtime Tool name: execute");
|
|
263
|
+
}
|
|
264
|
+
if (
|
|
265
|
+
!input.codeExecution ||
|
|
266
|
+
deny.has("execute") ||
|
|
267
|
+
allowsTool?.("execute") === false
|
|
268
|
+
) {
|
|
269
|
+
return Object.freeze({
|
|
270
|
+
candidates: Object.freeze(directVisible.map(classify)),
|
|
271
|
+
codeExecutionCandidates: Object.freeze([]),
|
|
272
|
+
}) satisfies FinalizedPiToolSurface;
|
|
273
|
+
}
|
|
274
|
+
const mergeable = finalized.filter(
|
|
275
|
+
(candidate) =>
|
|
276
|
+
!candidate.direct &&
|
|
277
|
+
!candidate.interaction &&
|
|
278
|
+
typeof candidate.tool.execute === "function",
|
|
279
|
+
);
|
|
280
|
+
const codeExecutionCandidates = Object.freeze([...mergeable]);
|
|
281
|
+
const codeExecutionToolNames = new Set(
|
|
282
|
+
codeExecutionCandidates.map(({ tool }) => tool.name),
|
|
283
|
+
);
|
|
284
|
+
const codeExecution = input.codeExecution.create(
|
|
285
|
+
codeExecutionCandidates,
|
|
286
|
+
);
|
|
287
|
+
const topLevelOnly = directVisible.filter(
|
|
288
|
+
({ tool }) => !codeExecutionToolNames.has(tool.name),
|
|
289
|
+
);
|
|
290
|
+
const routeList = (deferred: boolean) => {
|
|
291
|
+
const matches = topLevelOnly.filter((candidate) =>
|
|
292
|
+
(defersTool?.(candidate.tool.name) === true) === deferred
|
|
293
|
+
);
|
|
294
|
+
return matches.length > 0
|
|
295
|
+
? matches.map(({ tool }) =>
|
|
296
|
+
`- \`${tool.name}\` — ${(tool.label ?? tool.name)
|
|
297
|
+
.replaceAll(/\s+/g, " ").trim().replaceAll("`", "'")}`
|
|
298
|
+
).join("\n")
|
|
299
|
+
: "- None.";
|
|
300
|
+
};
|
|
301
|
+
const routeGuidance = [
|
|
302
|
+
"## Tool routing",
|
|
303
|
+
"Top-level deferred Tools are not callable through `tools.*`. Use Provider Tool Search with their exact name, then call them directly.",
|
|
304
|
+
"Top-level immediate Tools are already available outside execute; call them directly.",
|
|
305
|
+
"",
|
|
306
|
+
"## Top-level deferred Tools",
|
|
307
|
+
routeList(true),
|
|
308
|
+
"",
|
|
309
|
+
"## Top-level immediate Tools",
|
|
310
|
+
routeList(false),
|
|
311
|
+
"",
|
|
312
|
+
].join("\n");
|
|
313
|
+
const materializeGuidance = codeExecutionCandidates.some(
|
|
314
|
+
({ tool }) => tool.name === "materialize_skill_resource",
|
|
315
|
+
)
|
|
316
|
+
? "Skill resource copying is available only inside execute through " +
|
|
317
|
+
"`tools.materialize_skill_resource({ name, path, destination })`; " +
|
|
318
|
+
"no Direct Tool is registered for it. " +
|
|
319
|
+
"Use a loop or Promise.all when copying multiple resources.\n\n"
|
|
320
|
+
: "";
|
|
321
|
+
return Object.freeze({
|
|
322
|
+
candidates: Object.freeze([
|
|
323
|
+
classify(codeExecutionPiToolCandidate({
|
|
324
|
+
...codeExecution,
|
|
325
|
+
description:
|
|
326
|
+
routeGuidance + "\n" + materializeGuidance + codeExecution.description,
|
|
327
|
+
})),
|
|
328
|
+
...directVisible.map(classify),
|
|
329
|
+
]),
|
|
330
|
+
codeExecutionCandidates,
|
|
331
|
+
}) satisfies FinalizedPiToolSurface;
|
|
251
332
|
},
|
|
252
333
|
}),
|
|
253
334
|
extensions: input.extensions.filter(
|
|
@@ -275,6 +356,7 @@ class RuntimeBuilder {
|
|
|
275
356
|
private platform?: RuntimePlatformPort;
|
|
276
357
|
private gateway?: RuntimeGatewayPort;
|
|
277
358
|
private workspace?: WorkspacePort;
|
|
359
|
+
private codeExecution?: RuntimeCodeExecutionFactory;
|
|
278
360
|
private sandbox?: RuntimeSandboxPort;
|
|
279
361
|
private schedule?: RuntimeSchedulePort;
|
|
280
362
|
private memoryProfile: RuntimeMemoryProfile = DISABLED_MEMORY;
|
|
@@ -310,6 +392,7 @@ class RuntimeBuilder {
|
|
|
310
392
|
this.provider = input.provider;
|
|
311
393
|
this.platform = input.platform;
|
|
312
394
|
this.workspace = input.workspace;
|
|
395
|
+
this.codeExecution = input.codeExecution;
|
|
313
396
|
this.sandbox = input.sandbox;
|
|
314
397
|
this.memoryProfile = input.memoryProfile;
|
|
315
398
|
this.memory = input.memory;
|
|
@@ -449,6 +532,9 @@ class RuntimeBuilder {
|
|
|
449
532
|
if (!this.platform.loader) {
|
|
450
533
|
throw new Error("Runtime platform loader is required");
|
|
451
534
|
}
|
|
535
|
+
if (typeof this.platform.outbound !== "function") {
|
|
536
|
+
throw new Error("Runtime platform outbound port is required");
|
|
537
|
+
}
|
|
452
538
|
|
|
453
539
|
assertMemoryProfile(this.memoryProfile);
|
|
454
540
|
if (this.memoryProfile.enabled && (!this.workspace || !this.memory)) {
|
|
@@ -498,6 +584,7 @@ class RuntimeBuilder {
|
|
|
498
584
|
});
|
|
499
585
|
const skillSources = [...this.skillSources.values()];
|
|
500
586
|
const toolSurface = await createToolSurface({
|
|
587
|
+
...(this.codeExecution ? { codeExecution: this.codeExecution } : {}),
|
|
501
588
|
platform: this.platform,
|
|
502
589
|
...(this.workspace ? { workspace: this.workspace } : {}),
|
|
503
590
|
...(this.memoryProfile.enabled && this.memory
|
|
@@ -522,6 +609,7 @@ class RuntimeBuilder {
|
|
|
522
609
|
const bindings: RuntimeBindings = Object.freeze({
|
|
523
610
|
provider,
|
|
524
611
|
platform: Object.freeze({ ...this.platform }),
|
|
612
|
+
...(this.codeExecution ? { codeExecution: this.codeExecution } : {}),
|
|
525
613
|
...(this.gateway ? { gateway: this.gateway } : {}),
|
|
526
614
|
...(this.workspace ? { workspace: this.workspace } : {}),
|
|
527
615
|
...(this.memoryProfile.enabled && this.memory
|
|
@@ -666,6 +754,9 @@ export async function assembleRuntimeSnapshot<
|
|
|
666
754
|
...(toolAssembly.bindings?.workspace
|
|
667
755
|
? { workspace: toolAssembly.bindings.workspace }
|
|
668
756
|
: {}),
|
|
757
|
+
...(toolAssembly.bindings?.codeExecution
|
|
758
|
+
? { codeExecution: toolAssembly.bindings.codeExecution }
|
|
759
|
+
: {}),
|
|
669
760
|
...(memoryProfile.enabled && toolAssembly.bindings?.memory
|
|
670
761
|
? { memory: toolAssembly.bindings.memory }
|
|
671
762
|
: {}),
|
|
@@ -33,6 +33,7 @@ export interface ToolSurfaceSelectionPolicy {
|
|
|
33
33
|
/** Kernel 绑定仍需要的执行后端;不出现在 Definition 公开类型里。 */
|
|
34
34
|
export interface RuntimeToolBindings {
|
|
35
35
|
readonly workspace?: import("./kernel/bindings").WorkspacePort;
|
|
36
|
+
readonly codeExecution?: import("./kernel/bindings").RuntimeCodeExecutionFactory;
|
|
36
37
|
readonly memory?: import("./kernel/bindings").RuntimeMemoryPort;
|
|
37
38
|
readonly subagents?: import("./kernel/bindings").RuntimeSubagentPort;
|
|
38
39
|
}
|
package/src/runtime.ts
CHANGED
|
@@ -975,6 +975,28 @@ export abstract class AgentRuntimeKernel<
|
|
|
975
975
|
occurredAt: event.timestamp,
|
|
976
976
|
});
|
|
977
977
|
},
|
|
978
|
+
onNestedToolStarted: (event) => {
|
|
979
|
+
this.telemetry.capture("toolStarted", {
|
|
980
|
+
submissionId: submission.submissionId,
|
|
981
|
+
...event,
|
|
982
|
+
});
|
|
983
|
+
},
|
|
984
|
+
onNestedToolFinished: (event) => {
|
|
985
|
+
this.telemetry.capture("toolFinished", {
|
|
986
|
+
submissionId: submission.submissionId,
|
|
987
|
+
parentToolCallId: event.parentToolCallId,
|
|
988
|
+
toolCallId: event.toolCallId,
|
|
989
|
+
toolName: event.toolName,
|
|
990
|
+
outcome: event.outcome,
|
|
991
|
+
durationMs: event.durationMs,
|
|
992
|
+
...(event.output ? {
|
|
993
|
+
output: event.output,
|
|
994
|
+
outputBytes: json(event.output).length,
|
|
995
|
+
} : {}),
|
|
996
|
+
...(event.error ? { error: errorText(event.error) } : {}),
|
|
997
|
+
occurredAt: event.occurredAt,
|
|
998
|
+
});
|
|
999
|
+
},
|
|
978
1000
|
...(toolExecutors && Object.keys(toolExecutors).length > 0
|
|
979
1001
|
? { toolExecutors }
|
|
980
1002
|
: {}),
|