stackbone build

stackbone build runs inside a project folder and targets no installation, so it takes no --agent. Its --json payload uses the standard envelope and it exits with the shared exit codes.

Compile the project into the workspace bundle: the directory your container image serves the agent from. The running image carries no TypeScript and no bundler, so build compiles every agent and workflow here, ahead of time.

Use build instead of stackbone package when you already own the image and the deployment and want only the workspace bundle.

stackbone build                       # writes dist/workspace-bundle
stackbone build --out-dir out/bundle  # anywhere else (relative to the project)
Flag Type Description
--out-dir string Where to write the bundle. Relative paths resolve against the project. Default: dist/workspace-bundle.

What lands in the output directory:

dist/workspace-bundle/
  .well-known/workspace.json                    # the manifest the runtime reads first
  .well-known/config/v1/config-schema.json      # your config.schema.ts, as JSON Schema
  .well-known/workflow/v1/step.mjs              # your compiled workflow steps
  .well-known/workflow/v1/workflow.vm.js        # the workflow bundle the VM runs
  .well-known/workflow/v1/manifest.json         # workflow ids, schemas, schedules
  .well-known/workflow/v1/workflow-schemas.mjs  # the live validators `/start` checks input with
  .well-known/migrations/v1/                    # your database migrations, copied verbatim
  deep-agents/<name>/index.mjs                  # one prebuilt module per deep agent

workflow-schemas.mjs makes the deployed container reject a bad POST /api/workflows/:name/start the same way stackbone dev does, instead of starting the run and failing somewhere inside it. The build compiles it from your inputSchema / outputSchema exports with the same zod your workflows use, and inlines your dependencies into it, so the file loads wherever the image runs. Only the packages the runtime image already carries stay outside it.

When the build cannot inline a dependency (a native addon, a dynamic require), it warns and emits the older shape instead, which re-imports your packages at boot. That file loads on your laptop and may not load in the image. Name the package in build.external, or move the schemas into a <name>.contract.ts sibling that imports only zod. A container that cannot load the sidecar says so in its log and falls back to the shapes recorded in the manifest: /start still refuses a wrong-shaped payload with a 400, but your defaults, coercions and .transform() calls do not run. Grep for that warning after a deploy.

config-schema.json is your config.schema.ts, converted to JSON Schema at build time. The container has no TypeScript, so it reads this file to render the config form in the dashboard and to validate every save. Write no config.schema.ts and the build ships no file: config stays free-form. Write one the build cannot convert and it warns instead of failing, ships without the file, and the dashboard falls back to a raw JSON field with no validation on save. See stackbone.config.

The build bundles each deep agent with its own dependencies inlined, except for the packages that must stay a single copy per process. If one of your dependencies cannot survive bundling, keep it out with build.external. See Keeping a package out of the bundle. You install anything you list there into your image by hand, and the build prints the list so you know what to add.

If your agent has a database, the build copies .stackbone/migrations/ into the bundle and the container applies it on the first boot, straight off disk. There is nothing to download, so a container with no outbound network still comes up with your tables in place. Applying is idempotent: a restart adds nothing, and a boot against an already-migrated database logs that it found nothing to do. See stackbone.database. A workspace with no schema file, or with a migrations folder you never generated into, ships no migrations and the boot says so.

If the project is linked (see stackbone link), the manifest also records the agent and organization it was built for, so the deployed container needs fewer environment variables.

Two workflows always come along, the same two stackbone dev gives you: rag-ingest (it turns an uploaded document into searchable chunks) and mapping-suggest (the dashboard's Auto-map runs it). The build writes the platform version of each into .stackbone/managed-workflows/ unless you wrote your own at workflows/rag-ingest.workflow.ts or workflows/mapping-suggest.workflow.ts, in which case yours wins. Leave them out and a document upload fails in the container while it still works on your laptop.

A workspace with only agents builds fine, and so does one with only workflows. Anything you did not write stays out of the manifest, and the runtime skips it.

A workflow that fails to compile does not fail the build. It ships in the manifest marked degraded, carrying its own compile error, so the deployed box lists it with the reason instead of serving a shorter catalog that explains nothing. The build names each degraded workflow in its summary. An agent that fails to compile still fails the whole build.

The build also reads your sources for calls to the prompt catalog and records, per agent and per workflow, which prompt keys that code reads. The inventory travels in the manifest, so the deployment can show which keys your code asks for. Two cases stop the build, because a prompt belongs to one owner and the build will not guess which: a stackbone.prompts call in a file that belongs to no agent and no workflow, and a workflow file that reads the catalog while exporting more than one workflow. Each message names the file and the line. Move the call into the agent or workflow that owns the prompt, or split the file so it exports one workflow named after it. A project with no TypeScript installed gets a warning and a bundle with no inventory, not a failed build.

Copy the directory into your image (the runtime reads it from /app/workspace) and deploy. The variables that image boots on are listed at What you set on the deployed container.

Requirements: your project must have esbuild installed (for agents) and @workflow/builders (for workflows, which every bundle has because of the two above). The build resolves both from your own node_modules, so it builds the bundle with the exact versions your agent runs.

JSON payload

{
  "schema_version": 1,
  "outDir": "/abs/path/dist/workspace-bundle",
  "deepAgents": ["support", "billing"],
  "workflows": ["onboarding", "rag-ingest", "mapping-suggest"],
  "degradedWorkflows": [], // shipped but not runnable; each entry carries { name, message }
  "migrations": true, // false when the workspace ships no migrations
  "external": ["sharp"], // what you must install into the image yourself
  "manifestVersion": 2,
  "minImageVersion": "0.3.0", // the oldest runtime image that can read this manifest
}

Exit codes: 0 ok, 1 an agent compile failed (the message names the agent at fault; a failed workflow ships degraded instead, see above), a prompt call could not be attributed to an agent or a workflow, or the build could not resolve esbuild or @workflow/builders from your project.

BUILT WITH ❤️ FROM CANADA AND SPAIN