molecule-core/docs/agent-runtime/bundle-system.md
Hongming Wang e906f49ec0 chore: open-source preparation — scrub secrets, add community files
Security:
- Replace hardcoded Cloudflare account/zone/KV IDs in wrangler.toml
  with placeholders; add wrangler.toml to .gitignore, ship .example
- Replace real EC2 IPs in docs with <EC2_IP> placeholders
- Redact partial CF API token prefix in retrospective
- Parameterize Langfuse dev credentials in docker-compose.infra.yml
- Replace Neon project ID in runbook with <neon-project-id>

Community:
- Add CONTRIBUTING.md (build, test, branch conventions, CI info)
- Add CODE_OF_CONDUCT.md (Contributor Covenant 2.1)

Cleanup:
- Replace personal runner username/machine name in CI + PLAN.md
- Replace personal tenant URL in MCP setup guide
- Replace personal author field in bundle-system doc
- Replace personal login in webhook test fixture
- Rewrite cryptominer incident reference as generic security remediation
- Remove private repo commit hashes from PLAN.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 00:10:56 -07:00

149 lines
5.6 KiB
Markdown

# Bundle System
A workspace bundle is the portable unit of the platform. It is a single `.bundle.json` file that captures everything needed to recreate a workspace anywhere.
## Bundle Format
```json
{
"schema": "1.0",
"id": "seo-agent-vancouver",
"name": "Vancouver SEO Agent",
"description": "Bilingual EN/ZH SEO page generator",
"tier": 1,
"model": "anthropic:claude-sonnet-4-6",
"system_prompt": "...full prompt...",
"skills": [
{
"id": "generate-seo-page",
"name": "Generate SEO Landing Page",
"description": "...",
"files": {
"SKILL.md": "---\nname: Generate SEO Landing Page\ndescription: ...\n---\n\nInstructions for the agent...",
"tools/write_page.py": "def write_page(keyword, lang):\n ...",
"tools/check_gsc.py": "def check_gsc(url):\n ..."
}
}
],
"tools": [
{ "id": "web_search", "config": {} },
{ "id": "gsc_api", "config": { "scopes": ["search-console"] } }
],
"prompts": {
"prompts/page-generation.md": "...full content...",
"templates/renovation-page.html": "...full content..."
},
"sub_workspaces": [],
"agent_card": { "...": "A2A card snapshot" },
"author": "your-name",
"version": "1.2.0"
}
```
## Skill Serialization
Each skill folder is serialized into a `files` dict — every file in the skill folder becomes a key (relative path) with its content as the value. No special treatment for any file type — `SKILL.md`, tool scripts, templates, and any other files are all serialized the same way.
```python
def serialize_skill(skill_path: Path) -> dict:
skill_data = {"id": skill_path.name, "files": {}}
for file in skill_path.rglob("*"):
if file.is_file():
rel = str(file.relative_to(skill_path))
skill_data["files"][rel] = file.read_text()
# name/description extracted from SKILL.md frontmatter
md = skill_data["files"].get("SKILL.md", "")
skill_data["name"] = extract_frontmatter(md, "name")
skill_data["description"] = extract_frontmatter(md, "description")
return skill_data
```
On import, the importer reverses the process — it writes each key back as a file under the skill folder:
```python
def deserialize_skill(skill_data: dict, target_path: Path):
skill_dir = target_path / skill_data["id"]
for rel_path, content in skill_data["files"].items():
file_path = skill_dir / rel_path
file_path.parent.mkdir(parents=True, exist_ok=True)
file_path.write_text(content)
```
## What Is Included
- The full system prompt text
- All skill files (every file in each skill folder, inlined as strings in a `files` dict)
- All prompt templates (markdown files, inlined)
- All asset files (HTML templates, etc., inlined)
- Tool configurations (which tools to use, how configured)
- Sub-workspace bundles recursively (for team workspaces)
- A snapshot of the Agent Card
## What Is NOT Included
- **API keys or secrets** — buyer brings their own
- **Memory or conversation history** — buyer starts fresh
- **Database data** — not portable
## Recursive Sub-Workspaces
`sub_workspaces` is an array of nested bundles. A team workspace contains the full bundles of all its members. The importer walks the tree recursively and provisions each workspace it finds.
## How Bundles Are Built
The `workspace-configs-templates/` folder contains the source files for each workspace type. The `bundle-compile.sh` script walks each folder and inlines all files into a single `workspace.bundle.json`. This is the compiled artifact — like a built binary from source files.
## Import/Export Workflow
### Export
1. Right-click node on canvas
2. Select "Export as bundle"
3. Platform serializes the running workspace via `GET /bundles/export/:id`
4. Downloads `seo-agent.bundle.json`
### Import
1. Drag `.bundle.json` onto canvas
2. Canvas sends `POST /bundles/import`
3. Platform importer (`bundle/importer.go`) walks `sub_workspaces` recursively
4. Each workspace gets a container provisioned with extracted config
5. New nodes appear on canvas
### Partial Import Failure
If a bundle contains sub-workspaces and one fails to provision:
1. Successfully provisioned workspaces remain running
2. Failed workspaces get `WORKSPACE_PROVISION_FAILED` events
3. The parent workspace still comes online (it can operate with fewer sub-workspaces)
4. Canvas shows failed sub-workspace nodes in red with retry buttons
5. User can retry individual failed sub-workspaces without re-importing the whole bundle
### ID Generation on Import
**The platform always generates fresh workspace IDs on import.** The original bundle `id` is stored as `source_bundle_id` in the workspace record for traceability.
This means:
- You can import the same bundle twice to run two instances
- The bundle `id` is the **template identity**, the workspace `id` is the **instance identity**
- You can trace which template a workspace came from
- You can find all instances of the same bundle
- You can push updates from the original template
### Duplicate
Export + re-import with new IDs (always — no overwriting).
## Future: Marketplace
Bundles are the unit of sale:
- A seller lists a bundle with a price
- A buyer purchases it
- The platform provisions it in the buyer's environment with their own API keys
- The seller's prompts and skills are included
- Like buying a Shopify theme — you get the design and logic, not the store's data
## Related Docs
- [Workspace Runtime](./workspace-runtime.md) — Source files that compile into bundles
- [Platform API](../api-protocol/platform-api.md) — Import/export endpoints
- [Canvas UI](../frontend/canvas.md) — Drag-and-drop bundle interactions