oh-my-agent-web 0.7.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/README.en.md +394 -248
  2. package/README.md +387 -247
  3. package/dist/server/approval/rules.js +4 -16
  4. package/dist/server/approval/scope.js +3 -3
  5. package/dist/server/cli-config.js +3 -3
  6. package/dist/server/cli-info.js +9 -9
  7. package/dist/server/cli.js +108 -23
  8. package/dist/server/engine/builtin.js +64 -1
  9. package/dist/server/engine/catalog-probe.js +34 -0
  10. package/dist/server/engine/claude-map.js +296 -0
  11. package/dist/server/engine/claude-sessions.js +581 -0
  12. package/dist/server/engine/claude-transport.js +75 -0
  13. package/dist/server/engine/claude.js +414 -0
  14. package/dist/server/engine/cli-version.js +119 -0
  15. package/dist/server/engine/codex-map.js +19 -23
  16. package/dist/server/engine/codex-sessions.js +5 -5
  17. package/dist/server/engine/codex-subagents.js +349 -0
  18. package/dist/server/engine/codex.js +215 -2
  19. package/dist/server/engine/dsh-history.js +1 -1
  20. package/dist/server/engine/dsh-log.js +47 -36
  21. package/dist/server/engine/dsh-map.js +3 -5
  22. package/dist/server/engine/dsh-prompt-patch.js +48 -0
  23. package/dist/server/engine/dsh-sessions.js +10 -11
  24. package/dist/server/engine/dsh-transport.js +2 -2
  25. package/dist/server/engine/dsh.js +45 -2
  26. package/dist/server/engine/engine-versions.js +41 -0
  27. package/dist/server/engine/fake.js +38 -7
  28. package/dist/server/engine/model-registry-reload.js +1 -1
  29. package/dist/server/engine/opencode-map.js +2 -2
  30. package/dist/server/engine/opencode-projects.js +1 -1
  31. package/dist/server/engine/opencode-sessions.js +2 -2
  32. package/dist/server/engine/opencode.js +39 -1
  33. package/dist/server/engine/pi-events.js +4 -4
  34. package/dist/server/engine/pi-plugins.js +8 -6
  35. package/dist/server/engine/pi-sdk.js +179 -5
  36. package/dist/server/engine/pi-subagent-tool.js +2 -2
  37. package/dist/server/engine/pi-subagents.js +2 -2
  38. package/dist/server/engine/pi-transcript.js +1 -1
  39. package/dist/server/engine/registry.js +12 -2
  40. package/dist/server/engine/suggestion-guidance.js +22 -9
  41. package/dist/server/engine/wiring.js +57 -3
  42. package/dist/server/files/content-search.js +187 -0
  43. package/dist/server/files/file-search.js +3 -3
  44. package/dist/server/files/file-service.js +33 -15
  45. package/dist/server/files/files-routes.js +68 -7
  46. package/dist/server/files/git-info.js +51 -11
  47. package/dist/server/files/path-resolve.js +176 -0
  48. package/dist/server/files/pick-dir-routes.js +93 -0
  49. package/dist/server/files/pick-path-routes.js +126 -0
  50. package/dist/server/http/auth-routes.js +71 -4
  51. package/dist/server/http/auth.js +48 -30
  52. package/dist/server/http/aux-routes.js +20 -4
  53. package/dist/server/http/file-headers.js +3 -3
  54. package/dist/server/http/health-and-static.js +1 -1
  55. package/dist/server/http/live-routes.js +4 -4
  56. package/dist/server/http/login-page.js +128 -1
  57. package/dist/server/http/password-store.js +31 -0
  58. package/dist/server/index.js +93 -11
  59. package/dist/server/markers/actions.js +8 -8
  60. package/dist/server/mcp/codex-mcp-store.js +190 -0
  61. package/dist/server/mcp/json-mcp-store.js +276 -0
  62. package/dist/server/mcp/mcp-auth.js +1 -1
  63. package/dist/server/mcp/mcp-command.js +0 -2
  64. package/dist/server/mcp/mcp-engine-shared.js +112 -0
  65. package/dist/server/mcp/mcp-import.js +4 -5
  66. package/dist/server/mcp/mcp-market.js +2 -3
  67. package/dist/server/mcp/mcp-routes.js +4 -3
  68. package/dist/server/mcp/none-mcp-store.js +27 -0
  69. package/dist/server/mcp/pi-mcp-config.js +1 -1
  70. package/dist/server/plugins/engine-plugin-stores.js +265 -0
  71. package/dist/server/plugins/plugin-routes.js +4 -3
  72. package/dist/server/protocol-parse.js +52 -4
  73. package/dist/server/protocol.js +0 -1
  74. package/dist/server/providers/catalog-writes.js +1 -1
  75. package/dist/server/providers/catalog.js +2 -2
  76. package/dist/server/providers/codex-models.js +1 -1
  77. package/dist/server/providers/codex-test.js +1 -1
  78. package/dist/server/providers/enabled-models.js +1 -11
  79. package/dist/server/providers/import-routes.js +2 -2
  80. package/dist/server/providers/probe-routes.js +26 -0
  81. package/dist/server/providers/provider-handlers.js +3 -3
  82. package/dist/server/providers/provider-pool.js +2 -2
  83. package/dist/server/providers/provider-store.js +5 -2
  84. package/dist/server/providers/subscription-routes.js +4 -2
  85. package/dist/server/providers/validate.js +13 -14
  86. package/dist/server/push/push-routes.js +32 -3
  87. package/dist/server/push/push.js +43 -9
  88. package/dist/server/session/client-view.js +2 -2
  89. package/dist/server/session/close-ephemeral.js +23 -0
  90. package/dist/server/session/conversation-create.js +31 -9
  91. package/dist/server/session/delete-session.js +25 -14
  92. package/dist/server/session/engine-switch.js +12 -5
  93. package/dist/server/session/fork-session.js +38 -0
  94. package/dist/server/session/lock.js +4 -4
  95. package/dist/server/session/model-controller.js +14 -2
  96. package/dist/server/session/move-session.js +40 -0
  97. package/dist/server/session/persist-ephemeral.js +48 -0
  98. package/dist/server/session/pi-opener.js +2 -2
  99. package/dist/server/session/pi-refresh.js +1 -1
  100. package/dist/server/session/pi-sessions-provider.js +2 -2
  101. package/dist/server/session/pi-sessions.js +107 -18
  102. package/dist/server/session/runtime.js +144 -9
  103. package/dist/server/session/session-stats.js +2 -2
  104. package/dist/server/session/settings.js +45 -49
  105. package/dist/server/session/subagents.js +4 -1
  106. package/dist/server/session/suggest-title.js +33 -0
  107. package/dist/server/session/title-prompt.js +126 -0
  108. package/dist/server/session/turn-finish.js +2 -2
  109. package/dist/server/session/turn-queue.js +5 -5
  110. package/dist/server/session/turn-watchdog.js +2 -2
  111. package/dist/server/skills/skill-install.js +3 -5
  112. package/dist/server/skills/skill-store.js +3 -3
  113. package/dist/server/skills/skillhub.js +3 -4
  114. package/dist/server/snapshot/messages.js +2 -8
  115. package/dist/server/snapshot/sender.js +1 -1
  116. package/dist/server/snapshot/state.js +3 -5
  117. package/dist/server/subagents/profile-store.js +11 -9
  118. package/dist/server/terminal/terminal-session.js +9 -9
  119. package/dist/server/uploads/upload-routes.js +1 -1
  120. package/dist/server/uploads/upload-store.js +4 -6
  121. package/dist/server/util/env.js +27 -21
  122. package/dist/server/util/ids.js +1 -1
  123. package/dist/server/util/json-file.js +38 -0
  124. package/dist/server/util/log-file.js +3 -3
  125. package/dist/server/util/log.js +4 -4
  126. package/dist/server/util/origin.js +5 -24
  127. package/dist/server/util/paths.js +25 -3
  128. package/dist/server/util/pricing-index.js +2 -6
  129. package/dist/server/util/pricing.js +10 -10
  130. package/dist/server/util/spawn-cli.js +2 -2
  131. package/dist/server/util/upstream-version.js +10 -12
  132. package/dist/server/util/version.js +2 -2
  133. package/dist/server/web-proxy/proxy-routes.js +167 -0
  134. package/dist/server/web-proxy/proxy-url.js +60 -0
  135. package/dist/server/web-proxy/rewrite.js +84 -0
  136. package/dist/server/ws/handle-message.js +21 -2
  137. package/package.json +13 -14
  138. package/scripts/prune-prebuilds.mjs +1 -1
  139. package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_abnfDiagram-WEYOIA3C-Dq-2fRb4.js +1 -0
  140. package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_architectureDiagram-ZHVYKT53-Dh5CEs4k.js +1 -0
  141. package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_c4Diagram-XDPPPE54-Cb19QmmE.js +1 -0
  142. package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_cynefinDiagram-WF24UDRP-COk-IL9O.js +1 -0
  143. package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_diagram-CBK3MWTC-Gp_0nfEJ.js +1 -0
  144. package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_diagram-ODIL2UCW-rzPf_9IN.js +1 -0
  145. package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_diagram-PFBEJUXE-B5EnLWwl.js +1 -0
  146. package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_diagram-TYUNFSNR-DVpFKFwC.js +1 -0
  147. package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_ebnfDiagram-V4P3TTPT-CDL1qiPY.js +1 -0
  148. package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_ishikawaDiagram-HPEHV2GE-CSkoONzp.js +1 -0
  149. package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_mindmap-definition-6OY57UG3-B9sdVB6_.js +1 -0
  150. package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_pegDiagram-ECIUJNBX-B6P7baCF.js +1 -0
  151. package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_railroadDiagram-Y72NRBL7-W_a8Vy2l.js +1 -0
  152. package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_swimlanes-B3ENRKFQ-DvGif-RR.js +1 -0
  153. package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_swimlanesDiagram-AYQRV4AK-D0gFG_EI.js +1 -0
  154. package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_usecaseDiagram-WPOLBNNX-BjgUBJiR.js +1 -0
  155. package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_vennDiagram-Q3Y3IDCW-DnVuWTpF.js +1 -0
  156. package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_wardleyDiagram-IVAIDKWA-B2vDAqpl.js +1 -0
  157. package/web/dist/assets/{arc-BTAvbR16.js → arc-Df9SI9yh.js} +1 -1
  158. package/web/dist/assets/{architecture-WOLXFQ4H-fewLD5_7.js → architecture-WOLXFQ4H-iO49E8Y1.js} +1 -1
  159. package/web/dist/assets/{blockDiagram-RJAE7QA3-ClmgyxZl.js → blockDiagram-RJAE7QA3-D8d1ukIs.js} +1 -1
  160. package/web/dist/assets/channel-C2-OT56h.js +1 -0
  161. package/web/dist/assets/{chunk-55XCU76K-DdC5EhZG.js → chunk-55XCU76K-C5wOvqDb.js} +1 -1
  162. package/web/dist/assets/{chunk-7S6XSTFO-CgToWHK6.js → chunk-7S6XSTFO-WIrWcDbG.js} +1 -1
  163. package/web/dist/assets/{chunk-AQDTSKZB-D-rQMnp7.js → chunk-AQDTSKZB-OkzYqFsw.js} +1 -1
  164. package/web/dist/assets/{chunk-DSNLSHJZ-cuZoTGMH.js → chunk-DSNLSHJZ-CHEA9kYd.js} +2 -2
  165. package/web/dist/assets/{chunk-HNE4Q36P-CiKr9iSN.js → chunk-HNE4Q36P-CPsq34CI.js} +1 -1
  166. package/web/dist/assets/{chunk-VQ5W62QI-DbTfXAAT.js → chunk-VQ5W62QI-CamPnEuF.js} +1 -1
  167. package/web/dist/assets/{chunk-WQQL4CDK-DHanHfB5.js → chunk-WQQL4CDK-gNypBVAM.js} +1 -1
  168. package/web/dist/assets/{chunk-ZAUX2LTX-SrjFXpy2.js → chunk-ZAUX2LTX--4WyRBRV.js} +1 -1
  169. package/web/dist/assets/{chunk-ZCHMQSOR-7Wy8xXQ5.js → chunk-ZCHMQSOR-umv3Lkpn.js} +1 -1
  170. package/web/dist/assets/{classDiagram-v2-4MYWR3UA-CBe5ZDvZ.js → classDiagram-v2-4MYWR3UA-C1XldpO1.js} +1 -1
  171. package/web/dist/assets/{cose-bilkent-JH36ORCC-Dljdyvhw.js → cose-bilkent-JH36ORCC-BvugZzFU.js} +1 -1
  172. package/web/dist/assets/{cynefin-EF2NZ3EQ-CcZU7GFP.js → cynefin-EF2NZ3EQ-6qXB70s4.js} +1 -1
  173. package/web/dist/assets/{dagre-NK4GIZVX-Bq_6y8qp.js → dagre-NK4GIZVX-DDdlABYw.js} +1 -1
  174. package/web/dist/assets/{diagram-I5XIIV2D-C2lnnYVG.js → diagram-I5XIIV2D-T8oWy5CY.js} +1 -1
  175. package/web/dist/assets/{diagram-RJA3I4VV-Bqf8Z6wJ.js → diagram-RJA3I4VV-CkdHTtfV.js} +1 -1
  176. package/web/dist/assets/{elk-QONTBCTQ-Cdzq_cJ5.js → elk-QONTBCTQ-BiEUbJJX.js} +1 -1
  177. package/web/dist/assets/{erDiagram-KYQIGQAA-ZJLCjPFC.js → erDiagram-KYQIGQAA-q3WwhyzH.js} +1 -1
  178. package/web/dist/assets/{eventmodeling-K75KTNOO-DHqeIpes.js → eventmodeling-K75KTNOO-BO5gW_NC.js} +1 -1
  179. package/web/dist/assets/{flowDiagram-IUGLLWBU-AVgYvXKN.js → flowDiagram-IUGLLWBU-DubjP6UK.js} +1 -1
  180. package/web/dist/assets/{ganttDiagram-R7GPNTJY-CR1SG1M7.js → ganttDiagram-R7GPNTJY-lKNRH8CI.js} +1 -1
  181. package/web/dist/assets/{gitGraph-VSP46ZUC-CIZQPK0R.js → gitGraph-VSP46ZUC-Cwlv3omT.js} +1 -1
  182. package/web/dist/assets/{gitGraphDiagram-PDYL7XHY-BhOCWEh6.js → gitGraphDiagram-PDYL7XHY-Bo9GpFvW.js} +1 -1
  183. package/web/dist/assets/index-CeYhxqkk.js +113 -0
  184. package/web/dist/assets/index-GJgJwZ23.css +1 -0
  185. package/web/dist/assets/{info-OHQRW6UA-C2wuRxeQ.js → info-OHQRW6UA-g3MbTRmN.js} +1 -1
  186. package/web/dist/assets/{infoDiagram-MVDRKAX5-BP5doAB5.js → infoDiagram-MVDRKAX5-CmILYLoB.js} +1 -1
  187. package/web/dist/assets/{journeyDiagram-BOSHW7O7-BcIDRTPS.js → journeyDiagram-BOSHW7O7-Av8T0U1C.js} +1 -1
  188. package/web/dist/assets/{kanban-definition-FNSA4HC5-CRH_bcoT.js → kanban-definition-FNSA4HC5-B9NyWQYN.js} +1 -1
  189. package/web/dist/assets/{line-iA4GPA_S.js → line-BUrll1My.js} +1 -1
  190. package/web/dist/assets/{linear-BGUKwkTH.js → linear-DjET0phX.js} +1 -1
  191. package/web/dist/assets/{math-DgNW_bVP.js → math-BJAjkhlu.js} +1 -1
  192. package/web/dist/assets/{mermaid-parser.core-CsqmgpEv.js → mermaid-parser.core-CaTdkhkS.js} +3 -3
  193. package/web/dist/assets/{mermaid.core-C1yCGQK6.js → mermaid.core-Cktx937U.js} +4 -4
  194. package/web/dist/assets/{packet-JDAUHWVQ-2ggaoz2g.js → packet-JDAUHWVQ-DhG6l3PG.js} +1 -1
  195. package/web/dist/assets/{pie-XZMESJXO-h_yslQQs.js → pie-XZMESJXO-YPcOx7s0.js} +1 -1
  196. package/web/dist/assets/{pieDiagram-GNMPY74T-6-JZMFyk.js → pieDiagram-GNMPY74T-DesiZKUl.js} +1 -1
  197. package/web/dist/assets/{quadrantDiagram-D57K4MC3-K5r1HOgL.js → quadrantDiagram-D57K4MC3-Cn1kBVgu.js} +1 -1
  198. package/web/dist/assets/{radar-ABXABTNO-6fOJGGgq.js → radar-ABXABTNO-CtuRfYSz.js} +1 -1
  199. package/web/dist/assets/{railroad-I3PHUGI6-B_gUgQ3d.js → railroad-I3PHUGI6-D5Gnyy0R.js} +1 -1
  200. package/web/dist/assets/{railroad-abnf-LNEFI6M7-CJ_eqiXZ.js → railroad-abnf-LNEFI6M7-Kw4pmJEm.js} +1 -1
  201. package/web/dist/assets/{railroad-ebnf-DWYD2UWJ-BTsgjLTU.js → railroad-ebnf-DWYD2UWJ-D8Mk5dIe.js} +1 -1
  202. package/web/dist/assets/{railroad-peg-JR7BVNR7-D91uiLln.js → railroad-peg-JR7BVNR7-R5Jwzs8F.js} +1 -1
  203. package/web/dist/assets/{requirementDiagram-EQYSW5RA-uapnV_OO.js → requirementDiagram-EQYSW5RA-B9RDq-cL.js} +1 -1
  204. package/web/dist/assets/{sankeyDiagram-SY24JHO5-BQsbFgUb.js → sankeyDiagram-SY24JHO5-DoEatOm6.js} +1 -1
  205. package/web/dist/assets/{sequenceDiagram-BLEHNHQU-iRoSYX_e.js → sequenceDiagram-BLEHNHQU-DGE5Z9lT.js} +1 -1
  206. package/web/dist/assets/{src-BBrKcs9r.js → src-BnEL7pZw.js} +1 -1
  207. package/web/dist/assets/{stateDiagram-v2-A6IESKWZ-Bzj9z5KH.js → stateDiagram-v2-A6IESKWZ-_vn8W-k_.js} +1 -1
  208. package/web/dist/assets/{timeline-definition-JXYKGLGB-DLUUuJX0.js → timeline-definition-JXYKGLGB-BaSLTtgR.js} +1 -1
  209. package/web/dist/assets/{treeView-D4JQ5SDB-CphIQ3n5.js → treeView-D4JQ5SDB-DZElyFgL.js} +1 -1
  210. package/web/dist/assets/{treemap-SAJKECNS-DqU3insw.js → treemap-SAJKECNS-DWDO4K4G.js} +1 -1
  211. package/web/dist/assets/{wardley-7MLQ67FV-Ch7d8cmC.js → wardley-7MLQ67FV-DTOUsk6Q.js} +1 -1
  212. package/web/dist/assets/{xychartDiagram-4URNHDU4-DdxpWBeM.js → xychartDiagram-4URNHDU4-DiaCfgAB.js} +1 -1
  213. package/web/dist/index.html +2 -2
  214. package/docs/screenshots/01-chat.png +0 -0
  215. package/docs/screenshots/02-answer.png +0 -0
  216. package/docs/screenshots/03-files-diff.png +0 -0
  217. package/docs/screenshots/04-terminal.png +0 -0
  218. package/docs/screenshots/05-models.png +0 -0
  219. package/docs/screenshots/06-skills.png +0 -0
  220. package/docs/screenshots/07-skills-hub.png +0 -0
  221. package/docs/screenshots/08-mcp.png +0 -0
  222. package/docs/screenshots/09-subagents.png +0 -0
  223. package/docs/screenshots/10-settings.png +0 -0
  224. package/docs/screenshots/11-plugins.png +0 -0
  225. package/docs/screenshots/12-voice.png +0 -0
  226. package/docs/screenshots/13-push.png +0 -0
  227. package/docs/screenshots/README.md +0 -45
  228. package/web/dist/assets/_omaw-mermaid-pruned_chunk_abnfDiagram-WEYOIA3C-CvNbnJ75.js +0 -1
  229. package/web/dist/assets/_omaw-mermaid-pruned_chunk_architectureDiagram-ZHVYKT53-T7NZZ4ao.js +0 -1
  230. package/web/dist/assets/_omaw-mermaid-pruned_chunk_c4Diagram-XDPPPE54-ByuAPg39.js +0 -1
  231. package/web/dist/assets/_omaw-mermaid-pruned_chunk_cynefinDiagram-WF24UDRP-BY5DHttT.js +0 -1
  232. package/web/dist/assets/_omaw-mermaid-pruned_chunk_diagram-CBK3MWTC-D7ZYyrF7.js +0 -1
  233. package/web/dist/assets/_omaw-mermaid-pruned_chunk_diagram-ODIL2UCW-C6kcOZFc.js +0 -1
  234. package/web/dist/assets/_omaw-mermaid-pruned_chunk_diagram-PFBEJUXE-CzFPFq0D.js +0 -1
  235. package/web/dist/assets/_omaw-mermaid-pruned_chunk_diagram-TYUNFSNR-B4G4T8SL.js +0 -1
  236. package/web/dist/assets/_omaw-mermaid-pruned_chunk_ebnfDiagram-V4P3TTPT-BkQ4xwD0.js +0 -1
  237. package/web/dist/assets/_omaw-mermaid-pruned_chunk_ishikawaDiagram-HPEHV2GE-52xqckJv.js +0 -1
  238. package/web/dist/assets/_omaw-mermaid-pruned_chunk_mindmap-definition-6OY57UG3-7cQksbFn.js +0 -1
  239. package/web/dist/assets/_omaw-mermaid-pruned_chunk_pegDiagram-ECIUJNBX-udgtBpwv.js +0 -1
  240. package/web/dist/assets/_omaw-mermaid-pruned_chunk_railroadDiagram-Y72NRBL7-Cd2dcQWz.js +0 -1
  241. package/web/dist/assets/_omaw-mermaid-pruned_chunk_swimlanes-B3ENRKFQ-p34pL1Yk.js +0 -1
  242. package/web/dist/assets/_omaw-mermaid-pruned_chunk_swimlanesDiagram-AYQRV4AK-BslfPs4r.js +0 -1
  243. package/web/dist/assets/_omaw-mermaid-pruned_chunk_usecaseDiagram-WPOLBNNX-DJZoN9Xk.js +0 -1
  244. package/web/dist/assets/_omaw-mermaid-pruned_chunk_vennDiagram-Q3Y3IDCW-DPXo67cZ.js +0 -1
  245. package/web/dist/assets/_omaw-mermaid-pruned_chunk_wardleyDiagram-IVAIDKWA-BE6ukwkg.js +0 -1
  246. package/web/dist/assets/channel-DfaYIFFE.js +0 -1
  247. package/web/dist/assets/index-CZQjJbRg.js +0 -104
  248. package/web/dist/assets/index-ChDUWKZX.css +0 -1
  249. /package/web/dist/assets/{_omaw-mermaid-pruned_katex_katex-Zojedrce.js → _oh-my-agent-web-mermaid-pruned_katex_katex-Zojedrce.js} +0 -0
package/README.en.md CHANGED
@@ -2,78 +2,46 @@
2
2
 
3
3
  [中文](README.md) · **English**
4
4
 
5
- One line: **an external control surface for the agent you're already running.** The engine keeps running on your own machine; you use the web page to watch it work, manage its tools and models, cut in whenever it drifts, and jump into files or a shell when you need to.
5
+ One line: **an external control surface for the agent you're already running.** The engine keeps running on your own machine; the web page is where you watch it work, manage its tools and models, cut in to correct it whenever you like, and — when the moment comes — read files, open a terminal, or highlight a passage and ask for a change.
6
6
 
7
- Single user, loopback only, and the engine's own on-disk files are the source of truth for conversations — no database, no accounts. One `omaw` process is one person's workbench.
7
+ Single user, loopback only, and **the engine's own on-disk files are always the source of truth for a conversation** — no database, no account system, no second copy of the session in our own store. One `oh-my-agent-web` process is one person's workbench.
8
8
 
9
- ![Four subagents running in parallel in one session: projects and sessions on the left, process and tool cards in the middle, file preview on the right](docs/screenshots/01-chat.png)
9
+ ![A real session: projects and sessions on the left, thinking blocks and tool cards in the middle, a file preview of the same repo on the right](docs/screenshots/01-chat.png)
10
10
 
