> **Status: historical record** (through Phase 9 W16, 2026-09). Forward > planning now lives in [GitHub Issues + Milestones] > (https://github.com/sinrimin/harness-nexus/milestones) โ€” each release > milestone selects the issues that ship in it. The workflow is described in > the repository's [CONTRIBUTING.md] > (https://github.com/sinrimin/harness-nexus/blob/main/CONTRIBUTING.md). # Roadmap Phased so each milestone is independently useful. โœ… done, ๐Ÿšง in-progress, โณ not started. Doc links point at the PRD (`prd-`) and design (`design-`). ## Phase 0 โ€” Foundation - โœ… Monorepo layout, tooling, configs - โœ… Domain model + storage ports (`UnitOfWork`) - โœ… Server boot path (Fastify + storage factory + health routes) ## Phase 1 โ€” Users, roles & authentication - โœ… SQLite storage + migrations - โœ… Users / roles (admin, user) / registration switch + bootstrap admin - โœ… JWT access tokens + PAT (`hnpat_โ€ฆ`) - โœ… Front- and back-end permission interceptors - โœ… Web auth UI (login, register, dashboard, users, settings) - PRD: `prd-phase-1-auth.md` ยท Design: `design-phase-1-auth.md` ## Phase 2.1 โ€” MCP connection config & credentials - โœ… Credential store (AES-256-GCM at rest, masked previews) - โœ… MCP client connection CRUD (SSE / Streamable HTTP; stdio via direct mode) - โœ… `${cred:NAME}` placeholder injection (url/command/args/env/headers) - โœ… MCP mode: `proxy` (server dials) / `direct` (tool dials); stdio forces direct - โœ… Scope model: global (admin-mutate) / personal (owner-only) - PRD: `prd-phase-2.1-credentials.md` ยท Design: `design-phase-2.1-credentials.md` ## Phase 2.2 โ€” MCP registry, proxy & profiles - โœ… `McpRegistry`: pooled upstream connections, namespaced tool aggregation - โœ… `${cred:NAME}` placeholders resolved into real values at connect time - โœ… Proxy re-exposure: `/mcp` (Streamable HTTP) + `/mcp/sse` (SSE) - โœ… Profile CRUD + explicit profile routing (`?profile=`, PAT-gated) - โœ… Live connection status (`GET /api/mcp-servers/status`) + dashboard mesh - PRD: `prd-phase-2.2-registry.md` ยท Design: `design-phase-2.2-registry.md` ## Phase 2.3 โ€” Callable-function scripts โธ๏ธ on hold > **Status: on hold.** Not currently planned for development. The original > Phase 4 line item "Callable-function script editor + test-run (with 2.3)" is > removed until this phase resumes. - โธ๏ธ Admin-authored JS wrapping vendor APIs as MCP tools, run in a sandbox - โธ๏ธ `isolated-vm` executor + host-injected allowlisted `ctx.fetch` - โธ๏ธ `ToolSource` abstraction (CallableRegistry alongside McpRegistry) - โธ๏ธ Profile entries for callable-functions; management UI - PRD: `prd-phase-2.3-callable-functions.md` ยท Research: `research-phase-2.3-sandbox.md` ## Phase 2.4 โ€” proxy MCP connect & tool inspection - โœ… Per-row live connection status + tool-count badge on the MCP Management page (state-encoded colors per the Signal system; status polled on an interval) - โœ… Explicit Connect/Disconnect buttons for proxy servers (incremental โ€” startup auto-pooling from 2.2 is unchanged; Connect is for forcing a reconnect after a fix, Disconnect drops a live connection on demand) - โœ… Expandable tool-inspection panel: lists the upstream's tools (original names) with description + `inputSchema` parameters, plus a Refresh button - โœ… Direct rows show a neutral "dialed by the tool" hint, no connect controls - PRD: `prd-phase-2.4-connect-tools.md` ยท Design: `design-phase-2.4-connect-tools.md` ## Phase 3 โ€” Install pipeline (Pillars #2 & #3) > **Restructured** around the ECC adapter-factory pattern + plan/apply. > Priority: **Hermes โ†’ Claude Code โ†’ Codex**. ZCode is out of install scope > (no reproducible reference; retained in the enum but unsupported). > > **Direction decided โ€” "Harness Nexus client" unification (2026-09):** the > `hnx` CLI, the Phase 5 ACP bridge, machine registration, client-side MCP > serving, and remote deploy fold into one Harness Nexus client program, under > the broader repositioning of Harness Nexus as an **agent orchestration > platform** (control plane = server/web, data plane = client). See **Phase 8** > below โ€” PRD `prd-phase-8-client.md`, design > `design-phase-8-client.md`. Consequences for this phase: 3.6 and 3.7 are > absorbed into Phase 8 (C2 and C3 respectively); the local-write CC fallback > adapter stays deferred. - โœ… 3.1 โ€” `McpServer.mode` (proxy/direct) + stdio re-enabled in direct mode; "MCP Connections" โ†’ "MCP Management"; removed `proxied` (mode alone decides pooling) - โœ… 3.2 โ€” `Profile.target` (single, immutable) + target-narrowed creation form + `TARGET_IMMUTABLE`; `codex` added to the enum, `zcode` retained-but-unsupported - โœ… 3.3 โ€” Install pipeline skeleton: target adapter factory + registry + plan/apply separation + install-state ledger + `hnx install` CLI (dry-run default). No real target adapter yet. - โœ… 3.4 โ€” **Hermes adapter** (priority 1). Python plugin bundle + `config.yaml` `mcp_servers:` merge + `AGENTS.md` rules; `${HN_PAT_*}` env placeholders. - โœ… 3.5 โ€” **Marketplace emitter** (priority 2; re-planned). Serve each user's claude-code profiles as a native CC plugin marketplace over HTTP โ€” `claude plugin marketplace add /api/marketplace//marketplace.json` + archive-source zips; claude owns install/update/uninstall. Profile entries gained the `{resourceId, kind}` resource arm. The local-write CC adapter is demoted to an old-CLI (<2.1.224) / airgapped fallback โ€” deferred behind the emitter. - โžก๏ธ 3.6 โ€” **Codex adapter** โ†’ absorbed into Phase 8 (C2): under the client stdio unification every target emits one stdio shim entry, which makes the Codex adapter the trivial case (TOML `config.toml` stdio entry). - โžก๏ธ 3.7 โ€” Cross-target profile import โ†’ absorbed into Phase 8 (C3): the daemon's inventory scan + diff + one-click import generalizes it - โžก๏ธ 3.8 โ€” "Other well-known agents" half โ†’ superseded by the **Phase 8 T-wave** (T1 = DeepSeek Harness, explicitly prioritized ahead of the rest); ECC/Superpower import adapters remain here, pending - PRD: `prd-phase-3-install.md` ยท Design: `design-phase-3-install.md` ยท Design 3.5: `design-phase-3.5-marketplace-emitter.md` - Research: `research-phase-3-ecc-install-patterns.md` (adapter factory + plan/apply + install-state; Codex ground-truth) ยท `research-phase-3-plugin-targets.md` (per-target formats; CC/Hermes detail, ZCode superseded) ยท `research-phase-3.5-marketplace-emitter-spike.md` (empirical CC marketplace protocol constraints) ## Phase 4 โ€” Resource management & PAT UI - โœ… 4.1 โ€” PAT management UI (list / create-once / revoke); backend ready - โœ… 4.2 โ€” Sub-agent management: **shared Resource backend** (SQLite v5 `resources` table, dual-driver repos replacing `memoryResourceStub`, `/api/resources` CRUD, zod schema, SDK methods) + resource browser + sub-agent markdown editor - โœ… 4.3 โ€” Rule management (rule markdown editor on the 4.2 backend) - โœ… 4.4 โ€” Command management: slash-command editor (single-file markdown; `commands/.md` per target). Mirrors 4.2/4.3 โ€” `AVAILABLE_KINDS` + editor variant only. - โœ… 4.5 โ€” Hook management: structured eventโ†’command editor emitting `hooks.json` + event/target support matrix (`packages/shared/src/hooks.ts`, shared with Phase 3.2). Hermes excluded (different hook model); events validated against the matrix. Research: `research-phase-4.5-hooks.md` - โœ… 4.6 โ€” Skill management (local): **inline skills + multi-file bundles**. New `ResourceSource` `inline-bundle` variant (SKILL.md + `references/` + `scripts/`; ~42% of real skills are multi-file). Single-file skills reuse `inline`. External references / hub search are Phase 7. - PRD: `prd-phase-4-web-ui.md` ยท Design: `design-phase-4-web-ui.md` - Order: **by difficulty** โ€” command (4.4) โ†’ hook (4.5) โ†’ skill (4.6). External skill references / hub search are split out into Phase 7. ## Phase 5 โ€” ACP bridge - โžก๏ธ Absorbed into Phase 8 (C1 daemon + control channel, C5 ACP chat). The `packages/acp-bridge` skeleton is deleted when C1 lands. ## Phase 6 โ€” Platform features - โžก๏ธ stdio bridge entry โ†’ resolved by Phase 8 (C2): the per-session `hnx mcp serve` stdio shim IS the stdio entry; no server-side spawning of local processes ever happens. - โณ Chat-tool Channels (route external chats to controlled agents) โ€” adjacent to Phase 8's C6 orchestration - โณ LLM-WIKI knowledge base - โณ Global memory / notes ## Phase 7 โ€” Skill multi-source & plugin references Split by "independently verifiable + increasing difficulty + outbound network last". 7.1 is the zero-outbound foundation; 7.2 opens the first server-side outbound path (isolated in its own PR); 7.3 closes the UX loop; 7.4 is the large, least-certain multi-source block (can stop partway). | # | Sub-phase | Status | Carries | Depends on | Outbound? | | ------- | --------------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | --------------------------------- | | **7.1** | plugin source + trust model | โœ… | `ResourceSource` `plugin` variant (CC marketplace 4 source kinds); trust tiers (`builtin`/`trusted`/`community`) + provenance pin (`content_hash`); `SkillSource` port (abstract, one no-op `inspect`); flip `validateSkillResource` + smoke | Phase 4.6 | **none** (store spec only) | | **7.2** | marketplace allowlist fetch | โœ… | server's first outbound path: `GET /api/skills/marketplaces/:id/plugins` (fetch + cache + timeout `marketplace.json`); `MARKETPLACE_ALLOWLIST` config; trust by repo owner; "save marketplace entry as skill resource" | 7.1 | **yes** (allowlisted GitHub raw) | | **7.3** | hub search UI | โœ… | web marketplace browser (category filter + search + trust badge) โ†’ "save as skill resource" โ†’ reuses 7.1 `plugin` source storage; install-warning UX (trust tier + missing-pin warning) | 7.2 | no (calls 7.2 endpoint) | | **7.4** | Hermes-style multi-source | โœ… | 4 `SkillSource` adapters (github/well-known/url/marketplace-wrapper); parallel search + per-source timeout + merge dedupe (identifier key, trust-rank); `/api/skills/search`; hub page dual-mode (browse + search); skills-sh/browse-sh deferred, clawhub/lobehub/hermes-index skipped | 7.1, 7.3 | **yes** (github/well-known fetch) | - PRD: `prd-phase-7-skills.md` ยท Design 7.1: `design-phase-7.1-plugin-source.md` ยท Design 7.2: `design-phase-7.2-marketplace-fetch.md` ยท Design 7.3: `design-phase-7.3-hub-ui.md` ยท Design 7.4: `design-phase-7.4-multi-source.md` ยท Research: `research-phase-4.4-skills.md` - **Research corrections carried into the PRD** (ground-truth verified): Hermes has **10** adapters (not 9 โ€” `OptionalSkillSource` is the `official` source); trust is **4** tiers internally (the 4th, `agent-created`, is off-by-default and not surfaced); the live CC `marketplace.json` has **4** source kinds (`url`/`git-subdir`/string-path/`github`) and **no `npm`**; `category` is the filter axis (243/257 entries), not `tags` (3) or `metadata`. - Out of scope: content security scanning (Hermes `skills_guard.py` model) โ€” Harness Nexus stores references, the target tool executes. ## Phase 8 โ€” Harness Nexus client & agent orchestration ๐Ÿšง (C1โ€“C5, T1 shipped) > Repositioning: from asset-integration platform to **agent orchestration > platform**. Control plane (server + web) / data plane (one `hnx` client > program per enrolled machine). Decisions locked in the PRD; protocol, data > model, and per-phase plan in the design doc. > > **T-wave (target onboarding)**: adding a harness as a supported target > spans install adapter + inventory scanner + ACP chat row + remote deploy โ€” > all but the first are Phase 8 surfaces, so onboarding lives here. The > supported-harnesses list (one-click install + ACP chat): **Claude Code, > Codex, DeepSeek Harness, OpenCode** โ€” the user-facing docs mention only > these four for now (hermes keeps working unlisted). | # | Sub-phase | Delivers | Absorbs / impacts | | --------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------- | | **C1** โœ… | daemon + machine registration | `hnx daemon` + `Machine` entity + enrollment (machine PAT) + WSS control channel (Socket.IO `/ctl`) | Phase 5 (half); deletes `acp-bridge` | | **C2** โœ… | client MCP serving | stdio shims (`hnx mcp serve`), credential distributability + dial-site routing, `/mcp` outlet narrowing, emitter client mode | 2.1 `mode`, 2.2 pooling, 3.5 emitter, 3.6 | | **C3** โœ… | inventory + diff + import | per-target scans, profile diff, one-click import into resources + profile | 3.7 | | **C4** โœ… | remote deploy | job abstraction (queue/dispatch/replay) over the 3.3 pipeline + Agent instances | reuses 3.3 | | **C5** โœ… | ACP chat | `chat:*` over `/app`โ†”`/ctl` (semantic stream + ACP payload dialect), daemon ACP adapter subprocesses (claude-code/codex/hermes), permission watchdog, AcSession audit, web `/chat` UI (owner-only, off by default) | Phase 5 (other half) | | **T1** โœ… | DeepSeek Harness onboarding | `deepseek` `AgentTarget`: install adapter (`~/.dsh` skills + home `cordis.patch.yml` MCP rows via the `hnx mcp serve` shim), C3 scanner, C5 ACP row (`dsh --profile acp`), C4-deployable | supersedes 3.8 "other agents"; priority: first | | C6 | orchestration | deliberately undesigned until C1โ€“C5 land | Phase 6 Channels (adjacent) | - Key locked decisions: global-scope credentials non-distributable by default (server `/mcp` is their sole outlet); daemon is on-demand and MCP **never** depends on it; agents consume MCP uniformly as stdio shims (proxy/direct deleted, dial site derived + admin override); realtime = Socket.IO over WSS with one **bidirectional namespace per role** โ€” `/app` (browser: UI push + chat + agent control) and `/ctl` (daemon: management + routed interactive traffic; daemon adapts semantics โ†” agent protocol) โ€” plus `domain:verb` event names, room addressing, and session-per-channel isolation; the channel pattern is the substrate for future file-management / web-terminal extensions. - PRD: `prd-phase-8-client.md` ยท Design: `design-phase-8-client.md` ยท Design C1: `design-phase-8-c1.md` ยท Design C2: `design-phase-8-c2.md` ยท Design C3: `design-phase-8-c3.md` ยท Design C4: `design-phase-8-c4.md` - โœ… **C1 shipped (2026-09, branch `phase-8-c1`)**: `Machine` entity + repos + SQLite migration `0006`; machine PATs (`scopes: ['machine-ctl']`) rejected by the REST hook (realtime-only blast radius); realtime v0 โ€” `fastify-socket.io` with `/ctl` (daemon auth, `machine:hello`, rooms) + `/app` (JWT/PAT push, `user:`/`admins` rooms, `machine:status`), pure `MachinePresence` state machine; `/api/machines` CRUD (enroll token shown once, delete force-drops sockets + revokes); SDK machines methods; `hnx enroll` / `hnx daemon` (config in `~/.hnx/config.json` 0600); Machines web page with live presence; `acp-bridge` package deleted. - โœ… **C2 shipped (2026-09, branch `phase-8-c2`)**: `McpServer.mode` deleted โ†’ `dialSite: auto|client|server` (migration `0007` folds proxyโ†’auto, directโ†’client); `Credential.distributable` (personal always true, global admin opt-in); pure dial-site derivation in `shared/dial-site.ts`; NEW `packages/mcp-runtime` (`UpstreamPool` โ€” SSE/HTTP/**stdio** dialing + namespacing + passthrough, shared by the server registry and the shim); `/api/client/mcp-config` (machine-PAT exception; resolved transports for client-dialed only, secret-leak tested); registry pools exactly the server-dialed set (`/mcp` outlet contract unchanged); `409 STDIO_REQUIRES_CLIENT` / `409 CREDENTIAL_NOT_DISTRIBUTABLE` / `NOT_SERVER_DIALED`; `hnx mcp serve` stdio shim (low-level `Server`, JSON schemas pass through); install adapters emit ONE baked-path shim entry (Hermes simplified, install-time secret inlining gone) + **Codex adapter** (skills/prompts/TOML `[mcp_servers]` merge โ€” 3.6 delivered); emitter `emitMode: client` default (stdio entry, no PAT env var) with `server` fallback (`EMITTER_MODE`); web dial-site/distributable forms. - โœ… **C3 shipped (2026-09, branch `phase-8-c3`)**: daemon per-target scanners (claude-code `~/.claude` + `~/.claude.json`, codex `~/.codex` incl. TOML `[mcp_servers]` parse, hermes plugins/config.yaml/AGENTS.md) reporting normalized snapshots over `/ctl` (`inventory:scan|report|collect|payload`, auto-report after every hello, capability `inventory`); `machine_inventory` storage (migration `0008`, latest per machine+target, cascades on machine delete); `InventoryCoordinator` request/response waiters; pure `diffInventory` in shared (coarse MCP arm, hook entries skipped); `/api/machines/:id/inventory` + `/scan` + `/diff` + `/import` โ€” import = collect โ†’ reuse-or-create (identical body reuses โ‡’ idempotent re-import) โ†’ MCP items become `McpServer` rows with daemon-side env/header redaction to `${cred:}` placeholders โ†’ a new personal profile owned by the machine's owner; `/app` `inventory:updated` push; MachineDetail web page (inventory tables, diff view, import wizard) linked from Machines rows; SDK inventory methods; vitest wired into the CLI package. - โœ… **C4 shipped (2026-09, branch `phase-8-c4`)**: `Job` state machine (`queued โ†’ dispatched โ†’ running โ†’ succeeded | failed`, queued-only cancel; disconnect / ack-timeout-sweep recovery requeues with `attempts+1`, โ‰ฅ `JOB_MAX_ATTEMPTS` โ‡’ `failed JOB_ABANDONED`; terminal states ignore stale replay) in `JobService` (`app.realtime.jobs`); `AgentInstance` upserted per (machine, profile) on deploy success โ€” the C5/C6 addressable unit; migration `0009` + both drivers; `/api/machines/:id/jobs` (POST deploy โ€” soft `deploy`-capability gate when online, `409 TARGET_NOT_DEPLOYABLE` for claude-code which stays on the 3.5 emitter path) + jobs list + cancel + `/api/machines/:id/agents`; `GET /api/client/deploy-bundle` (machine PAT REST exception #2 โ€” the resolved bundle the daemon's executor fetches); daemon `job:dispatch` handler reusing the UNCHANGED 3.3 pipeline (plan/apply/ledger, `job:progress` per phase); `/app` `job:update` push; MachineDetail Deployments card (deploy form, live jobs table, agent instances). C3's interactive scan/import stay direct request/response โ€” jobs are for replayable fire-and-forget work. - โœ… **T1 shipped (2026-09, branch `phase-8-t1-deepseek`)**: `deepseek` `AgentTarget` end to end. Research pinned to dsh `v0.1.2-rc.1` (`~/.dsh` home; home-level `cordis.patch.yml` applies to EVERY profile and hot-reloads live; Agent-Skills format with MANDATORY frontmatter name+description, kebab-case; `dsh-mcp-client` stdio rows; native `dsh --profile acp`). CLI adapter (`install/adapters/deepseek.ts`): skills โ†’ `~/.dsh/skills//` with frontmatter synthesis, commands โ†’ flat `skills/.md` (the `/name` surface), MCP โ†’ per-profile MARKED region in the home patch (idempotent replace, other profiles' regions and user rows untouched), rule/sub_agent/hook skipped with warnings; C3 scanner (bundlesโ†’skill, flatโ†’command, patch rows keyed by `serverName`); C5 ACP row `['dsh', '--profile', 'acp']` (`HN_ACP_COMMAND_DEEPSEEK` override); C4-deployable; web target pickers. `HOOK_SUPPORT['deepseek'] = null` (the CC/Codex hook BRIDGES are opt-in per-profile packages). Surfaced + fixed a latent registry crash: stdio `auto` + missing credential (the C3-import `${cred:KEY}` shape) derives server-dial and crashed the fire-and-forget reload โ€” now skipped with a warning (`registry-resilience.test.ts`). Supported harnesses (install + ACP): Claude Code, Codex, DeepSeek Harness, OpenCode (W12). ## Release & CI infrastructure (2026-09) Cross-cutting (not a numbered phase): - โœ… **npm alpha shipped** โ€” `@harness-nexus/{core,shared,mcp-runtime,sdk,cli}` @ `0.1.0-alpha.2`, public, `latest` = alpha by design (young tool, `npx` must work out of the box). Manifest prep: `files` allowlists, `exports` maps, `publishConfig.access`, `prepublishOnly` build guards, `packages/cli/README.md` as the npm landing page. - โœ… **`ci.yml`** โ€” install โ†’ build โ†’ typecheck โ†’ test on every push + PR (Node 20, the engine floor). First run green on `f37e746`. - โœ… **`release.yml`** โ€” manual workflow_dispatch publishing via **OIDC trusted publishing** (per-package trusted publisher registered on npmjs.com; zero npm credentials in GitHub). Encoded gotchas: pnpm lacks tokenless publishing โ†’ `pnpm pack` (workspace-version substitution) then `npm publish --tag latest`; node โ‰ฅ 22.22.2 for npm@latest; npm โ‰ฅ 12 requires an explicit `--tag` for prerelease versions. - Manual channel (dev machine): granular bypass-2FA token + IP allowlist, `npm_config_registry=https://registry.npmjs.org/ pnpm -r publish`. - Release procedure: bump the five manifests โ†’ push โ†’ run the workflow. ## Phase 9 โ€” Harness runtime lifecycle ๐Ÿšง W1โ€“W7 shipped (2026-09) Manage the **harness software itself** on machines โ€” presence, version, install/upgrade, and the LLM provider/model configuration it runs with โ€” the piece users asked for after Phase 8 shipped artifact management only ("ไธ€้”ฎ้ƒจ็ฝฒ/ๅ‡็บง claude code / codex / dsh ่ฝฏไปถๆœฌไฝ“๏ผŒๆ‰ซๆ bin ไธŽ็‰ˆๆœฌ๏ผŒๅฏๆŸฅ็œ‹้…็ฝฎ"). - Research: `research-phase-9-harness-runtime.md` ยท Design: `design-phase-9-harness-runtime.md` (both 2026-09). Rides the Phase 8 machine/daemon/job infrastructure. - Waves: - **W1 Agent-first inventory โ€” SHIPPED (2026-09)** โ€” `runtimes` arm folded into every inventory report (bin path, `--version`, install method; daemon `runtime` capability), Agent cards with items nested under them + "not installed" states (MachineDetail), `AgentInstance (source: 'detected')` auto-registration with two-report hysteresis and deploy-precedence so **any detected Agent is chatable** (closes the emitter-installed claude-code gap), and capture-as-profile (`POST /api/machines/:id/inventory/capture`, zero-entry profiles allowed). - **W2 Install / upgrade / pin jobs โ€” SHIPPED (2026-09)** โ€” `type: 'harness'` jobs on the C4 pipeline (owner-only, `harness`-capability gated); daemon executor with npm command table + `claude update` for native CC upgrades + `DISABLE_AUTOUPDATER` settings merge; post-install re-probe auto-report; Agent-card Install/Upgrade/pin controls. - **W3 Provider config push โ€” SHIPPED (2026-09)** โ€” `RuntimeConfig` entity (migration `0012`, per machine ร— target) referencing a distributable credential; `PUT /api/machines/:id/runtime-config/:target` queues an `apply-config` harness job; the daemon fetches the resolved `{spec, secret}` bundle at execution time (machine-PAT REST exception #3) and writes each harness's native slots merge-preservingly (CC `settings.json` env + model, codex `config.toml` root keys + provider section with `requires_openai_auth` + `auth.json` apikey, dsh provider+default-model patch rows + `~/.dsh/.env` โ€” dsh's own user-env credential layer). ยง11 open questions resolved with source-verified facts (research ยง8): codex removed `wire_api="chat"` (Responses-only), CC version-less installs track `@stable`. - **W4 Redacted config viewer โ€” SHIPPED (2026-09)** โ€” daemon-side `runtime:config.get` with key-name-aware masking (JSON walk, unanchored TOML/YAML line masking, wholesale `.env` masking, broken-JSON fallback; masked before upload, display paths only) over the inventory-style coordinator; `GET /api/machines/:id/runtimes/:target/config` (live round-trip, `runtime-config-view` capability gate); right-side Drawer in MachineDetail. "Re-scan after apply" dropped as a no-op (config files aren't scanned artifacts โ€” the View button is the fresh read). - **W5 Modal containers โ€” SHIPPED (2026-09)** โ€” the shared `FormDialog` shell; every create flow (Profiles/Credentials/Mcp/Machines/Users, Resources re-shelled) opens in a modal instead of an inline Card. Also fixed the dev-only vite proxy swallowing `/mcp-servers`. Design: `design-phase-9-portal-ui.md` ยงW5. - **W6 Portal chat โ€” SHIPPED (2026-09)** โ€” chat rebuilt as Agent cards โ†’ session page (`/chat/agents/:id`): left session list grouped by workspace cwd, right the portal-reference row stream (assistant steps, lifecycle tool rows, disclosure cards with Read/Diff/Terminal/Search/Io bodies, markdown + code highlight, turn tails, stick-to-bottom scroll). New sessions pick a subdirectory of the machine's `baseWorkspace` (migration `0013`; daemon-routed `workspace:list` picker; `directory` on `chat:session.open`); the `acpToolCallView` wire carries toolName/rawInput/content/output. Research: `research-phase-9-portal-chat-ui.md` ยท Design: `design-phase-9-portal-ui.md` ยงW6. - **W7 Native agent sessions โ€” SHIPPED (2026-09)** โ€” the platform persists NOTHING session-shaped (`ac_sessions` dropped, migration `0014`; ChatService is live-channel-only; "close" is now disconnect). The session rail lists the AGENT'S OWN store live (`sessions:list` over /ctl, capability `sessions`; claude-code/codex via adapter `session/list`, dsh via a multi-frame-zstd file scan of `~/.dsh/sessions`); row click resumes (`session/load` with replay for claude/codex, `session/resume` + transcript-parse for dsh) and history ships as `chat:history` โ€” user blocks + ordinary events, folded through the same reducer; a per-channel ring replays on page-refresh rejoin. Design + adapter ground truth: `design-phase-9-w7-native-sessions.md`. W7.1 addendum (SHIPPED): the in-process dsh event tap โ€” spawn-time `--patch` insert of a zero-dep cordis plugin streaming the `session/event` bus over localhost JSON-lines โ€” is the PRIMARY dsh streaming source (rig A/B: 107 deltas / 1ms median gap vs the tail's 3โ€“4 batches), with the transcript tail as fallback and `HN_DISABLE_DSH_TAP=1` as the A/B switch. Design: `design-phase-9-w7.1-dsh-event-tap.md`. - **W8 Sender (composer) upgrade โ€” SHIPPED (2026-09)** โ€” the chat composer became a card-style two-row input (autogrow textarea + toolbar with a context-usage meter and a circular send/stop toggle), feature-shaped after the reference composer but styled per Signal; zero wire changes. The new `@agentclientprotocol` claude wrapper reports context occupancy too, so the meter is live for claude-code as well as dsh. Attach / permission-mode chip / model and effort selectors remain deferred stubs (each needs a wire arm first). Design + post-ship notes: `design-phase-9-w8-sender.md`. **Shipped with it: the chat channel lifecycle hardening + budget redesign** (the W8 rig pass exposed three coupled defects โ€” a page that never released the channel it left, a daemon that dropped closes racing the establishment, and a server that could not reap a restarted daemon's ghost channels; a full cap made sessions look empty). The budget itself was then redesigned per user feedback: 12 total / 5 active per machine, eviction of the oldest non-busy channel instead of rejection, MACHINE_BUSY at prompt time, and open-channel visibility (listing `open`/`openChannelId` + rail rejoin instead of hidden rows). See the C5 design doc ยง "Channel lifecycle hardening" and ยง "Channel budget redesign". - **W9 Sender controls โ€” SHIPPED (2026-09)** โ€” the four deferred composer controls, all riding STANDARD ACP surface verified across claude wrapper 0.76.0 / codex-acp 0.16.0 / dsh-acp 0.1.2-rc.1: permission-mode / model / reasoning-effort selectors (`session_config` snapshots + `chat:config.set`, data-driven rendering, opaque values, dangerous modes confirm-first), image attachments (inline `image` prompt blocks with browser-side downscale, `promptCapabilities.image` gating, thumbnails + lightbox; rig-verified with real model vision), and file references (`resource_link` chips via the workspace `files` arm + a lazy picker behind `+`/trailing `@`; the agent reads them with its own tools). Research: `research-phase-9-w9-composer-controls.md` ยท design + E2E notes: `design-phase-9-w9-sender-controls.md`. - **W10 LLM provider management โ€” SHIPPED (2026-09)** โ€” cc-switch-inspired provider catalog: the `LlmProvider` entity (`{name, api kind, baseUrl?, credentialName}` with the three kinds `openai-chat` / `openai-responses` / `anthropic` and a per-Agent support matrix so the machine page only offers providers the target speaks), a server-side ่Žทๅ–ๆจกๅž‹ model-list discovery button (`POST /api/llm-providers/query-models` โ€” the platform's second deliberate outbound HTTP surface; the key never leaves the server), a provider-first MachineDetail flow (pick provider โ†’ pick/fetch model โ†’ apply; manual arm kept), and multi-model via `RuntimeConfigSpec.models` (dsh writes its native per-provider switchable list; codex/claude-code are single-default by construction โ€” ground truth in the design doc ยง6). Design: `design-phase-9-w10-llm-providers.md`. - **W13 Multi-model picker โ€” SHIPPED (2026-09)** โ€” the session model dropdown lists only platform-configured models (default + `models` extras): claude-code via the writer's `availableModels` settings allowlist (wrapper-native, unknown ids synthesized verbatim), codex + opencode via a daemon-side option rewrite on our wire (codex accepts raw ids on set; opencode's registry already holds our `harness-nexus/*` values), deepseek native since W10. The configured set rides `chat:session.start modelOptions` from the stored RuntimeConfig. Research: `research-phase-9-w13-multi-model-picker.md` ยท design: `design-phase-9-w13-multi-model.md`. - **W14 Plan/todo panel + verification โ€” SHIPPED (2026-09-17)** โ€” the agent's ACP `plan` snapshots (FULL replace; claude wrapper surfaces TodoWrite/Task* exclusively this way, codex maps update_plan) became a `plan` stream kind + a TodoPanel above the composer (collapsed by default, progress summary, status glyphs). The wave's second deliverable โ€” verification โ€” confirmed the subagent (`Agent`-tool card) and permission (claude allow-once / opencode once-always-reject) flows E2E; rig truth: codex is the live plan producer today, claude's task-lane dormant on CLI 2.1.263 (headless sessions expose no task tools โ€” the wire+panel are ready for when they do). Research: `research-phase-9-w14-w15-plan-commands.md` ยท design: `design-phase-9-w14-plan-todo.md`. - **W15 Slash commands โ€” SHIPPED (2026-09-17)** โ€” the composer's `/` palette lists the agent's OWN advertised commands (`available_commands_update` โ†’ a `commands` stream kind; claude 46 on the rig incl. emitter-installed plugin commands, codex 6, opencode 3, dsh none โ€” honest absence, no fallback table). Invoking one is just a `/name args` prompt (no new RPC); palette interaction per the portal reference (first-word filter, bare Enter selects + fills, Enter-with- args sends). Research: `research-phase-9-w14-w15-plan-commands.md` ยท design: `design-phase-9-w15-commands.md`. - **W14.1 Ask User (ACP elicitation) + claude todo/plan revival โ€” SHIPPED (2026-09-17)** โ€” post-W14 review re-verified the ask-user surface against the pinned adapters AND live probes (ground truth: three-layer matrix, the SDK-bundled CLI 2.1.270 discovery, the raw `elicitation/create` wire capture). Two fixes: (1) the claude-code adapter env sets `CLAUDE_CODE_ENABLE_TODO_TOOLS=1`, reviving the Task tools โ†’ ACP plan snapshots โ†’ the W14 TodoPanel (the "dormant on 2.1.263" conclusion was right symptom, wrong cause โ€” portal runs the SDK-bundled 2.1.270, task tools disabled-by-default since 2.1.233); (2) full elicitation wiring โ€” daemon advertises `elicitation.form`, handles the top-level `elicitation/create` request (bounded field view; empty fields = message-only card), server relays + watchdogs + `chat:elicitation.respond` routing, web question cards (enum/multi/ text/number/boolean; values verbatim as ACP content). claude is the only target with an adapter bridge today (codex/opencode/dsh verified to have none). Research: `research-phase-9-w14.1-claude-ground-truth.md` ยท design: `design-phase-9-w14.1-claude-elicitation.md`. - **W16 pi agent onboarding โ€” SHIPPED (2026-09-17)** โ€” pi (Earendil Works' minimal coding harness, `@earendil-works/pi-coding-agent`; the old `@mariozechner` scope is deprecated) is the fifth runtime-managed Agent, full surface: npm install/upgrade/pin jobs (Node โ‰ฅ22.19 result warning), the W3 writer (models.json `harness-nexus` provider + `!cat` 0600 keyfile + settings defaults/enabledModels โ€” baseUrl VERBATIM, rig-proven on Ark), the W4 viewer (auth.json + key file wholesale-redacted), the W7 sessions rail (plain-JSONL file scan + leaf-chain replay), the C3 scanner + deploy adapter (skills with guaranteed frontmatter, prompt-template commands; mcp/rules/sub_agents skipped โ€” no declarative surface), and the SELF-DEVELOPED in-daemon ACP bridge (`PiRpcConnection`: ACP requests โ‡„ pi `--mode rpc` JSONL, prompt responses held until `agent_settled`, rig-captured dialect โ€” payload under `data`, deltas under `assistantMessageEvent`). Rig E2E end to end: install 0.85.1 โ†’ detected card โ†’ provider push (headless PONG) โ†’ masked viewer โ†’ rail rows โ†’ streamed chat turn โ†’ live model switch. Research: `research-phase-9-w16-pi-agent.md` ยท design: `design-phase-9-w16-pi-agent.md`. Daemon `0.22.0-p9w16`. - Non-goals v1: harness uninstall, zcode runtimes, managed-settings hierarchies. hermes runtime management was CANCELLED with the user (2026-09-17): hermes stays a profile-deploy + inventory-scan + ACP-chat target only โ€” never probed/installed/upgraded by the platform, and the former hermes-native-sessions follow-up is dropped with it.