@standardagents/cli 0.14.0 → 0.15.0

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/dist/index.js CHANGED
@@ -141,14 +141,19 @@ interface ThreadState {
141
141
  loadAgent(name): Promise<AgentConfig>; // Load agent definition
142
142
  env(propertyName): Promise<string>; // Resolve env value for this thread
143
143
  setEnv(propertyName, value): Promise<void>; // Set thread env + propagate to active descendants
144
+ getValue<T = unknown>(key: string): Promise<T | null>; // Read durable per-thread JSON value
145
+ setValue(key: string, value: unknown): Promise<void>; // Write durable value; null/undefined deletes
144
146
  notifyParent(content): Promise<void>; // Explicitly queue a silent message to the parent
145
147
  setStatus(status): Promise<void>; // Update this child in the parent registry
146
148
  getChildThread(referenceId): Promise<ThreadState | null>; // Resolve child thread
147
149
  getParentThread(): Promise<ThreadState | null>; // Resolve parent thread
148
150
  terminate(): Promise<void>; // Soft-terminate thread
151
+ runCode(source, options?): CodeExecution; // Run JS/TS in a fresh Dynamic Worker sandbox
149
152
  }
150
153
  \`\`\`
151
154
 
155
+ \`runCode\` has no implicit access to thread state, filesystem, network, timers, or host globals. Bridge only the exact capabilities needed through \`imports\` or \`globals\`; use \`modules\` for local relative ES modules; use \`execute\` to select the export and args to run. \`modules\` keys are graph-root relative paths; entry-source imports resolve from \`filename\` when supplied, including \`../\` parent paths that stay inside the supplied graph. TypeScript input is accepted by default and erased before evaluation. In thread endpoints, \`runCode\` is forwarded into the thread Durable Object; pass plain transferable options there and keep function-backed bridges in tools or in-thread execution contexts.
156
+
152
157
  ## Subagents
153
158
 
154
159
  \`dual_ai\` agents can be used as subagents from prompt tool configs:
@@ -165,6 +170,8 @@ Access in tools:
165
170
  export default defineTool('...', schema, async (state, args) => {
166
171
  const threadId = state.threadId;
167
172
  const { messages } = await state.getMessages();
173
+ const count = (await state.getValue<number>('invocation_count')) ?? 0;
174
+ await state.setValue('invocation_count', count + 1);
168
175
  // ...
169
176
  });
170
177
  \`\`\`
@@ -236,6 +243,7 @@ Models define which LLM provider and model to use for prompts.
236
243
  | \`outputPrice\` | \`number\` | No | Cost per 1M output tokens (USD) |
237
244
  | \`cachedPrice\` | \`number\` | No | Cost per 1M cached input tokens |
238
245
  | \`includedProviders\` | \`string[]\` | No | Provider prefixes for OpenRouter routing |
246
+ | \`providerTools\` | \`string[]\` | No | Provider-built-in tool names to make available for prompts using this model |
239
247
 
240
248
  ## Example
241
249
 
@@ -250,6 +258,19 @@ export default defineModel({
250
258
  });
251
259
  \`\`\`
252
260
 
261
+ ## Provider Tools
262
+
263
+ Providers can expose built-in tools such as search, file search, code execution,
264
+ or provider-specific tools through their \`getTools()\` implementation. Enable the
265
+ provider tool names on the model with \`providerTools\`; prompts can then include
266
+ those tools like normal tool names. Provider tools execute on the upstream
267
+ provider, not in AgentBuilder, and successful calls are logged as provider tool
268
+ entries.
269
+
270
+ OpenRouter exposes \`web_search\`, \`web_fetch\`, \`datetime\`, and \`image_generation\`
271
+ as provider-executed server tools. Configure optional OpenRouter parameters with
272
+ \`providerOptions.serverTools\` on the model.
273
+
253
274
  ## Recommended Models (OpenRouter)
254
275
 
255
276
  | Use Case | Model ID | Description |
@@ -372,6 +393,12 @@ export default definePrompt({
372
393
 
373
394
  \`type: 'env'\` inserts a resolved thread variable value. Use this only for non-secret values that are safe to expose to the LLM.
374
395
 
396
+ The sandboxed coding prompt declares \`HTTP_HOST\` as a text variable. Its URL helper uses that value as the origin for files exposed from the thread \`public/\` directory and endpoints exposed from the thread \`api/\` directory. Public URLs should usually omit extensions; extensionless public paths resolve \`.html\`, then \`.js\`, then \`.ts\`, then matching \`index\` files in the same order. Browser pages should reference only public files, \`api/\` endpoints, or absolute external URLs, using relative asset URLs like \`./app\` and \`./api/todos\`; private \`src/\` files are not served directly. Public \`.ts\` files are transformed to browser JavaScript and are not server endpoints. Server-side endpoints live under the thread filesystem \`api/\` directory, and the \`api/\` directory name stays in the public URL: \`api/todo.ts\` is \`/api/threads/{threadId}/api/todo\`, while \`api/todo.ts\` is not \`/api/threads/{threadId}/todo\` or \`/api/threads/{threadId}/todos\`. Endpoint files default-export a function that accepts \`Request\` and returns \`Response\` or \`Promise<Response>\`. Endpoint code can import \`fetch\`, \`fs\`, and \`storage\`, and can import private helpers with relative paths such as \`../src/todo.ts\`. When the coding agent creates a site, page, or endpoint, it should call \`get_public_url\` with the created file path and use the returned URL.
397
+
398
+ Sandboxed code and \`api/\` endpoints can persist small JSON-serializable runtime data through \`import { storage } from "storage"\`. Use \`storage.get\`, \`storage.set\`, \`storage.delete\`, and \`storage.has\` with stable logical keys. Do not expose, mention, or depend on internal storage key prefixes.
399
+
400
+ The sandboxed coding prompt includes \`delete_file\` for removing files or empty directories from the thread filesystem. Use it to clean up obsolete implementation files, stale public assets, and abandoned endpoint routes after the replacement is working.
401
+
375
402
  ## Tool Configuration
376
403
 
377
404
  Tools can be simple names or detailed configs:
@@ -389,6 +416,12 @@ tools: [
389
416
  ],
390
417
  \`\`\`
391
418
 
419
+ Provider-built-in tools use the same \`tools\` list once the selected model enables
420
+ them with \`providerTools\`. AgentBuilder passes them to the provider and records
421
+ provider-reported calls in request logs; it does not execute those tools locally.
422
+ Generated prompt files may store these selections as \`provider:<toolName>\` to
423
+ distinguish provider tools from local tools with the same name.
424
+
392
425
  ### Subagent Tool Configuration
393
426
 
394
427
  Use \`SubagentToolConfig\` when the tool is a \`dual_ai\` agent:
@@ -422,14 +455,14 @@ When prompt-configured resumable dual_ai subagents exist, the runtime auto-injec
422
455
  - \`subagent_create\` for spawning new child instances
423
456
  - \`subagent_message\` for messaging active resumable instances
424
457
 
425
- \`subagent_create\` supports optional \`name\` for human-friendly child thread labels.
426
- Pair it with \`initAgentNameProperty\` to map that argument into a \`name:<value>\` thread tag.
458
+ \`subagent_create\` requires a non-empty \`name\` for human-friendly child thread labels.
459
+ AgentBuilder stores that argument as a \`name:<value>\` thread tag.
427
460
 
428
461
  Set \`immediate: true\` to run a tool automatically when the prompt activates.
429
462
  Use \`immediate: { nameEnv, descriptionEnv, scopedEnv }\` when you want safe per-instance bootstrap hints while keeping copied scoped env runtime-only.
430
463
  Set \`optional: 'ENV_NAME'\` to enable a subagent branch only when env resolves to \`true\`, \`1\`, or \`yes\`.
431
464
  Set \`resumable.parentCommunication: 'explicit'\` when the child should stay quiet until its own tools or hooks call \`state.notifyParent()\` or \`state.setStatus()\`.
432
- If required scoped vars are missing, the runtime returns \`error_code: "subagent_env_required"\` with a temporary \`GET/POST /api/threads/:id/variables/:requestId\` endpoint in \`error_data.endpoint\`.
465
+ If required scoped vars are missing, the runtime returns \`error_code: "subagent_env_required"\` with a temporary \`GET/POST /api/threads/:threadId/variables/:requestId\` endpoint in \`error_data.endpoint\`.
433
466
 
434
467
  ## Input Validation
435
468
 
@@ -710,6 +743,10 @@ export default defineTool({
710
743
  });
711
744
  \`\`\`
712
745
 
746
+ ## Argument Schemas
747
+
748
+ Tool args must be a top-level \`z.object(...)\`. Prefer explicit, bounded schemas made from strings, numbers, booleans, nulls, literals, enums, arrays, objects, optionals, nullables, and defaults. Avoid provider-visible recursive or open-ended schemas such as \`z.unknown()\`, \`z.any()\`, and \`z.object({}).catchall(...)\`; strict tool providers can reject the generated JSON Schema. If a tool needs arbitrary JSON input, accept it as a JSON string and parse it inside \`execute\`.
749
+
713
750
  ## ToolResult Interface
714
751
 
715
752
  | Property | Type | Description |
@@ -742,11 +779,29 @@ execute: async (state, args) => {
742
779
  const apiKey = await state.env('GOOGLE_API_KEY');
743
780
  await state.setEnv('LAST_TOOL_RUN_AT', new Date().toISOString());
744
781
 
782
+ // Durable per-thread JSON values
783
+ const runCount = (await state.getValue<number>('run_count')) ?? 0;
784
+ await state.setValue('run_count', runCount + 1);
785
+
786
+ // Sandboxed JS/TS execution with explicit bridge capabilities
787
+ const run = state.runCode('export async function main(path) { return await read(path) }', {
788
+ execute: { fn: 'main', args: ['/notes.txt'] },
789
+ globals: {
790
+ read: async (path: string) => {
791
+ const data = await state.readFile(path);
792
+ return data ? new TextDecoder().decode(data) : null;
793
+ },
794
+ },
795
+ });
796
+ const codeResult = await run;
797
+
745
798
  // Parent state (for sub-prompts)
746
799
  const root = state.rootState;
747
800
  }
748
801
  \`\`\`
749
802
 
803
+ Use \`state.runCode()\` instead of \`eval\` or \`new Function\` for model- or user-authored JavaScript/TypeScript. The sandbox starts with no ambient host capabilities; provide imports/globals explicitly, use \`modules\` for local relative ES modules, use \`execute\` to select an export and pass args, use \`report\` for streamed observations, and call \`run.terminate(reason)\` from your own timeout budget when needed. \`modules\` keys are graph-root relative paths; entry-source imports resolve from \`options.filename\` when supplied. Relative module imports may use \`./\` and \`../\` as long as they resolve within the supplied module graph. In thread endpoints, \`runCode\` is forwarded into the thread Durable Object; pass plain transferable option values there, and do function-backed bridges from tools or in-thread execution contexts.
804
+
750
805
  ## Tool Without Arguments
751
806
 
752
807
  \`\`\`typescript
@@ -912,6 +967,9 @@ Hooks intercept and modify agent execution at specific lifecycle points.
912
967
 
913
968
  | Hook | Signature | Purpose |
914
969
  |------|-----------|---------|
970
+ | \`after_thread_created\` | \`(state: ThreadState) => void\` | Initialize a newly created thread before execution |
971
+ | \`after_subagent_created\` | \`(state: ThreadState, childState: ThreadState) => void\` | Run parent-side logic after a child thread is created |
972
+ | \`after_system_message\` | \`(state: ThreadState, systemMessage: string) => string \\| null \\| undefined\` | Modify the rendered system message for a request |
915
973
  | \`filter_messages\` | \`(state: ThreadState, messages: Message[]) => Message[]\` | Filter raw messages before LLM context |
916
974
  | \`prefilter_llm_history\` | \`(state: ThreadState, messages: LLMMessage[]) => LLMMessage[]\` | Modify chat completion messages before LLM |
917
975
  | \`before_create_message\` | \`(state: ThreadState, message: Message) => Message\` | Modify message before database insert |
@@ -922,7 +980,7 @@ Hooks intercept and modify agent execution at specific lifecycle points.
922
980
  | \`after_tool_call_success\` | \`(state: ThreadState, toolCall: HookToolCall, result: HookToolResult) => HookToolResult \\| null\` | Process successful tool execution |
923
981
  | \`after_tool_call_failure\` | \`(state: ThreadState, toolCall: HookToolCall, result: HookToolResult) => HookToolResult \\| null\` | Process failed tool execution |
924
982
 
925
- All hooks receive \`ThreadState\` as their first parameter, providing access to thread identity, message history, and resource loading.
983
+ All hooks receive \`ThreadState\` as their first parameter, providing access to thread identity, message history, resource loading, durable per-thread values via \`getValue\` / \`setValue\`, and sandboxed JS/TS execution via \`runCode\`.
926
984
 
927
985
  ## Creating a Hook
928
986
 
@@ -1086,6 +1144,7 @@ export default defineHook({
1086
1144
  ## Hook Categories
1087
1145
 
1088
1146
  **Transformation hooks** (return modified data):
1147
+ - \`after_system_message\` (return replacement string, or null/undefined for no change)
1089
1148
  - \`filter_messages\`
1090
1149
  - \`prefilter_llm_history\`
1091
1150
  - \`before_create_message\`
@@ -1095,6 +1154,8 @@ export default defineHook({
1095
1154
  - \`after_tool_call_failure\` (return modified result or null)
1096
1155
 
1097
1156
  **Event hooks** (side effects only):
1157
+ - \`after_thread_created\`
1158
+ - \`after_subagent_created\`
1098
1159
  - \`after_create_message\`
1099
1160
  - \`after_update_message\`
1100
1161
 
@@ -1121,25 +1182,33 @@ Define custom API endpoints that operate on specific threads with access to the
1121
1182
 
1122
1183
  | File Pattern | HTTP Method | Route |
1123
1184
  |--------------|-------------|-------|
1124
- | \`name.ts\` | GET | \`/api/threads/:id/name\` |
1125
- | \`name.get.ts\` | GET | \`/api/threads/:id/name\` |
1126
- | \`name.post.ts\` | POST | \`/api/threads/:id/name\` |
1127
- | \`name.put.ts\` | PUT | \`/api/threads/:id/name\` |
1128
- | \`name.delete.ts\` | DELETE | \`/api/threads/:id/name\` |
1129
- | \`nested/route.ts\` | GET | \`/api/threads/:id/nested/route\` |
1185
+ | \`name.ts\` | GET | \`/api/threads/:threadId/name\` |
1186
+ | \`name.get.ts\` | GET | \`/api/threads/:threadId/name\` |
1187
+ | \`name.post.ts\` | POST | \`/api/threads/:threadId/name\` |
1188
+ | \`name.put.ts\` | PUT | \`/api/threads/:threadId/name\` |
1189
+ | \`name.delete.ts\` | DELETE | \`/api/threads/:threadId/name\` |
1190
+ | \`index.ts\` | GET | \`/api/threads/:threadId\` |
1191
+ | \`nested/route.ts\` | GET | \`/api/threads/:threadId/nested/route\` |
1192
+ | \`items/[id].ts\` | GET | \`/api/threads/:threadId/items/:id\` |
1193
+ | \`files/[*].ts\` | GET | \`/api/threads/:threadId/files/*\` |
1194
+
1195
+ Files without a method suffix default to GET. Method suffixes are case-insensitive and support \`.get.ts\`, \`.post.ts\`, \`.put.ts\`, \`.patch.ts\`, and \`.delete.ts\`.
1196
+
1197
+ Dynamic segments use \`[name]\` and are passed to the handler as \`params.name\`. Catch-all segments use \`[*]\` and are passed as \`params['*']\`; the catch-all value may contain \`/\` for nested paths. Static routes match before dynamic routes, and dynamic routes match before catch-all routes.
1130
1198
 
1131
1199
  ## Basic Example
1132
1200
 
1133
1201
  \`\`\`typescript
1134
- // agents/api/status.get.ts -> GET /api/threads/:id/status
1202
+ // agents/api/status.get.ts -> GET /api/threads/:threadId/status
1135
1203
  import { defineThreadEndpoint } from '@standardagents/spec';
1136
1204
 
1137
- export default defineThreadEndpoint(async (req, state) => {
1205
+ export default defineThreadEndpoint(async (req, state, params) => {
1138
1206
  const { messages, total } = await state.getMessages();
1139
1207
  return Response.json({
1140
1208
  threadId: state.threadId,
1141
1209
  messageCount: total,
1142
1210
  status: 'active',
1211
+ params,
1143
1212
  });
1144
1213
  });
1145
1214
  \`\`\`
@@ -1149,9 +1218,10 @@ export default defineThreadEndpoint(async (req, state) => {
1149
1218
  | Parameter | Type | Description |
1150
1219
  |-----------|------|-------------|
1151
1220
  | \`req\` | \`Request\` | The incoming HTTP request |
1152
- | \`state\` | \`ThreadState\` | Thread state with messages, identity, file system, etc. |
1221
+ | \`state\` | \`ThreadState\` | Thread state with messages, identity, file system, \`runCode\`, etc. |
1222
+ | \`params\` | \`Record<string, string>\` | Route parameters captured from \`[name]\` and \`[*]\` path segments |
1153
1223
 
1154
- Note: \`state.execution\` is always \`null\` in endpoints since the thread is at rest.
1224
+ Note: \`state.execution\` is always \`null\` in endpoints since the thread is at rest. \`state.runCode()\` is available from endpoints and is forwarded into the thread runtime; pass plain transferable option values there, and do function-backed sandbox bridges from tools or in-thread execution contexts.
1155
1225
 
1156
1226
  ## ThreadState Methods
1157
1227
 
@@ -1177,6 +1247,10 @@ export default defineThreadEndpoint(async (req, state) => {
1177
1247
  const approvalGate = await state.env('TOPDOWN_APPROVAL_GATE');
1178
1248
  await state.setEnv('TOPDOWN_APPROVAL_GATE', approvalGate || 'open');
1179
1249
 
1250
+ // Durable per-thread JSON values
1251
+ const exports = (await state.getValue<number>('export_count')) ?? 0;
1252
+ await state.setValue('export_count', exports + 1);
1253
+
1180
1254
  // File system operations
1181
1255
  const files = await state.findFiles('**/*.json');
1182
1256
  const data = await state.readFile('/data/config.json');
@@ -1192,7 +1266,7 @@ export default defineThreadEndpoint(async (req, state) => {
1192
1266
  ## POST with Request Body
1193
1267
 
1194
1268
  \`\`\`typescript
1195
- // agents/api/export.post.ts -> POST /api/threads/:id/export
1269
+ // agents/api/export.post.ts -> POST /api/threads/:threadId/export
1196
1270
  import { defineThreadEndpoint } from '@standardagents/spec';
1197
1271
 
1198
1272
  export default defineThreadEndpoint(async (req, state) => {
@@ -1218,11 +1292,12 @@ Create directories for nested routes:
1218
1292
 
1219
1293
  \`\`\`
1220
1294
  agents/api/
1221
- \u251C\u2500\u2500 status.get.ts -> GET /api/threads/:id/status
1222
- \u251C\u2500\u2500 export.post.ts -> POST /api/threads/:id/export
1295
+ \u251C\u2500\u2500 status.get.ts -> GET /api/threads/:threadId/status
1296
+ \u251C\u2500\u2500 export.post.ts -> POST /api/threads/:threadId/export
1223
1297
  \u2514\u2500\u2500 messages/
1224
- \u251C\u2500\u2500 count.ts -> GET /api/threads/:id/messages/count
1225
- \u2514\u2500\u2500 search.post.ts -> POST /api/threads/:id/messages/search
1298
+ \u251C\u2500\u2500 count.ts -> GET /api/threads/:threadId/messages/count
1299
+ \u251C\u2500\u2500 [id].ts -> GET /api/threads/:threadId/messages/:id
1300
+ \u2514\u2500\u2500 [*].ts -> GET /api/threads/:threadId/messages/*
1226
1301
  \`\`\`
1227
1302
 
1228
1303
  ## Error Handling
@@ -1287,10 +1362,18 @@ var WRANGLER_TEMPLATE = (name) => {
1287
1362
  "name": "${name}",
1288
1363
  "main": "worker/index.ts",
1289
1364
  "compatibility_date": "${getScaffoldCompatibilityDate()}",
1290
- "compatibility_flags": ["nodejs_compat"],
1365
+ "compatibility_flags": ["nodejs_compat", "enable_ctx_exports"],
1291
1366
  "observability": {
1292
1367
  "enabled": true
1293
1368
  },
1369
+ "worker_loaders": [
1370
+ {
1371
+ "binding": "AGENT_BUILDER_CODE_LOADER"
1372
+ }
1373
+ ],
1374
+ "vars": {
1375
+ "AGENT_BUILDER_CODE_COMPATIBILITY_DATE": "${getScaffoldCompatibilityDate()}"
1376
+ },
1294
1377
  "assets": {
1295
1378
  "directory": "dist/client",
1296
1379
  "not_found_handling": "single-page-application",
@@ -1330,7 +1413,7 @@ var AGENT_BUILDER_TS = `import { DurableAgentBuilder } from 'virtual:@standardag
1330
1413
 
