Building a bundle: composing marketplace assets
Manifest kind: humanos.bundle.v1
Type: BundleManifestV1 (@human/platform-extensions)
Canon: kb/155
A bundle is the installable unit of the HUMΛN marketplace. It composes one or more members — agents, connectors, extensions, workflows, and muscles — into a single install, and it can declare its own extension points on top of those members: command palette entries, Companion prompt lenses, canvas renderers, workforce inbox renderers, proof gates, and install-time configuration.
Anatomy
kind: humanos.bundle.v1
id: org.example.my-suite
name: My Suite
version: 1.0.0
members:
- kind: agent
asset_id: human-agent-example
version: "^1.0"
required: true
- kind: extension
asset_id: human-extension-example
version: "^1.0"
required: true
install_order: [agents, extensions, connectors, workflows, muscles]
commands:
- id: my-suite.show_status
name: Show status
description: View the current status of My Suite's automation
icon: "lucide:Activity"
category: Ops
intent_triggers: ["show my suite status", "how is my suite doing", "my suite health check"]
autonomous_ok: true
commands[] is mandatory (Canon Rule 6)
Every BundleManifestV1 must declare commands with at least one entry. This is Canon Rule 6 (kb/10 §COMPANION AS PLATFORM — Bundle-Contributed Commands): the Companion is the primary UI for the platform, and if your bundle's capabilities aren't registered as commands, users cannot find them — the command palette can't surface them, intent routing can't match them, and the Companion can't invoke them. An empty or missing commands[] array is a validation error, not a warning — validateBundleManifest() rejects it at publish time, at install time, and in CI.
Field by field:
| Field | Type | Required | Notes |
|---|---|---|---|
id |
string |
yes | Globally unique, namespaced <bundle-prefix>.<slug> (e.g. hitl.show_approvals). Regex: /^[a-z][a-z0-9_-]*\.[a-z][a-z0-9_.\-]*$/. Never rename across versions — intent routing and analytics key on it. |
name |
string |
yes | Display name in the palette. Verb-noun, short: "Show approvals", "Escalate to Human". |
description |
string |
yes | One line stating the user benefit, not the implementation ("View active escalation sessions requiring attention", not "Calls the session list endpoint"). |
icon |
string |
yes | lucide:<IconName> scheme (PascalCase Lucide icon name) or an absolute URL to an icon image. |
category |
string |
no | Grouping label in the palette UI, Title Case (e.g. "Human-in-the-Loop", "Finance"). |
canvas_kind |
string |
no | If the command opens a living canvas, the registered CanvasKind it opens (must match a kind registered in @human/companion-canvas). Omit for commands that respond conversationally. |
intent_triggers |
string[] |
yes (may be empty at the schema level, but marketplace review requires ≥3) | Natural-language phrases the intent classifier matches to invoke this command. Use diverse phrasing, not keyword permutations, and avoid overlap with other commands in your bundle. |
autonomous_ok |
boolean |
no | Default false. When true, the Companion may invoke the command without an explicit user confirm — reserve this for strictly read-only commands. If the command mutates state, spends money, or touches anything a human would want to approve, leave it false (or omit it). |
shortcut |
string |
no | Optional palette slash shortcut, e.g. /approvals. Lowercase, hyphenated, starts with /. Shortcuts share one global namespace per org. |
capability_id |
string |
no | Formal capability ID linking this command to a CapabilityReflexEntry in the registry. If omitted, the bundle compiler may synthesize one from id. |
Extension points reference
Beyond members[] and commands[], a bundle can declare any of the following. Every one of these is a generic mechanism available to any bundle — first-party or third-party marketplace — with no bundle-ID-specific code anywhere in the platform.
| Extension point | Field | What it does |
|---|---|---|
| Composed members | members: BundleMemberV1[] |
The agents, connectors, extensions, workflows, and muscles this bundle installs together, in install_order. |
| Command palette | commands: BundleCommand[] |
Command palette entries + intent-routing triggers. Mandatory (Rule 6, above). |
| Proof gates | gates: ProofGate[] |
Measurable outcome definitions (hypothesis, event types, target metric, status thresholds) auto-registered via GateRegistry on install. Picked up by GrowthAgent for weekly measurement; LLM diagnosis stays opt-in per gate (diagnosis_config.enabled). |
| Canvas renderers | canvas_renderers: BundleCanvasRendererEntry[] (paired with canvas_kind on a command) |
Companion Canvas renderers this bundle contributes. Module mode (mode: 'module') is a compiled-in, trusted React renderer resolved by componentRef — this is the path for first-party and reviewed marketplace bundles, not a sandboxed iframe. Sandboxed iframe mode (mode: 'sandboxed_iframe') is for untrusted marketplace UI: hash- and signature-verified bytes rendered inside a strict-sandbox iframe. Pick module mode whenever your renderer ships as part of the reviewed bundle package. |
| Workforce inbox | workforce_module: WorkforceModuleBlockV1 |
Work-item renderer declarations (approval_gate, review_artifact, edit_artifact, guided_workflow) plus routing hints, so tasks your bundle creates render correctly in the Workforce Cloud inbox and route with the right urgency/escalation policy. |
| Prompt contributions | prompt_contributions: Array<{ lens_id, name, description, applies_to } & ({ system_content } | { render_hook })> |
Companion system-prompt lens snippets injected when the listed Companion states (applies_to, e.g. 'intent_detection', 'companion_chat') are active. See the static-vs-render_hook distinction below — this is the field most bundle authors under-use. |
| Config overrides | config_overrides (populated at install; not a manifest field — see below) |
Per-org, per-installation JSON configuration your bundle's runtime code reads back. |
| Install policy | install_policy: 'all_orgs' | 'staff_only' | 'human_org_only' | 'opt_in' |
Controls which orgs can install (or automatically receive) this bundle. See below. |
| Capability tags | capabilities?: string[] |
Opt-in vocabulary tags (e.g. 'escalation_routing') that let core runtime code detect cross-cutting bundle behavior generically instead of branching on your bundle's literal id. See below. |
prompt_contributions: static content vs. render_hook
Every prompt_contributions[] entry shares four fields — lens_id (unique lens identifier), name, description, and applies_to (the Companion states where the lens activates) — plus exactly one of two content sources:
system_content: string — a static string, written once at manifest-author time. This is baked into your bundle's source and is identical for every installing org. Use it when your prompt lens has no org-specific configuration to reflect — the same guidance applies no matter who installs the bundle.
prompt_contributions: [
{
lens_id: 'my-suite.summary-style',
name: 'Summary Style',
description: 'Always summarize before recommending an action.',
applies_to: ['companion_chat'],
system_content: 'When responding, lead with a one-sentence summary before any recommendation.',
},
],
render_hook: { module: string; export_name: string } — a dynamically resolved function, for lenses that need real conditional logic instead of flat {{variable}} substitution. At install (and reseed) time, the platform runs (await import(module))[export_name](configOverrides) and uses the returned string as the prompt content. The exported function receives the installing org's configOverrides and can return a string synchronously or a Promise<string> — build different prose per org from the same bundle package, evaluated fresh every time the org's configuration changes:
// my-suite-bundle/src/prompt-lens.ts
export function buildSummaryStylePrompt(configOverrides: Record<string, unknown>): string {
const style = (configOverrides.summary_style as string) ?? 'concise';
return style === 'detailed'
? 'Always provide a detailed, multi-paragraph summary before any recommendation.'
: 'Always summarize in one sentence before any recommendation.';
}
// bundle manifest
prompt_contributions: [
{
lens_id: 'my-suite.summary-style',
name: 'Summary Style',
description: "Adapts summary depth to the org's configured preference.",
applies_to: ['companion_chat'],
render_hook: { module: '@org/my-suite-bundle', export_name: 'buildSummaryStylePrompt' },
},
],
If you find yourself trying to string-concatenate conditionals into a system_content template, or asking for a special case in core prompt-seeding code so your bundle's prompt can vary by org — stop, and use render_hook instead. It exists precisely so you never need either of those.
config_overrides: write-once at install, updatable after
config_overrides is populated on marketplace_installations at install time for every asset kind (agent, connector, extension, workflow, muscle, or bundle) — it's how your bundle's runtime code reads back the per-org settings a user configured during install (against your shared_config_schema, if you declared one).
After install, it is not frozen: PATCH /v1/orgs/{orgId}/installations/{installationId} merges a JSON body into an installation's config_overrides (a shallow JSONB merge — keys you send overwrite the same keys, everything else is untouched). This is the same generic route for every asset kind; there is no bundle-specific update endpoint to build or ask for.
install_policy: all_orgs is mechanically enforced, not opt-in code
install_policy controls which orgs can install — or automatically receive — your bundle:
'all_orgs'(default) — any org can install; if your manifest also sets this exact policy, it will be automatically installed for every newly created org by a generic, org-creation-time hook that queries approved bundles forinstall_policy = 'all_orgs'and installs each one. You do not need to write, or ask HUMΛN to write, any code that names your bundle to get this behavior — declaring the field is enough.'staff_only'— only HUMΛN staff orgs may install.'human_org_only'— only the canonical HUMΛN internal org.'opt_in'— orgs must explicitly install; no automatic behavior.
capabilities: the generic alternative to a bundle-ID branch
If your bundle needs core platform runtime code to detect and react to something it does, declare a tag in capabilities: string[] rather than asking for your bundle's id to be special-cased. Runtime code checks entry.capabilities?.includes('your_tag') — never manifest.id === 'your.bundle.id'. This is the mechanism the first-party HITL bundle uses to advertise 'escalation_routing' so the Companion operator desk can offer HITL-aware routing without knowing the HITL bundle's ID:
prompt_contributions: [ /* ... */ ],
capabilities: ['escalation_routing'],
capabilities is distinct from reflex_capabilities (reflex/intent routing) and momentum_capabilities (recurring workflow loops) — it's a general-purpose, opt-in vocabulary any bundle can extend. A literal ID-equality branch in core install/lifecycle code (apps/api/src/services/**, apps/api/src/routes/**) fails CI (pnpm hardcoded-id-branches:check) unless it carries an adjacent @canon-deviation block explaining why no generic field fits yet.
Install behavior
The platform installs members in install_order, deduping shared members where policy allows, and records bundle_installations join rows. Uninstall rules must not remove a connector still required elsewhere (see install-bundle.ts policy in-repo).
Vetting
Submitting a bundle stores metadata.platform_extension_vetting with bundle_composition_review. If any extension member exists, global promotion should assume certified-grade review for the composed story — each extension member still has its own iframe/server-driven track.
CLI
human extension validate my-bundle.yaml
human extension publish my-bundle.yaml --dry-run
Brand Voice example
A full "brand voice" style suite (agents + connectors + extension + optional Companion module) is the reference mental model — see kb/155 and marketplace bundle listing UI in Command Plane.
If none of these extension points fit
The extension points above — members[], commands[], gates[], canvas_renderers[], workforce_module, prompt_contributions (static or render_hook), config_overrides, install_policy, and capabilities — cover composition, discovery, measurement, canvas UI, workforce routing, prompt customization, per-org configuration, org-wide rollout, and cross-cutting runtime detection. If none of these fit what your bundle needs: file a gap or open a plan — never ask for your bundle/asset ID to be special-cased in core code.