@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.
Files changed (59) hide show
  1. package/package.json +3 -1
  2. package/src/adapter/cloudflare/index.ts +60 -0
  3. package/src/adapter/cloudflare/resources/runtime-resources.ts +86 -0
  4. package/src/adapter/cloudflare/sandbox/adapter.ts +1509 -0
  5. package/src/adapter/cloudflare/sandbox/id.ts +23 -0
  6. package/src/adapter/cloudflare/sandbox/policy.ts +15 -0
  7. package/src/adapter/cloudflare/subagent/definition.ts +574 -0
  8. package/src/adapter/cloudflare/subagent/runner.ts +175 -0
  9. package/src/adapter/cloudflare/subagent/tools.ts +256 -0
  10. package/src/adapter/cloudflare/universal-agent/definition.ts +71 -0
  11. package/src/adapter/cloudflare/universal-agent/hooks.ts +35 -0
  12. package/src/adapter/cloudflare/universal-agent/preparation.ts +273 -0
  13. package/src/adapter/cloudflare/universal-agent/tools.ts +74 -0
  14. package/src/adapter/cloudflare/workspace/publisher.ts +31 -0
  15. package/src/adapter/cloudflare/workspace/scoped-workspace.ts +376 -0
  16. package/src/agent-tool-runtime.ts +152 -0
  17. package/src/db/agent-tool.repo.ts +27 -0
  18. package/src/db/index.ts +33 -0
  19. package/src/db/interaction.repo.ts +185 -0
  20. package/src/db/schema.ts +15 -0
  21. package/src/db/submission.repo.ts +29 -0
  22. package/src/index.ts +53 -21
  23. package/src/kernel/approval-lifecycle.ts +41 -6
  24. package/src/kernel/bindings.ts +37 -0
  25. package/src/kernel/interaction-lifecycle.ts +395 -0
  26. package/src/kernel/public-contracts.ts +2 -0
  27. package/src/kernel/recoverable-chat-agent.ts +10 -2
  28. package/src/kernel/runtime-assembly-view.ts +37 -0
  29. package/src/kernel/runtime-assembly.ts +41 -0
  30. package/src/kernel/runtime-config.ts +4 -0
  31. package/src/kernel/runtime-load.ts +102 -0
  32. package/src/kernel/state.ts +8 -1
  33. package/src/kernel/submission-lifecycle.ts +30 -0
  34. package/src/layers/orchestration/temporary-agent/core.ts +12 -1
  35. package/src/layers/orchestration/temporary-agent/runner.ts +1 -2
  36. package/src/pi/message/contract.ts +7 -0
  37. package/src/pi/message/conversion.ts +9 -1
  38. package/src/pi/runtime-adapter/assembly.ts +17 -3
  39. package/src/pi/runtime-adapter/execution.ts +109 -9
  40. package/src/pi/runtime-adapter/index.ts +15 -5
  41. package/src/pi/runtime-adapter/recovery.ts +188 -1
  42. package/src/pi/tool/base.ts +79 -9
  43. package/src/pi/tool/compiler.ts +34 -0
  44. package/src/pi/tool/core.ts +13 -0
  45. package/src/pi/tool/gateway.ts +54 -0
  46. package/src/pi/tool/index.ts +1 -0
  47. package/src/pi/tool/mcp.ts +93 -64
  48. package/src/pi/tool/schedule.ts +11 -0
  49. package/src/pi/tool/subagent.ts +14 -0
  50. package/src/pi/tool/workspace-sandbox.ts +15 -0
  51. package/src/pi/turn/index.ts +20 -0
  52. package/src/pi/turn/interaction.ts +181 -0
  53. package/src/pi/turn/tool-recovery.ts +244 -1
  54. package/src/runtime-agent-context.ts +112 -0
  55. package/src/runtime-agent.ts +569 -322
  56. package/src/{plugins.ts → runtime-assembler.ts} +312 -379
  57. package/src/runtime-definition.ts +173 -0
  58. package/src/runtime.ts +572 -164
  59. 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
+ }