UNPKG

@kontextmind/kxm

Version:

KXM local-first multi-agent orchestration and operator dashboard

140 lines (116 loc) • 6.84 kB
# KXM terminal components The terminal kit gives every KXM surface the same keys, colors and failure behavior. This page is for maintainers and integrators who draw a KXM surface: a live screen, a configuration panel, or a host adapter for another harness. The kit is the workspace package [`packages/core/tui`](../../packages/core/tui) (`@kontextmind/tui`), also published on the product as `@kontextmind/kxm/tui`. It is one declarative surface model, one renderer, one input decoder, one contribution registry, and thin host adapters. Today `kxm dash` uses only its ANSI theme; the panel, registry and adapters are exported for integrators, and no shipped `kxm` command draws a panel yet. See [Packages and workspaces](packages.md) for the layout convention it follows. ## Package layout `types/`, `tui/`, `services/`, `adapters/`, `exports/`, with `tests/unit/` and `tests/helpers/`. The layer rules, why each one exists, and the gate that enforces them live in [Packages and workspaces](packages.md); the short version is that a control which reads the filesystem cannot be rendered in a test, and every control in `tui/` is rendered in a test. ## The surface contract A surface is **published, not centralized**. An owner (configuration, roster, routes, gates, roles, workflow) contributes sections and fields; one renderer draws them all; the owner still performs every write. The panel never becomes a second source of truth for configuration. ```ts interface KxmTuiField { id: string; label: string; kind: "text" | "enum" | "choice" | "info"; value?: string; // absent means unset; `placeholder` is shown keyPath?: string; // the configuration key this writes choices?: KxmTuiChoice[]; // selectable values, with status and per-entry action status?: "ready" | "available" | "blocked"; statusText?: string; // doubles as the error line for a refused edit actions?: KxmTuiAction[]; // owner keys, e.g. a confirm step busy?: boolean; progress?: { label: string; ratio?: number }; steps?: KxmTuiStep[]; // multi-step work, listed not summarised output?: string[]; // tail of a running command updatedAt?: string; // ISO timestamp, drawn as an age } ``` `choice` vs `enum` is the model-selector rule: a short closed set cycles in place, a long one gets its own screen with groups, per-entry status, and a per-entry action. That is what lets one list hold an installed model that only needs selecting and an absent one that needs fetching, without the renderer knowing the difference. Bounds are protocol limits, not suggestions ([`KXM_TUI_LIMITS`](../../packages/core/tui/src/types/surface.ts)). An oversized or malformed published surface is refused with structured issues and drawn with an error notice, so a broken file is still repairable from the panel. ## Fail-closed behavior | Situation | What happens | |---|---| | Owner throws while publishing | The section still draws, with the failure as its notice | | Owner throws on a write | The old value stays, the reason lands on `statusText`, and the section carries the notice | | Value the operator typed is invalid | It is never written and never echoed; the field keeps the on-disk value | | A choice is `blocked` | Enter reports `statusText` and sends nothing (an unauthenticated route stays unauthenticated) | | Handler for an unknown action or unknown owner | Refused with a message; never guessed | | A write resolves after the registry was disposed or re-opened | Discarded as stale | | No TTY, or `NO_COLOR` or `KXM_TUI_NO_COLOR` set | One plain frame, exit `0`, no escape codes at all | | Contract violation in a published surface | Refused with issues; the panel draws the reason instead of hiding it | There is deliberately **no local echo**. A field is pending until its owner republishes, so anything on screen is a value that exists on disk. ## Keyboard model | Key | Action | |---|---| | `↑` `↓` / `k` `j` | move within the focused pane | | `←` `→` / `Tab` | switch between the sections pane and the fields pane | | `l` | move to the fields pane | | `PgUp` `PgDn` | jump sections | | `enter` | edit a text field, cycle an enum, open a choice list | | type | filter a choice list, or edit a text value | | `x` / `d` | clear the focused value (writes "unset", not a default) | | owner keys | any `actions[].key` on the focused field | | `esc` | back out of a pane, list, or draft, then quit | | `q` | quit | | `Ctrl+C` | abort a busy field, else quit | | `h` / `?` | help; `esc` or `enter` closes it | `h` opens help rather than moving to the sections pane, so use `←` or `Tab` to go back. The reducer is pure ([`reduceKxmTuiInput`](../../packages/core/tui/src/tui/panel.ts)) and returns effects; the component layer runs them. That is the same contract that keeps `kxm dash` navigation testable, and it is why no key handling needs a terminal. ## Adding a surface 1. Write the descriptor table. Each row is one setting: id, key path, label, kind, choices, scope, and how to read the current value. Adding a setting means adding a row, not a case. 2. Publish sections from a `listSections()` that reads live state — files, the route policy, harness auth — never a snapshot taken at open time. A panel that goes stale is worse than no panel. 3. Implement `set` / `clear` handlers that validate, then write through the existing CLI helper. A surface edit must be exactly the CLI edit. 4. Register the contribution and run it. `invoke()` calls the handler, re-reads the owner, and repaints; a throw becomes a notice. Selectable values come from reference sources, not from a model's memory: the harness catalog in [`harness.ts`](../../plugins/kxm/src/harness.ts), admitted routes in `.kxm/routes.yaml`, the model inventory and price catalog, gate ids in `.kxm/gates.yaml`, role ids in `.kxm/roles/`, and workflow ids in `.kxm/workflows/`. ## What this kit does not do - It is not an admission path. Listing a model, or editing a roster, grants nothing; writer admission is the trusted Git policy plus a live witness. - It does not write trusted Git YAML. Roster, routes, gates, roles, and workflow definitions stay Git-reviewed changes with a diff and a PR. - It is not a third verification gate. `npm run verify` and CI still decide whether work is green. - It does not replace `kxm dash`. The dashboard keeps its own hub and SSE loop and takes only this kit's ANSI theme. ## Related - [Packages and workspaces](packages.md): the layout convention and its gate - [Configuration](../reference/configuration.md): the settings a config surface edits - [Architecture](../concepts/architecture.md): component boundaries - [Test matrix](test-matrix.md): where a behavior is proven - [KXM contracts](../contracts/README.md): schemas behind the reference files