ADR-0033: Worker bundling for YAML agent definitions and Markdown prompts
ADR-0033: Worker bundling for YAML agent definitions and Markdown prompts
Section titled “ADR-0033: Worker bundling for YAML agent definitions and Markdown prompts”Status: Accepted Date: 2026-05-02 Extends: ADR-0031 (YAML agent definition format) Related:
- ADR-0014 (Cloudflare Workers as the runtime)
Context
Section titled “Context”ADR-0031 committed agents to YAML files on disk with system prompts in companion Markdown files. It deferred the worker bundling question:
The Worker has no fs at runtime. YAML files and prompt files are bundled at build time (esbuild text loader, wrangler text_blobs, or raw import of .yaml strings — to be locked in a future commit when the scenario lands).
That commit is now happening. The Phase 1 demo scenario (Order Triage + Refund Decision + Communication) lands three YAML agents and three Markdown prompts in apps/worker/agents/. The worker needs to load them at startup; runtime fs access doesn’t exist on Workers, so the files must be inlined into the bundle.
Decision
Section titled “Decision”Use Wrangler’s built-in [[rules]] configuration with type = "Text" for .yaml, .yml, and .md extensions. Static imports inside the worker (import yaml from './agents/triage.yaml') yield the file contents as a UTF-8 string at runtime.
Why this option
Section titled “Why this option”Three options were considered:
(a) Wrangler [[rules]] for Text — chosen.
Wrangler’s bundler natively supports declaring custom file extensions as text imports. Adds two [[rules]] blocks to wrangler.toml. No custom plugins, no custom build step, no extra tooling.
(b) Vite-style ?raw query suffix.
Standard with Vite/Rollup but not supported by Wrangler’s built-in esbuild bundler. This was my initial lean, corrected before scaffolding when I verified against the Cloudflare bundling docs. Listing it for transparency.
(c) Custom esbuild plugin or Custom Builds escape hatch. Wrangler supports running a custom build before its own. Overkill: we have no transformations to apply, just text inlining, which (a) handles natively.
How it works
Section titled “How it works”[[rules]]type = "Text"globs = ["**/*.yaml", "**/*.yml"]fallthrough = true
[[rules]]type = "Text"globs = ["**/*.md"]fallthrough = true// worker codeimport triageYaml from './agents/triage.yaml';// triageYaml is a string at runtime — the YAML source
import triagePrompt from './agents/prompts/triage.md';// triagePrompt is a string at runtime — the markdown sourceTypeScript needs ambient module declarations to recognize these imports. They live in apps/worker/src/agent-files.d.ts:
declare module '*.yaml' { const content: string; export default content;}declare module '*.yml' { const content: string; export default content;}declare module '*.md' { const content: string; export default content;}What this means for the loader
Section titled “What this means for the loader”The @agent-platform/agent-loader package’s loadAgentFromString primitive accepts a YAML source as a string. The worker code feeds bundled YAML text into it. For system prompt resolution ({ file: ./prompts/x.md } references), the worker supplies a custom FileReader that maps prompt paths to bundled markdown strings — a small in-process lookup, not a real filesystem access.
// Worker assemblyimport triageYaml from '../agents/triage.yaml';import triagePrompt from '../agents/prompts/triage.md';
const PROMPT_REGISTRY: Record<string, string> = { './prompts/triage.md': triagePrompt, // ... other prompts};
const reader: FileReader = async (path) => { const text = PROMPT_REGISTRY[path]; if (text === undefined) throw new Error(`prompt not bundled: ${path}`); return text;};
const triage = await loadAgentFromString(triageYaml, { reader, source: 'agents/triage.yaml' });This wiring lands in a follow-up commit (the assembleRuntime rewire). This ADR locks the bundling decision; the wiring is implementation.
Consequences
Section titled “Consequences”Becomes easy:
- Agent YAML and prompt Markdown ship in the worker bundle automatically. No fetch, no R2, no extra binding.
- Diff-friendly file layout: each agent is its own YAML, each prompt is its own Markdown file.
- Build-time validation possible: a future test or pre-deploy check can call
loadAgentFromStringagainst bundled YAML and fail the build on schema errors.
Becomes hard / accepted tradeoffs:
- Bundle size grows with each YAML/prompt. Phase 1’s three agents add ~10KB total — negligible. Hundreds of agents would matter; that’s the trigger to migrate to runtime loading from R2 or D1.
- Hot reload requires redeploy. Agent edits ship as code. Acceptable for v1; multi-tenant operator-uploaded agents will need a different path (deferred per ADR-0031).
- Wrangler’s built-in bundler is now load-bearing. A future migration to Vite plugin or Custom Builds is bounded but not free.
What this ADR does NOT do
Section titled “What this ADR does NOT do”- Doesn’t address agent registries / runtime loading. That’s a future ADR when multi-tenant lands.
- Doesn’t address worker-host integration with the loader. The wiring (replacing the hardcoded
assembleRuntime) is a follow-up commit. - Doesn’t add bundling for non-text artifacts. WASM, binary fixtures, etc. would need their own decisions.
Trigger conditions for revisit
Section titled “Trigger conditions for revisit”- Agent count grows beyond ~50 in the bundle: bundle size starts mattering; consider runtime loading from R2 or D1.
- Operator-authored agents arrive: build-time bundling can’t ship user content; runtime loading becomes mandatory.
- Wrangler bundler changes break this: esbuild updates have broken patterns before; the
[[rules]]config is the supported path, but if it ever becomes unsupported, Custom Builds is the escape hatch.
Implementation
Section titled “Implementation”Shipped 2026-05-02 alongside the Scenario B agent files:
apps/worker/wrangler.toml:[[rules]]blocks for.yaml,.yml,.mdapps/worker/src/agent-files.d.ts: ambient module declarations for TypeScriptapps/worker/agents/{triage,refund-decision,communication}.yaml: agent definitionsapps/worker/agents/prompts/{triage,refund-decision,communication}.md: system promptsapps/worker/src/agents-yaml.test.ts: load-and-validate test for all three YAMLs
The actual assembleRuntime rewire to use these files instead of the hardcoded research-assistant agent lands in a follow-up commit.