From 477daad4ff37046b016649eae61ba1f106054dbb Mon Sep 17 00:00:00 2001 From: kumarabhirup Date: Tue, 3 Mar 2026 15:39:27 -0800 Subject: [PATCH] feat(gateway): migrate chat transport to WebSocket and enforce single ironclaw profile This commit introduces a new plan to transition the chat transport from CLI processes to Gateway WebSocket, while maintaining the existing SSE API contract. It locks the web to a single `ironclaw` profile, disables workspace/profile switching, and updates relevant tests. Key changes include the implementation of a WebSocket-backed adapter, API lockdown with 403 responses for profile mutations, and UI adjustments to remove profile switching controls. --- .../gateway-ws_ironclaw_lock_0576496f.plan.md | 112 ++++++++++++++++++ apps/web/app/components/chain-of-thought.tsx | 35 +++--- apps/web/app/components/chat-panel.tsx | 16 +++ 3 files changed, 146 insertions(+), 17 deletions(-) create mode 100644 .cursor/plans/gateway-ws_ironclaw_lock_0576496f.plan.md diff --git a/.cursor/plans/gateway-ws_ironclaw_lock_0576496f.plan.md b/.cursor/plans/gateway-ws_ironclaw_lock_0576496f.plan.md new file mode 100644 index 00000000000..666c7b0d0eb --- /dev/null +++ b/.cursor/plans/gateway-ws_ironclaw_lock_0576496f.plan.md @@ -0,0 +1,112 @@ +--- +name: gateway-ws ironclaw lock +overview: Migrate `apps/web` chat transport from CLI `--stream-json` processes to Gateway WebSocket while preserving the existing SSE API contract, then lock web to a single `ironclaw` profile and disable workspace/profile switching (403 for disabled APIs). Add targeted web and bootstrap tests for the new behavior. +todos: + - id: ws-transport-adapter + content: Implement Gateway WebSocket-backed AgentProcessHandle adapter in apps/web/lib/agent-runner.ts while keeping existing NDJSON event contract. + status: completed + - id: active-runs-ws-rpc + content: Swap abort and subagent follow-up CLI gateway calls to WebSocket RPC calls in active-runs/subagent-runs. + status: completed + - id: profile-default-lock + content: Default web runtime profile resolution to ironclaw in workspace.ts and ensure state/web-chat/workspace paths resolve under ~/.openclaw-ironclaw. + status: completed + - id: api-lockdown + content: Return 403 for profile/workspace mutation APIs and keep /api/profiles compatible with a single ironclaw profile payload. + status: completed + - id: ui-single-profile + content: Remove profile switch/create workspace controls from sidebars and empty state; clean workspace page wiring accordingly. + status: completed + - id: dench-path-update + content: Update skills/dench/SKILL.md workspace path references to ~/.openclaw-ironclaw/workspace. + status: completed + - id: web-tests + content: Update/add apps/web tests covering WS transport behavior, API lock responses, and ironclaw path resolution. + status: completed + - id: bootstrap-tests + content: Add src/cli tests for run-main bootstrap cutover logic and bootstrap-external diagnostics behavior. + status: completed +isProject: false +--- + +# Migrate Web Chat to Gateway WS + Lock Ironclaw Profile + +## Final behavior + +- Keep frontend transport unchanged (`/api/chat` + `/api/chat/stream` SSE contract remains intact). +- Replace backend CLI stream/process transport with Gateway WebSocket transport. +- Force single-profile behavior in web runtime (`ironclaw`), so workspace/chat/session paths resolve to `~/.openclaw-ironclaw/*`. +- Disable profile/workspace mutation endpoints with `403` (`/api/profiles/switch`, `/api/workspace/init`). +- Remove/disable UI controls for profile switching and workspace creation. + +## Transport migration (backend only) + +- Add a Gateway WS runtime client in `[apps/web/lib/agent-runner.ts](apps/web/lib/agent-runner.ts)` that: + - opens a WS connection to Gateway, + - performs `connect` handshake, + - starts parent runs via Gateway RPC, + - tails `agent` events and emits NDJSON lines compatible with existing `ActiveRun` parsing. +- Preserve `AgentProcessHandle` shape so `[apps/web/lib/active-runs.ts](apps/web/lib/active-runs.ts)` and `[apps/web/lib/subagent-runs.ts](apps/web/lib/subagent-runs.ts)` can keep their SSE event transformation logic unchanged. +- Replace CLI `gateway call` usage with WS RPC helper calls for abort/follow-up paths in: + - `[apps/web/lib/active-runs.ts](apps/web/lib/active-runs.ts)` + - `[apps/web/lib/subagent-runs.ts](apps/web/lib/subagent-runs.ts)` + +## Profile/path locking + +- Update profile resolution in `[apps/web/lib/workspace.ts](apps/web/lib/workspace.ts)` so web runtime defaults to `ironclaw` (without changing test-mode assumptions), ensuring state dir resolves to `~/.openclaw-ironclaw` unless explicitly overridden. +- Keep filesystem resolvers (`resolveOpenClawStateDir`, `resolveWebChatDir`, `resolveWorkspaceRoot`) as the single source of truth used by chat/session/tree APIs. +- Update watcher ignore path in `[apps/web/next.config.ts](apps/web/next.config.ts)` to include ironclaw state dir. + +## Disable profile/workspace mutation surfaces + +- Return `403` in: + - `[apps/web/app/api/profiles/switch/route.ts](apps/web/app/api/profiles/switch/route.ts)` + - `[apps/web/app/api/workspace/init/route.ts](apps/web/app/api/workspace/init/route.ts)` +- Make `[apps/web/app/api/profiles/route.ts](apps/web/app/api/profiles/route.ts)` return a single effective `ironclaw` profile payload for UI compatibility. + +## UI updates (single-profile UX) + +- Remove profile/workspace creation controls from: + - `[apps/web/app/components/workspace/workspace-sidebar.tsx](apps/web/app/components/workspace/workspace-sidebar.tsx)` + - `[apps/web/app/components/sidebar.tsx](apps/web/app/components/sidebar.tsx)` + - `[apps/web/app/components/workspace/empty-state.tsx](apps/web/app/components/workspace/empty-state.tsx)` +- Update workspace page wiring in `[apps/web/app/workspace/page.tsx](apps/web/app/workspace/page.tsx)` to drop `onProfileSwitch` / `onWorkspaceCreated` refresh flow no longer reachable in single-profile mode. +- Keep chat/subagent naming semantics intact (`agent:main:web:` and existing subagent keys). + +## Dench skill path update + +- Replace `~/.openclaw/workspace` references with `~/.openclaw-ironclaw/workspace` in `[skills/dench/SKILL.md](skills/dench/SKILL.md)`. + +## Tests to add/update + +- Transport and runtime tests: + - update/add in `[apps/web/lib/agent-runner.test.ts](apps/web/lib/agent-runner.test.ts)` for WS handshake/start/subscribe/abort behavior and session-key naming. + - update in `[apps/web/lib/active-runs.test.ts](apps/web/lib/active-runs.test.ts)` where transport assumptions changed. +- API lock tests: + - update `[apps/web/app/api/profiles/route.test.ts](apps/web/app/api/profiles/route.test.ts)` for single-profile payload and `403` switch behavior. + - update `[apps/web/app/api/workspace/init/route.test.ts](apps/web/app/api/workspace/init/route.test.ts)` for `403` lock behavior. +- Path behavior tests: + - add/adjust targeted assertions in workspace resolver tests for ironclaw state/web-chat/workspace directories. +- Bootstrap tests (new): + - add `src/cli` tests for rollout/cutover behavior in `[src/cli/run-main.ts](src/cli/run-main.ts)`. + - add diagnostics/rollout gate tests for `[src/cli/bootstrap-external.ts](src/cli/bootstrap-external.ts)` exported helpers. + +## Runtime data flow (post-migration) + +```mermaid +flowchart LR +chatPanel[ChatPanel useChat] --> apiChat[/api/chat] +apiChat --> activeRuns[active-runs startRun] +activeRuns --> gatewayProc[agent-runner WS process-handle adapter] +gatewayProc --> gatewayWs[Gateway WebSocket] +gatewayWs --> gatewayProc +gatewayProc --> activeRuns +activeRuns --> sse[/api/chat/stream SSE] +sse --> chatPanel +``` + +## Verification after implementation + +- Run web tests for changed areas (`agent-runner`, `active-runs`, chat API, profiles/workspace-init API). +- Run bootstrap-focused tests for `src/cli/run-main.ts` and `src/cli/bootstrap-external.ts`. +- Smoke-check workspace tree and web sessions resolve under `~/.openclaw-ironclaw` with switching/creation controls disabled. diff --git a/apps/web/app/components/chain-of-thought.tsx b/apps/web/app/components/chain-of-thought.tsx index 5f7fa9d32fe..f84d833b872 100644 --- a/apps/web/app/components/chain-of-thought.tsx +++ b/apps/web/app/components/chain-of-thought.tsx @@ -1451,27 +1451,28 @@ function ToolStep({ )} - {/* Output toggle — skip for media files and diffs only */} + {/* Output toggle — show for completed tools, or partial output while running */} {outputText && - status === "done" && !isSingleMedia && !diffText && (
- - {showOutput && ( + {status === "done" && ( + + )} + {(showOutput || status === "running") && (
= 0; i--) {
+					const p = parts[i];
+					if (
+						p.type === "dynamic-tool" &&
+						p.toolCallId === event.toolCallId
+					) {
+						p.output =
+							(event.output as Record<
+								string,
+								unknown
+							>) ?? {};
+						break;
+					}
+				}
+				break;
 			case "tool-output-available":
 				for (let i = parts.length - 1; i >= 0; i--) {
 					const p = parts[i];