@springbrand/agent-runtime 0.1.3-alpha.3 → 0.1.3-alpha.5
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/index.ts +60 -0
- package/src/adapter/cloudflare/resources/runtime-resources.ts +86 -0
- package/src/adapter/cloudflare/sandbox/adapter.ts +1509 -0
- package/src/adapter/cloudflare/sandbox/id.ts +23 -0
- package/src/adapter/cloudflare/sandbox/policy.ts +15 -0
- package/src/adapter/cloudflare/subagent/definition.ts +574 -0
- package/src/adapter/cloudflare/subagent/runner.ts +175 -0
- package/src/adapter/cloudflare/subagent/tools.ts +256 -0
- package/src/adapter/cloudflare/universal-agent/definition.ts +71 -0
- package/src/adapter/cloudflare/universal-agent/hooks.ts +35 -0
- package/src/adapter/cloudflare/universal-agent/preparation.ts +273 -0
- package/src/adapter/cloudflare/universal-agent/tools.ts +74 -0
- package/src/adapter/cloudflare/workspace/publisher.ts +31 -0
- package/src/adapter/cloudflare/workspace/scoped-workspace.ts +376 -0
- package/src/agent-tool-runtime.ts +152 -0
- package/src/db/agent-tool.repo.ts +27 -0
- package/src/db/index.ts +33 -0
- package/src/db/interaction.repo.ts +185 -0
- package/src/db/schema.ts +15 -0
- package/src/db/submission.repo.ts +29 -0
- package/src/index.ts +53 -21
- package/src/kernel/approval-lifecycle.ts +41 -6
- package/src/kernel/bindings.ts +37 -0
- package/src/kernel/interaction-lifecycle.ts +395 -0
- package/src/kernel/public-contracts.ts +2 -0
- package/src/kernel/recoverable-chat-agent.ts +10 -2
- package/src/kernel/runtime-assembly-view.ts +37 -0
- package/src/kernel/runtime-assembly.ts +41 -0
- package/src/kernel/runtime-config.ts +4 -0
- package/src/kernel/runtime-load.ts +102 -0
- package/src/kernel/state.ts +8 -1
- package/src/kernel/submission-lifecycle.ts +30 -0
- package/src/layers/orchestration/temporary-agent/core.ts +12 -1
- package/src/layers/orchestration/temporary-agent/runner.ts +1 -2
- package/src/pi/message/contract.ts +7 -0
- package/src/pi/message/conversion.ts +9 -1
- package/src/pi/runtime-adapter/assembly.ts +17 -3
- package/src/pi/runtime-adapter/execution.ts +109 -9
- package/src/pi/runtime-adapter/index.ts +15 -5
- package/src/pi/runtime-adapter/recovery.ts +188 -1
- package/src/pi/tool/base.ts +79 -9
- package/src/pi/tool/compiler.ts +34 -0
- package/src/pi/tool/core.ts +13 -0
- package/src/pi/tool/gateway.ts +54 -0
- package/src/pi/tool/index.ts +1 -0
- package/src/pi/tool/mcp.ts +93 -64
- package/src/pi/tool/schedule.ts +11 -0
- package/src/pi/tool/subagent.ts +14 -0
- package/src/pi/tool/workspace-sandbox.ts +15 -0
- package/src/pi/turn/index.ts +20 -0
- package/src/pi/turn/interaction.ts +181 -0
- package/src/pi/turn/tool-recovery.ts +244 -1
- package/src/runtime-agent-context.ts +112 -0
- package/src/runtime-agent.ts +569 -322
- package/src/{plugins.ts → runtime-assembler.ts} +312 -379
- package/src/runtime-definition.ts +173 -0
- package/src/runtime.ts +572 -164
- package/src/tool-registry.ts +143 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import type { Sandbox } from "@cloudflare/sandbox";
|
|
2
|
+
import type {
|
|
3
|
+
ChatCapableAgentClass,
|
|
4
|
+
RunAgentToolOptions,
|
|
5
|
+
} from "agents";
|
|
6
|
+
import type {
|
|
7
|
+
RuntimeBrowserPort,
|
|
8
|
+
RuntimeMemoryPort,
|
|
9
|
+
RuntimePlatformPort,
|
|
10
|
+
RuntimeSubagentPort,
|
|
11
|
+
WorkspacePort,
|
|
12
|
+
} from "../../../kernel/bindings";
|
|
13
|
+
import type { RuntimeDegradation } from "../../../kernel/degradation";
|
|
14
|
+
import type { RuntimeMemoryProfile } from "../../../kernel/profile";
|
|
15
|
+
import { AGENT_TYPES } from "../../../layers/orchestration/subagents/agent-types/registry";
|
|
16
|
+
import type { RuntimeAgentConfigContext } from "../../../runtime-agent-context";
|
|
17
|
+
import { createWorkspaceCodeExecutionPort } from "../../../pi/tool/core-host";
|
|
18
|
+
import {
|
|
19
|
+
createCloudflareSandboxAdapter,
|
|
20
|
+
type SandboxAdmission,
|
|
21
|
+
type SandboxObserver,
|
|
22
|
+
} from "../sandbox/adapter";
|
|
23
|
+
import { sandboxIdForSession } from "../sandbox/id";
|
|
24
|
+
import type { WorkspacePublishPort } from "../workspace/publisher";
|
|
25
|
+
|
|
26
|
+
export type WorkspaceLoader = () => Promise<{
|
|
27
|
+
value: WorkspacePort | undefined;
|
|
28
|
+
degradations: readonly RuntimeDegradation[];
|
|
29
|
+
}>;
|
|
30
|
+
|
|
31
|
+
export type PlatformLoader = () => Promise<RuntimePlatformPort>;
|
|
32
|
+
|
|
33
|
+
export interface CloudflarePlatformBindings {
|
|
34
|
+
LOADER: WorkerLoader;
|
|
35
|
+
BROWSER?: RuntimeBrowserPort;
|
|
36
|
+
TELEMETRY_CONSOLE?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface WorkspaceRequirement {
|
|
40
|
+
memoryEnabled?: boolean;
|
|
41
|
+
sandboxEnabled?: boolean;
|
|
42
|
+
deniedToolNames?: readonly string[];
|
|
43
|
+
skillWorkspaceModes?: readonly ("none" | "read" | "read-write")[];
|
|
44
|
+
extensionWorkspaceModes?: readonly ("none" | "read" | "read-write")[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const DEFAULT_MEMORY: RuntimeMemoryProfile = Object.freeze({
|
|
48
|
+
enabled: true,
|
|
49
|
+
memoryTokens: 2_000,
|
|
50
|
+
preferencesTokens: 500,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const WORKSPACE_TOOL_NAMES = [
|
|
54
|
+
"read",
|
|
55
|
+
"write",
|
|
56
|
+
"edit",
|
|
57
|
+
"list",
|
|
58
|
+
"find",
|
|
59
|
+
"grep",
|
|
60
|
+
"delete",
|
|
61
|
+
"bash",
|
|
62
|
+
"execute",
|
|
63
|
+
] as const;
|
|
64
|
+
|
|
65
|
+
export function workspaceRequired(input: WorkspaceRequirement): boolean {
|
|
66
|
+
const denied = new Set(input.deniedToolNames ?? []);
|
|
67
|
+
return input.memoryEnabled !== false ||
|
|
68
|
+
input.sandboxEnabled === true ||
|
|
69
|
+
input.skillWorkspaceModes?.some((mode) => mode !== "none") === true ||
|
|
70
|
+
input.extensionWorkspaceModes?.some((mode) => mode !== "none") === true ||
|
|
71
|
+
WORKSPACE_TOOL_NAMES.some((name) => !denied.has(name));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function createWorkspaceLoader(
|
|
75
|
+
load: () => WorkspacePort,
|
|
76
|
+
): WorkspaceLoader {
|
|
77
|
+
let cached: ReturnType<WorkspaceLoader> | undefined;
|
|
78
|
+
return () => cached ??= Promise.resolve().then(() => {
|
|
79
|
+
try {
|
|
80
|
+
const workspace = load();
|
|
81
|
+
return {
|
|
82
|
+
value: workspace,
|
|
83
|
+
degradations: [],
|
|
84
|
+
};
|
|
85
|
+
} catch (error) {
|
|
86
|
+
return {
|
|
87
|
+
value: undefined,
|
|
88
|
+
degradations: [{
|
|
89
|
+
capability: "workspace" as const,
|
|
90
|
+
reason: "unavailable" as const,
|
|
91
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
92
|
+
}],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function createPlatformLoader<
|
|
99
|
+
Env extends Cloudflare.Env & CloudflarePlatformBindings,
|
|
100
|
+
>(context: RuntimeAgentConfigContext<Env>): PlatformLoader {
|
|
101
|
+
let cached: ReturnType<PlatformLoader> | undefined;
|
|
102
|
+
return () => cached ??= Promise.resolve().then(() => {
|
|
103
|
+
const exports = (context.ctx as unknown as {
|
|
104
|
+
exports: {
|
|
105
|
+
HttpGateway(options: Record<string, never>): Fetcher;
|
|
106
|
+
};
|
|
107
|
+
}).exports;
|
|
108
|
+
return {
|
|
109
|
+
loader: context.env.LOADER,
|
|
110
|
+
...(context.env.BROWSER ? { browser: context.env.BROWSER } : {}),
|
|
111
|
+
outbound: () => exports.HttpGateway({}),
|
|
112
|
+
telemetryConsole: context.env.TELEMETRY_CONSOLE === "1",
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export async function prepareWorkspace<Env extends Cloudflare.Env>(
|
|
118
|
+
context: RuntimeAgentConfigContext<Env>,
|
|
119
|
+
workspaceLoader: WorkspaceLoader,
|
|
120
|
+
platformLoader: PlatformLoader,
|
|
121
|
+
) {
|
|
122
|
+
const [workspace, platform] = await Promise.all([
|
|
123
|
+
workspaceLoader(),
|
|
124
|
+
platformLoader(),
|
|
125
|
+
]);
|
|
126
|
+
if (!workspace.value) return { degradations: workspace.degradations };
|
|
127
|
+
return {
|
|
128
|
+
workspace: workspace.value,
|
|
129
|
+
codeExecution: createWorkspaceCodeExecutionPort({
|
|
130
|
+
ctx: context.ctx,
|
|
131
|
+
loader: platform.loader,
|
|
132
|
+
outbound: platform.outbound(),
|
|
133
|
+
workspace: workspace.value,
|
|
134
|
+
}),
|
|
135
|
+
degradations: workspace.degradations,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export async function prepareMemory(
|
|
140
|
+
configured: Partial<RuntimeMemoryProfile> | null | undefined,
|
|
141
|
+
workspaceLoader: WorkspaceLoader,
|
|
142
|
+
port: RuntimeMemoryPort,
|
|
143
|
+
): Promise<{
|
|
144
|
+
memoryProfile: RuntimeMemoryProfile;
|
|
145
|
+
memory?: RuntimeMemoryPort;
|
|
146
|
+
degradations: RuntimeDegradation[];
|
|
147
|
+
}> {
|
|
148
|
+
const memoryConfig = configured ?? {};
|
|
149
|
+
if (memoryConfig.enabled === false) {
|
|
150
|
+
return {
|
|
151
|
+
memoryProfile: { ...DEFAULT_MEMORY, ...memoryConfig },
|
|
152
|
+
degradations: [{
|
|
153
|
+
capability: "memory" as const,
|
|
154
|
+
reason: "not_configured" as const,
|
|
155
|
+
}],
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const workspace = (await workspaceLoader()).value;
|
|
159
|
+
const memoryProfile = {
|
|
160
|
+
...DEFAULT_MEMORY,
|
|
161
|
+
...memoryConfig,
|
|
162
|
+
enabled: workspace !== undefined,
|
|
163
|
+
};
|
|
164
|
+
return {
|
|
165
|
+
memoryProfile,
|
|
166
|
+
...(memoryProfile.enabled ? { memory: port } : {}),
|
|
167
|
+
degradations: memoryProfile.enabled
|
|
168
|
+
? []
|
|
169
|
+
: [{
|
|
170
|
+
capability: "memory" as const,
|
|
171
|
+
reason: "unavailable" as const,
|
|
172
|
+
}],
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export async function prepareSubagents<Env extends Cloudflare.Env>(
|
|
177
|
+
context: RuntimeAgentConfigContext<Env>,
|
|
178
|
+
requested: readonly string[],
|
|
179
|
+
agentClass: ChatCapableAgentClass,
|
|
180
|
+
) {
|
|
181
|
+
const enabledSubagents: string[] = [];
|
|
182
|
+
const degradations: RuntimeDegradation[] = [];
|
|
183
|
+
|
|
184
|
+
for (const name of new Set(requested.filter(Boolean))) {
|
|
185
|
+
if (name in AGENT_TYPES.byName) enabledSubagents.push(name);
|
|
186
|
+
else {
|
|
187
|
+
degradations.push({
|
|
188
|
+
capability: "subagent",
|
|
189
|
+
reason: "not_deployed",
|
|
190
|
+
detail: name,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const subagents: RuntimeSubagentPort = {
|
|
196
|
+
run: async (agentType, input, options) => {
|
|
197
|
+
const runOptions: RunAgentToolOptions<unknown> = {
|
|
198
|
+
input: { agentType, ...input },
|
|
199
|
+
};
|
|
200
|
+
if (options?.detached) {
|
|
201
|
+
runOptions.detached = { maxBudgetMs: options.maxBudgetMs };
|
|
202
|
+
if (options.notifySource) {
|
|
203
|
+
runOptions.detached.notify = { source: options.notifySource };
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const result = await context.runAgentTool(agentClass, runOptions);
|
|
207
|
+
return {
|
|
208
|
+
status: result.status,
|
|
209
|
+
runId: result.runId,
|
|
210
|
+
output: result.output,
|
|
211
|
+
error: result.error,
|
|
212
|
+
};
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
enabledSubagents,
|
|
218
|
+
...(enabledSubagents.length > 0 ? { subagents } : {}),
|
|
219
|
+
degradations,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export async function prepareSandbox(options: {
|
|
224
|
+
enabled: boolean;
|
|
225
|
+
deploymentEnabled: boolean;
|
|
226
|
+
namespace?: DurableObjectNamespace<Sandbox>;
|
|
227
|
+
userId: string;
|
|
228
|
+
sessionId: string;
|
|
229
|
+
workspaceLoader: WorkspaceLoader;
|
|
230
|
+
publisher: WorkspacePublishPort;
|
|
231
|
+
admission?: SandboxAdmission;
|
|
232
|
+
observer?: SandboxObserver;
|
|
233
|
+
}) {
|
|
234
|
+
if (!options.enabled) return { degradations: [] };
|
|
235
|
+
if (!options.deploymentEnabled) {
|
|
236
|
+
return {
|
|
237
|
+
degradations: [{
|
|
238
|
+
capability: "sandbox" as const,
|
|
239
|
+
reason: "disabled_by_deployment" as const,
|
|
240
|
+
}],
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
if (!options.namespace) {
|
|
244
|
+
return {
|
|
245
|
+
degradations: [{
|
|
246
|
+
capability: "sandbox" as const,
|
|
247
|
+
reason: "unavailable" as const,
|
|
248
|
+
detail: "sandbox_binding_missing",
|
|
249
|
+
}],
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
const workspace = (await options.workspaceLoader()).value;
|
|
253
|
+
if (!workspace) {
|
|
254
|
+
return {
|
|
255
|
+
degradations: [{
|
|
256
|
+
capability: "sandbox" as const,
|
|
257
|
+
reason: "unavailable" as const,
|
|
258
|
+
detail: "persistent_workspace_unavailable",
|
|
259
|
+
}],
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
sandbox: createCloudflareSandboxAdapter({
|
|
264
|
+
sandboxNamespace: options.namespace,
|
|
265
|
+
sandboxId: await sandboxIdForSession(options.userId, options.sessionId),
|
|
266
|
+
workspace,
|
|
267
|
+
publisher: options.publisher,
|
|
268
|
+
...(options.admission ? { admission: options.admission } : {}),
|
|
269
|
+
...(options.observer ? { observer: options.observer } : {}),
|
|
270
|
+
}),
|
|
271
|
+
degradations: [],
|
|
272
|
+
};
|
|
273
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
RuntimeCodeExecutionPort,
|
|
3
|
+
RuntimeMemoryPort,
|
|
4
|
+
RuntimeSandboxPort,
|
|
5
|
+
RuntimeSchedulePort,
|
|
6
|
+
RuntimeSubagentPort,
|
|
7
|
+
WorkspacePort,
|
|
8
|
+
} from "../../../kernel/bindings";
|
|
9
|
+
import type { RuntimeDegradation } from "../../../kernel/degradation";
|
|
10
|
+
import type { RuntimeMemoryProfile } from "../../../kernel/profile";
|
|
11
|
+
import { createCodeExecutionTool } from "../../../pi/tool/core";
|
|
12
|
+
import { createScheduleTools } from "../../../pi/tool/schedule";
|
|
13
|
+
import { createSubagentTools } from "../../../pi/tool/subagent";
|
|
14
|
+
import {
|
|
15
|
+
createSandboxTools,
|
|
16
|
+
createWorkspaceTools,
|
|
17
|
+
} from "../../../pi/tool/workspace-sandbox";
|
|
18
|
+
import {
|
|
19
|
+
mergeToolRegistries,
|
|
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
|
+
}
|
|
33
|
+
|
|
34
|
+
export function assembleUniversalAgentTools(options: {
|
|
35
|
+
hostTools?: ToolRegistry;
|
|
36
|
+
workspace?: WorkspacePort;
|
|
37
|
+
codeExecution?: RuntimeCodeExecutionPort;
|
|
38
|
+
sandbox?: RuntimeSandboxPort;
|
|
39
|
+
schedule?: RuntimeSchedulePort;
|
|
40
|
+
subagents?: RuntimeSubagentPort;
|
|
41
|
+
enabledSubagents?: readonly string[];
|
|
42
|
+
memory?: RuntimeMemoryPort;
|
|
43
|
+
memoryProfile: RuntimeMemoryProfile;
|
|
44
|
+
degradations?: readonly RuntimeDegradation[];
|
|
45
|
+
dispose?: () => Promise<void>;
|
|
46
|
+
}): ToolAssemblyResult {
|
|
47
|
+
const tools = mergeToolRegistries(
|
|
48
|
+
options.hostTools ?? {},
|
|
49
|
+
options.workspace ? createWorkspaceTools(options.workspace) : {},
|
|
50
|
+
options.codeExecution
|
|
51
|
+
? createCodeExecutionTool(options.codeExecution)
|
|
52
|
+
: {},
|
|
53
|
+
options.sandbox ? createSandboxTools(options.sandbox) : {},
|
|
54
|
+
options.schedule ? createScheduleTools(options.schedule) : {},
|
|
55
|
+
options.subagents
|
|
56
|
+
? createSubagentTools(
|
|
57
|
+
options.subagents,
|
|
58
|
+
options.enabledSubagents ?? [],
|
|
59
|
+
)
|
|
60
|
+
: {},
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
tools,
|
|
65
|
+
bindings: {
|
|
66
|
+
...(options.workspace ? { workspace: options.workspace } : {}),
|
|
67
|
+
...(options.memory ? { memory: options.memory } : {}),
|
|
68
|
+
},
|
|
69
|
+
enabledSubagents: options.enabledSubagents ?? [],
|
|
70
|
+
memoryProfile: options.memoryProfile,
|
|
71
|
+
degradations: options.degradations ?? [],
|
|
72
|
+
...(options.dispose ? { dispose: options.dispose } : {}),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface WorkspaceFileVersion {
|
|
2
|
+
updatedAt: number;
|
|
3
|
+
size: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export type WorkspaceConditionalWriteResult =
|
|
7
|
+
| {
|
|
8
|
+
written: true;
|
|
9
|
+
version: WorkspaceFileVersion;
|
|
10
|
+
}
|
|
11
|
+
| {
|
|
12
|
+
written: false;
|
|
13
|
+
reason: "conflict";
|
|
14
|
+
current: WorkspaceFileVersion | null;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Host-only compare-and-write capability used by Sandbox publishing.
|
|
19
|
+
*
|
|
20
|
+
* Runtime and the model never receive this port. The expected version comes
|
|
21
|
+
* from the automatic hydration snapshot, so a later Workspace edit cannot be
|
|
22
|
+
* overwritten silently.
|
|
23
|
+
*/
|
|
24
|
+
export interface WorkspacePublishPort {
|
|
25
|
+
writeFileBytesIfUnchanged(
|
|
26
|
+
path: string,
|
|
27
|
+
data: Uint8Array,
|
|
28
|
+
mimeType: string,
|
|
29
|
+
expected: WorkspaceFileVersion | null,
|
|
30
|
+
): Promise<WorkspaceConditionalWriteResult>;
|
|
31
|
+
}
|