Files
wehub-resource-sync 426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

11 KiB

Universal slash commands

One command catalog, every shipped surface. A user types /settings model in the floating chat, a Discord channel, or a Telegram DM, and the same command runs — discovered from one source, rendered natively per surface.

Why one catalog

Before this, four disconnected slash systems existed: the web composer parsed slashes only at send-time (no menu), the Cmd+K palette had its own nav vocabulary, Discord had native application commands, and Telegram had almost nothing. Nothing shared a definition. This system makes @elizaos/plugin-commands the single source of truth and exposes it over HTTP so every surface consumes the same list.

            @elizaos/plugin-commands  (per-runtime registry)
            DEFAULT_COMMANDS + navigationCommandDefinitions() + skill/custom commands
                                   │
              getCatalogCommands(surface) / getConnectorCommands(surface)
              each definition → serializeCommand()  ── wire-safe, no functions
                                   │
        ┌──────────────────┬───────┴────────┐
   GET /api/commands   getConnectorCommands  getConnectorCommands
     ?surface=gui         ("discord")          ("telegram")
        │                    │                     │
   Web composer        Discord native        Telegram
   inline menu         app commands          setMyCommands +
   (SlashCommandMenu)  (application.commands) bot.command handlers

GET /api/commands is a pure projection: the route (packages/agent/src/api/commands-routes.ts) calls getCatalogCommands(surface, { activeViewId }) and serves the result. Every field — surfaces, requiresAuth, requiresElevated, category, args[].dynamicChoices, target, icon, source — comes from the CommandDefinition via serializeCommand() (src/serialize.ts). Nothing is fabricated at the HTTP boundary.

The command model

