Skip to content

feat(web): introduce initial external Web workbench framework - #326

Open
somewan820 wants to merge 8 commits into
openpi-dev:mainfrom
somewan820:feat/web-workbench-and-dead-code-cleanup
Open

feat(web): introduce initial external Web workbench framework#326
somewan820 wants to merge 8 commits into
openpi-dev:mainfrom
somewan820:feat/web-workbench-and-dead-code-cleanup

Conversation

@somewan820

@somewan820 somewan820 commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Problem

OpenPI needs a first local external Web workbench without creating a second agent runtime or weakening Pi's Session, model, trust, tool, extension, and lifecycle ownership. The original implementation established the product surface, but prompt admission, retained runtime provenance, metadata concurrency, SSE recovery, shutdown, resource bounds, and browser state still had race and fail-open paths.

Closes #325

Value

Delivers a usable local Web workbench whose browser-visible state remains a bounded projection of authoritative Pi state. Prompts cannot cross Session boundaries, a second Host cannot concurrently mutate the Web Session store, reconnects cannot silently skip state, and shutdown cannot release ownership while an admitted metadata mutation is still running.

Approach

  • Keep one standalone openpi web process with Pi AgentSessionRuntime as the execution authority; do not add another provider stack, Session store, orchestration layer, or persistent Web preference system.
  • Return prompt acceptance only after Pi preflight evidence, bind commands and model changes to the request-time Session epoch, and correlate create/switch events by command and Session path.
  • Scope Subagent, Workflow, and Background Terminal projections by owning SessionManager; expose only typed, bounded activity summaries without private transcript or output payloads.
  • Use bounded snapshot, message, event, replay, client, and queue projections. SSE reconnect uses an exact cursor and explicit 409 RESYNC_REQUIRED; stale same-epoch snapshot responses cannot overwrite newer state.
  • Serialize atomic workspace/archive metadata writes. Snapshot GET is pure; one-time startup restoration completes before the Host starts listening.
  • Protect the shared Web Session directory with one process lease using PID, OS boot/process-start identity, and nonce. Stop freezes mutation admission and drains admitted mutations before releasing the lease.
  • Keep candidate/released/stale lease artifacts in the private .openpi-web-host.artifacts/ container. Ordinary Session files are not scanned or counted; artifact and stale-fence caps fail closed with explicit operator recovery instructions.
  • Move the process-lifetime bootstrap token from the URL fragment to tab-scoped sessionStorage and clear the address bar. Render remote Markdown images as inert links and keep the Host loopback-only.
  • Package the TypeScript runtime loader and Web assets so npm pack installations run independently of the source checkout.

Validation

  • bun run check
  • bun run test: Node 1224 passed, 1 skipped; Vitest 30 passed
  • Focused Web tests: 109 passed
  • git diff --check upstream/main
  • Exact Git ancestry: current head is directly based on 2614b9b (main at push time); GitHub reports MERGEABLE with no code conflict
  • Fresh npm pack -> install -> openpi web --no-open: passed; packaged / and /marked.js returned successfully
  • Real browser acceptance against the packed CLI:
    • desktop 1280x800 and mobile 390x844
    • no horizontal overflow; composer visible inside the viewport
    • bootstrap fragment removed from the URL
    • no browser console warnings or errors
  • Two independent final review axes reported no P1/P2 findings before push

New-head GitHub CI is required; local validation is not a substitute for protected checks or independent approval.

Impact

  • User-visible behavior: local workspace/Session navigation, prompting, model selection, bounded activity views, reconnect recovery, and a minimal responsive Web shell. Theme/settings persistence and misleading message editing are intentionally excluded from this PR.
  • Model-visible context/tools: prompts use the selected Pi Session and existing OpenPI capabilities; no new resident model tool or reasoning workflow is introduced.
  • Runtime/lifecycle: acceptance receipts, retained runtime cleanup, shared dispatcher ownership, metadata transactions, Host shutdown, and cross-process ownership now fail closed at explicit boundaries.
  • Persisted data: Web Sessions remain separate from terminal Sessions. Workspace/archive metadata is atomic. Lease safety artifacts are package-owned and bounded; ordinary Session history is neither deleted nor subjected to the lease artifact budget.
  • Security/compatibility: loopback-only, Origin/Host/Bearer checks, cleared token fragment, inert remote images, CSP/referrer policy, and packaged CLI coverage. Remote/public hosting and full TUI parity remain out of scope.

@github-actions github-actions Bot added documentation Improvements or additions to documentation area:workflows Workflow engine, capability, skills, or tests area:subagents Subagent delegation, skills, or tests area:background-terminals Background terminal runtime, skill, or tests area:setup OpenPI setup, configuration, or setup documentation area:ui Terminal UI, rendering, themes, or visual assets labels Aug 31, 2026
@somewan820
somewan820 requested a review from tt-a1i August 31, 2026 15:48
@somewan820 somewan820 changed the title feat(web): add Pi-native Web workbench and runtime safeguards feat(web): introduce initial external Web workbench framework Aug 31, 2026

@tt-a1i tt-a1i left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

