create-dynamicagents 0.2.0 → 0.3.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.
Files changed (2) hide show
  1. package/dist/cli.js +35 -19
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1480,8 +1480,8 @@ var blueprintOf = (arg) => BLUEPRINTS.find((blueprint2) => blueprint2.value ===
1480
1480
  var CAPABILITIES = [
1481
1481
  {
1482
1482
  value: "browser",
1483
- label: "Read web pages",
1484
- hint: "@dynamicagents/plugins/browser — needs the BROWSER binding"
1483
+ label: "Drive a browser",
1484
+ hint: "@dynamicagents/plugins/browser — needs the BROWSER and LOADER bindings"
1485
1485
  }
1486
1486
  ];
1487
1487
 
@@ -1649,19 +1649,29 @@ var kindOf = (arg) => KINDS.find((kind2) => kind2.value === arg.toLowerCase());
1649
1649
 
1650
1650
  // src/train.ts
1651
1651
  var TRAIN = {
1652
- core: ">=0.17.0 <0.18.0",
1653
- plugins: ">=0.17.0 <0.18.0"
1652
+ core: ">=0.18.0 <0.19.0",
1653
+ plugins: ">=0.18.0 <0.19.0"
1654
1654
  };
1655
1655
  var RUNTIME = {
1656
- "@cloudflare/think": ">=0.21.0 <0.22.0",
1657
- agents: ">=0.30.0 <0.31.0",
1656
+ "@cloudflare/think": ">=0.23.0 <0.24.0",
1657
+ agents: ">=0.33.0 <0.34.0",
1658
1658
  ai: "^7.0.88",
1659
1659
  /**
1660
1660
  * Think's own non-optional peer, and core's. A generated project gets it
1661
1661
  * whichever way, since npm installs a missing peer, so what naming it buys is
1662
1662
  * a manifest that says what the project runs on.
1663
1663
  */
1664
- "@cloudflare/computer": "^0.5.1",
1664
+ "@cloudflare/computer": "^0.8.0",
1665
+ /**
1666
+ * Nobody's dependency and everybody's import. Think reaches
1667
+ * `agents/skills/runner`, which imports this, so it is in the module graph of
1668
+ * every generated project; `agents` and `@cloudflare/computer` both declare it
1669
+ * as an *optional* peer, so nothing installs it unless a manifest asks. It
1670
+ * arrived free until Computer moved it out of its `dependencies`, and the day
1671
+ * that happened every build that had not named it failed on an unresolved
1672
+ * import. Core declares it for the same reason.
1673
+ */
1674
+ "just-bash": "^3.0.1",
1665
1675
  "workers-ai-provider": "^4.0.0",
1666
1676
  /** The A2A protocol's own package: its constants and types. */
1667
1677
  "@a2a-js/sdk": "^1.2.0",
@@ -1674,12 +1684,12 @@ var TOOLCHAIN = {
1674
1684
  * miniflare a generated project's tests boot, and that runtime reads the
1675
1685
  * compatibility date too.
1676
1686
  */
1677
- "@cloudflare/vitest-plugin": "^1.4.7",
1687
+ "@cloudflare/vitest-plugin": "^1.5.1",
1678
1688
  "@types/node": "^24.19.2",
1679
- eslint: "^10.10.0",
1689
+ eslint: "^10.14.0",
1680
1690
  prettier: "^3.9.6",
1681
1691
  typescript: "^6.0.3",
1682
- "typescript-eslint": "^8.72.0",
1692
+ "typescript-eslint": "^8.72.1",
1683
1693
  /**
1684
1694
  * **4 until the Workers pool takes 5**, whatever this package tests itself
1685
1695
  * with. A generated project tests through `@cloudflare/vitest-plugin`, and
@@ -1691,10 +1701,10 @@ var TOOLCHAIN = {
1691
1701
  * what holds a new project clear in the meantime: an install takes the
1692
1702
  * newest the range allows, and on `^4` that can never be 5.
1693
1703
  */
1694
- vitest: "^4.1.11",
1695
- wrangler: "^4.152.0"
1704
+ vitest: "^4.1.12",
1705
+ wrangler: "^4.157.0"
1696
1706
  };
1697
- var WORKERD = "1.20261019.1";
1707
+ var WORKERD = "1.20261103.1";
1698
1708
  function compatibilityDateOf(workerd) {
1699
1709
  const stamp = /^\d+\.(\d{4})(\d{2})(\d{2})\.\d+$/.exec(workerd);
1700
1710
  if (stamp === null) {
@@ -2252,7 +2262,7 @@ var hostTs = (n3) => render(host_ts_default, {
2252
2262
  });
2253
2263
 
2254
2264
  // src/agent/templates/agent/manifest.ts.tpl
2255
- var manifest_ts_default = 'import type { AgentManifest } from "@dynamicagents/core/a2a";\n\n/**\n * The transport-independent half of this agent\'s AgentCard — everything that\n * does not depend on the request origin. Core adds `supportedInterfaces` (the\n * deployment\'s `/a2a` url, tagged with this agent\'s tenant id) and the security\n * scheme. It is served through `GetExtendedAgentCard`, since the well-known\n * path carries the deployment\'s stub card — `src/host-manifest.ts`.\n *\n * `AgentManifest` is core\'s, derived from the A2A SDK\'s `AgentCard` rather than\n * hand-declared, so a protocol field that gains a requirement fails the build\n * here instead of silently going unadvertised.\n *\n * **The skills are what this agent has.** Adding a capability in\n * `./plugins.ts`, or a sub-agent in `./children.ts`, means a skill here;\n * removing one means removing it. Each skill\'s `inputModes`, `outputModes` and\n * `securityRequirements` are empty, which means "inherit the card\'s" — the\n * default modes below, and the gatekeeper JWT.\n */\nexport const manifest: AgentManifest = {\n // $block CARD_IDENTITY\n version: "0.1.0",\n // `extensions` is a required (repeated) protobuf field in v1.0, and this\n // agent declares no protocol extensions, so it stays empty.\n capabilities: { streaming: false, pushNotifications: true, extensions: [] },\n defaultInputModes: ["text/plain"],\n defaultOutputModes: ["text/plain"],\n skills: [\n // $block SKILLS\n ]\n};\n';
2265
+ var manifest_ts_default = 'import type { AgentManifest } from "@dynamicagents/core/a2a";\n\n/**\n * The transport-independent half of this agent\'s AgentCard — everything that\n * does not depend on the request origin. Core adds `supportedInterfaces` (the\n * deployment\'s `/a2a` url, tagged with this agent\'s tenant id) and the security\n * scheme. It is served through `GetExtendedAgentCard`, since the well-known\n * path carries the deployment\'s stub card — `src/host-manifest.ts`.\n *\n * `AgentManifest` is core\'s, derived from the A2A SDK\'s `AgentCard` rather than\n * hand-declared, so a protocol field that gains a requirement fails the build\n * here instead of silently going unadvertised.\n *\n * **The skills are what this agent has.** Adding a capability in\n * `./plugins.ts`, or a sub-agent in `./children.ts`, means a skill here;\n * removing one means removing it. Each skill\'s `inputModes`, `outputModes` and\n * `securityRequirements` are empty, which means "inherit the card\'s" — the\n * default modes below, and the gatekeeper JWT.\n */\nexport const manifest: AgentManifest = {\n // $block CARD_IDENTITY\n version: "0.1.0",\n // `extensions` empty, `extendedAgentCard` unset: core\'s `AgentManifest`.\n capabilities: { streaming: false, pushNotifications: true, extensions: [] },\n defaultInputModes: ["text/plain"],\n defaultOutputModes: ["text/plain"],\n skills: [\n // $block SKILLS\n ]\n};\n';
2256
2266
 
2257
2267
  // src/agent/templates/agent/manifest.ts
2258
2268
  var CHAT = {
@@ -2270,7 +2280,7 @@ var DELEGATE = {
2270
2280
  var BROWSE = {
2271
2281
  id: "browse",
2272
2282
  name: "Browse the web",
2273
- description: "Read and scrape live web pages — render a page as Markdown, extract structured data, or list its links.",
2283
+ description: "Drive a real browser to read and interact with live web pages, writing code against a session that keeps its tabs, cookies and logins between calls.",
2274
2284
  tags: ["web", "browser"]
2275
2285
  };
2276
2286
  var skill = (it) => {
@@ -2312,7 +2322,7 @@ function manifestTs(spec2) {
2312
2322
  }
2313
2323
 
2314
2324
  // src/agent/templates/agent/plugins.ts.tpl
2315
- var plugins_ts_default = 'import type { AgentPlugin } from "@dynamicagents/core";\n// $when browser\nimport { browser } from "@dynamicagents/plugins/browser";\n// $end\n\n/**\n * The one file you edit to add or remove a capability for this agent.\n *\n * Delete a line and that module leaves the bundle entirely. Nothing in core\n * imports a plugin, and `@dynamicagents/plugins` has no root barrel — the bare\n * specifier does not resolve — so what the agent carries is what is listed\n * here.\n *\n * A plugin declares the bindings and secrets it needs, and core checks them\n * when the agent starts: an installed plugin whose binding is missing from\n * `wrangler.jsonc` fails with a sentence naming the plugin, rather than at the\n * first tool call inside a request someone is waiting on. So a line here and a\n * binding there arrive together.\n *\n * What the model is told about a capability comes from the plugin itself, not\n * from `./soul.ts`: removing a line removes its advice with it.\n * $when subAgent\n *\n * The agent and its `general` sub-agent install the same list. Files are\n * Think\'s own workspace, in each object\'s SQLite, so there is nothing to\n * install for them.\n * $end\n */\n// $when browser\nexport const plugins = (env: Env): AgentPlugin<Env>[] => [\n // Read web pages. Needs the `BROWSER` binding in wrangler.jsonc.\n browser({ binding: env.BROWSER })\n];\n// $end\n// $when !browser\nexport const plugins = (_env: Env): AgentPlugin<Env>[] => [\n // Nothing installed yet. Each capability is one line here plus its binding\n // in wrangler.jsonc plus a skill on the card in ./manifest.ts — for example:\n //\n // import { browser } from "@dynamicagents/plugins/browser";\n // browser({ binding: env.BROWSER })\n //\n // The parameter is `_env` only because nothing reads it yet; the first\n // plugin that needs a binding takes the underscore off.\n];\n// $end\n';
2325
+ var plugins_ts_default = 'import type { AgentPlugin } from "@dynamicagents/core";\n// $when browser\nimport { browser } from "@dynamicagents/plugins/browser";\n// $end\n\n/**\n * The one file you edit to add or remove a capability for this agent.\n *\n * Delete a line and that module leaves the bundle entirely. Nothing in core\n * imports a plugin, and `@dynamicagents/plugins` has no root barrel — the bare\n * specifier does not resolve — so what the agent carries is what is listed\n * here.\n *\n * A plugin declares the bindings and secrets it needs, and core checks them\n * when the agent starts: an installed plugin whose binding is missing from\n * `wrangler.jsonc` fails with a sentence naming the plugin, rather than at the\n * first tool call inside a request someone is waiting on. So a line here and a\n * binding there arrive together.\n *\n * What the model is told about a capability comes from the plugin itself, not\n * from `./soul.ts`: removing a line removes its advice with it.\n * $when subAgent\n *\n * The agent and its `general` sub-agent install the same list. Files are\n * Think\'s own workspace, in each object\'s SQLite, so there is nothing to\n * install for them.\n * $end\n */\n// $when browser\nexport const plugins = (env: Env): AgentPlugin<Env>[] => [\n // Drive a browser by writing code. Needs the `BROWSER` binding and the\n // `LOADER` worker loader in wrangler.jsonc.\n browser({ binding: env.BROWSER, loader: env.LOADER })\n];\n// $end\n// $when !browser\nexport const plugins = (_env: Env): AgentPlugin<Env>[] => [\n // Nothing installed yet. Each capability is one line here plus its binding\n // in wrangler.jsonc plus a skill on the card in ./manifest.ts — for example:\n //\n // import { browser } from "@dynamicagents/plugins/browser";\n // browser({ binding: env.BROWSER, loader: env.LOADER })\n //\n // The parameter is `_env` only because nothing reads it yet; the first\n // plugin that needs a binding takes the underscore off.\n];\n// $end\n';
2316
2326
 
2317
2327
  // src/agent/templates/agent/plugins.ts
2318
2328
  var pluginsTs = (spec2) => render(plugins_ts_default, {
@@ -2545,7 +2555,7 @@ var env_example_default = '# Copy to `.env` for local `wrangler dev`. In product
2545
2555
  var envExample = (spec2) => render(env_example_default, { tokens: { TENANT: spec2.tenant } });
2546
2556
 
2547
2557
  // src/agent/templates/project/host-manifest.ts.tpl
2548
- var host_manifest_ts_default = 'import type { AgentManifest } from "@dynamicagents/core/a2a";\n\n/**\n * The **stub** card served at `/.well-known/agent-card.json`.\n *\n * It describes the deployment, not the agent. The agent is a tenant of this\n * endpoint, so this card exists to be the single conformant, signed AgentCard at\n * the URI RFC 8615 and A2A\'s IANA registration reserve — advertising where to\n * call, which protocol, and that real cards are reachable through\n * `GetExtendedAgentCard`.\n *\n * It cannot list tenants as interfaces: a card carries one interface entry and\n * spec §8.3.2 has clients take the first, so one entry per tenant would just\n * make every client address whichever came first. They go in `description` for a\n * human reading the deploy in a browser, and are registered out of band.\n *\n * **The description must name every mounted tenant.** It is the only place this\n * card says what is here, so a tenant missing from it is a tenant an operator\n * reading the deploy has no way to discover. Mounting a second agent in\n * `src/index.ts` means naming it here too.\n *\n * `skills` is empty for the same reason: the skills belong to the tenants, and a\n * client that picked one from here would have no way to act on it.\n */\nexport const hostManifest: AgentManifest = {\n // $block CARD_IDENTITY\n version: "0.1.0",\n // `extensions` is a required (repeated) protobuf field in v1.0, and this\n // deployment declares no protocol extensions, so it stays empty.\n // `extendedAgentCard` is set by core, which owns that contract.\n capabilities: { streaming: false, pushNotifications: true, extensions: [] },\n defaultInputModes: ["text/plain"],\n defaultOutputModes: ["text/plain"],\n skills: []\n};\n';
2558
+ var host_manifest_ts_default = 'import type { AgentManifest } from "@dynamicagents/core/a2a";\n\n/**\n * The **stub** card served at `/.well-known/agent-card.json`.\n *\n * It describes the deployment, not the agent. The agent is a tenant of this\n * endpoint, so this card exists to be the single conformant, signed AgentCard at\n * the URI RFC 8615 and A2A\'s IANA registration reserve — advertising where to\n * call, which protocol, and that real cards are reachable through\n * `GetExtendedAgentCard`.\n *\n * It cannot list tenants as interfaces: a card carries one interface entry and\n * spec §8.3.2 has clients take the first, so one entry per tenant would just\n * make every client address whichever came first. They go in `description` for a\n * human reading the deploy in a browser, and are registered out of band.\n *\n * **The description must name every mounted tenant.** It is the only place this\n * card says what is here, so a tenant missing from it is a tenant an operator\n * reading the deploy has no way to discover. Mounting a second agent in\n * `src/index.ts` means naming it here too.\n *\n * `skills` is empty for the same reason: the skills belong to the tenants, and a\n * client that picked one from here would have no way to act on it.\n */\nexport const hostManifest: AgentManifest = {\n // $block CARD_IDENTITY\n version: "0.1.0",\n // `extensions` empty, `extendedAgentCard` unset: core\'s `AgentManifest`.\n capabilities: { streaming: false, pushNotifications: true, extensions: [] },\n defaultInputModes: ["text/plain"],\n defaultOutputModes: ["text/plain"],\n skills: []\n};\n';
2549
2559
 
2550
2560
  // src/agent/templates/project/host-manifest.ts
2551
2561
  function hostManifestTs(spec2) {
@@ -2574,6 +2584,12 @@ function dependenciesFor(spec2) {
2574
2584
  "@cloudflare/think": RUNTIME["@cloudflare/think"],
2575
2585
  agents: RUNTIME.agents,
2576
2586
  ai: RUNTIME.ai,
2587
+ // Unconditional, like Think itself: Think imports `agents/skills/runner`,
2588
+ // which imports this, so it is in every generated project's module graph
2589
+ // whatever the agent installs. `agents` and `@cloudflare/computer` declare
2590
+ // it only as an *optional* peer, so a manifest that leaves it out is a
2591
+ // project relying on npm to install a peer nobody named.
2592
+ "just-bash": RUNTIME["just-bash"],
2577
2593
  "workers-ai-provider": RUNTIME["workers-ai-provider"],
2578
2594
  "@a2a-js/sdk": RUNTIME["@a2a-js/sdk"]
2579
2595
  };
@@ -2720,7 +2736,7 @@ var indexTs = (spec2, n3) => render(index_ts_default, {
2720
2736
  });
2721
2737
 
2722
2738
  // src/agent/templates/project/wrangler.jsonc.tpl
2723
- var wrangler_jsonc_default = '/**\n * One Worker, one agent.\n *\n * The agent is a **tenant** of this deployment, routed by `src/index.ts` on the\n * A2A `params.tenant`. That holds with one agent exactly as it would with\n * several, which is what makes adding a second one additive: another directory\n * under `src/agents/`, another block in each list below, and a new migration\n * tag. AGENTS.md carries the recipe.\n *\n * https://developers.cloudflare.com/workers/wrangler/configuration/\n */\n{\n "$schema": "node_modules/wrangler/config-schema.json",\n "name": "$TENANT",\n "main": "src/index.ts",\n // The date is the runtime: a workerd version *is* a release date, and this is\n // the one the pinned wrangler version installs. No `compatibility_flags`\n // beside it, and that is the date\'s doing — `nodejs_compat` is on by default\n // from 2026-08-04, so declaring it here would say nothing this date does not\n // already say.\n "compatibility_date": "$COMPATIBILITY_DATE",\n\n // **One origin, and keep it.**\n //\n // Core derives the card\'s interface URL, its `jku` and the token audience it\n // verifies from `new URL(request.url).origin`. A second origin therefore\n // advertises a second audience, and a gatekeeper token minted for one does\n // not verify against the other — it could only ever serve a card nobody can\n // call.\n //\n // `workers_dev` is on so the first deploy is reachable at a URL you already\n // have. Moving to a domain of your own is one change: turn it off, declare\n // the route, deploy, and re-register the tenant with your gatekeeper against\n // the new origin.\n //\n // "workers_dev": false,\n // "routes": [{ "pattern": "agents.example.com", "custom_domain": true }]\n "workers_dev": true,\n\n "observability": {\n "enabled": true\n },\n\n // --- required by core -----------------------------------------------------\n\n // Workers AI backs the agent\'s model, through AI Gateway. There is no model\n // credential in this project: Cloudflare authenticates this binding for the\n // Worker. `remote: true` reflects reality — Workers AI has no local execution\n // mode and always hits remote resources — and suppresses wrangler\'s per-run\n // warning; the test pool turns it off (vitest.config.ts) so no connection is\n // opened per test file.\n "ai": {\n "binding": "AI",\n "remote": true\n },\n // $when browser\n\n // `@dynamicagents/plugins/browser`, installed in\n // $DIR/plugins.ts. Browser Rendering is a paid-plan\n // binding, and `remote: true` is required for it under `wrangler dev`.\n "browser": {\n "binding": "BROWSER",\n "remote": true\n },\n // $end\n\n // The agent\'s task host, which holds a caller\'s A2A tasks, and the step agent\n // its pipeline runs on, which holds the SQLite-backed Session — one\n // continuous history plus writable memory. Both are keyed by the verified\n // caller, which is what makes a task unreachable from any other caller by\n // construction.\n //\n // A host\'s binding is named as its class: `hostBinding` in\n // $DIR/host.ts names it, and the pipeline reaches the\n // host through it.\n //\n // $when subAgent\n // The sub-agent class (`$CHILD_CLASS`) is deliberately absent. It is a\n // facet created beneath its agent, so it needs no binding and no\n // `new_sqlite_classes` entry — only its export from src/index.ts. It does\n // need a test-only binding; see the comment in vitest.config.ts.\n //\n // $end\n // `ARTIFACTS` is **not optional**. Core files every sub-agent note on the\n // task\'s transcript and posts a link instead of the note, so an agent Durable\n // Object that starts without this binding throws `ArtifactsNotBoundError`\n // naming every part of the wiring — the route delegation at the top of\n // `fetch` in src/index.ts included, since without that one an artifact URL\n // falls through to routes that know nothing about it. The class is core\'s\n // own, re-exported unchanged from src/index.ts, and one object serves the\n // whole deployment: a reader arrives holding a token and nothing else, so the\n // object it reaches cannot be keyed by task.\n "durable_objects": {\n "bindings": [\n // $block DO_BINDINGS\n ]\n },\n\n // **A tag is applied once, ever.** Anything appended to a tag already\n // deployed is simply never applied: the class is never created, and the\n // failure surfaces far from this file — at the first request to the agent, or\n // at deploy with "New version of script does not export class \'X\' which is\n // depended on by existing Durable Objects". Always add a new tag.\n //\n // Removing a class from the code reclaims nothing on its own either: every\n // object it created keeps its storage, unreachable and still billed.\n // `deleted_classes` in a new tag is the line that frees them, and it is\n // irreversible.\n "migrations": [\n {\n // $block MIGRATION_V1\n }\n ],\n\n // The agent\'s pipeline: every task is an instance, its id the task\'s. The\n // name is derived from the tenant id rather than from this Worker, so moving\n // the agent into another Worker never renames it — a rename would abandon\n // every instance still running under the old name.\n "workflows": [\n {\n "name": "$WORKFLOW_NAME",\n "binding": "$WORKFLOW_BINDING",\n "class_name": "$WORKFLOW_CLASS"\n }\n ],\n\n // What an operator fills in; `.env.example` says what each one is. Declared\n // here because `secrets.required` is what drives type generation and\n // `wrangler dev`\'s missing-secret warnings — so a name left here and unset is\n // typed as a definite string that is undefined at runtime.\n "secrets": {\n "required": ["A2A_SIGNING_KEY", "GATEKEEPER_ORIGINS"]\n }\n}\n';
2739
+ var wrangler_jsonc_default = '/**\n * One Worker, one agent.\n *\n * The agent is a **tenant** of this deployment, routed by `src/index.ts` on the\n * A2A `params.tenant`. That holds with one agent exactly as it would with\n * several, which is what makes adding a second one additive: another directory\n * under `src/agents/`, another block in each list below, and a new migration\n * tag. AGENTS.md carries the recipe.\n *\n * https://developers.cloudflare.com/workers/wrangler/configuration/\n */\n{\n "$schema": "node_modules/wrangler/config-schema.json",\n "name": "$TENANT",\n "main": "src/index.ts",\n // The date is the runtime: a workerd version *is* a release date, and this is\n // the one the pinned wrangler version installs. No `compatibility_flags`\n // beside it, and that is the date\'s doing — `nodejs_compat` is on by default\n // from 2026-08-04, so declaring it here would say nothing this date does not\n // already say.\n "compatibility_date": "$COMPATIBILITY_DATE",\n\n // **One origin, and keep it.**\n //\n // Core derives the card\'s interface URL, its `jku` and the token audience it\n // verifies from `new URL(request.url).origin`. A second origin therefore\n // advertises a second audience, and a gatekeeper token minted for one does\n // not verify against the other — it could only ever serve a card nobody can\n // call.\n //\n // `workers_dev` is on so the first deploy is reachable at a URL you already\n // have. Moving to a domain of your own is one change: turn it off, declare\n // the route, deploy, and re-register the tenant with your gatekeeper against\n // the new origin.\n //\n // "workers_dev": false,\n // "routes": [{ "pattern": "agents.example.com", "custom_domain": true }]\n "workers_dev": true,\n\n "observability": {\n "enabled": true\n },\n\n // --- required by core -----------------------------------------------------\n\n // Workers AI backs the agent\'s model, through AI Gateway. There is no model\n // credential in this project: Cloudflare authenticates this binding for the\n // Worker. `remote: true` reflects reality — Workers AI has no local execution\n // mode and always hits remote resources — and suppresses wrangler\'s per-run\n // warning; the test pool turns it off (vitest.config.ts) so no connection is\n // opened per test file.\n "ai": {\n "binding": "AI",\n "remote": true\n },\n // $when browser\n\n // `@dynamicagents/plugins/browser`, installed in\n // $DIR/plugins.ts. Browser Rendering is a paid-plan\n // binding, and `remote: true` is required for it under `wrangler dev`.\n "browser": {\n "binding": "BROWSER",\n "remote": true\n },\n\n // The Dynamic Worker the browser tool runs the model\'s code in: a fresh one\n // per call, with no network access of its own. The plugin declares this\n // binding alongside `BROWSER`, so an agent that has one and not the other\n // fails at start rather than at its first browser call.\n //\n // Not inherited by a named environment, so each one needs its own entry.\n "worker_loaders": [{ "binding": "LOADER" }],\n // $end\n\n // The agent\'s task host, which holds a caller\'s A2A tasks, and the step agent\n // its pipeline runs on, which holds the SQLite-backed Session — one\n // continuous history plus writable memory. Both are keyed by the verified\n // caller, which is what makes a task unreachable from any other caller by\n // construction.\n //\n // A host\'s binding is named as its class: `hostBinding` in\n // $DIR/host.ts names it, and the pipeline reaches the\n // host through it.\n //\n // $when subAgent\n // The sub-agent class (`$CHILD_CLASS`) is deliberately absent. It is a\n // facet created beneath its agent, so it needs no binding and no\n // `new_sqlite_classes` entry — only its export from src/index.ts. It does\n // need a test-only binding; see the comment in vitest.config.ts.\n //\n // $end\n // `ARTIFACTS` is **not optional**. Core files every sub-agent note on the\n // task\'s transcript and posts a link instead of the note, so an agent Durable\n // Object that starts without this binding throws `ArtifactsNotBoundError`\n // naming every part of the wiring — the route delegation at the top of\n // `fetch` in src/index.ts included, since without that one an artifact URL\n // falls through to routes that know nothing about it. The class is core\'s\n // own, re-exported unchanged from src/index.ts, and one object serves the\n // whole deployment: a reader arrives holding a token and nothing else, so the\n // object it reaches cannot be keyed by task.\n "durable_objects": {\n "bindings": [\n // $block DO_BINDINGS\n ]\n },\n\n // **A tag is applied once, ever.** Anything appended to a tag already\n // deployed is simply never applied: the class is never created, and the\n // failure surfaces far from this file — at the first request to the agent, or\n // at deploy with "New version of script does not export class \'X\' which is\n // depended on by existing Durable Objects". Always add a new tag.\n //\n // Removing a class from the code reclaims nothing on its own either: every\n // object it created keeps its storage, unreachable and still billed.\n // `deleted_classes` in a new tag is the line that frees them, and it is\n // irreversible.\n "migrations": [\n {\n // $block MIGRATION_V1\n }\n ],\n\n // The agent\'s pipeline: every task is an instance, its id the task\'s. The\n // name is derived from the tenant id rather than from this Worker, so moving\n // the agent into another Worker never renames it — a rename would abandon\n // every instance still running under the old name.\n "workflows": [\n {\n "name": "$WORKFLOW_NAME",\n "binding": "$WORKFLOW_BINDING",\n "class_name": "$WORKFLOW_CLASS"\n }\n ],\n\n // What an operator fills in; `.env.example` says what each one is. Declared\n // here because `secrets.required` is what drives type generation and\n // `wrangler dev`\'s missing-secret warnings — so a name left here and unset is\n // typed as a definite string that is undefined at runtime.\n "secrets": {\n "required": ["A2A_SIGNING_KEY", "GATEKEEPER_ORIGINS"]\n }\n}\n';
2724
2740
 
2725
2741
  // src/agent/templates/project/wrangler.ts
2726
2742
  function wranglerJsonc(spec2, n3) {
@@ -2844,7 +2860,7 @@ var turnSpec = (spec2) => render(turn_spec_ts_default, {
2844
2860
  });
2845
2861
 
2846
2862
  // src/agent/templates/test/manifest.spec.ts.tpl
2847
- var manifest_spec_ts_default = 'import { describe, expect, it } from "vitest";\nimport { env } from "cloudflare:workers";\nimport { manifest } from "@/agents/$TENANT/manifest";\nimport { hostManifest } from "@/host-manifest";\nimport { plugins } from "@/agents/$TENANT/plugins";\n\n/**\n * The card a gatekeeper registers from.\n *\n * What it says is what callers believe this agent can do, so the assertions\n * here are about it staying true to the agent rather than about its prose.\n */\n\ndescribe("the agent card", () => {\n it("names the agent and describes it", () => {\n expect(manifest.name).not.toBe("");\n expect(manifest.description).not.toBe("");\n });\n\n it("gives every skill a unique id, a name and a description", () => {\n const ids = manifest.skills.map((skill) => skill.id);\n expect(new Set(ids).size).toBe(ids.length);\n for (const skill of manifest.skills) {\n expect(skill.id).not.toBe("");\n expect(skill.name).not.toBe("");\n expect(skill.description).not.toBe("");\n }\n });\n\n it("declares the protocol fields core cannot default", () => {\n // `extensions` is a required (repeated) protobuf field in v1.0: empty is a\n // declaration, absent is a protocol error. `capabilities` itself is\n // optional in the SDK\'s type, which is why it is read through `?.` here.\n expect(manifest.capabilities?.extensions).toEqual([]);\n expect(manifest.capabilities?.pushNotifications).toBe(true);\n });\n\n it("advertises a browse skill only when the browser plugin is installed", () => {\n // The assertion that keeps the card honest: a skill nobody can call is\n // worse than a short card. `plugins()` is read rather than a literal, so\n // adding or removing a capability fails here until the card follows.\n const installed = plugins(env).map((plugin) => plugin.name);\n const browses = manifest.skills.some((skill) => skill.id === "browse");\n expect(browses).toBe(installed.includes("browser"));\n });\n});\n\n/** The tenant this Worker mounts, as its stub card has to name it. */\nconst TENANT = "$TENANT";\n\ndescribe("the deployment\'s stub card", () => {\n it("names every tenant this Worker mounts", () => {\n // It is the only place this card says what is here, so a tenant missing\n // from it is a tenant an operator reading the deploy cannot discover.\n expect(hostManifest.description).toContain(TENANT);\n // The skills belong to the tenants: a client picking one from here would\n // have no way to act on it.\n expect(hostManifest.skills).toEqual([]);\n });\n});\n';
2863
+ var manifest_spec_ts_default = 'import { describe, expect, it } from "vitest";\nimport { env } from "cloudflare:workers";\nimport { manifest } from "@/agents/$TENANT/manifest";\nimport { hostManifest } from "@/host-manifest";\nimport { plugins } from "@/agents/$TENANT/plugins";\n\n/**\n * The card a gatekeeper registers from.\n *\n * What it says is what callers believe this agent can do, so the assertions\n * here are about it staying true to the agent rather than about its prose.\n */\n\ndescribe("the agent card", () => {\n it("names the agent and describes it", () => {\n expect(manifest.name).not.toBe("");\n expect(manifest.description).not.toBe("");\n });\n\n it("gives every skill a unique id, a name and a description", () => {\n const ids = manifest.skills.map((skill) => skill.id);\n expect(new Set(ids).size).toBe(ids.length);\n for (const skill of manifest.skills) {\n expect(skill.id).not.toBe("");\n expect(skill.name).not.toBe("");\n expect(skill.description).not.toBe("");\n }\n });\n\n it("declares the protocol fields the card has to carry", () => {\n // `capabilities` is typed `AgentCapabilities | undefined`, which is why it\n // is read through `?.`: a manifest may set it to `undefined` and still\n // typecheck, and `pushNotifications` would then go unadvertised. What\n // `extensions: []` declares is on core\'s `AgentManifest`.\n expect(manifest.capabilities?.extensions).toEqual([]);\n expect(manifest.capabilities?.pushNotifications).toBe(true);\n });\n\n it("advertises a browse skill only when the browser plugin is installed", () => {\n // The assertion that keeps the card honest: a skill nobody can call is\n // worse than a short card. `plugins()` is read rather than a literal, so\n // adding or removing a capability fails here until the card follows.\n const installed = plugins(env).map((plugin) => plugin.name);\n const browses = manifest.skills.some((skill) => skill.id === "browse");\n expect(browses).toBe(installed.includes("browser"));\n });\n});\n\n/** The tenant this Worker mounts, as its stub card has to name it. */\nconst TENANT = "$TENANT";\n\ndescribe("the deployment\'s stub card", () => {\n it("names every tenant this Worker mounts", () => {\n // It is the only place this card says what is here, so a tenant missing\n // from it is a tenant an operator reading the deploy cannot discover.\n expect(hostManifest.description).toContain(TENANT);\n // The skills belong to the tenants: a client picking one from here would\n // have no way to act on it.\n expect(hostManifest.skills).toEqual([]);\n });\n});\n';
2848
2864
 
2849
2865
  // src/agent/templates/test/plugins.spec.ts.tpl
2850
2866
  var plugins_spec_ts_default = 'import { describe, expect, it } from "vitest";\nimport { env } from "cloudflare:workers";\nimport { plugins } from "@/agents/$TENANT/plugins";\n\n/**\n * The capabilities this agent installs, against the bindings this project\n * actually declares.\n *\n * A plugin declares what it needs and core checks it when the agent starts, so\n * a capability added without its `wrangler.jsonc` binding is an agent that\n * throws on its first request. This spec is that check, run in a second rather\n * than in production: it reads the `env` the pool builds from\n * `wrangler.jsonc`, so it fails the moment the two disagree.\n */\n\ndescribe("plugins", () => {\n it("installs each capability once", () => {\n const names = plugins(env).map((plugin) => plugin.name);\n expect(new Set(names).size).toBe(names.length);\n });\n\n it("has every binding and secret its plugins require", () => {\n const missing: string[] = [];\n for (const plugin of plugins(env)) {\n const required = [\n ...(plugin.requires?.bindings ?? []),\n ...(plugin.requires?.secrets ?? [])\n ];\n for (const key of required) {\n if ((env as unknown as Record<string, unknown>)[key] == null) {\n missing.push(`${plugin.name} needs ${key}`);\n }\n }\n }\n expect(missing).toEqual([]);\n });\n});\n';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-dynamicagents",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Create gates, leaders and agents on Dynamic Agents: npm create dynamicagents@latest",
5
5
  "keywords": [
6
6
  "create",