1331
1414
  export default class AgentBuilder extends DurableAgentBuilder {}
1332
1415
  `;
1333
- var WORKER_INDEX = `import { router } from "virtual:@standardagents/builder"
1416
+ var WORKER_INDEX = `import { router, CodeExecutionBridge } from "virtual:@standardagents/builder"
1334
1417
  import DurableThread from '../agents/Thread';
1335
1418
  import DurableAgentBuilder from '../agents/AgentBuilder';
1336
1419
 
@@ -1341,7 +1424,7 @@ export default {
1341
1424
  },
1342
1425
  } satisfies ExportedHandler<Env>
1343
1426
 
1344
- export { DurableThread, DurableAgentBuilder }
1427
+ export { DurableThread, DurableAgentBuilder, CodeExecutionBridge }
1345
1428
  `;
1346
1429
  function getProjectName(cwd) {
1347
1430
  const packageJsonPath = path3.join(cwd, "package.json");
@@ -1427,11 +1510,34 @@ function createOrUpdateWranglerConfig(cwd, projectName, force) {
1427
1510
  const hasBindings = config.durable_objects?.bindings?.some(
1428
1511
  (b) => b.name === "AGENT_BUILDER_THREAD" || b.name === "AGENT_BUILDER"
1429
1512
  );
1430
- if (hasBindings) {
1513
+ const hasCodeLoader = config.worker_loaders?.some(
1514
+ (loader) => loader.binding === "AGENT_BUILDER_CODE_LOADER"
1515
+ );
1516
+ const hasCodeCompatibilityDate = config.vars?.AGENT_BUILDER_CODE_COMPATIBILITY_DATE === getScaffoldCompatibilityDate();
1517
+ const hasCtxExportsFlag = Array.isArray(config.compatibility_flags) ? config.compatibility_flags.includes("enable_ctx_exports") : false;
1518
+ if (hasBindings && hasCodeLoader && hasCodeCompatibilityDate && hasCtxExportsFlag) {
1431
1519
  logger.info("wrangler.jsonc already configured for Standard Agents");
1432
1520
  return true;
1433
1521
  }
1434
1522
  let result = text;
1523
+ if (!hasCodeLoader) {
1524
+ const loaders = [...config.worker_loaders || [], { binding: "AGENT_BUILDER_CODE_LOADER" }];
1525
+ const edits = modify(result, ["worker_loaders"], loaders, {});
1526
+ result = applyEdits(result, edits);
1527
+ }
1528
+ if (!hasCodeCompatibilityDate) {
1529
+ const vars = {
1530
+ ...config.vars || {},
1531
+ AGENT_BUILDER_CODE_COMPATIBILITY_DATE: getScaffoldCompatibilityDate()
1532
+ };
1533
+ const edits = modify(result, ["vars"], vars, {});
1534
+ result = applyEdits(result, edits);
1535
+ }
1536
+ if (!hasCtxExportsFlag) {
1537
+ const flags = Array.isArray(config.compatibility_flags) ? [...config.compatibility_flags, "enable_ctx_exports"] : ["nodejs_compat", "enable_ctx_exports"];
1538
+ const edits = modify(result, ["compatibility_flags"], flags, {});
1539
+ result = applyEdits(result, edits);
1540
+ }
1435
1541
  if (!config.durable_objects) {
1436
1542
  const edits = modify(result, ["durable_objects"], {
1437
1543
  bindings: [
@@ -1442,10 +1548,12 @@ function createOrUpdateWranglerConfig(cwd, projectName, force) {
1442
1548
  result = applyEdits(result, edits);
1443
1549
  } else {
1444
1550
  const bindings = config.durable_objects.bindings || [];
1445
- bindings.push(
1446
- { name: "AGENT_BUILDER_THREAD", class_name: "DurableThread" },
1447
- { name: "AGENT_BUILDER", class_name: "DurableAgentBuilder" }
1448
- );
1551
+ if (!bindings.some((b) => b.name === "AGENT_BUILDER_THREAD")) {
1552
+ bindings.push({ name: "AGENT_BUILDER_THREAD", class_name: "DurableThread" });
1553
+ }
1554
+ if (!bindings.some((b) => b.name === "AGENT_BUILDER")) {
1555
+ bindings.push({ name: "AGENT_BUILDER", class_name: "DurableAgentBuilder" });
1556
+ }
1449
1557
  const edits = modify(result, ["durable_objects", "bindings"], bindings, {});
1450
1558
  result = applyEdits(result, edits);
1451
1559
  }
@@ -1512,10 +1620,23 @@ async function createOrUpdateWorkerEntry(cwd, force) {
1512
1620
  const content = fs3.readFileSync(entryPath, "utf-8");
1513
1621
  const hasRouter = content.includes("virtual:@standardagents/builder") && content.includes("router");
1514
1622
  const hasDurableExports = content.includes("DurableThread") && content.includes("DurableAgentBuilder");
1515
- if (hasRouter && hasDurableExports && !force) {
1623
+ const hasCodeBridge = content.includes("CodeExecutionBridge");
1624
+ if (hasRouter && hasDurableExports && hasCodeBridge && !force) {
1516
1625
  logger.info(`${path3.relative(cwd, entryPath)} already configured`);
1517
1626
  return true;
1518
1627
  }
1628
+ if (hasRouter && hasDurableExports && !hasCodeBridge && !force) {
1629
+ fs3.writeFileSync(
1630
+ entryPath,
1631
+ `${content.trimEnd()}
1632
+
1633
+ export { CodeExecutionBridge } from "virtual:@standardagents/builder"
1634
+ `,
1635
+ "utf-8"
1636
+ );
1637
+ logger.success(`Updated ${path3.relative(cwd, entryPath)} with CodeExecutionBridge export`);
1638
+ return true;
1639
+ }
1519
1640
  try {
1520
1641
  const mod = await loadFile(entryPath);
1521
1642
  if (!content.includes("virtual:@standardagents/builder") || force) {
@@ -1565,6 +1686,7 @@ async function createOrUpdateWorkerEntry(cwd, force) {
1565
1686
  logger.log("");
1566
1687
  logger.log(" // Export Durable Objects:");
1567
1688
  logger.log(" export { DurableThread, DurableAgentBuilder }");
1689
+ logger.log(' export { CodeExecutionBridge } from "virtual:@standardagents/builder"');
1568
1690
  logger.log("");
1569
1691
  return false;
1570
1692
  }
@@ -1880,7 +2002,7 @@ function getWorkspacePackageVersion() {
1880
2002
  if (process.env.STANDARDAGENTS_WORKSPACE === "1") {
1881
2003
  return "workspace:*";
1882
2004
  }
1883
- return "0.14.0";
2005
+ return "0.15.0";
1884
2006
  }
1885
2007
  function getSipPackageVersion() {
1886
2008
  return "1.0.1";
@@ -4111,7 +4233,7 @@ async function availableModels(options = {}) {
4111
4233
 
4112
4234
  // src/index.ts
4113
4235
  var program = new Command();
4114
- program.name("agents").description("CLI tool for Standard Agents / AgentBuilder").version("0.14.0");
4236
+ program.name("agents").description("CLI tool for Standard Agents / AgentBuilder").version("0.15.0");
4115
4237
  program.command("init [project-name]").description("Create a new Standard Agents project (runs create vite, then scaffold)").option("-y, --yes", "Skip prompts and use defaults").option("--template <template>", "Vite template to use", "vanilla-ts").action(init);
4116
4238
  program.command("scaffold").description("Add Standard Agents to an existing Vite project").option("--force", "Overwrite existing configuration").action(scaffold);
4117
4239
  program.command("init-chat [project-name]").description("Scaffold a frontend chat application").option("-y, --yes", "Skip prompts and use defaults").option("--framework <framework>", "Framework to use (vite or nextjs)").action(initChat);