@springbrand/agent-runtime 0.2.0-alpha.34 → 0.2.0-alpha.38
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 +3 -1
- package/src/adapter/cloudflare/dynamic-worker-observability.ts +84 -0
- package/src/adapter/cloudflare/index.ts +11 -2
- package/src/adapter/cloudflare/scheduling/zoned-cron.ts +114 -0
- package/src/adapter/cloudflare/universal-agent/tools.ts +17 -30
- package/src/index.ts +7 -35
- package/src/kernel/bindings.ts +6 -6
- package/src/pi/assembly/extensions.ts +23 -38
- package/src/pi/assembly/snapshot.ts +7 -1
- package/src/pi/runtime-adapter/assembly.ts +19 -14
- package/src/pi/runtime-adapter/execution.ts +7 -10
- package/src/pi/runtime-adapter/recovery.ts +15 -34
- package/src/pi/runtime-adapter/transcript.ts +5 -12
- package/src/pi/tool/base.ts +0 -15
- package/src/pi/tool/compiler.ts +0 -2
- package/src/pi/tool/core-host.ts +125 -49
- package/src/pi/tool/core.ts +8 -3
- package/src/pi/tool/index.ts +1 -0
- package/src/pi/tool/nested-tools.ts +42 -0
- package/src/pi/tool/schedule.ts +0 -9
- package/src/pi/tool/skill.ts +10 -21
- package/src/pi/tool/subagent.ts +0 -13
- package/src/pi/tool/time.ts +31 -0
- package/src/pi/tool/workspace-revision.ts +0 -12
- package/src/pi/tool/workspace-sandbox.ts +0 -10
- package/src/runtime-agent.ts +35 -45
- package/src/runtime-assembler.ts +68 -192
- package/src/runtime-definition.ts +35 -109
- package/src/runtime.ts +75 -47
- package/src/adapter/cloudflare/universal-agent/hooks.ts +0 -57
- package/src/kernel/tool-surface.ts +0 -42
- package/src/tool-registry.ts +0 -142
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@springbrand/agent-runtime",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.38",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"src",
|
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
"@cloudflare/think": "0.15.0",
|
|
24
24
|
"@earendil-works/pi-agent-core": "0.83.0",
|
|
25
25
|
"@earendil-works/pi-ai": "0.83.0",
|
|
26
|
+
"@types/lodash-es": "^4.17.12",
|
|
27
|
+
"cron-schedule": "6.0.0",
|
|
26
28
|
"isomorphic-git": "1.38.6",
|
|
27
29
|
"lodash-es": "^4.18.1",
|
|
28
30
|
"zod": "^4.4.3"
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { RuntimePlatformPort } from "../../kernel/bindings";
|
|
2
|
+
import { WorkerEntrypoint } from "cloudflare:workers";
|
|
3
|
+
|
|
4
|
+
export type DynamicWorkerObservation = {
|
|
5
|
+
source: string;
|
|
6
|
+
attributes?: Readonly<Record<string, string>>;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type TailProps = DynamicWorkerObservation & {
|
|
10
|
+
workerId: string;
|
|
11
|
+
mainModule: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type ExportContext = Pick<DurableObjectState, "exports">;
|
|
15
|
+
|
|
16
|
+
function attachTail(
|
|
17
|
+
context: ExportContext,
|
|
18
|
+
code: WorkerLoaderWorkerCode,
|
|
19
|
+
observation: DynamicWorkerObservation,
|
|
20
|
+
workerId: string,
|
|
21
|
+
): WorkerLoaderWorkerCode {
|
|
22
|
+
const entrypoint = (context.exports as unknown as {
|
|
23
|
+
DynamicWorkerTail(options: { props: TailProps }): Fetcher;
|
|
24
|
+
}).DynamicWorkerTail;
|
|
25
|
+
const tail = entrypoint({
|
|
26
|
+
props: { ...observation, workerId, mainModule: code.mainModule },
|
|
27
|
+
});
|
|
28
|
+
return { ...code, tails: [...(code.tails ?? []), tail] };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function observeDynamicWorkers(
|
|
32
|
+
context: ExportContext,
|
|
33
|
+
loader: WorkerLoader,
|
|
34
|
+
observation: DynamicWorkerObservation,
|
|
35
|
+
): WorkerLoader {
|
|
36
|
+
return {
|
|
37
|
+
load: (code) =>
|
|
38
|
+
loader.load(attachTail(context, code, observation, crypto.randomUUID())),
|
|
39
|
+
get: (name, getCode) => {
|
|
40
|
+
const workerId = name ?? crypto.randomUUID();
|
|
41
|
+
return loader.get(name, async () =>
|
|
42
|
+
attachTail(context, await getCode(), observation, workerId)
|
|
43
|
+
);
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function observeDynamicWorkerPlatform(
|
|
49
|
+
context: ExportContext,
|
|
50
|
+
load: () => Promise<RuntimePlatformPort>,
|
|
51
|
+
observation: DynamicWorkerObservation,
|
|
52
|
+
): () => Promise<RuntimePlatformPort> {
|
|
53
|
+
return async () => {
|
|
54
|
+
const platform = await load();
|
|
55
|
+
return {
|
|
56
|
+
...platform,
|
|
57
|
+
loader: observeDynamicWorkers(context, platform.loader, observation),
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export class DynamicWorkerTail extends WorkerEntrypoint<Cloudflare.Env, TailProps> {
|
|
63
|
+
tail(events: TraceItem[]): void {
|
|
64
|
+
for (const event of events) {
|
|
65
|
+
console.log({
|
|
66
|
+
...this.ctx.props.attributes,
|
|
67
|
+
source: "dynamic-worker-tail",
|
|
68
|
+
dynamicWorkerSource: this.ctx.props.source,
|
|
69
|
+
dynamicWorkerId: this.ctx.props.workerId,
|
|
70
|
+
durableObjectId: event.durableObjectId ?? null,
|
|
71
|
+
mainModule: this.ctx.props.mainModule,
|
|
72
|
+
eventTimestamp: event.eventTimestamp,
|
|
73
|
+
scriptName: event.scriptName,
|
|
74
|
+
entrypoint: event.entrypoint,
|
|
75
|
+
outcome: event.outcome,
|
|
76
|
+
truncated: event.truncated,
|
|
77
|
+
cpuTime: event.cpuTime,
|
|
78
|
+
wallTime: event.wallTime,
|
|
79
|
+
logs: event.logs,
|
|
80
|
+
exceptions: event.exceptions,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -13,9 +13,18 @@ export {
|
|
|
13
13
|
} from "./universal-agent/preparation";
|
|
14
14
|
export {
|
|
15
15
|
assembleUniversalAgentTools,
|
|
16
|
-
selectTools,
|
|
17
16
|
} from "./universal-agent/tools";
|
|
18
|
-
export {
|
|
17
|
+
export {
|
|
18
|
+
DynamicWorkerTail,
|
|
19
|
+
observeDynamicWorkerPlatform,
|
|
20
|
+
observeDynamicWorkers,
|
|
21
|
+
type DynamicWorkerObservation,
|
|
22
|
+
} from "./dynamic-worker-observability";
|
|
23
|
+
export {
|
|
24
|
+
isValidTimeZone,
|
|
25
|
+
nextCronOccurrence,
|
|
26
|
+
zoneOffsetMs,
|
|
27
|
+
} from "./scheduling/zoned-cron";
|
|
19
28
|
|
|
20
29
|
export {
|
|
21
30
|
CloudflareSandboxAdapter,
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { parseCronExpression } from "cron-schedule";
|
|
2
|
+
|
|
3
|
+
export function zoneOffsetMs(instant: number, timeZone: string): number {
|
|
4
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
5
|
+
timeZone,
|
|
6
|
+
hour12: false,
|
|
7
|
+
year: "numeric",
|
|
8
|
+
month: "2-digit",
|
|
9
|
+
day: "2-digit",
|
|
10
|
+
hour: "2-digit",
|
|
11
|
+
minute: "2-digit",
|
|
12
|
+
second: "2-digit",
|
|
13
|
+
}).formatToParts(new Date(instant));
|
|
14
|
+
const field = (type: Intl.DateTimeFormatPartTypes) =>
|
|
15
|
+
Number(parts.find((part) => part.type === type)?.value ?? 0);
|
|
16
|
+
const wallAsUtc = Date.UTC(
|
|
17
|
+
field("year"),
|
|
18
|
+
field("month") - 1,
|
|
19
|
+
field("day"),
|
|
20
|
+
field("hour") % 24,
|
|
21
|
+
field("minute"),
|
|
22
|
+
field("second"),
|
|
23
|
+
);
|
|
24
|
+
return wallAsUtc - Math.floor(instant / 1_000) * 1_000;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function nextCronOccurrence(
|
|
28
|
+
cron: string,
|
|
29
|
+
timeZone: string,
|
|
30
|
+
after = Date.now(),
|
|
31
|
+
): number {
|
|
32
|
+
const parsed = parseCronExpression(cron);
|
|
33
|
+
const secondPrecision = parsed.seconds.length !== 1 || parsed.seconds[0] !== 0;
|
|
34
|
+
const step = secondPrecision ? 1_000 : 60_000;
|
|
35
|
+
for (
|
|
36
|
+
let instant = Math.floor(after / step) * step + step;
|
|
37
|
+
instant <= after + 3 * 3_600_000;
|
|
38
|
+
instant += step
|
|
39
|
+
) {
|
|
40
|
+
if (matches(parsed, instant + zoneOffsetMs(instant, timeZone))) return instant;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const wallAfter = after + zoneOffsetMs(after, timeZone);
|
|
44
|
+
const wallDate = new Date(wallAfter);
|
|
45
|
+
const firstDay = Date.UTC(
|
|
46
|
+
wallDate.getUTCFullYear(),
|
|
47
|
+
wallDate.getUTCMonth(),
|
|
48
|
+
wallDate.getUTCDate(),
|
|
49
|
+
);
|
|
50
|
+
for (let dayOffset = 0; dayOffset <= 366 * 5; dayOffset += 1) {
|
|
51
|
+
const day = firstDay + dayOffset * 86_400_000;
|
|
52
|
+
if (!matchesDate(parsed, new Date(day))) continue;
|
|
53
|
+
for (const hour of parsed.hours) {
|
|
54
|
+
for (const minute of parsed.minutes) {
|
|
55
|
+
for (const second of parsed.seconds) {
|
|
56
|
+
const wall = day + hour * 3_600_000 + minute * 60_000 + second * 1_000;
|
|
57
|
+
if (wall <= wallAfter) continue;
|
|
58
|
+
const resolved = wallTimeCandidates(wall, timeZone, after)[0];
|
|
59
|
+
if (resolved !== undefined) return resolved;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
throw new Error("Cron has no occurrence in the next five years");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function isValidTimeZone(timeZone: string): boolean {
|
|
68
|
+
try {
|
|
69
|
+
new Intl.DateTimeFormat("en-US", { timeZone });
|
|
70
|
+
return true;
|
|
71
|
+
} catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function wallTimeCandidates(wallUtc: number, timeZone: string, after: number): number[] {
|
|
77
|
+
const offsets = new Set([
|
|
78
|
+
zoneOffsetMs(after, timeZone),
|
|
79
|
+
zoneOffsetMs(wallUtc - 2 * 86_400_000, timeZone),
|
|
80
|
+
zoneOffsetMs(wallUtc, timeZone),
|
|
81
|
+
zoneOffsetMs(wallUtc + 2 * 86_400_000, timeZone),
|
|
82
|
+
]);
|
|
83
|
+
return [...offsets]
|
|
84
|
+
.map((offset) => wallUtc - offset)
|
|
85
|
+
.filter((instant) =>
|
|
86
|
+
instant > after && instant + zoneOffsetMs(instant, timeZone) === wallUtc
|
|
87
|
+
)
|
|
88
|
+
.sort((left, right) => left - right);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function matches(
|
|
92
|
+
cron: ReturnType<typeof parseCronExpression>,
|
|
93
|
+
wallUtc: number,
|
|
94
|
+
): boolean {
|
|
95
|
+
const wall = new Date(wallUtc);
|
|
96
|
+
return cron.seconds.includes(wall.getUTCSeconds()) &&
|
|
97
|
+
cron.minutes.includes(wall.getUTCMinutes()) &&
|
|
98
|
+
cron.hours.includes(wall.getUTCHours()) &&
|
|
99
|
+
matchesDate(cron, wall);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function matchesDate(
|
|
103
|
+
cron: ReturnType<typeof parseCronExpression>,
|
|
104
|
+
wall: Date,
|
|
105
|
+
): boolean {
|
|
106
|
+
if (!cron.months.includes(wall.getUTCMonth())) return false;
|
|
107
|
+
const daysRestricted = cron.days.length !== 31;
|
|
108
|
+
const weekdaysRestricted = cron.weekdays.length !== 7;
|
|
109
|
+
const dayMatches = cron.days.includes(wall.getUTCDate());
|
|
110
|
+
const weekdayMatches = cron.weekdays.includes(wall.getUTCDay());
|
|
111
|
+
return daysRestricted && weekdaysRestricted
|
|
112
|
+
? dayMatches || weekdayMatches
|
|
113
|
+
: dayMatches && weekdayMatches;
|
|
114
|
+
}
|
|
@@ -8,31 +8,18 @@ import type {
|
|
|
8
8
|
import type { RuntimeDegradation } from "../../../kernel/degradation";
|
|
9
9
|
import type { RuntimeMemoryProfile } from "../../../kernel/profile";
|
|
10
10
|
import type { WorkspaceRevisionRestorePort } from "../../../workspace-versioning";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { schedulePiToolCandidates } from "../../../pi/tool/schedule";
|
|
12
|
+
import { workspaceRevisionPiToolCandidate } from "../../../pi/tool/workspace-revision";
|
|
13
13
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
sandboxPiToolCandidates,
|
|
15
|
+
workspacePiToolCandidates,
|
|
16
16
|
} from "../../../pi/tool/workspace-sandbox";
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
type ToolAssemblyResult,
|
|
21
|
-
type ToolRegistry,
|
|
22
|
-
} from "../../../tool-registry";
|
|
23
|
-
|
|
24
|
-
export function selectTools(
|
|
25
|
-
tools: ToolRegistry,
|
|
26
|
-
names: Iterable<string>,
|
|
27
|
-
): ToolRegistry {
|
|
28
|
-
const selected = new Set(names);
|
|
29
|
-
return Object.fromEntries(
|
|
30
|
-
Object.entries(tools).filter(([name]) => selected.has(name)),
|
|
31
|
-
);
|
|
32
|
-
}
|
|
17
|
+
import type { RuntimeCodeExecutionFactory } from "../../../kernel/bindings";
|
|
18
|
+
import type { ToolAssemblyResult } from "../../../runtime-definition";
|
|
19
|
+
import type { PiToolCandidate } from "../../../pi/tool/compiler";
|
|
33
20
|
|
|
34
21
|
export function assembleUniversalAgentTools(options: {
|
|
35
|
-
hostTools?:
|
|
22
|
+
hostTools?: readonly PiToolCandidate[];
|
|
36
23
|
workspace?: WorkspacePort;
|
|
37
24
|
workspaceRevisions?: WorkspaceRevisionRestorePort;
|
|
38
25
|
codeExecution?: RuntimeCodeExecutionFactory;
|
|
@@ -45,15 +32,15 @@ export function assembleUniversalAgentTools(options: {
|
|
|
45
32
|
degradations?: readonly RuntimeDegradation[];
|
|
46
33
|
dispose?: () => Promise<void>;
|
|
47
34
|
}): ToolAssemblyResult {
|
|
48
|
-
const tools =
|
|
49
|
-
options.hostTools ??
|
|
50
|
-
options.workspace ?
|
|
51
|
-
options.workspaceRevisions
|
|
52
|
-
?
|
|
53
|
-
:
|
|
54
|
-
options.sandbox ?
|
|
55
|
-
options.schedule ?
|
|
56
|
-
|
|
35
|
+
const tools = [
|
|
36
|
+
...(options.hostTools ?? []),
|
|
37
|
+
...(options.workspace ? workspacePiToolCandidates(options.workspace) : []),
|
|
38
|
+
...(options.workspaceRevisions
|
|
39
|
+
? [workspaceRevisionPiToolCandidate(options.workspaceRevisions)]
|
|
40
|
+
: []),
|
|
41
|
+
...(options.sandbox ? sandboxPiToolCandidates(options.sandbox) : []),
|
|
42
|
+
...(options.schedule ? schedulePiToolCandidates(options.schedule) : []),
|
|
43
|
+
];
|
|
57
44
|
|
|
58
45
|
return {
|
|
59
46
|
tools,
|
package/src/index.ts
CHANGED
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
* - `canonical transcript` 是 Pi 持久化和恢复 Turn 时使用的权威消息历史。
|
|
24
24
|
* - `admission` 是提示进入 Turn 前的接纳、去重和状态登记流程。
|
|
25
25
|
* - `defineRuntimeAgent.load` 返回 config 和已加载 Resource。
|
|
26
|
-
* - `defineRuntimeAgent.tools`
|
|
27
|
-
* - `assembleRuntimeSnapshot`
|
|
26
|
+
* - `defineRuntimeAgent.tools` 返回 Host 的 Pi Tool Candidate 装配结果。
|
|
27
|
+
* - `assembleRuntimeSnapshot` 消费已加载 Resource 与 Tool,产出候选 Snapshot。
|
|
28
28
|
* - `Port` 是 Runtime 调用外部能力时依赖的最小接口(Definition 作者不直接装配)。
|
|
29
29
|
* - `RuntimeBindings` 是本次装配选中的 Port 和可执行对象集合。
|
|
30
30
|
* - `RuntimeBuilder` 是包内实现,用来校验贡献并生成候选结果。
|
|
@@ -61,33 +61,16 @@ export type {
|
|
|
61
61
|
RuntimeSkillContribution,
|
|
62
62
|
RuntimeToolSurfacePolicy,
|
|
63
63
|
} from "./runtime-assembler";
|
|
64
|
-
export { assembleRuntimeSnapshot
|
|
64
|
+
export { assembleRuntimeSnapshot } from "./runtime-assembler";
|
|
65
65
|
export type {
|
|
66
|
-
PlatformToolContext,
|
|
67
|
-
PlatformToolRegistry,
|
|
68
|
-
PlatformToolSpec,
|
|
69
|
-
ProfileOverrides,
|
|
70
66
|
ResolvedConnectors,
|
|
71
67
|
ResolvedResources,
|
|
72
68
|
RuntimeAgentContext,
|
|
73
|
-
RuntimeAgentHooks,
|
|
74
69
|
RuntimeSettings,
|
|
75
70
|
RuntimeToolBindings,
|
|
76
71
|
ToolAssemblyResult,
|
|
77
|
-
ToolContext,
|
|
78
|
-
ToolRegistry,
|
|
79
|
-
ToolSpec,
|
|
80
72
|
} from "./runtime-definition";
|
|
81
|
-
export {
|
|
82
|
-
emptyPlatformToolRegistry,
|
|
83
|
-
emptyToolRegistry,
|
|
84
|
-
mergeToolRegistries,
|
|
85
|
-
normalizeToolAssembly,
|
|
86
|
-
piCandidateToToolSpec,
|
|
87
|
-
toolRegistryFromPiCandidates,
|
|
88
|
-
} from "./tool-registry";
|
|
89
73
|
export type { ModelOption } from "./lib/model-catalog";
|
|
90
|
-
export type { RuntimeCodeExecutionFactory } from "./tool-registry";
|
|
91
74
|
export {
|
|
92
75
|
assembleSubagentPrompt,
|
|
93
76
|
assembleSystemPrompt,
|
|
@@ -118,26 +101,15 @@ export type {
|
|
|
118
101
|
PiDeclaredToolCallContext,
|
|
119
102
|
PiDeclaredToolPolicy,
|
|
120
103
|
} from "./pi/tool";
|
|
104
|
+
export { memoryPiToolCandidate } from "./pi/tool";
|
|
105
|
+
export { GET_TIME_TOOL_NAME, getTimePiToolCandidate } from "./pi/tool";
|
|
106
|
+
export { schedulePiToolCandidates } from "./pi/tool";
|
|
121
107
|
export {
|
|
122
|
-
createMemoryTools,
|
|
123
|
-
memoryPiToolCandidate,
|
|
124
|
-
} from "./pi/tool";
|
|
125
|
-
export {
|
|
126
|
-
createScheduleTools,
|
|
127
|
-
schedulePiToolCandidates,
|
|
128
|
-
} from "./pi/tool";
|
|
129
|
-
export {
|
|
130
|
-
createSandboxTools,
|
|
131
|
-
createWorkspaceRevisionTools,
|
|
132
|
-
createWorkspaceTools,
|
|
133
108
|
sandboxPiToolCandidates,
|
|
134
109
|
workspaceRevisionPiToolCandidate,
|
|
135
110
|
workspacePiToolCandidates,
|
|
136
111
|
} from "./pi/tool";
|
|
137
|
-
export {
|
|
138
|
-
createSubagentTools,
|
|
139
|
-
subagentPiToolCandidates,
|
|
140
|
-
} from "./pi/tool";
|
|
112
|
+
export { subagentPiToolCandidates } from "./pi/tool";
|
|
141
113
|
export { skillPiToolCandidates } from "./pi/tool";
|
|
142
114
|
export type { PiSkillBinding } from "./pi/tool";
|
|
143
115
|
export {
|
package/src/kernel/bindings.ts
CHANGED
|
@@ -2,7 +2,8 @@ import type { SkillSource } from "agents/skills";
|
|
|
2
2
|
import type { ScheduleSpec } from "./receipts";
|
|
3
3
|
import type { RuntimeActivityProjection } from "./state";
|
|
4
4
|
import type { ExecutionLevel } from "../lib/execution-level";
|
|
5
|
-
import type {
|
|
5
|
+
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
6
|
+
import type { PiToolCandidate } from "../pi/tool/compiler";
|
|
6
7
|
import type {
|
|
7
8
|
RuntimeEventConfirmation,
|
|
8
9
|
RuntimeLifecycleFact,
|
|
@@ -213,7 +214,7 @@ export interface WorkspacePort {
|
|
|
213
214
|
*/
|
|
214
215
|
export interface RuntimeCodeExecutionPort {
|
|
215
216
|
readonly description: string;
|
|
216
|
-
execute(input: { code: string }): Promise<unknown
|
|
217
|
+
execute(input: { code: string }): Promise<AgentToolResult<unknown>>;
|
|
217
218
|
}
|
|
218
219
|
|
|
219
220
|
/**
|
|
@@ -1022,12 +1023,11 @@ export interface RuntimeSkillSourceBinding {
|
|
|
1022
1023
|
* 延迟到最终 Tool Surface 完成后再创建 Code Mode 执行能力。
|
|
1023
1024
|
*
|
|
1024
1025
|
* @remarks
|
|
1025
|
-
*
|
|
1026
|
-
*
|
|
1027
|
-
* tool-registry 会让两个模块互相 import。`tool-registry.ts` 继续对外导出它。
|
|
1026
|
+
* 工厂与返回 Port 同住 Host 绑定边界;输入直接使用最终 Pi Tool candidates,
|
|
1027
|
+
* 不再经过 Registry 或另一份 Tool 元数据协议。
|
|
1028
1028
|
*/
|
|
1029
1029
|
export interface RuntimeCodeExecutionFactory {
|
|
1030
|
-
create(
|
|
1030
|
+
create(candidates: readonly PiToolCandidate[]): RuntimeCodeExecutionPort;
|
|
1031
1031
|
}
|
|
1032
1032
|
|
|
1033
1033
|
export interface RuntimeBindings {
|
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
type ExtensionManifest,
|
|
4
4
|
type ExtensionPermissions,
|
|
5
5
|
} from "@cloudflare/think/extensions";
|
|
6
|
-
import type { AgentTool } from "@earendil-works/pi-agent-core";
|
|
7
6
|
import { asSchema } from "ai";
|
|
8
7
|
import type {
|
|
9
8
|
LoadedRuntimeExtension,
|
|
@@ -14,6 +13,7 @@ import type { RuntimeDegradation } from "../../kernel/degradation";
|
|
|
14
13
|
import { withRuntimeLoadTimeout } from "../../kernel/runtime-load";
|
|
15
14
|
import { sanitizeExtensionName } from "../../lib/extension-name";
|
|
16
15
|
import type { PiToolCandidate } from "../tool/compiler";
|
|
16
|
+
import { createPiDeclaredToolCandidate } from "../tool/declared";
|
|
17
17
|
|
|
18
18
|
export interface PiExtensionContextContribution {
|
|
19
19
|
readonly extensionName: string;
|
|
@@ -563,45 +563,30 @@ export async function assemblePiExtensions({
|
|
|
563
563
|
const toolName = `${prefix}_${descriptor.name}`;
|
|
564
564
|
toolNames.push(toolName);
|
|
565
565
|
const description = `[${name}] ${descriptor.description}`;
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
566
|
+
const candidate = createPiDeclaredToolCandidate(
|
|
567
|
+
{
|
|
568
|
+
name: descriptor.name,
|
|
569
|
+
title: `${name}: ${descriptor.name}`,
|
|
570
|
+
description,
|
|
571
|
+
inputSchema: descriptor.inputSchema,
|
|
572
|
+
},
|
|
573
|
+
(input, { signal }) => loaded.execute(
|
|
574
574
|
descriptor.name,
|
|
575
|
-
|
|
575
|
+
input,
|
|
576
576
|
signal,
|
|
577
|
-
)
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
result,
|
|
586
|
-
},
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
extension: string;
|
|
591
|
-
result: unknown;
|
|
592
|
-
}> = Object.freeze({
|
|
593
|
-
name: toolName,
|
|
594
|
-
label: `${name}: ${descriptor.name}`,
|
|
595
|
-
description,
|
|
596
|
-
parameters: descriptor.inputSchema as AgentTool["parameters"],
|
|
597
|
-
execute,
|
|
598
|
-
});
|
|
599
|
-
candidates.push(Object.freeze({
|
|
600
|
-
owner: `extension:${name}@${extension.manifest.version}`,
|
|
601
|
-
requiredExecutionLevel,
|
|
602
|
-
summary: description,
|
|
603
|
-
tool,
|
|
604
|
-
}));
|
|
577
|
+
),
|
|
578
|
+
{
|
|
579
|
+
owner: `extension:${name}@${extension.manifest.version}`,
|
|
580
|
+
modelName: toolName,
|
|
581
|
+
requiredExecutionLevel,
|
|
582
|
+
summary: description,
|
|
583
|
+
project: (result) => ({
|
|
584
|
+
content: [{ type: "text", text: resultText(result) }],
|
|
585
|
+
details: { extension: name, result },
|
|
586
|
+
}),
|
|
587
|
+
},
|
|
588
|
+
);
|
|
589
|
+
candidates.push(Object.freeze({ ...candidate, requiredExecutionLevel }));
|
|
605
590
|
}
|
|
606
591
|
loadedExtensions.push(Object.freeze({
|
|
607
592
|
name,
|
|
@@ -23,7 +23,13 @@ import type { PiToolCandidate } from "../tool/compiler";
|
|
|
23
23
|
export interface PiToolSurface {
|
|
24
24
|
finalize(
|
|
25
25
|
candidates: readonly PiToolCandidate[],
|
|
26
|
-
):
|
|
26
|
+
): FinalizedPiToolSurface;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Internal finalization output; not a second Tool protocol. */
|
|
30
|
+
export interface FinalizedPiToolSurface {
|
|
31
|
+
readonly candidates: readonly PiToolCandidate[];
|
|
32
|
+
readonly codeExecutionCandidates: readonly PiToolCandidate[];
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
/** Immutable inputs consumed directly by Pi Agent Core. */
|
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
import type { RuntimeProfile } from "../../kernel/profile";
|
|
11
11
|
import type { ExecutionLevel } from "../../lib/execution-level";
|
|
12
12
|
import type { RuntimeSnapshot } from "../../runtime-assembler";
|
|
13
|
-
import type { PiRuntimeAssembly } from "../assembly";
|
|
13
|
+
import type { FinalizedPiToolSurface, PiRuntimeAssembly } from "../assembly";
|
|
14
14
|
import {
|
|
15
15
|
assemblePiSystemContext,
|
|
16
16
|
assemblePiExtensions,
|
|
@@ -77,7 +77,7 @@ interface PreparedPiAssembly {
|
|
|
77
77
|
readonly loaded: readonly PiLoadedExtension[];
|
|
78
78
|
readonly context: readonly PiExtensionContextContribution[];
|
|
79
79
|
readonly degradations: PiExtensionAssembly["degradations"];
|
|
80
|
-
readonly
|
|
80
|
+
readonly toolSurface: FinalizedPiToolSurface;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/**
|
|
@@ -193,6 +193,7 @@ export interface PreparedPiRuntimeState {
|
|
|
193
193
|
readonly snapshot: RuntimeSnapshot;
|
|
194
194
|
readonly assembly: PreparedPiAssembly;
|
|
195
195
|
readonly candidates: readonly PiToolCandidate[];
|
|
196
|
+
readonly codeExecutionCandidates: readonly PiToolCandidate[];
|
|
196
197
|
}
|
|
197
198
|
|
|
198
199
|
// #endregion
|
|
@@ -411,21 +412,22 @@ async function preparePiAssembly(
|
|
|
411
412
|
});
|
|
412
413
|
},
|
|
413
414
|
});
|
|
415
|
+
const toolSurface = snapshot.pi.toolSurface.finalize([
|
|
416
|
+
...extensions.candidates,
|
|
417
|
+
listExtensionsPiToolCandidate(extensions.loaded),
|
|
418
|
+
...(options.gatewaySession
|
|
419
|
+
? createPiGatewayToolCandidates(options.gatewaySession)
|
|
420
|
+
: []),
|
|
421
|
+
...createPiMcpToolCandidates(
|
|
422
|
+
options.mcpHost,
|
|
423
|
+
snapshot.profile.mcpServers,
|
|
424
|
+
),
|
|
425
|
+
]);
|
|
414
426
|
return Object.freeze({
|
|
415
427
|
loaded: extensions.loaded,
|
|
416
428
|
context: extensions.context,
|
|
417
429
|
degradations: extensions.degradations,
|
|
418
|
-
|
|
419
|
-
...extensions.candidates,
|
|
420
|
-
listExtensionsPiToolCandidate(extensions.loaded),
|
|
421
|
-
...(options.gatewaySession
|
|
422
|
-
? createPiGatewayToolCandidates(options.gatewaySession)
|
|
423
|
-
: []),
|
|
424
|
-
...createPiMcpToolCandidates(
|
|
425
|
-
options.mcpHost,
|
|
426
|
-
snapshot.profile.mcpServers,
|
|
427
|
-
),
|
|
428
|
-
]),
|
|
430
|
+
toolSurface,
|
|
429
431
|
});
|
|
430
432
|
}
|
|
431
433
|
|
|
@@ -446,7 +448,7 @@ export async function preparePiRuntime(
|
|
|
446
448
|
owner: object,
|
|
447
449
|
): Promise<PreparedPiRuntime> {
|
|
448
450
|
const assembly = await preparePiAssembly(options);
|
|
449
|
-
const candidates = assembly.candidates;
|
|
451
|
+
const candidates = assembly.toolSurface.candidates;
|
|
450
452
|
return Object.freeze(new PreparedRuntime(
|
|
451
453
|
describeRuntime(options.snapshot, candidates, assembly.loaded),
|
|
452
454
|
Object.freeze([
|
|
@@ -460,6 +462,9 @@ export async function preparePiRuntime(
|
|
|
460
462
|
snapshot: options.snapshot,
|
|
461
463
|
assembly,
|
|
462
464
|
candidates: Object.freeze([...candidates]),
|
|
465
|
+
codeExecutionCandidates: Object.freeze([
|
|
466
|
+
...assembly.toolSurface.codeExecutionCandidates,
|
|
467
|
+
]),
|
|
463
468
|
}),
|
|
464
469
|
));
|
|
465
470
|
}
|
|
@@ -49,7 +49,6 @@ import {
|
|
|
49
49
|
import { EXECUTION_LEVELS } from "../../lib/execution-level";
|
|
50
50
|
import { projectToolOutputForModel } from "../../layers/context/budget/gate";
|
|
51
51
|
import type { SpillWorkspace } from "../../lib/artifacts";
|
|
52
|
-
import { toolRegistryFromPiCandidates } from "../../tool-registry";
|
|
53
52
|
|
|
54
53
|
// #region Single-run Pi bridge
|
|
55
54
|
|
|
@@ -816,7 +815,7 @@ export class PreparedPiTurnAdapter {
|
|
|
816
815
|
},
|
|
817
816
|
});
|
|
818
817
|
this.candidates = state.candidates.map((candidate) => {
|
|
819
|
-
if (
|
|
818
|
+
if (candidate.tool.name !== "execute") return bindCandidate(candidate);
|
|
820
819
|
const factory = state.snapshot.bindings.codeExecution;
|
|
821
820
|
if (!factory) {
|
|
822
821
|
throw new Error("Prepared Code Mode Tool requires a Runtime factory");
|
|
@@ -827,14 +826,12 @@ export class PreparedPiTurnAdapter {
|
|
|
827
826
|
...candidate.tool,
|
|
828
827
|
execute: (toolCallId, input, signal, onUpdate) => {
|
|
829
828
|
const runtimeCandidate = codeExecutionPiToolCandidate(
|
|
830
|
-
factory.create(
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
bindCandidate(inner, toolCallId, false)
|
|
837
|
-
),
|
|
829
|
+
factory.create(state.codeExecutionCandidates.map((inner) =>
|
|
830
|
+
// Code Mode owns replay of its connector calls. Pi persists
|
|
831
|
+
// only the parent execute attempt/result; recording an inner
|
|
832
|
+
// input without a Pi settlement creates an orphan recovery
|
|
833
|
+
// action that cannot be found on the direct Tool surface.
|
|
834
|
+
bindCandidate(inner, toolCallId, false)
|
|
838
835
|
)),
|
|
839
836
|
);
|
|
840
837
|
return runtimeCandidate.tool.execute(
|