Ploinky WebChat
An authenticated browser interface for a selected agent CLI, its conversations, asynchronous work, and workspace-scoped files.
Sessions
WebChat opens through the Router login flow and attaches a browser tab to one selected agent CLI runtime. The browser sends input over authenticated HTTP and receives output through one server-sent event stream; it never connects directly to the agent process.
sequenceDiagram
participant B as Browser
participant R as Router WebChat
participant C as Selected CLI
B->>R: authenticated input
R->>C: write to CLI TTY
C-->>R: output and session state
R-->>B: EventSource eventsThe CLI owns conversation persistence. It publishes validated session summaries and the current session through its structured session-state envelopes. WebChat keeps only the latest runtime snapshot needed to restore a reconnecting browser and asks the CLI to create or resume a session through its normal command channel.
/session resume <session-id> from the selected CLI. The opaque session identifier stays under CLI ownership.Agent CLI and interactions
The selected agent manifest provides the CLI command that WebChat launches. WebChat creates that command as a child process behind the Router and keeps its standard input, standard output, and standard error inside the Router-owned runtime. A browser never opens a terminal connection to an agent container. It sends authenticated HTTP requests to WebChat, and receives the resulting terminal output through the existing server-sent event stream.
Typed text normally reaches the CLI as the original text followed by a newline, so an agent continues to own its normal command-line syntax and conversation history. An agent can opt in to a structured WebChat message envelope with manifest.json webchat.forwardEnvelope: true, or an equivalent WebChat launch option. In that mode, the Router writes one JSON object with __webchatMessage: 1 and version: 1 instead of plain text. The object carries the text, validated attachment metadata, workspace-relative references, presentation choice, source tab and page-instance identifiers, and, when available, the public origin and request-bound invocation token. WebChat does not include a reconstructed chat history; conversation persistence remains the CLI's responsibility.
A compatible CLI can publish an interaction request with a title, detail, options, and default choice. WebChat presents the choices above the composer and sends the selected answer through the authenticated interaction route. The answer is matched to the authenticated browser session, active runtime, tab, optional page instance, request identifier, and declared option before it is delivered to the CLI. A pending interaction blocks ordinary input until it is submitted or cancelled, and a second use of the same request identifier is rejected.
TTY and event protocol
The CLI-to-WebChat protocol is line-oriented. Ordinary standard-output or standard-error text is conversation output. A control record must be a complete JSON object on its own newline-delimited output line; WebChat removes terminal control characters, recognizes the reserved marker, validates the versioned schema, stores the latest accepted state in memory for reconnecting browsers, and emits a named server-sent event. A malformed record cannot update WebChat state.
sequenceDiagram
participant B as Browser
participant R as Router WebChat
participant C as Selected agent CLI
B->>R: POST input with browser mutation proof
R->>C: stdin: text or __webchatMessage JSON plus newline
C-->>R: stdout/stderr: text or one JSON control line
R-->>B: EventSource: message or named state event
B->>R: POST interaction answer
R->>C: stdin: __webchatInteractionResponse JSON plus newline| CLI output marker | Named browser event and purpose |
|---|---|
__webchatSession: 1 | session-state publishes the current session or session list. |
__webchatTask: 1 | task-update publishes a task snapshot, update, or bounded log data. |
__webchatRuntimeState: 1 | runtime-state publishes the selected model label. |
__webchatWorkspaceFiles: 1 | workspace-files publishes a validated workspace-relative file index or delta. |
__webchatSkills: 1 | skills-state publishes the CLI-owned skills catalog state. |
__webchatInteraction: 1 and __webchatInteractionResolved: 1 | interaction-request and interaction-resolved control a pending choice or input prompt. |
All reserved control records use version: 1. The interaction response uses a separate newline-delimited record, __webchatInteractionResponse: 1, containing the request id and exactly one accepted result: an optionId, a bounded text response, or cancelled: true. The Router never forwards browser credentials or provider credentials to the CLI as part of this protocol.
Per-agent command catalogs
Slash-command suggestions are agent-defined, not a hardcoded Ploinky command list. For the selected agent, WebChat asks the routed /<agent>/mcp endpoint to initialize an MCP session, sends notifications/initialized, lists the tools with tools/list, and calls list_achilles_cli_commands when that tool is available. The agent command catalog response is text content containing a JSON object with type: "achilles-slash-command-catalog" and a commands array. Each command may declare its name, description, subcommands, argument completions, matching mode, and suggestion limit.
WebChat loads this catalog once when a browser page starts, keeps it in the browser, and performs filtering and completion locally while the user types. The catalog is not sent with each prompt and it is not reloaded merely because the EventSource reconnects. A fresh page load obtains a fresh catalog; a successful skills save also explicitly refreshes it because enabled skills can change the agent's command set. If the structured catalog tool is absent, WebChat derives a fallback list from the selected agent's MCP tools whose names begin with execute_.
The MCP catalog request travels through the same authenticated Router route and carries the browser mutation proof required for the selected agent. This lets each enabled agent expose only its own available commands and lets WebChat remain generic across CLI implementations.
Tasks
A CLI can publish structured task updates for asynchronous work. WebChat validates these envelopes, shows a compact task reference in the conversation, and exposes the workspace task overlay and detail panel. Task metadata, logs, continuation state, and reattachment remain owned by the selected CLI.
flowchart TD
C[CLI starts task] --> W[WebChat task event]
W --> O[Tasks overlay]
W --> M[Conversation task reference]
O --> D[Detail panel]
D --> CThe detail panel asks its parent page to send the appropriate CLI task command and receives updates from the existing EventSource stream. Stopping or continuing a task remains a Router-mediated, request-bound operation; the browser does not call the provider tool directly or receive its credentials.
Files, uploads, and previews
The attachment flow lets a user choose a file, folder, or camera capture and select a destination below the session's working directory. The bytes are uploaded when the message is sent. The server rejects absolute paths, traversal, symlink paths, reserved secrets, and Ploinky runtime directories.
| Browser action | Result |
|---|---|
| Upload a file or folder | POST /webchat/uploads writes below the selected working directory after destination and conflict checks. |
| Open an assistant file reference | WebChat reads the approved target through /workspace-files/... after independent path validation. |
| Preview content | Text uses an escaped code view, images and PDFs use native previews, and HTML opens in a sandboxed frame. |
The CLI can publish a live workspace-file index. WebChat turns a recognizable assistant path into a preview link only when that index contains the normalized working-directory-relative file.
Intermediate and final messages
WebChat renders user and assistant messages with timestamps and Markdown support. Long output can be folded, while code blocks, tables, and lists preserve their readable structure. The stored conversation remains a CLI concern.
Structured control envelopes for session state, interactions, tasks, runtime state, and workspace files are removed from normal assistant output after validation. This keeps the conversation readable while allowing the browser to update its session picker, task overlay, interaction selector, model indicator, and preview links.