A CommandDefinition (see src/types.ts) carries these dimensions beyond name + description:

  • surfaces?: CommandSurface[] — which client surfaces it appears on (gui · discord · telegram, with tui reserved in the contract). Absent = all known surfaces. e.g. /clear, /fullscreen, and /transcribe are ["gui"], so they are filtered off chat connectors and reserved surfaces.
  • target?: CommandTarget — what it does, surface-agnostically. Absent = { kind: "agent" }:
    • { kind: "navigate", path, tab?, viewId?, section? } — jump to a view / sub-view. GUI selects the tab/section; chat connectors reply with a deep link.
    • { kind: "agent", action? } — send the command text to the agent; a deterministic *_COMMAND action produces the reply. Works on every surface.
    • { kind: "client", clientAction } — a pure-client behavior (clear chat, toggle fullscreen, toggle transcription). Local client only.
  • icon?: string — a lucide icon hint for menu rendering.
  • views?: string[] — view-scoping (#8798): the command is surfaced only while one of these views is the active foreground surface. Omitted = global.

Arguments (args[]) can declare static choices or a dynamicChoices source (a CommandArgSource — e.g. views · settings-sections) that each surface resolves against its own live data. serializeCommand() (src/serialize.ts) drops function-valued choices and carries dynamicChoices through so the catalog is always JSON-safe over the wire. Navigation + client commands are first-class CommandDefinitions in src/navigation-commands.ts (navigationCommandDefinitions()); the catalog unions them with the agent registry, so all three target kinds flow through one serializer.

The catalog

Navigation (target: navigate) — src/navigation-commands.ts

Command Destination
/settings [section] settings tab; section arg (dynamicChoices: settings-sections) jumps to a sub-view (model→ai-model, voice, connectors, security, …)
/chat chat surface
/views [view] apps & views launcher; view arg dynamicChoices: views
/orchestrator orchestrator workbench view
/character character editor
/knowledge knowledge & documents
/wallet wallet & inventory
/automations automations & heartbeats
/tasks tasks view
/skills skills library
/plugins installed plugins
/logs logs view
/database database browser

Client (target: client) — surfaces: ["gui"]

Command Action
/clear clear the current chat thread
/fullscreen toggle full-screen chat
/transcribe toggle long-form transcription mode

Agent capability (target: agent) — src/registry.ts DEFAULT_COMMANDS

/help /commands /status /context /whoami · /stop /restart /reset /new /compact · /think /verbose /reasoning /elevated /model /models /usage /queue · /allowlist /approve /subagents · /tts · /bash (and /config /debug, gated off by default) · plus skill-<slug> commands registered from loaded skills and any custom actions the user has defined. These flow to the agent and reply in-channel. Auth-gated commands (/restart /reset /compact /elevated /allowlist /approve /subagents /bash /config /debug) carry requiresAuth: true, which survives serialization onto the wire.

Design decisions — what is not a command

  • No natural-language similes on command actions — the LLM would misroute "I need help" to /help. Commands are slash-only.
  • /voice is deliberately not a navigation command; it's owned by /tts (toggle text-to-speech). Voice settings are reached via /settings voice.
  • Connector navigation degrades gracefully: a Discord/Telegram user has no app view to jump to, so navigate commands reply with a destination + deep link rather than failing.
  • client commands declare surfaces: ["gui"], so they are filtered out of connector and reserved surfaces entirely (/fullscreen makes no sense in Telegram).
  • /new is an agent command (a new conversation is a runtime concern), not a client command — only /clear, /fullscreen, and /transcribe are client.

Per-surface rendering

Web / desktop chat — the floating composer

ContinuousChatOverlay (the always-present ambient composer) gets an inline autocomplete menu (SlashCommandMenu + useSlashMenu):

  • Type / → dark-glass menu floats above the bar listing all gui commands.
  • Type /se → fuzzy-ranked filter (alias prefix > native name > description).
  • Tab completes the highlighted command (/settings — drills into args).
  • Enter runs the highlighted command. Arrow keys move; Esc dismisses (keeps the draft); click/pointerdown executes.
  • /settings shows the section choices (model · voice · connectors · …); /settings model → Enter navigates to the ai-model settings sub-view.
  • Navigation runs client-side (setTab / eliza:navigate:settings / eliza:navigate:view); client commands run overlay/app effects; deterministic agent commands (/status, /model gpt-5, /think high, /reset, …) resolve through registered *_COMMAND actions before inference; pipeline-owned agent commands still route through the normal send pipeline. Combobox a11y (role=combobox, aria-expanded, aria-activedescendant).

Catalog source: GET /api/commands?surface=gui (merged client-side with saved custom commands + custom actions). See packages/ui/src/chat/slash-menu.ts (pure logic, unit-tested) and useSlashCommandController.ts.

Discord — native application commands

plugins/plugin-discord maps getConnectorCommands("discord")DiscordSlashCommand[] and registers them via the existing DISCORD_REGISTER_COMMANDSclient.application.commands.set(...) path, alongside the existing built-ins (built-ins win on name collisions, so the role-gated /help//status//model//settings keep their behavior). The section arg becomes a string option with choices. On invocation: deterministic agent commands answer locally through resolveCommand; pipeline-owned agent commands route through the message pipeline and reply (deferReply→editReply); navigate commands reply (ephemeral) with the destination + deep link.

Telegram — setMyCommands + handlers

plugins/plugin-telegram calls bot.telegram.setMyCommands(getTelegramBotCommands()) after launch (so commands appear in Telegram's / menu) and registers bot.command(name, handler) per catalog entry. Deterministic agent commands answer locally through resolveCommand; pipeline-owned agent commands force a reply through the message pipeline even when TELEGRAM_AUTO_REPLY is off (an explicit command is explicit intent); navigate commands reply with the destination + optional deep link. Command names are sanitized to Telegram's [a-z0-9_]{1,32}.

Future Non-GUI Surfaces

The command catalog still accepts a surface query parameter so future non-GUI hosts can ask for scoped commands without changing the API shape. No concrete terminal UI package ships today; GUI clients use the same deterministic agent / navigate / client command classification described above.

Verification status

  • Web (gui): live-verified — Storybook story + Playwright screenshots (desktop + mobile: all-commands / filtered / sections / filtered-section), 22 pure-logic tests + 12 jsdom integration tests, all green.
  • plugin-commands: 51 unit tests (catalog, surface + view filtering, serialization, settings-section resolution, connector mapping, navigation command defs, dispatch/handlers) + the route handler's 17 tests (packages/agent/src/api/commands-routes.test.ts drives handleCommandsRoutes with mocked json/error: surface filtering, auth pass-through, dynamic-choice emission; commands-routes.real-server.test.ts exercises the wire over a real loopback socket).
  • Discord / Telegram: code-complete + unit-tested (mapping + dispatch branching). End-to-end requires live bot tokens, not available here — exercised at the unit level only.

Adding a command

import { registerCommand } from "@elizaos/plugin-commands";

registerCommand({
  key: "my-view",
  nativeName: "myview",
  description: "Open my custom view",
  textAliases: ["/myview"],
  scope: "both",
  category: "docks",
  surfaces: ["gui"],                 // omit for every known surface
  target: { kind: "navigate", viewId: "my-view", path: "/my-view" },
});

It appears automatically in the web menu and, if a connector surface is listed, Discord/Telegram registration — no per-surface wiring.