Adapter
When to use wrap, composite, openapi or sense_only, the fields and safety rules of a wrap, and how a pack grows from pass-through to composite.
An Adapter is how a command runs, and it sits beneath the pack's identity. A pack can move from a wrap over a demo bin to the real CLI and then to composite while its id, commands, Policy, Skill, host projection and grants stay the same, which is why commands deserve good names from the start.
| Kind | Meaning | Reach for it when |
|---|---|---|
wrap | Spawn an existing CLI with a rendered argv | The capability already exists as a binary. This is the default |
composite | Call in-process (HTTP/SDK) | You own the SDK, or spawn cost matters |
openapi | Commands bootstrapped from an OpenAPI document | You want a large surface quickly and will review the Policy it infers |
sense_only | Emit triggers; no user commands | The pack observes and never acts |
Wrap#
Most packs start as a wrap, which leaves the wrapped tool unchanged.
verb_key ──► argv from cli[] / argv_template (no shell interpolation)
──► spawn allow-listed wrap.bin … --json
──► parse stdout · non-zero exit → { ok: false, error }"kind": "wrap",
"wrap": {
"bin": "../../bins/ledger.mjs",
"argv_template": ["{{verb}}"],
"agent_env": { "LEDGER_OUTPUT": "json" }
}| Field | Meaning |
|---|---|
bin | The executable. Relative paths resolve against the pack directory |
argv_template | Fallback argv when a command has no cli[]. {{verb}} expands to the command tail |
agent_env | Environment defaults the bin already understands. Pack-owned; runtimes never special-case bin ids |
Arguments are templated into an argv and never into a shell string. The bin
must be on the sandbox allow-list, and an unknown bin is denied, never looked
up on PATH. The minted Binding reaches the bin as the env or files it already
reads, and tokens never appear in Skill logs. A wrap declares named commands or
an explicit pass_through bootstrap (HCP/1.1), and lint fails when it declares
neither.
Pass-through is a bootstrap#
"pass_through": { "allowedSubcommands": ["status", "list"], "maxArgv": 6 }Pass-through gets a binary under Policy in an hour. Then promote the hot path to
named commands with Policy rows and drop the allow-list. A mega-command run
does not conform.
Composite#
When the SDK is yours, calling in-process saves serialising across a process
boundary. The hcp-cli bootstrap pack is composite, and so are most
linked-channel packs once they settle, usually in steps:
pass_through ──► named 5–12 commands + policy.json ──► composite for hot pathsOpenAPI#
An openapi pack points at a document and lets tooling propose commands, with
GET as read and mutating methods as write. Review the inferred Policy as a
draft and still name the commands; HCP/1 requires no full codegen UI.
"kind": "openapi",
"openapi": { "url": "https://api.example.com/openapi.json" }Sense-only#
A sense_only pack declares no user commands and at least one stimulus, and
lint fails without a trigger ref. It fits pure ingress such as a webhook source
or an event feed. Add connect or setup.* only when the upstream source
needs a registration call, never bash to "install a webhook".
Related#
- Wrap a CLI
- Sandbox: allow-lists and the cwd jail
- Specification: Manifest: adapter fields