Skip to content

feat(workspaces): generalize WebPi into a multi-runtime Web conversation surface - #1390

Merged
luokerenx4 merged 4 commits into
TraderAlice:devfrom
RainMona:cursor/web-conversation-transports-1c9d
Sep 6, 2026
Merged

feat(workspaces): generalize WebPi into a multi-runtime Web conversation surface#1390
luokerenx4 merged 4 commits into
TraderAlice:devfrom
RainMona:cursor/web-conversation-transports-1c9d

Conversation

@RainMona

@RainMona RainMona commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Summary

WebPi proved that a browser conversation over a long-lived structured CLI process beats a PTY for many tasks, but it shipped as a Pi-only special case: the host spoke Pi RPC, routes checked agent === 'pi', and the UI gated every affordance on the same literal.

This PR turns it into one Web conversation surface that any Agent runtime joins by declaring a wire protocol. Claude Code, Codex, Cursor Agent, Grok Build, opencode, Oh My Pi, and Pi now all open in the browser; permission prompts from the runtimes that ask before running tools are answered from a card in the UI.

Plan: plans/web-conversation-surface.md. Owner guide: docs/web-conversation-surface.md. Based on current dev (6ec3966b).

Design alternatives considered

  1. One native host per runtime (repeat WebPi N times) — N parsers, N snapshot shapes, N presenters. Rejected as structure.
  2. Everything through ACP — one client covers cursor/grok/opencode natively, but claude/codex/pi need an extra npm adapter, agy has no trustworthy implementation, and ACP is the lowest common denominator (no compaction/thinking controls without vendor _meta). Rejected as the only path; adopted as one transport.
  3. Hybrid (chosen) — a neutral WebSessionHost with pluggable transports: pi-rpc (pi, omp), acp (cursor, grok, opencode), claude-stream-json, codex-app-server. Pi's minimal message shape becomes the neutral model because every protocol maps onto it for presentation and the browser already groups it into turns.

UI decision for permission prompts: (a) inline transcript item, (b) modal, (c) card pinned above the composer. (c) keeps the transcript an audit trail, doesn't steal focus, and matches the compaction status already pinned in that slot. This was chosen autonomously per the UI design workflow, not maintainer-approved.