11
- ```
12
- npm ci && npm run build && npm start → http://127.0.0.1:25257
13
- ```
11
+ > Every screenshot below is the real UI of this repo (the 0.8.0 build) — pi engine + DeepSeek, working on a small `demo-app` project.
12
+ > Directories, commands, the `+6 −1` diff, tokens and cost all come from that actual run — no collages, no mockups.
13
+ > What each shot shows, how it was taken and how it was compressed is in [`docs/screenshots/README.md`](docs/screenshots/README.md).
14
14
 
15
15
  ---
16
16
 
17
- ## Modules
17
+ ## Install
18
18
 
19
- The top bar *is* the menu; the sidebar holds projects, sessions and files; the right pane is preview and terminal.
19
+ Requires **Node.js ≥ 22.19** and **npm ≥ 9**; macOS / Linux / Windows all work. You end up with an `oh-my-agent-web` command (short aliases: `aw`, `agent-web` — all equivalent).
20
20
 
21
- | Module | Where | What it does |
22
- |---|---|---|
23
- | **Conversation** | center | Streaming text + collapsible thinking blocks + tool cards (arguments / full command / output / duration / ok or failed) + a "N steps" process block + a usage footer (tokens / cache / cost) + chips for changed files |
24
- | **Steer & queue** | composer | Cut in mid-turn and the next message takes effect immediately; queued messages can be retracted; sent messages fold into the process block |
25
- | **Engines** | top bar picker | pi / Codex / DeepSeek Harness / opencode each with its native loop (not one prompt in four costumes); every session remembers its engine, model and thinking level |
26
- | **Subagents** | top bar "subagents" + the strip under the conversation | The main agent gets an `Agent` tool and can run several **real sessions** in parallel; run cards show status / duration / tool count / tokens / cost, expand in place for the result, and can be stopped individually. The panel manages templates: tool allowlist, model, thinking level, max turns, concurrency (1–12) |
27
- | **Models & providers** | top bar "models" | The 41 providers in pi's official catalog work **with just a key** — no base URLs, no hand-copied model IDs. Custom providers expose the api shape (openai-completions / responses / anthropic-messages / codex / typesafe), base URL and custom headers; tick models, batch-test speed, pull the list from `/models`, save a whitelist. **Keys never leave the server** (only the last four digits come back) |
28
- | **Skills** | top bar "skills" | Installed list with enable/disable and a reader for `SKILL.md` plus the skill's files; **Skill Hub** search and one-click install (200k+ index), install from a Git repo, or create by hand. Enabling means renaming `SKILL.md` — there is no second state file |
29
- | **MCP** | top bar "MCP" | Edit both `mcp.json` files (global and project; a project entry with the same name wins), enable/disable, pick an exposure level (codemode / deferred / direct / hidden), **test the connection** (real handshake + tool list), OAuth login, browse a marketplace |
30
- | **Plugins** | top bar "plugins" | pi packages (npm / git / local dir) with four states (loaded / installed / missing / disabled), per-package disabling, plus loose single-file extensions; state comes from pi's own resolution — only install/uninstall/update touch the disk |
31
- | **File manager** | sidebar "files" | Directory tree (switchable root), search by name, show hidden files, collapse all, upload and download, reveal in Finder; uncommitted changes are dotted in the tree |
32
- | **File viewer** | right pane (multi-tab) | Markdown / source with line numbers / images / PDF / audio-video streams; select lines and "add to conversation" to use them as context; full screen and draggable width |
33
- | **Diff** | "diff" at the top of the viewer | Compared against HEAD: a `+285 −78` summary, line-level red/green highlighting, switch between diff and full file; opening from a "changed files" chip lands straight on the diff |
34
- | **git** | top of the files pane | Branch chip (click to switch local branches, confirms first on a dirty tree), `M` / `U` / `D` markers after filenames |
35
- | **Terminal** | files pane `>_`, right-pane tab | A real PTY (`node-pty` + xterm.js) sharing the session's cwd, with an optional initial command; on Linux it tells you exactly which build tools are missing |
36
- | **Push** | top bar "push" | Web Push subscriptions (VAPID); one notification per finished turn — title is the project, body the session, with ❌ for failures and ⏹ for interrupts. Multiple devices, test push, clear all; needs https or localhost |
37
- | **Voice** | top bar "voice" | Four event sounds (start / finish / error / waiting for approval) with preview and volume; local TTS (the browser's own voices, no network) that can read the final reply, with speed and voice selection |
38
- | **Settings** | top bar "settings" | Four pages: **UI** (which chunks appear in the top bar and status bar), **general** (saved phrases, etc.), **appearance** (chat font size ×4, content width ×4, six palettes: bright white, warm paper, mist, Tokyo night, mocha, nord), **language** (Chinese / English, applies instantly) |
39
- | **Sessions** | sidebar | Grouped by directory into "projects"; pin, rename, archive (undoable), delete (renames to `.deleted-<timestamp>`, the file survives). A session *is* the engine's own file — identical to what the CLI shows |
40
- | **Attachments** | composer | Drag-and-drop upload, `@` file mentions, pasted images; uploads land in the data dir, never in your project |
41
- | **Mobile** | — | Responsive: the sidebar becomes a drawer, process blocks collapse to a caret, below 400px it switches to the phone layout |
42
-
43
- > Every screenshot below comes from a **real, running instance** (taken on this project's own session) — nothing is mocked up.
44
-
45
- ---
21
+ ### Install globally with npm (recommended)
46
22
 
47
- ## Screens
23
+ ```bash
24
+ npm -g install oh-my-agent-web
25
+ ```
48
26
 
49
- | | |
50
- |---|---|
51
- | ![Answer with a markdown table, changed files and usage](docs/screenshots/02-answer.png) | ![Diff view of a file](docs/screenshots/03-files-diff.png) |
52
- | **Answer**: markdown, tables, code highlighting, token and cost footer | **Diff**: against HEAD, line-level red/green |
53
- | ![Embedded terminal](docs/screenshots/04-terminal.png) | ![Models and providers](docs/screenshots/05-models.png) |
54
- | **Terminal**: a real PTY in the session's cwd | **Models**: the 41 built-ins need only a key; ticked models apply |
55
- | ![Skills panel](docs/screenshots/06-skills.png) | ![Skill Hub](docs/screenshots/07-skills-hub.png) |
56
- | **Skills**: installed list, toggles, and `SKILL.md` | **Skill Hub**: search by category, install in one click |
57
- | ![MCP](docs/screenshots/08-mcp.png) | ![Subagents](docs/screenshots/09-subagents.png) |
58
- | **MCP**: global and project config, exposure, connection test | **Subagents**: who is running, results, templates and tool allowlists |
59
- | ![Settings](docs/screenshots/10-settings.png) | ![Plugins](docs/screenshots/11-plugins.png) |
60
- | **Settings → appearance**: font size, width, six palettes | **Plugins**: load state of pi packages, with toggles |
61
-
62
- Two more: [voice (event sounds + local TTS)](docs/screenshots/12-voice.png), [push (subscriptions and devices)](docs/screenshots/13-push.png).
27
+ No version tag means the current release. To pin a version or use a mirror:
63
28
 
64
- ---
29
+ ```bash
30
+ npm -g install oh-my-agent-web@latest
31
+ npm -g install oh-my-agent-web@<version> --registry=https://registry.npmmirror.com
32
+ ```
65
33
 
66
- ## Quick start
34
+ You get three commands that all point at the same entry point: **`oh-my-agent-web`** (used throughout this README),
35
+ `aw` (the short alias you'll probably type), and `agent-web`.
67
36
 
68
- Requires **Node.js ≥ 22.19** (a hard requirement of `@earendil-works/pi-*` 1.x) and **npm ≥ 9**. macOS / Linux / Windows all work.
37
+ Upgrade and uninstall:
69
38
 
70
39
  ```bash
71
- npm ci # recommended: exact versions from the lockfile (no re-resolution)
72
- npm run build
73
- npm start # foreground; Ctrl-C to quit
40
+ oh-my-agent-web upgrade # same as npm -g install oh-my-agent-web@latest
41
+ npm -g uninstall oh-my-agent-web # uninstall; data in ~/.oh-my-agent-web is left alone
74
42
  ```
75
43
 
76
- Open <http://127.0.0.1:25257>.
44
+ On Linux, `node-pty` (the embedded terminal) ships prebuilds only for macOS / Windows, so Linux compiles it in place: you need `make`, `g++` and `python3`. When those are missing, both the page and the install script say exactly what's missing — the terminal won't open until then.
77
45
 
78
46
  ### One-line install (never touches global npm)
79
47
 
@@ -89,294 +57,472 @@ Windows (PowerShell):
89
57
  powershell -c "irm https://raw.githubusercontent.com/duzhenxun/oh-my-agent-web/main/scripts/install.ps1 | iex"
90
58
  ```
91
59
 
92
- The script does four things: **finds a new enough Node** (downloading the official build into `~/.omaw/node` with a sha256 check if needed), **runs `npm install -g --prefix ~/.omaw`** (no system-wide directory, no sudo), **links `omaw` into `~/.local/bin`** (patching PATH if necessary), and **smoke-tests once** (version string plus whether `node-pty` can be required — i.e. whether the terminal will open).
60
+ The script does four things: **finds a new enough Node** (downloads an official build into `~/.oh-my-agent-web/node` and checks its sha256 if needed), **`npm install -g --prefix ~/.oh-my-agent-web`** (never the system-wide prefix, no sudo), **links `aw` / `agent-web` / `oh-my-agent-web` into `~/.local/bin`** (patching PATH if necessary), and **smoke-tests once** (version string + whether `node-pty` can be required, i.e. whether the terminal will open).
93
61
 
94
- | What you want to change | How (`sh` version; the PowerShell version uses env vars) |
95
- |---|---|
96
- | Version | `OMAW_VERSION=0.7.0` (or `--version 0.7.0`) |
97
- | Install location | `OMAW_HOME=/opt/omaw` (or `--home /opt/omaw`) |
98
- | Registry mirror | `OMAW_REGISTRY=https://registry.npmmirror.com` (or `--registry …`) |
99
- | Leave PATH alone | `OMAW_NO_PATH=1` (or `--no-modify-path`) |
100
- | Install build tools | `OMAW_BUILD_TOOLS=1` (or `--with-build-tools`) |
101
- | Uninstall | `… \| sh -s -- --uninstall` (removes the install dir and symlink only, never your data dir) |
62
+ That `~/.oh-my-agent-web` is also the **data directory**: what gets installed (`node/`, `lib/`, `bin/`) sits next to your `settings.json`, `password`, `uploads/` and `logs/`.
102
63
 
103
- On Linux the script also checks for `make` / `g++` / `python3`: `node-pty` ships prebuilds for macOS / Windows only, so Linux compiles the PTY in place. If the tools are missing it tells you what to install, or you can pass `--with-build-tools` and let it handle that.
64
+ | What you want to change | How (`sh` version; PowerShell uses env vars) |
65
+ |---|---|
66
+ | Version | `OH_MY_AGENT_WEB_VERSION=<version>` (or `--version <version>`) |
67
+ | Install elsewhere | `OH_MY_AGENT_WEB_HOME=/opt/oh-my-agent-web` (or `--home /opt/oh-my-agent-web`) |
68
+ | Registry mirror | `OH_MY_AGENT_WEB_REGISTRY=https://registry.npmmirror.com` (or `--registry …`) |
69
+ | Leave PATH alone | `OH_MY_AGENT_WEB_NO_PATH=1` (or `--no-modify-path`) |
70
+ | Install build tools | `OH_MY_AGENT_WEB_BUILD_TOOLS=1` (or `--with-build-tools`) |
71
+ | Uninstall | `… \| sh -s -- --uninstall` (removes only what was installed; `settings.json` / password / uploads / logs stay) |
104
72
 
105
- > You can also just type `omaw` in your home directory after installing, but the home directory is an unsafe workspace (the file tree, search and the agent's cwd would all be your entire home). In that case it falls back to `<dataDir>/workspace` and prints a note. To point it at a real project use `AGENT_WEB_CWD=/path/to/project`.
73
+ ---
106
74
 
107
- ### Install as a global command
75
+ ## Commands
108
76
 
109
- `oh-my-agent-web` / `omaw` / `agent-web` are three bin names for the same entry point:
77
+ The three commands are completely equivalent; everything below is written as `oh-my-agent-web` (`aw` is its short alias).
110
78
 
111
- ```bash
112
- npm i -g .
113
- omaw -d # start in background (logs to <dataDir>/daemon.log)
114
- omaw status # pid / port / uptime / engine / session count
115
- omaw restart -d # restart after code changes
116
- omaw stop
117
- omaw version # current version; omaw upgrade upgrades to the latest release
118
- ```
79
+ | Command | What it does |
80
+ |---|---|
81
+ | `oh-my-agent-web` | **Start (default)**: runs in the background and opens the browser, handing the terminal straight back; default workspace = current directory |
82
+ | `oh-my-agent-web start -f` | Run in the foreground, logs right in the terminal, `Ctrl-C` to quit |
83
+ | `oh-my-agent-web status` | pid / port / uptime / engine / session count / workspace |
84
+ | `oh-my-agent-web restart` | Stop then start (background by default; `-f` for foreground) |
85
+ | `oh-my-agent-web stop` | Stop the running instance (foreground or background — it reads the lock file in the data dir instead of guessing a port) |
86
+ | `oh-my-agent-web version` | Version string (`-v` / `--version` are synonyms) |
87
+ | `oh-my-agent-web upgrade` | Upgrade to the latest release (it runs `npm -g install oh-my-agent-web@latest`) |
88
+ | `oh-my-agent-web help` | Show help (`-h` / `--help` are synonyms) |
119
89
 
120
- ### CLI flags
90
+ ### Flags
121
91
 
122
92
  | Flag | Env var | Default | Notes |
123
- | --- | --- | --- | --- |
93
+ |---|---|---|---|
124
94
  | `-p, --port` | `AGENT_WEB_PORT` | `25257` | Listen port |
125
- | `-H, --host` | `AGENT_WEB_HOST` | `127.0.0.1` | Bind address; only change it for remote access |
126
- | `-P, --password` | `AGENT_WEB_PASSWORD` | none | Access password; unset = local machine only |
127
- | `--allow-host` | `AGENT_WEB_ALLOWED_HOSTS` | loopback only | Extra allowed domains / LAN IPs (repeatable or comma-separated) |
128
- | `--data-dir` | `AGENT_WEB_DATA_DIR` | `~/.oh-my-agent-web` | Settings, password, uploads, logs, lock file |
129
- | — | `AGENT_WEB_CWD` | `process.cwd()` | Default workspace (used before any session exists) |
95
+ | `-H, --host` | `AGENT_WEB_HOST` | `127.0.0.1` | Listen address; only change it for remote access |
96
+ | `-P, --password` | `AGENT_WEB_PASSWORD` | none | Login password; unset = loopback only (a remote first visit asks you to set one) |
97
+ | `-f, --foreground` | — | off (background by default) | Foreground run, logs in the terminal; `-d` / `--detach` is the old spelling and now equals the default |
98
+ | `--no-open` | — | on | Don't open the browser |
99
+ | `--allow-host` | `AGENT_WEB_ALLOWED_HOSTS` | loopback only | Extra allowed hostnames / LAN IPs (repeatable or comma-separated) |
100
+ | `--data-dir` | `AGENT_WEB_DATA_DIR` | `~/.oh-my-agent-web` | Settings, password, uploads, logs and the lock file live here |
101
+ | — | `AGENT_WEB_CWD` | `process.cwd()` | Default workspace (before any session exists) |
130
102
  | — | `AGENT_WEB_ENGINE` | `pi` | Default engine |
131
- | — | `AGENT_WEB_APPROVAL_MODE` | `allow` | Default approval mode for new sessions; set `ask` to confirm every time |
132
- | — | `AGENT_WEB_TURN_TIMEOUT_MS` | 20 min | Per-turn watchdog timeout |
103
+ | — | `AGENT_WEB_APPROVAL_MODE` | `allow` | Default approval mode for new sessions; `ask` confirms every tool call |
104
+ | — | `AGENT_WEB_TURN_TIMEOUT_MS` | 20 minutes | Per-turn watchdog timeout |
133
105
  | — | `AGENT_WEB_LOG` | `info` | `debug` / `info` / `warn` / `error` |
134
106
 
135
- > To use it from your phone: `omaw -d -H 0.0.0.0 -P your-password --allow-host your.domain`. If you put a reverse proxy or tunnel in front, make sure it forwards `X-Forwarded-For` (origin checks look at it).
107
+ ### Common examples
108
+
109
+ ```bash
110
+ oh-my-agent-web # background + open the page (the usual one)
111
+ oh-my-agent-web -f # foreground, logs in your face, Ctrl-C to quit
112
+ oh-my-agent-web -p 25258 # different port (when 25257 is taken)
113
+ oh-my-agent-web status && oh-my-agent-web restart # check, then restart
114
+ oh-my-agent-web stop # stop it (works for foreground and background)
115
+ oh-my-agent-web --data-dir ~/agent-web-data # different data directory
116
+ AGENT_WEB_CWD=~/code/my-proj oh-my-agent-web # set the default workspace
117
+ oh-my-agent-web -H 0.0.0.0 --allow-host agent.example.com # allow outside access (first visit asks you to set a password)
118
+ ```
119
+
120
+ > **Reaching it from your phone**: `oh-my-agent-web -H 0.0.0.0 --allow-host your.domain`. The first visit from the phone asks you to **set an access password** (stored as `password` in the data dir; after that you just log in). Reverse proxies / tunnels should pass `X-Forwarded-For` (origin checking looks at it).
121
+ >
122
+ > **Running `oh-my-agent-web` from your home directory** works, but home is an unsafe workspace (the file tree, search and the agent's cwd would be your whole home). In that case it falls back to `<data dir>/workspace` and prints a line about it; to point at a project use `AGENT_WEB_CWD=/path/to/project`.
123
+
124
+ Each engine also has its own escape hatches (all have defaults; you normally don't touch them): `AGENT_WEB_CODEX_BIN` / `_HOME`, `AGENT_WEB_DSH_BIN` / `_HOME` / `_MODEL` / `_PATCH`, `AGENT_WEB_OPENCODE_BIN` / `_ARGS` / `_MODEL`, `AGENT_WEB_CLAUDE_BIN` / `_ARGS` / `_MODEL` (it auto-detects `claude-code-acp` / `claude-agent-acp` and says so when neither is installed).
125
+
126
+ ---
127
+
128
+ ## What it is, what it isn't
129
+
130
+ **It is**: a front end that spreads the agent's *process* out in front of you. Thinking blocks, tool calls, changed files, tokens and cost all live in the conversation stream. You can cut in mid-turn, highlight a passage to file a comment on it, or open a scratch chat that never touches disk just to ask one question.
131
+
132
+ **It is not**: a new agent engine, and it is not a SaaS that moves your sessions into the cloud. It does not rewrite prompts and does not take over tool implementations — what you install is pi / Codex / Claude Code / opencode / DeepSeek Harness, and the workbench just gives them a readable UI, clickable buttons, and a session list that survives restarts.
133
+
134
+ Three hard rules:
135
+
136
+ 1. **No database**: a conversation *is* the JSONL / thread the engine wrote. The web UI reads it, renames it, and deletes it when asked.
137
+ 2. **No pretending**: anything an engine can't do is hidden in the UI (the capability matrix); a panel that can't get data says so, and no other engine's config is passed off as this one's.
138
+ 3. **No overreach**: it listens on `127.0.0.1` only by default; exposing it remotely takes all three of `-H`, `-P` and `--allow-host`.
139
+
140
+ ---
141
+
142
+ ## Conversation
143
+
144
+ **Streaming text + collapsible thinking blocks + tool cards.** A tool card shows arguments, the full command (copyable), output, duration and pass/fail. Thinking and tool calls inside one turn are grouped into "process, N steps" blocks that expand when clicked.
145
+
146
+ ![The footer of one reply: a markdown table, the changed-files chip, and this turn's tokens / cache / cost](docs/screenshots/02-answer.png)
147
+
148
+ The footer of every reply is the usage **for that turn**: input (cache miss) / output / cache read / cost, with a "Copy" that takes the whole block. The running total for the session is behind the context ring in the status bar (subagents are separate sessions billed separately and are not counted there).
149
+
150
+ A few things in the conversation are clickable:
151
+
152
+ - **Changed-files chip**: which files this reply touched (the chip at the bottom of the screenshot above shows `store.ts`); one click lands you straight on the diff in the right pane.
153
+ - **Branch icon**: "Branch from here", which starts a new conversation at this message; the original stays byte-for-byte unchanged.
154
+ - **File paths**: a path like `web/src/App.tsx` in the body opens in the preview.
155
+
156
+ ### Cutting in, queueing, stopping
157
+
158
+ While a turn is running, the composer does not lock up:
159
+
160
+ | Action | Meaning |
161
+ |---|---|
162
+ | **Steer** | Takes effect immediately, inserted into the current turn as the next instruction |
163
+ | **Queue** | Wait until this turn ends; you can see what's at the head of the queue and retract anything |
164
+ | **Stop** | Interrupt the turn and keep whatever was already produced |
165
+
166
+ Steering is pi-only (its SDK has `streamingBehavior: "steer"`). Other engines have no such notion, so the host **degrades it to queueing** rather than firing a second message that would flush the active turn.
136
167
 
137
168
  ---
138
169
 
139
- ## UI tour
170
+ ## Notes: highlight a passage and comment on it
171
+
172
+ **Select some text in the conversation** and a small toolbar floats up: `Add to session` / `Annotate` / `Ask in a temp session`.
173
+
174
+ <table><tr>
175
+ <td width="50%"><img src="docs/screenshots/14-selection-toolbar.png" alt="The toolbar that floats up after you select a passage"></td>
176
+ <td width="50%"><img src="docs/screenshots/15-annotations.png" alt="After annotating: a numbered badge stays in the body, and a 1-note pill shows up in the composer"></td>
177
+ </tr></table>
178
+
179
+ "Annotate" attaches a comment to that text. The body keeps a **①②③ numbered badge**, and an "N notes" pill appears in the composer — open it to read, edit or delete each comment, or `Quote it`.
180
+
181
+ On send it is assembled into a block like this (you can also hit `Quote it` yourself, in which case only your own words go out):
140
182
 
141
183
  ```
142
- ┌─ top bar: brand · new chat · engine picker · subagent badge · panels · right-pane toggle ─┐
143
- ├────────────┬──────────────────────────────┬──────────────────────┤
144
- │ sidebar │ conversation │ right pane │
145
- │ · projects │ · message stream (thinking │ · file preview │
146
- │ · sessions │ blocks + tool cards) │ (multi-tab) │
147
- │ · archived │ · steer / queued messages │ · terminal │
148
- │ · files │ · subagent run strip │ · status bar │
149
- │ │ · composer (attachments, /) │ │
150
- └────────────┴──────────────────────────────┴──────────────────────┘
184
+ Note 1:
185
+ > The panel starts at y=0, but the top bar's z-index is 45
186
+
187
+ This needs to be one sentence shorter
151
188
  ```
152
189
 
153
- The sidebar and right pane can both be dragged wider or collapsed; widths persist in `localStorage`. The composer takes `/` slash commands, `@` file mentions, drag-and-drop attachments and pasted images; the chips above it are prompt templates, the thinking toggle and suggested actions.
190
+ Notes are **local anchors**: they locate themselves by "quoted text + surrounding context", so badges still find their place after the body reflows or a stream appends more text. They live in this browser's `localStorage`, isolated per session — never on the server, never in the cloud, invisible from another device.
154
191
 
155
- On narrow screens (mobile UA or ≤400px) the layout switches to overlay drawers and the sidebar collapses shortly after you pick a session. Which chunks appear in the top bar and status bar — and whether process blocks start expanded — are all switchable in settings.
192
+ **Line references in files** are the other road: select lines in the preview (`Shift+click` for a range, `Ctrl/Cmd+click` to add, right-click for the line menu) and "Add to conversation" writes a **path plus line numbers** such as `@web/src/App.tsx:120-138`, leaving the agent to read it.
156
193
 
157
194
  ---
158
195
 
159
- ## Engines
196
+ ## Side chat: ask one question and go
197
+
198
+ ![The "side chat" on the right is a channel of its own: never saved, while the main conversation keeps running](docs/screenshots/13-tempchat.png)
199
+
200
+ The `Temp session` button in the top bar (or the first card on the right pane's landing page) opens another conversation **in the right pane**:
201
+
202
+ - **Not persisted**: it lives in memory, dies with the tab, never enters the sidebar and leaves no file;
203
+ - the main conversation keeps running; the two don't interrupt each other;
204
+ - to keep it, hit `Save as a regular chat` — only then is a session file written (history the ephemeral chat never had is preserved too);
205
+ - you can have several open at once, each on its own channel and its own tab.
206
+
207
+ > This depends on the engine having an in-memory session: pi supports it; a Codex thread already lives in Codex's own store, so there is no "save" button. It is not "a new session in a different skin", and it is not a subagent — **an ephemeral chat is a window you opened, a subagent is a subtask the agent dispatched**.
208
+
209
+ ---
210
+
211
+ ## Files, diffs, git, search
212
+
213
+ The files area sits at the bottom of the sidebar and also works as a tab in the right pane. **The web side is read-only**: you can look, compare, reference and search; editing files in place and saving them is not this layer's job — give that to the agent's tools or the terminal, and use the diff here to review it.
214
+
215
+ | Capability | Notes |
216
+ |---|---|
217
+ | Directory tree | Switchable root (follow the session / pick a project), lazy loaded, at most 500 entries per directory, `node_modules` / `.git` / `dist` hidden by default, "show hidden files" reveals `.env` and friends |
218
+ | Search by file name | Matches names only, never reads contents, 250 ms debounce, at most 200 hits |
219
+ | Preview | markdown (tables / code highlighting / mermaid), source with line numbers, images, PDF, audio and video streams; multiple tabs, drag to reorder, right-click to close left / right / all, and it can be spread over the full width |
220
+ | Line references | Select lines → "Add to conversation" writes `@path:line`; a text selection can also be "copy selected text" |
221
+ | Reveal in Finder | macOS `open -R`, Windows `explorer.exe /select,`, Linux opens the parent directory |
222
+ | Uncommitted changes | Status dots in the file tree, plus `M` / `U` / `D` after the filename |
223
+
224
+ ### Diff
160
225
 
161
- | Engine | Notes |
162
- | --- | --- |
163
- | **pi** (default) | The full plugin / skill ecosystem, lazy tool loading, approval gate, goal mode and subagents; providers and models are managed by this workbench |
164
- | **Codex** | The official Codex agent (app-server JSON-RPC) with its own loop and sandbox; sessions are shared with Codex Desktop / CLI; approvals are command/file level |
165
- | **DeepSeek Harness** | The official DeepSeek agent (ACP); model and thinking level are switchable; permissions confirmed one by one |
166
- | **opencode** | The official opencode agent (ACP) with its own tools / MCP / plugin ecosystem; sessions are shared with the opencode CLI / TUI. **Approvals follow opencode's own `permission` config** (a global allow means you will never be asked) |
167
- | **fake** | A scripted, zero-token engine used only to exercise the protocol / tools / approvals (`AGENT_WEB_ENGINE=fake`, not shown in the picker by default) |
226
+ ![The diff view: against HEAD, a `+6 −1` summary plus line-level red/green](docs/screenshots/03-diff.png)
168
227
 
169
- Capability matrix (`server/engine/registry.ts`):
228
+ The "diff" toggle at the top of the preview is a comparison against `HEAD`: a `+285 −78` summary, line-level red/green, and a switch between "changes only" and the full file. Coming in from a changed-files chip lands directly on the diff.
170
229
 
171
- | Capability | pi | codex | dsh | opencode |
172
- | --- | :-: | :-: | :-: | :-: |
173
- | Approvals | ✅ | ✅ | ✅ | per config |
174
- | Model / thinking level | ✅ | ✅ | ✅ | ✅ |
175
- | Resume sessions | ✅ | ✅ | ✅ | ✅ |
176
- | Steering | ✅ | — | — | — |
177
- | Subagent panel | ✅ | — | — | — |
178
- | Workbench-managed providers | ✅ | — | — | — |
230
+ ### git
179
231
 
180
- A missing engine does not block startup: you only get "not found" when you switch to it.
232
+ The branch chip at the top of the files area lists local branches and switches them (a dirty working tree asks first). It only shells out to `git` for reads and branch switches — it never commits. Committing stays with the agent, or with you in a terminal.
181
233
 
182
- Per-engine escape hatches (all have defaults, usually leave them alone):
234
+ ### Content search
183
235
 
184
- | Variable | Purpose |
185
- | --- | --- |
186
- | `AGENT_WEB_OPENCODE_BIN` / `_ARGS` / `_MODEL` | opencode binary, extra args (e.g. `--pure` to skip external plugins), preferred model for new sessions (`provider/model`) |
187
- | `AGENT_WEB_DSH_BIN` / `_HOME` / `_MODEL` / `_PATCH` | DSH binary, data dir, model, patch toggle |
188
- | `AGENT_WEB_CODEX_BIN` / `_HOME` | Codex binary and data dir |
236
+ "Search file contents" in the toolbar is a full-text search: add a **path mask** (such as `web/src/**`) to narrow it, results show `file:line` plus a snippet of the source, and clicking one jumps to that line. When there are too many hits it truncates at the cap and says so.
189
237
 
190
238
  ---
191
239
 
192
- ## Sessions and on-disk state
240
+ ## Terminal
193
241
 
194
- No database: **the engine's own files are always the source of truth** for a conversation. The web UI only reads them, renames them, and deletes them when asked.
242
+ ![The right-pane terminal is a real PTY: zsh, colors, git output, and a cwd that matches the session](docs/screenshots/04-terminal.png)
195
243
 
196
- | What | Where |
197
- | --- | --- |
198
- | pi sessions | `~/.pi/agent/sessions/<encoded cwd>/<ISO>_<uuid>.jsonl` (first line `{type:"session",id,timestamp,cwd}`) |
199
- | Codex sessions | Codex's own thread store (via `thread/list \| resume \| delete`) |
200
- | opencode / DSH sessions | Their own data dirs (opencode lists by cwd; DSH reads the workspace projection under `~/.dsh`) |
201
- | Session title | pi takes the first user message (reads only the first 60 lines); renaming appends `session_info.name` to the end of the jsonl |
202
- | Workbench state | `<dataDir>/settings.json`, `approval-rules.json`, `server.lock`, `logs/`, `uploads/`, `push-subscriptions.json`, `vapid-keys.json` |
244
+ The terminal in the right pane is a **real PTY** (`node-pty` + xterm.js): vim / top / Ctrl-C / colors / line editing all behave, the cwd matches the session's project, and you can pass one initial command (the upgrade flow uses exactly that).
245
+
246
+ Reloading the page does not restore a terminal session (the PTY exits with the page); switching tabs does not break it. On Linux `node-pty` has to be compiled in place, and when the tools are missing the UI says exactly what to install.
203
247
 
204
- "Delete session" **renames to `.deleted-<timestamp>`** (native delete APIs win when the engine has one). The file stays on disk and can be recovered.
248
+ ### Built-in web page
249
+
250
+ The right pane also opens web pages: type a URL and view it in an iframe, with requests going through **a local proxy** (`/web/p/<url>`) — it fetches the document and strips the `X-Frame-Options` / CSP framing restrictions, so sites like Baidu open too. The proxy has boundaries: HTML/CSS documents only, **no cookies in and none out**, a 12 MB upstream cap, and an iframe sandbox without `allow-same-origin`. For sites that need a login or heavy JS, hit `⧉` to jump to the system browser.
251
+
252
+ ---
253
+
254
+ ## Engines: each runs its own native loop
255
+
256
+ The engines are not "the same prompt in different costumes" — each is that vendor's own agent loop:
257
+
258
+ | Engine | How it's wired | Notes |
259
+ |---|---|---|
260
+ | **pi** (default) | Native SDK | The full plugin / skill ecosystem, lazy tool loading, the approval gate, subagents, steering; models and providers are managed by this workbench |
261
+ | **Codex** | app-server (JSON-RPC) | Its own loop and sandbox; sessions are shared with Codex Desktop / CLI; approvals are command- and file-level |
262
+ | **DeepSeek Harness** | ACP | Model and thinking level are switchable; permissions confirmed one by one |
263
+ | **opencode** | ACP | Its own tools / MCP / plugin ecosystem; sessions are shared with the opencode CLI / TUI; approvals follow opencode's own `permission` config |
264
+ | **Claude Code** | ACP (the `claude-code-acp` adapter) | Switchable models, per-call tool confirmation; sessions are shared with the `claude` CLI; this ACP surface exposes neither thinking levels nor subagents |
265
+ | **fake** | Built-in scripted engine | Zero tokens, only for exercising the protocol / tools / approval flow (not in the picker by default) |
266
+
267
+ The differences (the capability matrix in `server/engine/registry.ts`, which the UI uses to hide buttons):
268
+
269
+ | Capability | pi | codex | dsh | opencode | claude |
270
+ |---|:-:|:-:|:-:|:-:|:-:|
271
+ | Approvals | ✅ | ✅ | ✅ | per config | ✅ |
272
+ | Model / thinking level | ✅ | ✅ | ✅ | ✅ | model only / no levels |
273
+ | Resume past sessions | ✅ | ✅ | ✅ | ✅ | ✅ |
274
+ | Steering | ✅ | — | — | — | — |
275
+ | Subagent panel | ✅ | — | — | — | — |
276
+ | Workbench-managed providers | ✅ | — | — | — | — |
277
+ | Side chat (in memory) | ✅ | — | — | — | — |
278
+
279
+ **The models / plugins / MCP panels follow the active engine**: switch to Codex and the panels read Codex's own data (`model/list`, `plugin/list`, `config/read`); claude reads `~/.claude.json` and `~/.claude/settings.json`; opencode reads `opencode.json`. A panel that has nothing wired says so instead of borrowing pi's config.
280
+
281
+ Engines are switched in the **bottom status bar** (not the top bar), and every session remembers its own engine, model and thinking level.
205
282
 
206
283
  ---
207
284
 
208
285
  ## Models and providers
209
286
 
210
- The models panel groups providers into two columns: **official built-ins** (the ones pi's catalog has models for) and **custom providers**.
287
+ ![The models panel: the providers with a key or a signed-in subscription on the left, and that provider's endpoint, API shape and model allowlist on the right](docs/screenshots/05-models.png)
288
+
289
+ - **Subscriptions**: OAuth login (9 providers), usable right after signing in — no key to copy by hand.
290
+ - **Official built-ins**: the ones pi's catalog carries models for — **paste one key and it works**; endpoints and model tables come from the catalog.
291
+ - **Bring your own**: change the API shape (openai-completions / responses / anthropic-messages / codex / typesafe), the base URL and custom headers, and add models one by one.
292
+ - Tick models individually (only ticked ones are saved), batch-test connectivity, pull the list from `/models`, and save the ticked set as the allowlist.
211
293
 
212
- - Built-ins need only **an API key** — endpoint and model table come from pi's catalog.
213
- - Custom providers let you add models, edit base URLs, **import** from another client (actually `GET {baseUrl}/models` and tick the ones you want) and **test each one**.
214
- - **Keys never leave the server**: the API returns only `hasKey` and the last four digits; writes are serialized inside the process so concurrent edits can't corrupt files.
294
+ **Keys never leave the server**: the API returns only `hasKey` and the last 4 digits; writes are serialized inside the process so concurrent edits can't corrupt a file.
215
295
 
216
- The config truth is spread over three files plus the engine catalog: `~/.pi/agent/models.json` (pi's own writable provider catalog), `models-store.json` (legacy location), `auth.json` (`api_key` or oauth subscription credentials), with the allowlist in `settings.json.enabledModels`.
296
+ The truth is spread across `~/.pi/agent/models.json` (the provider catalog), `models-store.json` (the legacy location) and `auth.json` (`api_key` or OAuth credentials), with the allowlist in `settings.json.enabledModels`.
217
297
 
218
298
  ---
219
299
 
220
- ## Approvals and security model
300
+ ## Skills
221
301
 
222
- - Binds `127.0.0.1` by default and validates `Origin` / `Host` (including the `AGENT_WEB_ALLOWED_HOSTS` allowlist, the vite port and LAN addresses).
223
- - Identity: loopback peer **and** loopback Host → allowed. With no password set, remote requests always get 403. With a password, either the cookie (`agent-web-auth`, 7 days, HttpOnly) or HTTP Basic will do; failed logins are throttled at 20 attempts / 10 minutes.
224
- - Tools and commands go through approvals: **read-only tools** (read / grep / find / ls) and read-only commands pass automatically, everything else is asked per `scopeKey` (tool + executable name or target directory). Picking "always allow" writes the rule into `approval-rules.json` (atomic write; a corrupt file is treated as empty).
225
- - When a turn ends, every pending approval for that session is cleared so nothing hangs waiting for a human.
226
- - Uploaded temp files and session caches live in the data directory, never in your project.
302
+ ![The skills panel: installed / enabled / disabled on the left, this skill's SKILL.md summary and its directory files on the right](docs/screenshots/06-skills.png)
227
303
 
228
- > Exposing it remotely is explicit: `-H`, `-P` and `--allow-host` must all be present, or it won't work.
304
+ Reads `~/.agents/skills/<dir>/SKILL.md`; the list parses frontmatter only (no yaml dependency). **Enabling or disabling means renaming `SKILL.md` to `.SKILL.md.disabled`** — there is no second state file, so what you see in the CLI agrees with what you see here.
305
+
306
+ **Skill Hub**: search by category plus one-click install (200k+ index); it can also install a specific `skills/<name>` from a Git repo, or create one by hand.
229
307
 
230
308
  ---
231
309
 
232
- ## Subagents (parallel, manageable)
310
+ ## MCP
233
311
 
234
- The pi engine ships its own subagent implementation (**no pi-subagents plugin required**):
312
+ ![The MCP panel: global and project configs, enable/disable and exposure, test connection, OAuth](docs/screenshots/07-mcp.png)
235
313
 
236
- - **How**: the main agent gets an `Agent` tool (`subagent_type` / `prompt` / `description` / `run_in_background`) and can run several at once. Every delegation is **a real pi session** with its own context, session file and tool allowlist.
237
- - **Where to watch**: run cards appear under the conversation (the strip above the composer) with status, duration, tool count, tokens and cost. Expand one to see the raw result and its session file path, and stop any of them at any time. Switching sessions swaps the strip to that session's runs. A top-bar badge gives the at-a-glance count.
238
- - **Management**: the panel manages **templates** (built-in explore / general-purpose / plan, each disableable; create / edit / delete your own). Templates live at `~/.pi/agent/agents/*.md` with frontmatter `name / display_name / description / tools / model / thinking / max_turns`; the body is the system prompt — the same format pi uses, so they're readable, editable and git-friendly.
239
- - **Concurrency**: a cap of 1–12 (default 4) in the panel; extra delegations queue. Turn the master switch off and the model no longer sees the `Agent` tool.
240
- - **Isolation**: subagents load no skills / plugins / themes / context files by default (fast and predictable). They inherit the parent's model when they don't pin their own. Models registered by extensions fail inside subagents with "specify a model in the template".
314
+ Reads and writes two config files: `~/.pi/agent/mcp.json` (global) and `<workspace>/.pi/mcp.json` (project), where **a project entry with the same name overrides the global one**, and writes preserve indentation and CRLF.
241
315
 
242
- Subagent session files are archived next to the parent session (`<parent session dir>/<parent name>/subagents/`) and never appear in the sidebar.
316
+ - exposure has four levels: **codemode (default) / deferred / direct / hidden**;
317
+ - **Test connection**: a real handshake plus `tools/list`, then disconnect; it reports the connection state and the tool count;
318
+ - OAuth login, pick a server from a marketplace, paste a command / URL / JSON and let it be parsed;
319
+ - **the config is the truth**: pi connects at session start, so edits apply to **new sessions**.
243
320
 
244
321
  ---
245
322
 
246
- ## MCP · plugins · skills
323
+ ## Plugins
247
324
 
248
- **MCP**: reads and writes `~/.pi/agent/mcp.json` (global) plus `<workspace>/.pi/mcp.json` (project), where a project entry with the same name overrides the global one. Writes preserve indentation and CRLF.
249
- Exposure has four levels `codemode | deferred | direct | hidden` and tools are namespaced `mcp__<name>`.
250
- **The config is the truth** — pi connects at session start, so panel changes apply to **new sessions**. Testing a connection does a real handshake with `@earendil-works/pi-mcp` (`tools/list`) and disconnects immediately; OAuth goes through `pi mcp login`. There's also a marketplace (registry / smithery / github) and a "paste a command / URL / JSON and let it parse" flow.
325
+ ![The plugins panel: the four states of a pi package, and disabling per package](docs/screenshots/09-plugins.png)
251
326
 
252
- **Plugins**: these are pi packages — `settings.json`'s `packages` (npm / git / local) plus loose `~/.pi/agent/extensions/` and `<ws>/.pi/extensions/`. State is decided by the pi SDK's `resolve()` (querying **never** triggers `npm install`), with four states: `loaded / installed / missing / disabled`. Disabling writes `autoload:false` and deletes nothing.
327
+ Plugins are pi packages: the `packages` entry in `settings.json` (npm / git / local directory) plus the loose `~/.pi/agent/extensions/` and `<ws>/.pi/extensions/`.
253
328
 
254
- **Skills**: read from `~/.agents/skills/<dir>/SKILL.md`; the list parses frontmatter only (no yaml dependency) and caps the file listing at 200 entries.
255
- **Toggling = renaming `SKILL.md` to `.SKILL.md.disabled`** — there is no second state file. Skill Hub supports category search plus zip installs, and git clone can target `skills/<name>`.
329
+ State comes from the pi SDK's `resolve()` (**querying never triggers `npm install`**), with four states `loaded / installed / missing / disabled`; disabling per package writes `autoload:false` and deletes nothing.
256
330
 
257
331
  ---
258
332
 
259
- ## Architecture: SSE + RPC, no WebSocket
333
+ ## Subagents: run many, manage each
260
334
 
261
- Early versions used WS; today there are **only two HTTP channels** (ADR-0032):
335
+ ![The subagents panel: built-in templates, default concurrency, the master switch](docs/screenshots/08-subagents.png)
262
336
 
263
- ```
264
- browser ──GET /api/events (SSE)──▶ server snapshot · message_delta · notice
265
- ──POST /api/rpc (JSON)──▶ ClientRequest → { reply } consumed in place
266
- ```
337
+ Once the main agent has the `Agent` tool it can run several subagents in parallel, and each one is **a real session** (its own context, session file and tool allowlist).
267
338
 
268
- - **Downstream**: one full snapshot first, then deltas only. Message text arrives as `message_delta` chunks; structural changes (tool cards, errors, state) push a new snapshot, coalesced in a 60 ms window — snapshots are droppable, events are not. SSE sends a heartbeat every 25 s and supports gzip `Z_SYNC_FLUSH`.
269
- - **Upstream**: `send_message` (text / attachments / mode), `interrupt`, `approve`, `new_chat`, `switch_conversation`, `set_engine` / `set_model` / `set_thinking_level` / `set_approval_mode`, `cancel_queued`, `rename_session`, and a handful for providers and subagents.
270
- - **The protocol lives in one place**, `server/protocol.ts`, and the frontend reuses its types; unit tests keep both ends aligned.
271
- - **The snapshot is the single source of truth**: the frontend's `store.ts` uses `useSyncExternalStore` and replaces state wholesale when a snapshot arrives. Locally it keeps only optimistic state like `preview` / `draft` / `pendingActive`.
339
+ - **Watch**: the subagent card strip above the composer — status, duration, tool count, tokens, cost. "View result" expands the raw result and the session file path in place, and any single run can be stopped. The heading gives the at-a-glance count (`2 running` / `8 finished`).
340
+ - **Manage**: the top-bar "subagents" panel manages **templates** (built in: explore / general-purpose / plan, each disableable; you can also create, edit and delete your own): `name / display_name / description / tools / model / thinking / max_turns`. They land in `~/.pi/agent/agents/*.md`, in the same format as pi's agent files — readable, editable, git-friendly.
341
+ - **Concurrency**: 1–12 (default 4); anything beyond that queues. Turn the master switch off and the model no longer sees the `Agent` tool.
342
+ - **Isolation**: subagents load no skills / plugins / themes / context files by default (fast and predictable); a subagent that doesn't pin a model keeps using the parent session's model.
272
343
 
273
- **Process model**: `cli.ts` parses flags → `session/lock.ts` grabs `<dataDir>/server.lock` (`{pid,port,startedAt}`; a live pid means refuse to start, a dead pid means take over the stale lock) → `runtime.init()` → `listen`. `SIGINT/SIGTERM` and `/api/shutdown` share one graceful shutdown path (flush, disconnect engines, release the lock).
344
+ A subagent's session file is archived next to the parent session (`<parent session dir>/<parent name>/subagents/`) and never shows up in the sidebar's project list.
274
345
 
275
346
  ---
276
347
 
277
- ## Development
348
+ ## Push and voice
278
349
 
279
- ```bash
280
- npm run dev # starts both processes with one command
281
- ```
350
+ <table><tr>
351
+ <td width="50%"><img src="docs/screenshots/11-push.png" alt="Push: local push state, target devices, test push"></td>
352
+ <td width="50%"><img src="docs/screenshots/10-voice.png" alt="Voice: four event sounds plus local TTS"></td>
353
+ </tr></table>
282
354
 
283
- - Externally there is still a single port **25257** (Vite listens and proxies); the backend hides on internal **25258**, so dev and prod use the same URL.
284
- - Frontend HMR via Vite, backend auto-restart via `tsx --watch`.
285
- - Phone debugging: `npm run dev -- --host 0.0.0.0`.
355
+ **Push**: Web Push (VAPID) subscriptions, one notification per finished turn — title is the project, body is the session, with a ❌ / ⏹ prefix for failures and interrupts. Multiple devices, a test push, and a one-click clear-all. Needs https or localhost.
286
356
 
287
- | Command | Purpose |
288
- | --- | --- |
289
- | `npm test` | `node --test` over `tests/unit/*.test.ts` (22 files, no extra dependencies) |
290
- | `npm run typecheck` | Two `tsc --noEmit` runs: server + web |
291
- | `npm run lint` | oxlint over `server` / `web/src` / `scripts` |
292
- | `npm run format` | Prettier across the repo (`format:check` only checks; `*.md` is deliberately skipped) |
293
- | `npm run build` | Server `tsc` + frontend `vite build` into `web/dist` |
294
- | `npm run icons` | Regenerate PWA icons with playwright-core |
357
+ **Voice**: four event sounds (start / finish / error / waiting for approval) with preview and adjustable volume; local TTS reads the final reply using the browser's built-in voices, with adjustable rate and voice, and never touches the network.
295
358
 
296
- CI (`.github/workflows/ci.yml`): `npm ci` → typecheck → lint → test → build, on ubuntu + Node 22.
359
+ ---
360
+
361
+ ## Settings
362
+
363
+ ![Settings → Appearance: three font sizes, content width in 4 steps, 6 palettes](docs/screenshots/12-settings.png)
364
+
365
+ The top bar is the entry point for every panel, and **each entry can be hidden in Settings → Interface** (settings and the split toggle are exempt — they're the only way back to the others). The sidebar and the right pane both drag wider and collapse, and their widths are remembered locally in `localStorage`.
366
+
367
+ The "settings" button opens five pages:
368
+
369
+ | Page | Contents |
370
+ |---|---|
371
+ | **Interface** | Which top-bar entries show (text and icon ticked separately), which status-bar chunks show (queue / working / context usage), whether cost shows in the conversation, which blocks sit above the composer (templates / thinking / quick commands), and whether thinking blocks and tool cards start expanded |
372
+ | **General** | The text of your quick phrases and the default thinking level — saved server-side, identical on every device |
373
+ | **Appearance** | Three font sizes (chat body / sidebar project names / sidebar session names, slider 2–20px), content width in 4 steps (720 / 900 / 1100 / full), 6 palettes (bright white, warm paper, mist teal, Tokyo night, mocha, nord), and the top-bar name and logo (hideable, or replaced with your own) |
374
+ | **Language** | Chinese / English, applied immediately |
375
+ | **Shortcuts** | 11 actions, rebindable, clearable and restorable to defaults, plus a cheat sheet |
376
+
377
+ Appearance, interface and language preferences stay on the device only (`localStorage`, with the `agent-web.` prefix) — one copy per phone, tablet and desktop. Only the quick phrases in "General" are shared across devices.
378
+
379
+ ---
380
+
381
+ ## Mobile
382
+
383
+ <table><tr>
384
+ <td width="34%"><img src="docs/screenshots/16-mobile.png" alt="Mobile single column: icon-only top-bar entries, 18px composer"></td>
385
+ <td width="34%"><img src="docs/screenshots/17-mobile-drawer.png" alt="On a phone the project sidebar is an overlay drawer"></td>
386
+ </tr></table>
387
+
388
+ On narrow screens (mobile UA or ≤400px) the layout collapses to a single column:
389
+
390
+ - the sidebar becomes an overlay drawer (`clamp(260px, 76vw, 340px)`, tap the scrim to close, closes on its own after you pick a session);
391
+ - the top bar wraps, keeps icons only, and scrolls horizontally;
392
+ - the right-pane workspace becomes a full-screen second page that starts below the top bar using `100dvh` (dodging the iOS toolbars);
393
+ - the composer is 18px so iOS doesn't zoom on focus; the model menu becomes a bottom sheet;
394
+ - the status bar scrolls horizontally, and the context-usage popover becomes a full-width sheet.
395
+
396
+ ---
397
+
398
+ ## Sessions: pin, archive, fork, move project
399
+
400
+ The sidebar groups sessions into "projects" by directory:
401
+
402
+ | Action | Behaviour |
403
+ |---|---|
404
+ | Pin | Lifts a session or a project to the top |
405
+ | Rename | For pi: append `session_info.name` to the end of the jsonl |
406
+ | Archive | Into "archived", undoable |
407
+ | Delete | Renames to `.deleted-<timestamp>` (the engine's native delete is preferred when it has one) — **the file is still on disk** |
408
+ | Fork | Derive a new session from one message (the title becomes `Branch · …`), the original untouched |
409
+ | Move project | Drag a session row onto another project; the id and history stay, only the `cwd` in the file header changes and the directory is moved |
410
+
411
+ Sorting is a deliberate choice: **newest-created first inside a group** — a new session lands on top, and updating an old one doesn't move it (sort by last activity instead and a single message in an old session shoots it to the top, dragging the whole list along).
412
+
413
+ ---
297
414
 
298
- ### Directory layout
415
+ ## Shortcuts and slash commands
416
+
417
+ **Every shortcut** is rebindable in Settings → Shortcuts (a binding must include `Mod`/`Alt`; bare letters are rejected). On macOS `Mod` is ⌘, elsewhere it's Ctrl.
418
+
419
+ | Action | Default | Action | Default |
420
+ |---|---|---|---|
421
+ | New session | `Mod+K` | Open right panel | `Mod+Alt+O` |
422
+ | New project | `Mod+M` | Close right panel | `Mod+Alt+W` |
423
+ | Temp session | `Mod+Shift+K` | Toggle left sidebar | `Mod+Alt+L` |
424
+ | Settings | `Mod+Alt+S` | Files | `Mod+Alt+F` |
425
+ | New terminal | `Mod+Alt+T` | Search file contents | `Mod+Shift+F` |
426
+ | Shortcut cheat sheet | `Mod+/` | | |
427
+
428
+ **Slash commands** pop up when you type `/` in the composer. There are two kinds — `local` ones the workbench runs itself, and `to the agent` ones handed to the engine verbatim as a message:
429
+
430
+ | Command | Kind | What it does |
431
+ |---|---|---|
432
+ | `/new [first message]` | local | New conversation, optionally with its first message |
433
+ | `/name <name>` | local | Rename the current session |
434
+ | `/model <name>` | local | Switch model (fuzzy match on id or display name) |
435
+ | `/thinking <level>` | local | Set the thinking level (off / low / … / max) |
436
+ | `/copy` | local | Copy the last assistant message |
437
+ | `/session` | local | Message count / tokens / cost for the session |
438
+ | `/resume` | local | Refresh the session list (sessions another terminal just opened) |
439
+ | `/skill <skill name>` | to the agent | Use a skill (the panel lists the installed ones) |
440
+ | `/compact [note]` | to the agent | Compact the context |
441
+ | `/plan [on\|off]` | to the agent | Turn plan mode on or off |
442
+ | `/cwd <path>` | to the agent | Change the working directory |
443
+
444
+ ---
445
+
446
+ ## Architecture: SSE + RPC, no WebSocket
299
447
 
300
448
  ```
301
- oh-my-agent-web/
302
- ├─ server/ Backend (Express 5, TypeScript ESM)
303
- │ ├─ cli.ts Command entry: start / stop / restart / status / version / upgrade
304
- │ ├─ index.ts Process assembly: middleware, routes, static assets, graceful exit
305
- │ ├─ protocol.ts Single source for the protocol (frontend reuses the types)
306
- │ ├─ engine/ Engine adapters: pi (native SDK) / codex / dsh / opencode (ACP) / fake
307
- │ │ plus the capability matrix (registry), catalog, wiring, pi-plugins
308
- │ ├─ session/ Runtime: runtime, turn-*, approval bridge, rename/delete, lock, settings
309
- │ ├─ sessions/ HTTP routes for deleting sessions (scanning lives in session/pi-sessions.ts)
310
- │ ├─ snapshot/ Server state snapshots: state, buildSnapshot, fan-out, 60 ms coalescing
311
- │ ├─ http/ auth, live-routes (SSE + RPC), aux-routes, compression, file headers
312
- │ ├─ files/ File tree, preview, search, media streaming, git branches
313
- │ ├─ terminal/ PTY sessions and their SSE stream
314
- │ ├─ approval/ Approval scopes and rules
315
- │ ├─ providers/ Providers / model catalogs / probes / imports / subscription OAuth
316
- │ ├─ mcp/ mcp.json read-write, connectivity probe, OAuth, marketplace
317
- │ ├─ skills/ Skill discovery, toggling, Hub installs
318
- │ ├─ subagents/ Subagent template storage (`~/.pi/agent/agents/*.md`)
319
- │ ├─ plugins/ HTTP routes for plugin packages
320
- │ ├─ push/ Web Push (VAPID)
321
- │ ├─ markers/ Parsing and stream-filtering `[[action:suggest:…]]` markers
322
- │ └─ util/ env, paths, spawn-cli (cross-platform), origin, etc.
323
- ├─ web/ Frontend (React 19 + Vite + TypeScript)
324
- │ ├─ src/components/ ~70 components: sidebar, message stream, composer, file tree, terminal, panels
325
- │ ├─ src/styles/ ~45 sectioned CSS files + six theme palettes (no CSS framework)
326
- │ ├─ src/ws.ts SSE + RPC client (the name is historical)
327
- │ └─ src/store.ts Snapshot store (useSyncExternalStore)
328
- ├─ scripts/ Dev dual-process, installers, prebuild pruning, icon generation
329
- ├─ tests/unit/ node:test unit tests
330
- ├─ docs/adr/README.md ADR index
331
- └─ docs/screenshots/ Real screenshots used by the READMEs (PNG 256 colors, ~1.4 MB)
449
+ browser ──GET /api/events (SSE)──▶ server one snapshot first, deltas afterwards
450
+ ──POST /api/rpc (JSON)──▶ ClientRequest → { reply } consumed in place
332
451
  ```
333
452
 
334
- ### Code conventions
453
+ - **Downstream**: one complete snapshot first, then deltas only. Text arrives as `message_delta` chunks appended block by block; structural changes (tool cards, errors, state) push a fresh snapshot, coalesced in a 60 ms window — snapshots are droppable, events are not. SSE heartbeats every 25 s.
454
+ - **Upstream**: `send_message`, `interrupt`, `approve`, `new_chat` (optionally with `ephemeral`), `switch_conversation`, `set_engine` / `set_model` / `set_thinking_level` / `set_approval_mode`, `persist_conversation`, `close_ephemeral`, `fork_session`, `move_session`, `cancel_queued`, `rename_session` and more.
455
+ - **The protocol has a single source** in `server/protocol.ts`; the frontend reuses its types, so one definition serves both ends.
456
+ - **The snapshot is the only source of truth**: `store.ts` uses `useSyncExternalStore` and replaces everything when a snapshot arrives. Locally only optimistic state survives (`preview` / `draft` / `pendingActive`).
457
+ - **Each client sees its own session** (ADR-0033): one server, two devices, each parked on a different conversation; bulk data such as the session list is only sent to a client when that client needs it.
335
458
 
336
- - Prettier: **tab indentation**, `printWidth 120`. oxlint runs its default rule set.
337
- - 400 lines per file is a guardrail; exceeding it means splitting (`cli-info.ts` and `snapshot/*` exist for that reason).
338
- - **ADR prose lives in the file-header comment of the code it constrains**, not in standalone `docs/` files — anyone editing the code will read it. `docs/adr/README.md` is only an index from number to file. Update that table when you change the code.
459
+ Process model: `cli.ts` parses the flags → `session/lock.ts` grabs `<dataDir>/server.lock` (a live pid refuses to start, a dead pid means taking over a stale lock) → `runtime.init()` → `listen`. `SIGINT` / `SIGTERM` / `/api/shutdown` all go through the same graceful exit (flush, disconnect engines, release the lock).
339
460
 
340
461
  ---
341
462
 
342
- ## Install size: pick the command that fits
463
+ ## Where data lives
343
464
 
344
- Most of the dependency tree is frontend build-time weight (`mermaid` alone is 121 MB, plus React) and build tools that only matter on your machine. At runtime the server needs just
345
- `express` / `web-push` / `node-pty` / `@earendil-works/pi-*` — the browser half already ships as `web/dist`.
346
- That's why `react` / `mermaid` / `xterm` / `highlight.js` all live in `devDependencies`:
465
+ | Content | Location |
466
+ |---|---|
467
+ | pi sessions | `~/.pi/agent/sessions/<encoded cwd>/<ISO>_<uuid>.jsonl` (first line `{type:"session",id,timestamp,cwd}`) |
468
+ | Codex sessions | Codex's own thread store (`thread/list \| resume \| delete`) |
469
+ | opencode / DSH / claude sessions | Their own data dirs (opencode lists per cwd with `session/list`; DSH reads `~/.dsh`; claude reads `~/.claude`) |
470
+ | Session titles | For pi, the first user message (reads only the first 60 lines); renaming appends `session_info.name` |
471
+ | Workbench state | `<dataDir>/settings.json`, `approval-rules.json`, `server.lock`, `logs/`, `uploads/`, `push-subscriptions.json`, `vapid-keys.json` |
472
+ | Skills / subagent templates / MCP | `~/.agents/skills/`, `~/.pi/agent/agents/`, `~/.pi/agent/mcp.json` |
473
+ | Local UI preferences | Browser `localStorage` (`agent-web.` prefix: theme, font sizes, widths, shortcuts, notes, tabs) |
347
474
 
348
- | Scenario | Command | Packages | Disk |
349
- |---|---|---|---|
350
- | Developing / building yourself | `npm ci` | 456 | 547 MB |
351
- | Already built, just run it | `npm ci --omit=dev` | 192 | **158 MB** (~3 s) |
352
- | Build then slim down | `npm ci && npm run build && npm prune --omit=dev` | 192 | **158 MB** |
353
- | Ship to another machine | `npm pack` → `npm i -g ./oh-my-agent-web-*.tgz` | 193 | 223 MB (3.1 MB tarball, README screenshots included) |
475
+ ---
354
476
 
355
- Two automatic slimming steps run in `postinstall` (`scripts/prune-prebuilds.mjs`):
477
+ ## Security model
356
478
 
357
- - **Keep only the current platform's `node-pty` prebuilds**: the package ships binaries for four platforms, and the two Windows ones alone are 58 MB. Pruning on macOS / Linux takes 62 MB → 5 MB; `npm ci` restores them as-is.
358
- - Fix the executable bit on `spawn-helper`.
479
+ - Binds `127.0.0.1` by default and validates `Origin` / `Host` (including the `AGENT_WEB_ALLOWED_HOSTS` allowlist, the vite port and LAN addresses).
480
+ - Identity: a loopback peer **and** a loopback Host → allowed. With no password set, remote requests always get 403. With a password, either the cookie (`agent-web-auth`, 7 days, HttpOnly) or HTTP Basic will do; failed logins are throttled at 20 attempts / 10 minutes.
481
+ - Tools and commands go through approvals: **read-only tools** (read / grep / find / ls) and read-only commands pass automatically, everything else is asked per `scopeKey` (tool + executable name or target directory). Choosing "always allow" writes the rule into `approval-rules.json` (atomic write; a corrupt file counts as empty).
482
+ - When a turn ends, every pending approval for that session is cleared, so nothing hangs waiting for a human.
483
+ - Uploaded temp files and session caches live in the data directory, never in your project.
484
+ - The built-in web page's proxy carries no cookies, and its iframe sandbox omits `allow-same-origin` (otherwise remote JS could reach `localStorage` and the RPC endpoint).
359
485
 
360
- > Of the remaining 158 MB, almost everything is pi's model layer: the official SDKs for each provider (`openai` 17.6 MB, `@google/genai` 10.8 MB, `@anthropic-ai/sdk` 9.1 MB, `@aws-sdk/*` + `@smithy/*` 5.2 MB) —
361
- > all hard dependencies of `@earendil-works/pi-ai`, unavoidable without trimming vendors.
486
+ > Exposing it remotely is an explicit act: `-H`, `-P` and `--allow-host` must all be present — miss one and it stays shut.
362
487
 
363
488
  ---
364
489
 
365
- ## Troubleshooting
490
+ ## Development
366
491
 
367
- > **Why `npm ci` and not `npm install`**: this project's direct dependencies are all very new (most within a month of release).
368
- > `npm install` re-resolves the version ranges from `package.json` against the registry, so if your mirror (corporate Nexus, npmmirror, etc.) hasn't synced those versions yet you get `npm error notarget No matching version found for …`.
369
- > `npm ci` uses the exact versions from the lockfile and never touches that path.
492
+ From the repo root, run `npm install` first, then:
493
+
494
+ ```bash
495
+ npm run dev # starts both processes with one command
496
+ ```
497
+
498
+ - Externally there is still a single port **25257** (Vite listens and proxies); the backend hides on internal **25258**, so dev and production use the same address.
499
+ - Frontend HMR via Vite; the backend auto-restarts through `tsx --watch`.
500
+ - Phone debugging: `npm run dev -- --host 0.0.0.0`.
501
+
502
+ | Command | Purpose |
503
+ |---|---|
504
+ | `npm run typecheck` | Two `tsc --noEmit` runs: server + web |
505
+ | `npm run lint` | oxlint over `server` / `web/src` / `scripts` |
506
+ | `npm run format` | Prettier across the repo (`format:check` only checks; `*.md` is deliberately skipped) |
507
+ | `npm run build` | Server `tsc` + frontend `vite build` into `web/dist` |
508
+ | `npm run icons` | Regenerate the PWA icons with playwright-core |
509
+
510
+ CI (`.github/workflows/ci.yml`): `npm ci` → typecheck → lint → test → build, on ubuntu + Node 22.
511
+
512
+ ---
513
+
514
+ ## Troubleshooting
370
515
 
371
- Check these in order:
516
+ > **Why `npm ci` and not `npm install`**: the direct dependencies are all very new, and `npm install` re-resolves the version ranges against the registry — if your mirror (corporate Nexus, npmmirror, …) hasn't synced those versions you get `npm error notarget`. `npm ci` uses the exact versions from the lockfile.
372
517
 
373
- 1. `npm error notarget No matching version found` → the mirror is behind. Check `npm config get registry`, then retry against the official one: `npm ci --registry=https://registry.npmjs.org/`.
374
- 2. `EBADENGINE` / pi complains about the Node version right at startup → you need Node ≥ 22.19 (`node -v`).
375
- 3. `node-pty` fails to build (mostly on Linux) → prebuilds ship for macOS / Windows only, so Linux compiles it in place and needs `python3`, `make`, `g++`.
376
- 4. Won't start but `omaw status` mentions a stale lock → a previous process didn't exit cleanly; the next `start` takes over automatically. If it's truly stuck, delete `<dataDir>/server.lock`.
377
- 5. Port already in use → `omaw -p 25258`, or run `omaw stop` first.
518
+ 1. `npm error notarget No matching version found` → the mirror is behind. Check `npm config get registry`, then fall back to the official one: `npm -g install oh-my-agent-web --registry=https://registry.npmjs.org/`.
519
+ 2. `EBADENGINE` / a Node version complaint at startup → you need Node ≥ 22.19 (check with `node -v`).
520
+ 3. `node-pty` fails to build (mostly on Linux) → install `python3` / `make` / `g++` before `npm -g install`, or let the one-line installer handle it with `--with-build-tools`.
521
+ 4. It won't start but `oh-my-agent-web status` mentions a stale lock → a previous process didn't exit cleanly; the next `start` takes over automatically. If it's truly stuck, delete `<dataDir>/server.lock`.
522
+ 5. Port already in use → `oh-my-agent-web -p 25258`, or run `oh-my-agent-web stop` first.
523
+ 6. The built-in web page is blank → that site needs a login or heavy scripts, and the proxy does not tunnel cookies; hit `⧉` to use the system browser.
378
524
 
379
- Windows pitfalls we've already hit (shell parsing, killTree, path checks, chmod, CRLF) are catalogued with P0/P1/P2 severities in `WINDOWS-COMPAT.md`.
525
+ Windows differences in shell resolution, process-tree teardown, path handling, permissions and CRLF are all centralised in three modules: `server/util/spawn-cli.ts` (PATH/PATHEXT, `.cmd` via `cmd.exe`, `killTree`), `server/util/paths.ts` and `web/src/path-utils.ts` (separators and absolute paths), and `server/util/file-mode.ts` (`icacls` on Windows). Read those headers before touching cross-platform code.
380
526
 
381
527
  ---
382
528