forked from molecule-ai/molecule-core
Forked clean from public hackathon repo (Starfire-AgentTeam, BSL 1.1) with full rebrand to Molecule AI under github.com/Molecule-AI/molecule-monorepo. Brand: Starfire → Molecule AI. Slug: starfire / agent-molecule → molecule. Env vars: STARFIRE_* → MOLECULE_*. Go module: github.com/agent-molecule/platform → github.com/Molecule-AI/molecule-monorepo/platform. Python packages: starfire_plugin → molecule_plugin, starfire_agent → molecule_agent. DB: agentmolecule → molecule. History truncated; see public repo for prior commits and contributor attribution. Verified green: go test -race ./... (platform), pytest (workspace-template 1129 + sdk 132), vitest (canvas 352), build (mcp). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
149 lines
5.6 KiB
Markdown
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": "hongming",
|
|
"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
|