Backend

  • src/workspaces/web-session/ — neutral model.ts (messages, WebPermissionRequest, awaiting-input phase, snapshot), transport.ts contract, json-rpc.ts, transcript-builder.ts, and four transports.
  • src/workspaces/web-session-host.ts replaces webpi-session-host.ts: spawn/supervise, revision, prompt/abort/respond, stderr tail; no agent or wire branching.
  • Adapters declare capabilities.web = { wire, permissionPrompts, freshSession } and implement composeWebCommand (claude -p --input-format stream-json … --permission-prompt-tool stdio, codex app-server --listen stdio://, cursor-agent acp, grok agent stdio, opencode acp, omp --mode rpc --auto-approve). agy stays TUI-only.
  • Routes /api/workspaces/:id/sessions/:sid/web/{open,prompt,abort,respond} + GET /web; open checks capabilities.web and returns 409 unsupported_surface otherwise.
  • SessionRecord.surface: 'webpi' (shipped, migration 0040) is kept as the persisted value for every runtime; only free-floating identifiers and copy move to "Web". No migration.

UI

  • WebSessionView / useWebConversation / web-presentation.ts / web-transcript.ts (renamed from WebPi*); runtime identity only drives copy (placeholder, stop label, wire tooltip).
  • New ConversationRequestCard pinned in the composer status slot; options verbatim from the runtime, allow/deny/neutral tones; queued count for further requests; inline error keeps the card.
  • New notice conversation item for system remarks (stopped turn, etc.).
  • Launch gating via agentSupportsWeb(agents, agent) in Resume CTA, Workspace header surface toggle, Manager page; no agent === 'pi' literals remain.
  • Demo: capability table mirrors adapters; quick chat for prompting runtimes scripts open → prompt → permission card → respond/deny/stop.
  • i18n copy: "WebPi" → "Web" (en/zh/zh-Hant/ja).

Docs

  • New docs/web-conversation-surface.md, indexed in docs/README.md.
  • ui-interaction-and-motion.md, workspace-manager.md, project-structure.md, and incidental mentions updated.

Real-binary smoke (commit 2b6cf119)

Installed the actual CLIs under a throwaway HOME (@earendil-works/pi-coding-agent 0.85.1, opencode-ai 1.18.29, @openai/codex 0.153.4, @anthropic-ai/claude-code 2.1.263) and drove WebSessionHost with the argv each adapter composes. No API keys were available, so this covers handshake + prompt rejection, not tool turns.

Wire Handshake Prompt without credentials
pi-rpc get_state/get_messages OK, session id bound rejected → idle + error (was stuck in working; fixed)
acp (opencode) initializesession/new OK opencode's bundled free model completed a real turn: user → thinking → pong
codex-app-server thread/start OK after fix 401 surfaced as error, phase back to idle
claude-stream-json flags accepted, process live system/init binds session id; "Not logged in" → idle + error

Contract fixes this uncovered (verified against codex app-server generate-json-schema):

  • Codex approvalPolicy / sandbox are kebab-case (on-request, workspace-write), not onRequest / workspaceWritethread/start was rejected outright.
  • item/permissions/requestApproval answers with { permissions, scope } (granted profile), not { decision }. Grant echoes the requested profile for turn or session; decline/cancel send an empty grant. Cancel payloads are per-request so item/tool/requestUserInput is never answered with a decision enum.
  • Pi/Codex/Claude transports record a synchronous prompt rejection in snapshot.error and return to idle rather than leaving the browser spinner on.

Verification

  • npx tsc --noEmit ✓, cd ui && npx tsc -b ✓.
  • Backend: web-session-host.spec.ts drives a fake child over stdio for all four wires (17 tests); web-command.spec.ts pins argv per runtime; routes/service/public-session/adapter specs green.
  • UI: ui/src/components/workspace, demo, contexts, conversation, Workspace/Manager page specs green through the root config; new coverage for the request card (render, respond, error, queue), runtime labels, notice items, capability gating, and demo handlers.
  • Demo route walked in a real browser (pnpm -F open-alice-ui dev:demo): Claude quick chat → permission card → Allow → activity + answer; Deny → failed activity; Stop → notice; Pi answers directly; Antigravity stays on the terminal placeholder; no console errors.
  • pnpm test full run: 3 files fail identically on an untouched dev worktree in the authoring sandbox (template-upgrade.spec, workspace-absorb.spec, project-transfer.spec git hook timeouts; supervisor-tui.pty.spec needs Node ≥ 22.19, sandbox has 22.14). Not related to this branch.

Remaining gap

Credentialed acceptance needs a maintainer machine: per runtime, open one Session in Web, send a prompt that needs a tool, answer the permission card, stop mid-turn, then reopen the same Session in the TUI and confirm the native transcript is shared. cursor-agent and grok were not smoke-tested (not installable via npm in the sandbox); their ACP path is the same client code that completed a real turn against opencode. Recorded as the open item in the plan.

Suggested labels (a fork contributor cannot apply them): workflow:parallel, theme:reliability, area:workspace, review:deep (cross-surface structure, adapter argv, permission handling).

cursoragent and others added 4 commits September 6, 2026 11:47
Extract the Pi RPC session host into a transport-driven WebSessionHost with
pi-rpc, acp, claude-stream-json, and codex-app-server transports. Declare
web-surface capabilities on every adapter that exposes a structured stdio
protocol and expose the surface through /api/workspaces/:id/web/* routes.

Co-authored-by: RainMona <RainMona@users.noreply.github.com>
…face

Rename the WebPi browser modules to runtime-neutral names, read the new
/web/* snapshot shape, gate Web launch on the runtime's declared capability,
and add a pinned request card so permission prompts from Claude, Codex, and
ACP runtimes can be answered from the browser. Demo handlers mirror the
capability table and script a permission turn for prompting runtimes.

Co-authored-by: RainMona <RainMona@users.noreply.github.com>
Add docs/web-conversation-surface.md, register it in the owner-guide index,
retire WebPi wording from the guides that referenced it, and record plan
progress.

Co-authored-by: RainMona <RainMona@users.noreply.github.com>
…real app-server; settle rejected prompts

Real-binary smoke against codex 0.153.4, pi 0.85.1, opencode 1.18.29 and
claude 2.1.263 found three contract gaps:

- Codex thread/start rejected approvalPolicy 'onRequest' / sandbox
  'workspaceWrite'; AskForApproval and SandboxMode are kebab-case on the
  wire ('on-request', 'workspace-write'), verified via
  codex app-server generate-json-schema.
- item/permissions/requestApproval answers with { permissions, scope }, not
  { decision }; grant now echoes the requested profile (turn or session
  scope) and decline/cancel send an empty grant. Cancel payloads are now
  per-request so user-input questions are not answered with a decision enum.
- Pi rejects prompts synchronously when no model credential is configured;
  the transport left the snapshot in 'working'. Pi, Codex and Claude now
  record the rejection in snapshot.error and return to idle.

Plan and owner guide record the smoke matrix and the remaining credentialed
acceptance gap.

Co-authored-by: RainMona <RainMona@users.noreply.github.com>
@vercel

vercel Bot commented Sep 6, 2026

Copy link
Copy Markdown

@cursoragent is attempting to deploy a commit to the luokerenx4's Team Team on Vercel.

A member of the Team first needs to authorize it.

@luokerenx4
luokerenx4 marked this pull request as ready for review September 6, 2026 13:04
@luokerenx4
luokerenx4 merged commit f89feee into TraderAlice:dev Sep 6, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants