@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/runtime-assembler.ts
CHANGED
|
@@ -30,11 +30,15 @@ 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";
|
|
36
37
|
import type { AgentTool } from "@earendil-works/pi-agent-core";
|
|
37
|
-
import type {
|
|
38
|
+
import type {
|
|
39
|
+
PiToolCandidate,
|
|
40
|
+
ToolExposureMode,
|
|
41
|
+
} from "./pi/tool/compiler";
|
|
38
42
|
import type {
|
|
39
43
|
AssembleRuntimeSnapshotInput,
|
|
40
44
|
RuntimeSettings,
|
|
@@ -43,6 +47,7 @@ import type {
|
|
|
43
47
|
RuntimeSkillContribution,
|
|
44
48
|
RuntimeToolSurfacePolicy,
|
|
45
49
|
} from "./runtime-definition";
|
|
50
|
+
import type { RuntimeCodeExecutionFactory } from "./kernel/bindings";
|
|
46
51
|
import {
|
|
47
52
|
basePiToolCandidates,
|
|
48
53
|
memoryPiToolCandidate,
|
|
@@ -50,6 +55,7 @@ import {
|
|
|
50
55
|
import {
|
|
51
56
|
BROWSER_EXECUTE_TOOL_NAME,
|
|
52
57
|
browserExecutionPiToolCandidate,
|
|
58
|
+
codeExecutionPiToolCandidate,
|
|
53
59
|
} from "./pi/tool/core";
|
|
54
60
|
import { skillPiToolCandidates } from "./pi/tool/skill";
|
|
55
61
|
import { createWebSearch } from "./pi/tool/web-search";
|
|
@@ -80,6 +86,7 @@ export interface RuntimeAssemblyInput {
|
|
|
80
86
|
readonly provider: RuntimeProviderPort;
|
|
81
87
|
readonly platform: RuntimePlatformPort;
|
|
82
88
|
readonly workspace?: WorkspacePort;
|
|
89
|
+
readonly codeExecution?: RuntimeCodeExecutionFactory;
|
|
83
90
|
readonly sandbox?: RuntimeSandboxPort;
|
|
84
91
|
readonly memory?: RuntimeMemoryPort;
|
|
85
92
|
readonly hostTools: readonly PiToolCandidate[];
|
|
@@ -153,6 +160,7 @@ function assertMemoryProfile(profile: RuntimeMemoryProfile): void {
|
|
|
153
160
|
}
|
|
154
161
|
|
|
155
162
|
interface ToolSurfaceInput {
|
|
163
|
+
readonly codeExecution?: RuntimeCodeExecutionFactory;
|
|
156
164
|
readonly platform: RuntimePlatformPort;
|
|
157
165
|
readonly workspace?: WorkspacePort;
|
|
158
166
|
readonly memory?: {
|
|
@@ -173,6 +181,10 @@ interface ToolSurface {
|
|
|
173
181
|
readonly degradations: readonly RuntimeDegradation[];
|
|
174
182
|
}
|
|
175
183
|
|
|
184
|
+
function exposureOf(candidate: PiToolCandidate): ToolExposureMode {
|
|
185
|
+
return candidate.exposureMode ?? "both";
|
|
186
|
+
}
|
|
187
|
+
|
|
176
188
|
// 唯一 Tool Surface seam:只读取已配置 Port、Resource 和 Agent policy,
|
|
177
189
|
// 统一生成可见 Tool,并在一处做名称、执行档位与冲突校验。
|
|
178
190
|
async function createToolSurface(
|
|
@@ -198,10 +210,15 @@ async function createToolSurface(
|
|
|
198
210
|
});
|
|
199
211
|
// 平台没有浏览器能力时注册空集:宁可没有这个 Tool,也不注册一个必然失败的 Tool 误导模型。
|
|
200
212
|
// `create()` 推迟到确认这个名字真的可见之后,被 deny 的装配不白建一个浏览器连接器。
|
|
213
|
+
// `direct` exposure 使它只走 Direct 调用,不再被并进 `execute` 的工具集——两个 Code Mode 类工具互相嵌套
|
|
214
|
+
// 会让「哪次执行被记录、被重放」变得无法解释,而它们的分工本就由 System Prompt 划清。
|
|
201
215
|
const browserVisible = !deny.has(BROWSER_EXECUTE_TOOL_NAME) &&
|
|
202
216
|
allowsTool?.(BROWSER_EXECUTE_TOOL_NAME) !== false;
|
|
203
217
|
const browserCandidates = input.platform.browser && browserVisible
|
|
204
|
-
? [
|
|
218
|
+
? [{
|
|
219
|
+
...browserExecutionPiToolCandidate(input.platform.browser.create()),
|
|
220
|
+
exposureMode: "direct" as const,
|
|
221
|
+
}]
|
|
205
222
|
: [];
|
|
206
223
|
const baseCandidates = basePiToolCandidates(input.webSearch);
|
|
207
224
|
const memoryCandidates = input.memory
|
|
@@ -238,16 +255,87 @@ async function createToolSurface(
|
|
|
238
255
|
if (!EXECUTION_LEVELS.includes(candidate.requiredExecutionLevel)) {
|
|
239
256
|
throw new Error(`SpringBrand Tool ${name} execution level is invalid`);
|
|
240
257
|
}
|
|
241
|
-
if (name === "execute") {
|
|
242
|
-
throw new Error("SpringBrand reserved Runtime Tool name: execute");
|
|
243
|
-
}
|
|
244
258
|
if (tools.has(name)) {
|
|
245
259
|
throw new Error(`Duplicate Runtime SpringBrand Tool: ${name}`);
|
|
246
260
|
}
|
|
247
|
-
tools.set(name,
|
|
261
|
+
tools.set(name, Object.freeze({ ...candidate }));
|
|
248
262
|
}
|
|
249
263
|
|
|
250
|
-
|
|
264
|
+
const finalized = [...tools.values()];
|
|
265
|
+
const directVisible = finalized.filter(
|
|
266
|
+
(candidate) => exposureOf(candidate) !== "codemode",
|
|
267
|
+
);
|
|
268
|
+
if (tools.has("execute")) {
|
|
269
|
+
throw new Error("SpringBrand reserved Runtime Tool name: execute");
|
|
270
|
+
}
|
|
271
|
+
if (
|
|
272
|
+
!input.codeExecution ||
|
|
273
|
+
deny.has("execute") ||
|
|
274
|
+
allowsTool?.("execute") === false
|
|
275
|
+
) {
|
|
276
|
+
return Object.freeze({
|
|
277
|
+
candidates: Object.freeze(directVisible.map(classify)),
|
|
278
|
+
codeExecutionCandidates: Object.freeze([]),
|
|
279
|
+
}) satisfies FinalizedPiToolSurface;
|
|
280
|
+
}
|
|
281
|
+
const mergeable = finalized.filter(
|
|
282
|
+
(candidate) =>
|
|
283
|
+
exposureOf(candidate) !== "direct" &&
|
|
284
|
+
!candidate.interaction &&
|
|
285
|
+
typeof candidate.tool.execute === "function",
|
|
286
|
+
);
|
|
287
|
+
const codeExecutionCandidates = Object.freeze([...mergeable]);
|
|
288
|
+
const codeExecutionToolNames = new Set(
|
|
289
|
+
codeExecutionCandidates.map(({ tool }) => tool.name),
|
|
290
|
+
);
|
|
291
|
+
const codeExecution = input.codeExecution.create(
|
|
292
|
+
codeExecutionCandidates,
|
|
293
|
+
);
|
|
294
|
+
const topLevelOnly = directVisible.filter(
|
|
295
|
+
({ tool }) => !codeExecutionToolNames.has(tool.name),
|
|
296
|
+
);
|
|
297
|
+
const routeList = (deferred: boolean) => {
|
|
298
|
+
const matches = topLevelOnly.filter((candidate) =>
|
|
299
|
+
(defersTool?.(candidate.tool.name) === true) === deferred
|
|
300
|
+
);
|
|
301
|
+
return matches.length > 0
|
|
302
|
+
? matches.map(({ tool }) =>
|
|
303
|
+
`- \`${tool.name}\` — ${(tool.label ?? tool.name)
|
|
304
|
+
.replaceAll(/\s+/g, " ").trim().replaceAll("`", "'")}`
|
|
305
|
+
).join("\n")
|
|
306
|
+
: "- None.";
|
|
307
|
+
};
|
|
308
|
+
const routeGuidance = [
|
|
309
|
+
"## Tool routing",
|
|
310
|
+
"Top-level deferred Tools are not callable through `tools.*`. Use Provider Tool Search with their exact name, then call them directly.",
|
|
311
|
+
"Top-level immediate Tools are already available outside execute; call them directly.",
|
|
312
|
+
"",
|
|
313
|
+
"## Top-level deferred Tools",
|
|
314
|
+
routeList(true),
|
|
315
|
+
"",
|
|
316
|
+
"## Top-level immediate Tools",
|
|
317
|
+
routeList(false),
|
|
318
|
+
"",
|
|
319
|
+
].join("\n");
|
|
320
|
+
const materializeGuidance = codeExecutionCandidates.some(
|
|
321
|
+
({ tool }) => tool.name === "materialize_skill_resource",
|
|
322
|
+
)
|
|
323
|
+
? "Skill resource copying is available only inside execute through " +
|
|
324
|
+
"`tools.materialize_skill_resource({ name, path, destination })`; " +
|
|
325
|
+
"no Direct Tool is registered for it. " +
|
|
326
|
+
"Copy multiple resources sequentially in one execute so durable replay preserves call order.\n\n"
|
|
327
|
+
: "";
|
|
328
|
+
return Object.freeze({
|
|
329
|
+
candidates: Object.freeze([
|
|
330
|
+
classify(codeExecutionPiToolCandidate({
|
|
331
|
+
...codeExecution,
|
|
332
|
+
description:
|
|
333
|
+
routeGuidance + "\n" + materializeGuidance + codeExecution.description,
|
|
334
|
+
})),
|
|
335
|
+
...directVisible.map(classify),
|
|
336
|
+
]),
|
|
337
|
+
codeExecutionCandidates,
|
|
338
|
+
}) satisfies FinalizedPiToolSurface;
|
|
251
339
|
},
|
|
252
340
|
}),
|
|
253
341
|
extensions: input.extensions.filter(
|
|
@@ -275,6 +363,7 @@ class RuntimeBuilder {
|
|
|
275
363
|
private platform?: RuntimePlatformPort;
|
|
276
364
|
private gateway?: RuntimeGatewayPort;
|
|
277
365
|
private workspace?: WorkspacePort;
|
|
366
|
+
private codeExecution?: RuntimeCodeExecutionFactory;
|
|
278
367
|
private sandbox?: RuntimeSandboxPort;
|
|
279
368
|
private schedule?: RuntimeSchedulePort;
|
|
280
369
|
private memoryProfile: RuntimeMemoryProfile = DISABLED_MEMORY;
|
|
@@ -310,6 +399,7 @@ class RuntimeBuilder {
|
|
|
310
399
|
this.provider = input.provider;
|
|
311
400
|
this.platform = input.platform;
|
|
312
401
|
this.workspace = input.workspace;
|
|
402
|
+
this.codeExecution = input.codeExecution;
|
|
313
403
|
this.sandbox = input.sandbox;
|
|
314
404
|
this.memoryProfile = input.memoryProfile;
|
|
315
405
|
this.memory = input.memory;
|
|
@@ -449,6 +539,9 @@ class RuntimeBuilder {
|
|
|
449
539
|
if (!this.platform.loader) {
|
|
450
540
|
throw new Error("Runtime platform loader is required");
|
|
451
541
|
}
|
|
542
|
+
if (typeof this.platform.outbound !== "function") {
|
|
543
|
+
throw new Error("Runtime platform outbound port is required");
|
|
544
|
+
}
|
|
452
545
|
|
|
453
546
|
assertMemoryProfile(this.memoryProfile);
|
|
454
547
|
if (this.memoryProfile.enabled && (!this.workspace || !this.memory)) {
|
|
@@ -498,6 +591,7 @@ class RuntimeBuilder {
|
|
|
498
591
|
});
|
|
499
592
|
const skillSources = [...this.skillSources.values()];
|
|
500
593
|
const toolSurface = await createToolSurface({
|
|
594
|
+
...(this.codeExecution ? { codeExecution: this.codeExecution } : {}),
|
|
501
595
|
platform: this.platform,
|
|
502
596
|
...(this.workspace ? { workspace: this.workspace } : {}),
|
|
503
597
|
...(this.memoryProfile.enabled && this.memory
|
|
@@ -522,6 +616,7 @@ class RuntimeBuilder {
|
|
|
522
616
|
const bindings: RuntimeBindings = Object.freeze({
|
|
523
617
|
provider,
|
|
524
618
|
platform: Object.freeze({ ...this.platform }),
|
|
619
|
+
...(this.codeExecution ? { codeExecution: this.codeExecution } : {}),
|
|
525
620
|
...(this.gateway ? { gateway: this.gateway } : {}),
|
|
526
621
|
...(this.workspace ? { workspace: this.workspace } : {}),
|
|
527
622
|
...(this.memoryProfile.enabled && this.memory
|
|
@@ -666,6 +761,9 @@ export async function assembleRuntimeSnapshot<
|
|
|
666
761
|
...(toolAssembly.bindings?.workspace
|
|
667
762
|
? { workspace: toolAssembly.bindings.workspace }
|
|
668
763
|
: {}),
|
|
764
|
+
...(toolAssembly.bindings?.codeExecution
|
|
765
|
+
? { codeExecution: toolAssembly.bindings.codeExecution }
|
|
766
|
+
: {}),
|
|
669
767
|
...(memoryProfile.enabled && toolAssembly.bindings?.memory
|
|
670
768
|
? { memory: toolAssembly.bindings.memory }
|
|
671
769
|
: {}),
|
|
@@ -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
|
@@ -899,6 +899,7 @@ export abstract class AgentRuntimeKernel<
|
|
|
899
899
|
return this.pi.createTurn({
|
|
900
900
|
prepared: this.preparedPi(),
|
|
901
901
|
pinnedDescriptor: submission.assemblyDescriptor,
|
|
902
|
+
modelSessionId: this.name,
|
|
902
903
|
submission: {
|
|
903
904
|
id: submission.submissionId,
|
|
904
905
|
requestId: submission.requestId,
|
|
@@ -975,6 +976,29 @@ export abstract class AgentRuntimeKernel<
|
|
|
975
976
|
occurredAt: event.timestamp,
|
|
976
977
|
});
|
|
977
978
|
},
|
|
979
|
+
onNestedToolStarted: (event) => {
|
|
980
|
+
this.telemetry.capture("toolStarted", {
|
|
981
|
+
submissionId: submission.submissionId,
|
|
982
|
+
...event,
|
|
983
|
+
});
|
|
984
|
+
},
|
|
985
|
+
onNestedToolFinished: (event) => {
|
|
986
|
+
this.telemetry.capture("toolFinished", {
|
|
987
|
+
submissionId: submission.submissionId,
|
|
988
|
+
parentToolCallId: event.parentToolCallId,
|
|
989
|
+
toolCallId: event.toolCallId,
|
|
990
|
+
toolName: event.toolName,
|
|
991
|
+
outcome: event.outcome,
|
|
992
|
+
durationMs: event.durationMs,
|
|
993
|
+
...(event.output ? {
|
|
994
|
+
output: event.output,
|
|
995
|
+
outputBytes: json(event.output).length,
|
|
996
|
+
} : {}),
|
|
997
|
+
...(event.error ? { error: errorText(event.error) } : {}),
|
|
998
|
+
occurredAt: event.occurredAt,
|
|
999
|
+
});
|
|
1000
|
+
this.projectNestedToolSettlement(submission.submissionId, event);
|
|
1001
|
+
},
|
|
978
1002
|
...(toolExecutors && Object.keys(toolExecutors).length > 0
|
|
979
1003
|
? { toolExecutors }
|
|
980
1004
|
: {}),
|
|
@@ -2178,6 +2202,32 @@ export abstract class AgentRuntimeKernel<
|
|
|
2178
2202
|
return this.drainRuntimeEvents();
|
|
2179
2203
|
}
|
|
2180
2204
|
|
|
2205
|
+
private projectNestedToolSettlement(
|
|
2206
|
+
submissionId: string,
|
|
2207
|
+
event: Readonly<{
|
|
2208
|
+
toolCallId: string;
|
|
2209
|
+
toolName: string;
|
|
2210
|
+
outcome: "completed" | "failed" | "cancelled";
|
|
2211
|
+
occurredAt: number;
|
|
2212
|
+
}>,
|
|
2213
|
+
): void {
|
|
2214
|
+
const settlement: RuntimeToolSettlementEvent = {
|
|
2215
|
+
eventId: `${submissionId}:tool:${event.toolCallId}`,
|
|
2216
|
+
submissionId,
|
|
2217
|
+
toolCallId: event.toolCallId,
|
|
2218
|
+
toolName: event.toolName,
|
|
2219
|
+
status: event.outcome === "completed" ? "success" : "error",
|
|
2220
|
+
};
|
|
2221
|
+
this.db.transaction(() => {
|
|
2222
|
+
this.db.runtimeEvents.insert({
|
|
2223
|
+
eventId: settlement.eventId,
|
|
2224
|
+
body: json({ type: "tool-settlement", event: settlement }),
|
|
2225
|
+
createdAt: event.occurredAt,
|
|
2226
|
+
});
|
|
2227
|
+
});
|
|
2228
|
+
this.ctx.waitUntil(this.drainRuntimeEvents());
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2181
2231
|
// 终态事务用这个同步内核先补齐 ToolResult,再在同一事务末尾写 marker。
|
|
2182
2232
|
private settleToolSync(
|
|
2183
2233
|
submissionId: string,
|