Execution

Who may call a command and what the runtime does with the call, from the exec envelope through the fail-closed pipeline and wrap argv to the result.

On this page

Every Invoker reaches a command through one exec envelope and one fail-closed pipeline, which ends in a result an agent can branch on. Build your runtime's exec path against this page; Specification: Execute holds the normative text.

Invoker#

An Invoker is whoever runs a command, and HCP treats all four the same:

InvokerTypical faceExample
HumanCLIledger transfer 2.80 --to demo-transit --confirm --json at a terminal
AgentCLI or JSONA tool loop calling hcpx exec or Hcp.exec()
Product APIJSONA backend posting { pack_id, verb_key, args }
UIJSONA button that fires the same envelope

Invokers never redefine Policy or Binding (L11). An Invoker carries a Principal and adds no auth, confirm logic or policy of its own, because with two policies on one command the weaker one wins. Nor does its type pick the Binding mode: an agent acting for a human uses user Binding for that human's mail, an operator job uses mothership Binding for fleet wraps, and "it's an agent, give it the service token" is how elevation bugs start.

Agent harness ─┐
Product API   ─┼─► same command ─► Policy + grants ─► Binding mint ─► Adapter
UI gesture    ─┤
Human CLI     ─┘

Routing agent calls through bash and UI calls through HTTP is a runtime detail, as long as both reach the same command, Policy row and Binding under a Principal. An agent Invoker runs unattended, so it stays non-interactive (no prompts, -y on installers), asks for --json, reads availability before planning, surfaces writes as approvals and passes confirm: true only after agreement, and loads a Skill's lead first and the full text on demand. A UI Invoker fires the JSON envelope, shows the confirm gate as an approval dialog and never pre-sets confirm: true, takes badges and "credential stale" banners from the runtime's Sense sink without owning mint, and never embeds credentials in the client.

The envelope#

JSON
{
  "pack_id": "ledger",
  "verb_key": "transfer",
  "args": { "amount": 10, "confirm": true }
}
FieldMeaning
pack_idThe pack. surface_id is accepted as a legacy alias
verb_keyThe command name
argsCommand arguments. confirm: true or apply: true satisfies a confirm gate. mode: "mock" requests explicit mock Binding

The CLI face (ledger transfer 2.80 --to demo-transit --confirm --json) is the same command, and hcpx exec ledger transfer --json -- amount=2.80 payee=demo-transit confirm=true is how the CLI sends the envelope.

The pipeline#

The steps run in order, and nothing runs after a denial.

#StepDenies with
1Resolve the pack, its activation on this session and the command's Policy row, which a linted pack always hasunknown pack <id>, pack <id> not activated, no policy for <verb_key>
2Authenticate the Principal and take the tenant from the session. Land brain needs a project in scope, and land local binds the sandbox cwdprivacy.land brain requires project_id
3Assert the capability: verb.exec.read for operation: read, verb.exec.write for operation: write, pack.connect for a connect command, with grants scoped by pack_id and verb_keysdenied: missing verb.exec.write for ledger.transfer
4Enforce confirm: a Policy row with confirm: true needs confirm: true or apply: true in the envelopeconfirm required
5Mint Binding from the hub for this Principal, pack and family (user, local, mothership). public skips the step and mock must be explicit in the envelopeThe mint failure, never a fallback to an environment token
6Run the Adapter in the sandbox: a wrap renders argv, checks the allow-list and spawns, and a composite calls in-processsandbox: bin "x" not allowlisted (exit 127)
7Return a structured result and audit writes with the attribution tupleNone

Wrap rendering#

For kind: wrap, the runtime builds argv from the command's cli[]:

cli[]          ["ledger", "transfer", "{{amount}}", "--to", "{{payee}}", "--memo", "{{memo}}", "--confirm"]
args           { amount: "2.80", payee: "demo-transit", confirm: true }
argv           ["transfer", "2.80", "--to", "demo-transit", "--confirm", "--json"]
spawn          <resolved wrap.bin> transfer 2.80 --to demo-transit --confirm --json   (cwd = pack dir)
  • The first cli[] token is the bin name and is dropped, since wrap.bin is what spawns; a relative wrap.bin resolves against the pack directory.
  • {{name}} renders as String(args.name) and --flag={{name}} as --flag=<value>. A missing value renders empty and drops out with the flag in front of it (--memo above).
  • Without cli[], wrap.argv_template renders with {{verb}} set to the command tail (setup.apply becomes apply).
  • The JSON flag (--json unless json_flag says otherwise) is appended, and the arguments go to the process as an array, with no shell.
  • The working directory follows wrap.workspace.mode: the pack directory by default, the caller's project for project, or a private directory for workspace and ephemeral (Where the CLI runs).
  • wrap.agent_env is merged into the child environment, and HCP_SANDBOX is set to sandbox or native-local.

Result envelope#

JSON
{
  "ok": true,
  "pack_id": "transit",
  "verb_key": "plan",
  "result": { "ok": true, "verb": "plan", "from": "A", "to": "B", "optionId": "opt-1" },
  "stdout": "…",
  "stderr": ""
}
FieldMeaning
oktrue on exit 0 and no denial
resultParsed JSON stdout if it parses, else the raw string
errorPresent when ok is false: a denial reason, confirm required, stderr, or exit N
stdout / stderrRaw streams (wrap only)

A denial comes back as a value, never a thrown error, so agents can branch on it. Errors has the full list.

Composite and other adapters#

composite returns an in-process result. openapi and sense_only are not executable in the reference-local runtime, and a proprietary runtime implements them behind the same exec signature. The bootstrap pack hcp-cli is composite: its read commands (hcp.doctor, hcp.list, hcp.availability, hcp.skill) return directly, and commands that change the store answer with deferred_to_cli: true and a hint, because the CLI process owns the durable store.