基于 exact head 01faec23264d0a6b205acfc1e30a308004f5b102 请求修改。方向有价值,但当前存在可复现的运行时阻塞:

  1. /api/prompt 在 Pi preflight 前即发布 prompt_accepted 并返回 202 accepted:true;即使运行时明确拒绝,客户端仍收到 accepted。accepted receipt 必须证明 admission,而不是仅证明 Host 收到请求。
  2. sendPrompt() 等待前一 admission 后才读取 this.runtime。请求在 Session A 排队、等待期间切换到 B 时,会实际投递到 B,但 trace 仍属于 A。命令必须绑定请求时的 runtime/session,或在 admission barrier 后按 expected session fail closed。
  3. 每个 AgentSessionRuntime 都安装一个进程全局 Undici dispatcher。切换后释放新 runtime 的 lease,会让全局 dispatcher 继续指向已关闭实例;已复现后续 fetch 失败 UND_ERR_DESTROYED。该资源应由整个 Web 进程统一拥有。
  4. Web capability registry 以进程级 kind -> provider 存储,没有 Session/runtime provenance;保留旧 runtime 时可把旧 Session 的 Subagent/Background Terminal 状态投影到当前 Session。
  5. 文档承诺 cursor/Last-Event-ID、gap detection 与 409 RESYNC_REQUIRED,实现却在每次 SSE 连接无条件重放最近 200 条,客户端既不提交 cursor,也不在正常 EOF 重连,可能重复、静默丢失或永久陈旧。
  6. Session 索引、message parts 聚合、SSE client/queue/backpressure 没有完整硬上限,与本 PR 自己的 bounded protocol/host 契约不符。
  7. UI 从结果文字中的 fail/error/uncertain 猜 canonical 状态;历史 Session “只读”、短期 token exchange 等文档表述也与实现不一致。
  8. Issue #325 将视觉 polish、移动端和交互一致性列为后续工作,但第二提交又扩大到主题、动画和名为“编辑”实为重发的交互;建议缩小或拆分,避免框架 PR 同时承担大面积产品 polish。

验证:bun run check 通过;Web 16/16;完整 Node 1102 通过、1 跳过;Vitest 30/30。另有三个独立最小复现确认 false accepted、A->B prompt 错投和 closed global dispatcher。测试绿不能覆盖这些并发/恢复缺口。建议保持 Pi-native:不新增第二套 runtime,只收紧现有 Host、AgentSessionRuntime、SSE 和 projection 的权威边界。

@somewan820

Copy link
Copy Markdown
Collaborator Author

已在 exact head 5cbcb671b3a84c4ab22a6c686cabd41347547004 处理本次 review:

  1. /api/prompt 现在等待 Pi preflightResult(true) 才发布/返回 accepted;拒绝返回 409 accepted:false,并补 Host/runtime 回归。
  2. prompt 在请求进入时绑定 runtime + Session;等待 admission barrier 期间切换 Session 会 fail closed,不再 A 排队、B 投递。
  3. Undici dispatcher 改为进程共享、lease 引用计数;单个 runtime 释放不能关闭其他 runtime 仍在使用的 dispatcher,trace 报告真实生效 timeout。
  4. capability provider 必须携带 owning sessionId;Web snapshot 只投影当前 Session,旧 retained runtime 不再污染当前视图。
  5. SSE 支持 cursor / Last-Event-ID 增量重放、gap detection、409 RESYNC_REQUIRED、正常 EOF 重连和 snapshot resync。
  6. 增加 Session/entry/message part/event/SSE client/queue/event bytes/details bytes 上限;Session branch 投影从 leaf 起最多遍历 250 条。
  7. UI 使用 canonical status / isError,不再从结果文本猜状态;历史 Session 不显示重发入口;当前 Session 的入口改名为“作为新消息发送”;架构文档改为实际的 process-lifetime Bearer token bootstrap。
  8. Issue 首次引入 OpenPI 外部 Web 工作台框架 #325 已按实际形成的首版工作台扩大范围,明确包含主题、品牌/菜单动效、活动视图、移动端和当前交互面,同时保留非目标与后续边界,因此不删除或丢失现有 UI。

提交拆分:

  • 2c86fc5 runtime admission / Session binding / dispatcher ownership
  • 7521c6e capability provenance / bounded projections
  • 5cbcb67 Host receipt / SSE recovery / canonical UI state / docs

验证:bun run checkbun run test(Node 1110 passed、1 skipped;Vitest 30 passed);focused Web/runtime 21 passed;当前源码 HTTP smoke 页面和授权 snapshot 均为 200。当前环境没有可控浏览器实例,因此没有把视觉 smoke 记为已执行。

旧 review 绑定 01faec2 且状态为 CHANGES_REQUESTED,请针对新 exact head 重新 review。

@somewan820
somewan820 requested a review from tt-a1i September 1, 2026 11:54
@somewan820

Copy link
Copy Markdown
Collaborator Author

自审补充:在前三个 review 修复提交之后,又确认并修复了一个 fail-closed 异常路径。若底层 prompt operation 正常 settle、但 Pi 从未调用 preflightResult,现在会因缺少 admission evidence 明确拒绝,不再从 operation completion 推断 accepted。

  • commit: 8f9c809
  • regression: prompt admission fails closed without preflight evidence
  • final local validation: bun run check; Node 1111 passed / 1 skipped; Vitest 30 passed

