AIP-45: AGENT-CLI.md — agentcli-interactive/v1 (interactive agent CLI manifest)
A markdown + frontmatter format for declaring an interactive agent CLI — a long-running, bidirectional, agent-as-process binary like Hermes Agent, Claude Code, OpenCode, Goose, or Gemini CLI — and how to install, spawn, and converse with it. Layers over AIP-29 (CLI.md) for install/version/auth blocks and AIP-44 (ACP.md) for the session/wire model when protocol=acp; falls back to MCP or proprietary protocols via a discriminator.
| Field | Value |
|---|---|
| AIP | 45 |
| Title | AGENT-CLI.md — agentcli-interactive/v1 (interactive agent CLI manifest) |
| Status | Draft |
| Type | Schema |
| Domain | cli.sh |
| Requires | AIP-17 (RUNNER), AIP-19 (SECRETS), AIP-29 (CLI), AIP-36 (SANDBOX), AIP-44 (ACP) |
| Reference Impl | @agentproto/driver-agent-cli |
| Resources | ./resources/aip-45 — ADAPTER.md, AGENT-CLI.schema.json, EXAMPLES.md |
Abstract
AGENT-CLI.md packages an interactive agent CLI — a binary that
hosts an agent loop (Hermes, Claude Code, OpenCode, Goose, Gemini CLI)
and converses bidirectionally with a client over stdio or a similar
duplex channel. Each manifest declares how to install, version-check,
sandbox, authenticate, and talk to the binary, with a protocol
discriminator (acp | mcp | proprietary | print) selecting the
wire shape.
AIP-45 is a sibling, not a rival, of AIP-29. AIP-29
remains the home for tool CLIs — gh, stripe, kubectl,
ffmpeg — whose semantics are one-shot cmd → output. AIP-45 covers
agent CLIs whose semantics are persistent sessions with streamed
turn events. The two share the install / version / auth / sandbox
substrate via JSON Schema $ref, so a future "tool CLI that has both
modes" can carry both manifests without duplication.
Motivation
Agent runtimes shipped as CLIs are a fast-growing category:
- Hermes Agent (Nous Research, ACP server)
- Claude Code (Anthropic, ACP via
@agentclientprotocol/claude-agent-acp) - OpenCode (sst, native ACP support)
- Goose (Block, MCP-over-stdio)
- Gemini CLI (Google, proprietary REPL)
- Cursor background agents (ACP)
Today every product wires each one bespoke: invoke it as a subprocess, parse its prompt / output / streaming events, manage its lifecycle, hand it secrets, route tool calls. The cost compounds across products — Guilde, Katchy, Simone, dev tooling — and across platforms (macOS, Linux, headless servers, Modal/Daytona sandboxes).
AIP-45 factors the integration shape into a single declarative file
plus a single TypeScript runner (@agentproto/driver-agent-cli), the
same way AIP-29 factors tool-CLI integration. The discriminator means
new agent CLIs join the catalog by writing a manifest, not by writing
runner code.
The companion observation: agent CLIs converge on ACP. Hermes,
Claude Code, OpenCode, Cursor all speak it. Treating ACP as the
default while leaving room for MCP (Goose), proprietary REPLs
(Gemini CLI today), and headless one-shot CLIs driven print-style
yields one runner with one wire-stable arm and three fallback arms.
Specification
This section uses RFC 2119 keywords (MUST / SHOULD / MAY).
Conformance
A manifest is AIP-45-conformant iff:
- Its frontmatter validates against
./resources/aip-45/draft/AGENT-CLI.schema.json. - Its
install,version_check,authblocks satisfy AIP-29's schemas (referenced via$ref). - When
protocol: "acp", the binary's ACP server satisfies the upstream ACP specification at the AIP-44acp_revdeclared by the boundACP.md. - When
protocol: "mcp", the binary speaks MCP-over-stdio permodelcontextprotocol.io. - When
protocol: "proprietary", the manifest's adapter package (adapterfield) implements theAgentCliClientinterface from@agentproto/driver-agent-cli. - When
modesis present, every entry has a uniqueidmatching^[a-z0-9][a-z0-9\-]*$; mode patches reference no symbols beyondbin_args_appendandenv. - When
optionsis present, every entry has a uniqueidmatching^[a-z0-9][a-z0-9_]*$;type: enumentries declare anenumarray;min/maxonly appear ontype: integer. - When
continuationis present,defaultis insupported; ifdefault == "native-resume"thencapabilities.resumable: trueMUST also be declared. - When
protocol: "print", the manifest MAY omit theprintblock (Claude Code defaults apply); whenprint.event_schemais declared, it MUST be one of the closed enum values listed in § Theprintarm below.
Frontmatter
The full schema lives in
./resources/aip-45/draft/AGENT-CLI.schema.json.
Top-level fields (informative summary):
| Field | Required | Description |
|---|---|---|
name | Yes | Kebab id matching parent dir |
id | Yes | Stable runtime id |
description | Yes | One-paragraph purpose |
version | Yes | Semver of this manifest |
bin | Yes | Path to the binary |
bin_args | No | Default argv (e.g. ["acp"] for hermes acp) |
env | No | Static, always-on environment variables (string → string) merged into the spawn env before any mode/option env patch, so those can override. The always-on counterpart to modes[].env / options[].env; the main user is a generic ACP agent whose whole config surface is bin + args + env. |
install | Yes | Array of install methods (AIP-29 $ref) |
version_check | Yes | Version detection (AIP-29 $ref) |
setup | No | Post-install configuration steps (AIP-29 $ref) — daemons, bind-time prompts, OAuth, external pickers |
auth | No | Auth surface (AIP-29 $ref) |
sandbox | Yes | Sandbox profile (AIP-36 $ref) |
protocol | Yes | acp | mcp | proprietary | print |
acp | When protocol=acp | Ref to AIP-44 ACP.md describing the wire profile |
mcp | When protocol=mcp | Inline MCP server config |
adapter | When protocol=proprietary | NPM package implementing AgentCliClient |
print | When protocol=print | Inline print surface config (optional — Claude Code defaults when omitted) |
session | No | Session policy: mode, idle_timeout_ms, max_turns |
models | No | Model routing: default, allowed, env-mapped. Each allowed entry is a bare id string (provider unstated — the projector MUST NOT guess one) or { id, provider?, mode? }: id is unchanged on the wire, provider is the ProviderPreset id (or direct provider) that serves and bills the model, mode is this adapter's mode id that routes to provider. |
presets | No | Gateway presets (Anthropic/OpenAI-compatible backends) this adapter can drive. See Presets. |
capabilities | No | Declarative capability flags (mirror of ACP capability map for non-ACP runtimes) |
modes | No | Mutually-exclusive operation modes (e.g. claude-code's plan / accept-edits / bypass-permissions). One active per turn. |
options | No | Independent typed knobs (model, max-turns, auto, ...). Multiple may be active per turn. |
continuation | No | How prior turns reach the CLI. Declares default + supported strategies. |
runner | No | AIP-17 RUNNER.md ref or inline runner block |
protocol discriminator
Four values, with cross-field requirements:
# protocol: "acp" → MUST declare `acp` referencing an AIP-44 ACP.md
protocol: acp
acp: ./hermes-acp.ACP.md
# protocol: "mcp" → MUST declare `mcp` with server config
protocol: mcp
mcp:
command: goose
args: [serve]
transport: stdio
# protocol: "proprietary" → MUST declare `adapter` (NPM package)
protocol: proprietary
adapter: "@agentproto/adapter-gemini-cli"
# protocol: "print" → one fresh subprocess per turn (headless one-shot).
# The `print` block is OPTIONAL — omitted, Claude Code defaults apply.
protocol: print
print:
prompt_flag: "--prompt" # absent ⇒ prompt is the last positional arg
output_format: ["--output", "jsonl"]
pre_prompt: ["--no-interactive"]
resume: { flag: "--resume", kind: "value" }
event_schema: mastra-jsonlRuntimes that don't recognise the declared protocol MUST refuse to
load the manifest; this is a hard error, not a warning.
The print arm
Amendment — codifies shipped behaviour.
protocol: "print"is a fourth discriminator arm. It is validated by the closed enumz.enum(["acp", "mcp", "proprietary", "print"])inpackages/driver/agent-cli/src/schema.tsand driven bypackages/driver/agent-cli/src/protocol/print-arm.tsin the reference implementation.
print serves headless one-shot CLIs: instead of a long-lived
connection, the driver spawns one fresh subprocess per turn and
maps the CLI's stdout event stream onto the normalised taxonomy. The
driver-side session object persists across turns; only the per-turn
child is fresh. Because no child outlives a turn, the arm is immune
to the stale-proxy race that long-lived connections are exposed to.
The print manifest block declares the CLI's one-shot surface:
| Field | Description |
|---|---|
prompt_flag | How the prompt text is passed. Absent (or "positional") — appended as the last positional arg. A string — passed as --<prompt_flag> <text> before any trailing positional args. |
output_format | Output-format flags appended before the prompt. Default ["--output-format", "stream-json"] (Claude Code); e.g. Mastra Code uses ["--output", "jsonl"]. |
pre_prompt | Extra flags inserted between output_format and the prompt (e.g. Claude Code's ["--no-interactive"]). |
resume | Resume/continue flag for reattaching to a prior session: flag plus kind — "value" passes --flag <sessionId>, "boolean" passes the bare flag. |
event_schema | Closed enum selecting the event mapper: claude-stream-json (default) | mastra-jsonl | antigravity-stream-json | jcode-ndjson. |
Session tracking and resume. The session's sessionId starts
empty (or pre-seeded from a prior session id) and is captured after
the first successful turn from the wire event that carries the
session / thread identifier (Claude: result.session_id; Mastra:
result.threadId). Subsequent turns pass the declared resume flag
so the CLI rehydrates the conversation. Because the session object —
not the child process — carries the id, resume works without any
long-lived connection.
Session model
session:
mode: ephemeral | persistent | resumable # default: ephemeral
idle_timeout_ms: integer # default: 600000 (10 min)
max_turns: integer # default: unlimited
context_carryover: boolean # default: true within a sessionephemeral sessions are torn down on disconnect. persistent sessions
survive client reconnect within idle_timeout_ms. resumable sessions
are durable across runs (requires the binary to support session/load
or equivalent — clients MUST refuse to declare resumable against a
binary lacking the capability).
Stream event taxonomy
When the runtime relays the binary's output to a host, events conform to a closed taxonomy regardless of underlying protocol:
| Event | Description |
|---|---|
text-delta | Streaming agent message text chunk |
tool-call | Agent invoking a tool — args + tool id |
tool-result | Tool's response delivered back to the agent |
thought | Agent's internal reasoning (when capability declared) |
agent-prompt | Agent asking the user / client for input mid-turn |
turn-end | Agent has finished the current turn |
error | Out-of-band error during the turn |
The runner translates protocol-specific events to this taxonomy exactly once at the protocol arm boundary; downstream consumers never see protocol-specific shapes.
Lifecycle
Discover (manifest in registry)
↓
Install (run install method matching host OS/arch)
↓
Version-check (regex against `bin_args + version_check.cmd`)
↓
Spawn subprocess (apply sandbox + auth env)
↓
Handshake (per protocol arm)
↓
Open session (session/new or equivalent)
↓
LOOP: send turn → consume stream events → emit AIP-7 audit
↓
Close session (session/close or equivalent)
↓
Tear down subprocess (SIGTERM 5s → SIGKILL escalation)Capabilities
capabilities is a manifest-declared mirror of behaviours (separate
from the runtime-negotiated ACP capabilities). It tells the host what
to expect before boot:
capabilities:
streaming: boolean # streams text-delta events
tool_calls: boolean # emits tool-call events
sub_agents: boolean # may delegate to other agents
file_io: boolean # may read/write workspace files
multimodal: boolean # accepts non-text input
resumable: boolean # supports session/load semantics
bidirectional: boolean # may emit agent-promptWhen protocol: "acp", capabilities MUST be a subset of the ACP
capabilities the bound AIP-44 manifest declares; the runner enforces
this at load time.
Modes
modes is OPTIONAL. When present, it declares the set of mutually-
exclusive operation profiles the CLI exposes. The host picks at
most one mode per turn via OPERATOR.runtime.config.mode (AIP-9).
Each mode may patch the spawn bin_args and env; the runner applies
mode patches AFTER the manifest's default bin_args and BEFORE option
patches.
modes:
- id: default
description: Standard interactive session
- id: plan
description: Plan-only mode — no edits, no commands
bin_args_append: ["--permission-mode", "plan"]
- id: accept-edits
bin_args_append: ["--permission-mode", "acceptEdits"]
- id: bypass-permissions
bin_args_append: ["--permission-mode", "bypassPermissions"]Mode ids MUST be unique. The default mode (when the operator omits
config.mode) is "no patch applied" — the manifest's bin_args run
verbatim. Hosts MUST reject an OPERATOR.runtime.config.mode value
that is not in the manifest's modes[].id list.
Besides bin_args_append and env, a mode MAY declare:
| Field | Type | Description |
|---|---|---|
bin_args_prepend | string[] | Extra argv prepended before the manifest's bin_args when the mode is active. |
env_unset | string[] | Env keys to DELETE from the spawn env when the mode is active. Applied at the runtime's env-merge point, after ambient process.env and mode/option env are combined, so a mode can scrub a credential that must never reach a non-native endpoint (e.g. a gateway mode scrubbing ANTHROPIC_API_KEY). Deletion is the only safe semantics for scrubbing; env cannot express it. |
kind | "context" | Which session-config axis the mode expresses. Narrowed to context (what enters context) — the only concern with no ACP-protocol home. Route is derived from the model catalog; posture comes from the harness's own ACP mode registry. Omitted ⇒ inferred from the id. |
status | active | noop | planned | Honest support status surfaced to clients. active does what its description claims; noop is declared and accepted but measured to have no effect; planned is declared but not wired yet. Absent ⇒ active. |
status_note | string | Human-readable reason backing status (e.g. what was measured). |
Options
options is OPTIONAL. When present, it declares typed knobs the CLI
accepts. Unlike modes, options are independent — multiple may be
active in the same turn. Each option declares a type (boolean |
integer | string | enum) and how its value patches bin_args /
env.
options:
- id: model
type: enum
enum: [claude-sonnet-4-6, claude-opus-4-7, claude-haiku-4-5]
bin_args_template: ["--model", "{value}"]
- id: max_turns
type: integer
min: 1
max: 200
bin_args_template: ["--max-turns", "{value}"]
- id: auto
type: boolean
bin_args_append_when_true: ["--auto"]Patch semantics:
bin_args_template— appended when the value is non-default. The literal token{value}is replaced with the option's value (stringified). Use for value-bearing flags.bin_args_append_when_true— applies only totype: boolean. Value must betrue. Use for bare flags.bin_args_prepend— argv template prepended before the manifest'sbin_argswhen the value is non-default;{value}is replaced as inbin_args_template.env— merged into the spawn env. Values may contain{value}.env_unset— env keys to DELETE from the spawn env when the value is non-default. Symmetric withmodes[].env_unsetand applied at the same env-merge point, so a value-bearing option (e.g. abase_urlpointing at a third-party Anthropic-compatible gateway) can scrub the ambient credential it displaces without also selecting a preset mode.
Hosts MUST validate OPERATOR.runtime.config.options.<id> against the
matching option's type / enum / bounds before spawn. Unknown ids are a
hard load-time error.
Presets
presets is OPTIONAL. Each entry declares a gateway preset — a backend an
Anthropic/OpenAI-compatible client can front — so the provider-preset
catalog surfaces adapter-contributed presets alongside the built-in
registry. The catalog dedupes by id; an adapter-declared id that
collides with a built-in is ignored in favour of the built-in.
| Field | Required | Description |
|---|---|---|
id | Yes | Stable preset id. SHOULD equal the mode id when the adapter projects this preset as a mode. |
label | Yes | Human label for the catalog UI. |
schemaFlavor | Yes | anthropic | openai — selects which adapter family can consume the preset. |
baseUrl | Yes | Base URL the client hits. |
keyEnv | Yes | Conventional env var holding the provider's API key. Catalog status is derived from its presence in the daemon's environment (ready vs available). |
description | No | Free text. |
scrubEnv | No | Env vars to scrub when the preset is active. Not surfaced to catalog clients. |
defaultModel | No | Conventional default model id for this provider. |
homepage | No | Homepage/docs URL for the catalog UI. |
A preset carries no adapter projection (env / env_unset / bin_args);
that stays in modes / options.
Continuation
continuation is OPTIONAL but RECOMMENDED. It declares how prior
conversation turns reach the CLI on subsequent invocations. The
driver hosts a strategy registry; this block names which strategies
fit this CLI and which is the default.
continuation:
default: pinned-session
supported: [pinned-session, transcript, none]
pinned_session:
idle_timeout_ms: 1800000
key_scope: [conversation, operator]Built-in strategy ids (continuationStrategyId enum):
| Id | Behaviour | Best for |
|---|---|---|
none | Fresh session per turn, no prior context. Current behaviour. | One-shot tools; debugging |
pinned-session | Driver keeps the spawned child process alive across turns; subsequent session.send calls hit the SAME child, so the CLI's in-memory model context carries over. Idle TTL eviction. | CLIs declaring session.mode: persistent AND context_carryover: true (Claude Code, OpenCode, ...) |
transcript | Caller (host) supplies the last N turns as text; the strategy prepends them as a preamble before the current message. Token-costly but stateless and survives process restarts. | CLIs with resumable: false AND ephemeral session model; debugging when pinning misbehaves |
native-resume | Caller passes a prior session id; the strategy passes it to the CLI's own --resume/--continue flag at start. Requires capabilities.resumable: true. | CLIs with first-class durable sessions |
Hosts MUST refuse to dispatch with a strategy outside supported. The
operator MAY override default via OPERATOR.runtime.config.continuation
provided the chosen id is in supported.
pinned_session.key_scope is the composite key the driver uses to
decide whether two turns share the same pinned child process. Default
[conversation, operator] means: different conversations get different
children; one operator-per-conversation reuses one child across turns.
Sandbox integration
sandbox is REQUIRED. AIP-45 inherits AIP-36 verbatim — the same
provider (local, mastra-modal, mastra-daytona, mastra-e2b,
node-permission, etc.), network.egress, mounts, limits, env
fields apply. The runner passes the resolved sandbox to the protocol
arm; the protocol arm decides how to expose it (e.g. ACP's cwd
parameter on session/new).
Auth surface
auth is OPTIONAL but RECOMMENDED. When present, follows AIP-29's
schema verbatim: ref to an AIP-19 SECRETS.md, state.{paths,env},
login.cmd, refresh.cmd, expiry. The runner resolves secrets via
@agentproto/secrets and passes them to the subprocess via
sandbox.env.set (never argv, never logs).
Setup surface
setup is OPTIONAL. When present, mirrors
AIP-29 § Setup verbatim — an ordered, idempotent
pipeline of post-install configuration steps the host runs once per
(bundle.id, workspace.id, user.id) after install and before the
agent is considered ready. Step kinds: cmd (e.g. openclaw onboard --install-daemon), prompt (e.g. gateway URL, default channel),
oauth (AIP-19 driver), external (browser-based pickers).
Use setup for one-time concerns the auth state machine isn't
designed to model: sidecar daemons, bind-time configuration choices,
secret pastes from web consoles, Discord-channel / Slack-workspace
selection. Recurring credential rotation stays in auth.refresh.
Lifecycle integration:
install → version_check → setup → auth.login → spawn(bin) → handshake → sessionWhen the manifest declares setup, the runner MUST refuse to spawn
the binary until every step has either succeeded or matched its
skip_if. Headless callers receive error.code = "setup_required"
with the pending step list.
Rationale
Why a sibling of AIP-29, not an extension. AIP-29's central
abstraction is commands — a tree mapping subcommand paths to
TOOL.md refs. An agent CLI has no commands tree (or a degenerate
one with one entry, "talk"). Stretching commands to model
session-based interaction would either make AIP-29 schema bloat or
make commands ambiguous. A sibling spec keeps both clean.
Why a protocol discriminator, not separate AIPs per protocol.
The install / version / auth / sandbox / capabilities / session
fields are 90% of an agent-CLI manifest. Splitting per protocol
would force four near-duplicate schemas and four runners. One
runner with four protocol arms keeps the integration surface one
package.
Why a declarative print arm. A whole class of agent CLIs
(Claude Code's --print, mastracode) are best driven headless: one
subprocess per turn, events on stdout. Giving them a dedicated arm
keeps them out of the proprietary arm — which exists for
in-process adapter packages, a different integration shape — and
out of long-lived ACP connections, whose stale-proxy failure modes
the per-turn subprocess simply doesn't have.
Why ACP is the default. Network effects: 14k weekly TS-SDK downloads, multi-vendor adoption, a growing IDE ecosystem. New agent CLIs increasingly ship ACP support out of the box (Claude Code, OpenCode, Cursor announced). Treating ACP as the default and MCP / proprietary as fallbacks tracks where the ecosystem is going.
Why declarative capabilities mirror runtime capabilities. Hosts need to make routing decisions before boot — "send this user to Hermes (multimodal) or Goose (text-only)?" — without spawning every candidate. Manifest-declared capabilities give pre-flight visibility; runtime negotiation is the verification step.
Why stream events are normalised. A consumer (Guilde Shell view,
Katchy workflow runner) doesn't care that Hermes uses ACP and Goose
uses MCP — it cares that both produce text-delta and tool-call
events. Normalising at the protocol-arm boundary lets every consumer
ship one renderer.
Reference Implementation
@agentproto/driver-agent-cli
— TypeScript runner that loads an AGENT-CLI.md manifest, applies
the sandbox + auth, spawns the binary, dispatches to the protocol
arm, and emits normalised stream events.
The package exposes:
defineAgentCli({...})— validates manifest, returns anAgentCliHandlewith astart(opts) → Sessionmethod.Session.send(message) → AsyncIterable<StreamEvent>— write a turn, consume normalised events.Session.cancel()— abort the in-flight turn (forwardssession/cancelfor ACP, equivalent for other protocols).Session.close()— tear down the session and (if last) the subprocess.- Protocol arms under
src/protocol/{acp,mcp,proprietary,print}.ts. The ACP arm delegates to@agentproto/acp(AIP-44).
The reference impl is the source of truth for the schema during the
Draft phase; spec changes propagate through scaffold-aip regen.
Backward compatibility
Not applicable — first agentproto AIP for interactive agent CLIs. AIP-29 stays correct for tool CLIs; AIP-45 covers the agent-CLI case that AIP-29 wasn't designed for.
Security considerations
Inherits AIP-29's threat surface (install/version/auth/sandbox) and adds the bidirectional-protocol surface.
- Untrusted agent output as prompt. The agent's
text-deltaandagent-promptevents end up in the host's UI. They are attacker-controlled if the agent's model was compromised by upstream prompt injection. Hosts MUST apply the same prompt- injection mitigations as for any user input. - Tool calls from the subprocess. Events of type
tool-callask the host to do something. Hosts MUST gate every tool call through AIP-7 governance — the manifest'scapabilities.tool_callsflag is permission to emit, not permission to auto-execute. - Sandbox bypass via auth env. Secrets passed via
sandbox.env.setmay leak into the agent's tool calls (e.g.bashinvocations) if the sandbox provider doesn't isolate child env. Sandbox providers that don't isolate child env MUST NOT be used withauth. - Resumable sessions and replay. A
session: { mode: resumable }manifest persists conversation state. If the persistence store is shared, a malicious actor with read access can inject prior turns into the agent's context. Hosts MUST scope persistent session storage to the authenticated identity. adapterpackages from npm. Whenprotocol: proprietary, the manifest names an NPM package the runner imports. Hosts MUST treat these the same as any other dependency — pin versions, audit publishers, prefer first-party packages.
See also
- AIP-29 — CLI.md — tool CLIs (one-shot
cmd → output); shares install/version/auth blocks via$ref - AIP-44 — ACP.md — wire protocol when
protocol: acp - AIP-9 — OPERATOR.md — runtime that consumes an agent CLI via the optional
runtime: { kind: "agent-cli" }binding - AIP-17 — RUNNER.md — process boundary
- AIP-19 — SECRETS.md — auth surface
- AIP-36 — SANDBOX.md — sandbox profile
./AGENT-CLI.schema.json— JSON Schema validator./EXAMPLES.md— Hermes (acp), Goose (mcp), Gemini CLI (proprietary)./ADAPTER.md— implementer's guide
Resources
Supporting artifacts for AIP-45. Links open the file on GitHub — markdown and JSON render natively in GitHub's viewer. Browse the full resource tree →
AIP-44: ACP.md — agentacp/v1 (Agent Client Protocol profile)
An agentproto profile of the upstream Agent Client Protocol (agentclientprotocol.com) that defines how agentproto operators (AIP-9) participate in ACP — both as clients driving subprocess agents and as servers exposed to ACP-speaking IDEs (Zed, VSCode, JetBrains, Cursor). Pins the upstream protocol revision, layers governance/audit hooks, and standardises operator-binding extensions under metadata.aip44.*.
AIP-46: AGENT-SESSIONS — agent-session-lifecycle/v1 (long-running multi-turn agent orchestration)
A generic session-management protocol for orchestrating long-running, multi-turn agent CLIs (Hermes, Claude Code, OpenCode, …) on a host. Standardises the lifecycle (spawn → multi-turn → kill), the data model (sessions[] with status / ground / workspace bindings, nested via parentSessionId), the over-the-wire surface (HTTP + SSE + MCP tools), the workspace-resolution pattern that lets a single host serve many bound directories, the per-workspace state partitioning that keeps one workspace's history from evicting another's, and the spawn gate that decides whether a session may delegate to further sessions. Layers over AIP-45 (AGENT-CLI) for the per-adapter spawn semantics and AIP-44 (ACP) for the wire format inside one turn; binds AIP-42 (AGENT) as the session's definition, AIP-38 (POLICY) as the delegation authority, and AIP-54 (REF) as the session's ground.