Runtime command discovery¶
The composer discovers slash commands when a runtime session connects. Command metadata is cached for that session and refreshed after a workspace/profile change, a runtime recovery, an advertised catalog change, or the menu's Refresh commands action. A failed or unsupported discovery request does not prevent chat.
| Adapter | Catalog | Execution |
|---|---|---|
| Codex app-server | Existing native controls plus skills/list |
/clear, /new, /goal, and /help keep their existing mappings. /skill:name uses a typed Codex skill input with the discovered path. |
| OpenCode / Gemini CLI / Hermes / OpenClaw ACP | session/update:available_commands_update |
session/prompt, with the slash text preserved at the start. Later advertisements replace that session's catalog, including empty lists. |
| Pi RPC | get_commands |
prompt, preserving command text. Extension commands that finish without agent events receive a completed turn after the runtime confirms it is idle. |
| Hermes Gateway | commands.catalog |
slash.exec, or command.dispatch for skills and quick commands; runtime-produced skill/prompt expansions go to prompt.submit. Plain command output is rendered without a model prompt. |
| OpenClaw Gateway | commands.list for the session's agent and text scope |
chat.send, preserving command text. |
| Claude Code | Commands advertised by stream-json initialization | Runtime-owned commands; use Focalet controls for session/model changes and Claude's terminal for account or permission settings. |
Catalogs describe the commands available through each protocol, not every command in the runtime's terminal application. Pi explicitly omits built-in terminal commands. Hermes terminal commands and session actions requiring a separate client workflow remain visible but disabled. Model/provider/profile/workspace and reasoning commands use Focalet's settings controls until adapters can synchronize authoritative post-command settings; otherwise the next prompt could overwrite the command's changes. Unexpected interactive result types return an explicit error. Codex does not expose a universal built-in command catalog, so new built-in controls still need explicit API mappings.
Discovered runtime commands are disabled while the chat is busy or read-only. Attachments must be sent or removed before a runtime command runs: adding captured context to command arguments would change their meaning. Ordinary messages keep the existing context handoff. Unknown commands on structured adapters are rejected, retaining the draft, rather than being submitted as a model request.
Core contract¶
session.commandstakesruntimeTargetId,sessionId, and optionalforce. It returns normalizedcommandswith name, description, input hint, optional subcommands, source, and disabled reason. Discovery is bounded to five seconds.commands.updatedcarries the complete replacement list for an exact runtime and session.commands.invalidatedcauses rediscovery for that runtime. Push updates take precedence over older pending reads in the controller.command.executetakes the same identity, model settings, and operation ID asturn.start. The adapter validates the command against its catalog before dispatching it. It uses the normal turn event stream and operation deduplication; command intent participates in the operation fingerprint. An accepted or uncertain operation is never replayed automatically.
The core normalizes bounded metadata and chooses the dispatch method itself. A catalog entry cannot supply an arbitrary RPC method or executable.
Verification¶
tests/test_runtime_commands.py exercises the real core host against protocol
fixtures for ACP, Codex, Hermes, OpenClaw, Pi prompts, and Pi extensions. It checks
raw command prefixes, native skill input, immediate Gateway/extension completion, rejection, and
duplicate-operation behavior. Flutter tests cover caching, stale reads, session
isolation, empty replacements, fallback, completion, and the rendered menu.