当前 exact head 为 8f9c8094ca9adf9272dd4226d6e2077bcaae51bb

somewan820 and others added 8 commits September 1, 2026 20:10
Add the local Web entrypoint, workspace and session UI, runtime event projection, dispatcher lifecycle leases, and metadata persistence. Clean obsolete UI and unused code, fix concurrent prompt attribution and atomic writes, and add regression coverage and development documentation.

Refs openpi-dev#325
- pi.dev-style engineering grid background with paper reading lane
- frosted-glass sidebar, hover transitions, menu/dialog motion
- landing OpenPI brand animation (tetromino assembly, collision, sink)
- favicon from pi.dev
- protocol projection keeps toolCallId/isError/details/custom messages
- subagent/workflow activity cards + live capability chips
- tool lines with icons and summaries, step grouping, thinking timer
- message copy/edit actions, turn rail navigation, stick-to-bottom
- settings: language + theme (PI grid / clean white / midnight)
- CJK bold markdown extension; vm-based app render smoke test
Wait for Pi preflight before accepting prompts, fail closed across session replacement, and keep the process dispatcher alive until every runtime lease releases it.
Bind capability providers to their owning Pi session and enforce bounded session, entry, message-part, event, client, and queue projections.
Return prompt receipts only after admission, bound SSE clients and queues, replay from cursors with explicit resync, and render canonical runtime states without text guessing.
Fail closed when a prompt operation settles without invoking Pi's admission callback instead of inferring acceptance from operation completion.
@tt-a1i
tt-a1i force-pushed the feat/web-workbench-and-dead-code-cleanup branch from e7ec5f1 to b0dac01 Compare September 1, 2026 13:27
@tt-a1i
tt-a1i requested a review from a team as a code owner September 1, 2026 13:27
@github-actions github-actions Bot added the area:github GitHub workflows, templates, ownership, or tests label Sep 1, 2026
@tt-a1i

tt-a1i commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

@somewan820 已在你最新的 e7ec5f1 基础上完成 maintainer 修复和复审;你的 “remove duplicate client state” 意图已保留为当前提交 b0dac01,并适配了 canonical snapshot 回归测试,没有直接覆盖掉这次更新。

当前 exact head:b0dac01059d23125131c3a4ad81448775fbd3013

  • 直接基于 push 时最新 main2614b9b062ea630325f315ad7d3397aa17e81a8e
  • GitHub:MERGEABLE,无代码冲突
  • bun run check:通过
  • 全量:Node 1224 passed / 1 skipped;Vitest 30 passed
  • focused Web:109 passed
  • fresh npm pack/install/CLI、桌面与 390px 移动浏览器验收:通过
  • 两个独立终审轴:未发现 P1/P2

麻烦你重点复核最终方案是否保持了原始产品意图:Pi 仍是 Session/模型/执行事实来源;Web 只做有界投影与串行控制;prompt/Session epoch、SSE cursor、metadata transaction、Host lease/shutdown 的 fail-closed 边界;以及 theme/settings/edit 等额外复杂度的收窄。

你是 PR 作者,GitHub 不能用自己的 approve 满足独立审批门槛;请先回复确认或指出异议,之后由另一位有 write 权限的 reviewer 完成正式 approve。

@somewan820

Copy link
Copy Markdown
Collaborator Author

已基于 exact head b0dac01059d23125131c3a4ad81448775fbd3013 完成最终复核,并使用 $simplify-codebase 做了只读简化审计。

确认最终方案保持了原始 Pi-native 产品意图:

  • Pi 仍是 Session、模型和执行事实来源;Web 只负责有界投影与串行控制。
  • prompt admission、Session epoch、SSE cursor/resync、metadata transaction、Host lease/shutdown 均保持 fail-closed 边界。
  • theme/settings/edit 等额外 UI 复杂度已收窄,未发现会破坏前端 UI 的残留复杂度。
  • sessionEpochsnapshotGeneration、prompt admission token,以及 runtime candidate/retained/in-flight/disposal 状态分别保护不同竞态和生命周期窗口,不能安全合并。
  • 未发现新的 P1/P2,也没有达到契约证明、适合在本 PR 删除的重复控制面;少数无仓库内调用的导出 helper 因外部深层导入兼容性无法证明而保留。

本地验证:bun run check 通过;全量 Node 1224 passed / 1 skipped;focused Web 109 passed;Vitest 30 passed;git diff --check 通过。

我确认没有异议。作为 PR 作者不提交自我 approval,请由另一位有 write 权限的 reviewer 完成独立 approval。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:background-terminals Background terminal runtime, skill, or tests area:github GitHub workflows, templates, ownership, or tests area:setup OpenPI setup, configuration, or setup documentation area:subagents Subagent delegation, skills, or tests area:ui Terminal UI, rendering, themes, or visual assets area:workflows Workflow engine, capability, skills, or tests documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

首次引入 OpenPI 外部 Web 工作台框架

2 participants