3.8 KiB
3.8 KiB
Headless console — an interactive CLI for the bot host
Date: 2026-09-07
Status: ACTIVE (owner direction 2026-09-07: "the headless client should have
a CLI as well. Like we have the chat loaded in headless so we can see what it
does and we can talk via it if we want and control plugins like /moss bla or
/say hello")
Belongs with the "one plugin, two hosts" model recorded in
docs/plans/2026-09-06-campaign-vt-vtank-oracle.md. Independent of Campaign
VT's slices (host side only) — runs in parallel.
What it is
When the headless host runs with a terminal attached (or --console /
ACDREAM_HEADLESS_CONSOLE=1, documented in docs/launch-options.md), it
becomes a chat-shaped REPL for the session:
- Output. The ordered event stream rendered one line per event, chat
first-class:
[Tell] Bob: hi,[Fellowship] …,[Local] …, system text, plugin output (everything a plugin writes through the host log/chat sink), and lifecycle/command outcomes (connected,entered world,portal → Holtburg,command rejected: …). Same channel names and ordering the graphical SpewBox shows; no colors required (ANSI channel colors only when the terminal supports them, off when piped). - Input. Each typed line goes through the SAME command pipeline the
graphical chat box uses: a
/-prefixed line is dispatched to the plugin command registry first (/vt …, any plugin verb) and otherwise to the client/server slash commands (/say,/tell,/t,/f,/a, emotes,/help), a plain line is/say. No second parser, no bot-only shortcuts: it must reuse the exact seam the retained chat box calls (find it — the CH-campaign command registry and the chat-send route), through the Runtime command surface Slice K already exposes ("chat and slash/server commands"). - Control.
/quit(graceful logout, exit 0),/status(session generation, position, macro state if the plugin reports one). Ctrl+C keeps today's graceful behavior. - Multi-session. Out of scope for the first cut: the console attaches to
the single-session host (K1's production mode). A multi-session prefix
(
@name …) is a later addition; leave the seam shaped for it (input routed through a session-scoped adapter, not a static).
Rules
- Headless stays Runtime-only: no App references, no rendering types. If the chat-send or command-dispatch seam currently lives in App, lift the presentation-free part into Runtime/Core with a dependency guard, and leave the graphical client calling the same lifted code (behavior-preserving).
- Reads of stdin never block the scheduler: a reader thread feeding a thread-safe queue drained on the session tick (Slice K's monotonic scheduler contract).
- Every line of output goes through one renderer over the typed event stream — the same events the K2 bot API emits — so tests assert on the rendered text without a terminal.
- Portable: Windows and Linux; no ANSI when
Console.IsOutputRedirected.
Tests (Headless test project, no live server)
- Scripted stdin → rendered stdout: chat lines, a plugin command reaching
the plugin's registered verb,
/sayproducing the same outbound message the graphical route produces (assert on the command the fake Runtime received), plain text → say,/quit→ graceful teardown, unknown verb → the same error text the chat box prints. - Reader-thread/scheduler test: input queued while the tick is busy is drained in order on the next tick, never on the reader thread.
- Launch-options doc row + the documentation test.
One connected proof at closeout (real ACE,
testaccount, type/say helloand see it echo; type/vt startwith MossTank loaded) — the owner may run it or the lead may, it is not a visual gate.
Ledger
- 2026-09-07 planned; implementer dispatched.