No human in the loop — by design
CommandAGI is built for machines that run unattended. An agent that holds a grant arms a unit, runs programs and sends commands in real time, through the workbench, the API or MCP, without a confirmation for each action. What keeps that safe is not a person watching; it is the grant, the driver's bounds and the record.
Principals
Everything that acts is a principal: agent:<name> or person:<name>. A principal proves who it is with a credential — it never just names itself. A request that only says who it is is refused.
- Host credential —
cagp_…, issued once bycommandagi grant token --principal agent:<name>(or when a grant is added). The host stores only its SHA-256.grant tokenrotates it;grant forgetremoves it. - Account credential —
cagc.<payload>.<signature>, signed with your account's key (Ed25519) for agents running in the cloud. It names the principal, the account, an expiry and optionally one host, and is honoured only for grants that account made. Revoke one withgrant revoke-credential.
Standing grants
A standing grant is authority an account gives one principal, made by a person's explicit act. No tool or agent route creates or widens a grant; an agent's API key is refused on every route that changes authority.
commandagi grant add --principal agent:night-shift \
--world shop --unit mill-1 --channel gcode \
--verb open --verb arm --verb run --verb send \
--for 8h --note "night shift"
commandagi grant list
commandagi grant revoke g-3f9a1c20b7deA grant names a world and, optionally, units, channels and verbs — leaving one out means all of them. It may expire (--for 8h, --expires <ISO time>). Grants are kept in ~/.commandagi/grants.json, read on every action, and can also be made in the workbench under settings → grants. A malformed grants file honours no grant at all.
{ "grants": [ { "id": "g-3f9a1c20b7de", "principal": "agent:night-shift",
"grantedBy": "acct:user:u_8f2k", "world": "shop", "units": ["mill-1"],
"channels": ["gcode"], "verbs": ["open", "arm", "run", "send"],
"expires": "2026-10-01T06:00:00Z", "note": "night shift" } ] }The same command makes three other kinds of grant: --file PREFIX… (write files under a path), --outreach APP… --per-day N (send messages through connected apps) and --market VENUE… --symbol S --max-qty N --max-notional N --per-day N (place orders).
Grants apply to physical worlds. A simulation world needs none.
Verbs, arming and safing
A grant can give four verbs: open a channel, arm a unit, run a program, send a command.
- Drivers that move hardware open disarmed. Any principal with a grant on the unit can arm it. An armed session that sits idle for five minutes disarms itself.
- Drivers that only take input — a shell, a Python kernel, a job hand-off — need no arming; the grant is checked on every input.
- Safing is always allowed.
stop,disarmandclose— and safing commands such as feed hold, pause, emergency stop and torque off — are allowed to anyone holding any unexpired grant on the unit, to whoever opened the session, and to the account's own person. Cancelling a resting order is safing too.
The rules every action follows
- Recorded before it is sent. Before the driver is called, a note — the principal and the action — is appended to the run's stream and flushed. A read-only project refuses the action rather than act without a record.
- An acknowledgement is not a state. A reply or
oknever implies the commanded state was reached. Commands stay pending until the machine reports a state. - Unknown stays unknown. A lost link is recorded as
state: unknown. A stream that ended without an end record reads interrupted: state unknown. - No silent retries. A physical command is never retried by the system. An agent may decide to try again; that decision is its own recorded command.
- Bounds are the driver's job. Declared limits, intervals and allowed verbs are enforced by the driver, not by a person. Input streams are paced: one action at a time, at least 30 ms apart.
- Simulated and physical never mix. A run belongs to one world, and its records' source must match the world's kind.
Threads and their agent
Starting a thread in a physical world grants the thread's agent (threads.agent, agent:mcp by default) every verb on that world's units, for at most 24 hours, and revokes it when the thread closes. Turn this off with the setting "threads.agentControl": false.
Next
Connect an agent to all of this over MCP: SDK and MCP.