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
@@ -11,9 +11,9 @@
11
11
  */
12
12
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
13
13
  import { dirname, join } from "node:path";
14
- export const RULES_FILE = "approval-rules.json";
15
- export const RULES_VERSION = 1;
16
- export function rulesPath(dataDir) {
14
+ const RULES_FILE = "approval-rules.json";
15
+ const RULES_VERSION = 1;
16
+ function rulesPath(dataDir) {
17
17
  return join(dataDir, RULES_FILE);
18
18
  }
19
19
  function isRule(value) {
@@ -23,7 +23,7 @@ function isRule(value) {
23
23
  return typeof candidate.scopeKey === "string" && candidate.scopeKey !== "";
24
24
  }
25
25
  /** 纯函数:解析规则文件;坏内容一律返回空规则(不抛错) */
26
- export function parseRules(text) {
26
+ function parseRules(text) {
27
27
  let parsed;
28
28
  try {
29
29
  parsed = JSON.parse(text);
@@ -71,15 +71,3 @@ export function createFileRuleStore(dataDir) {
71
71
  },
72
72
  };
73
73
  }
74
- /** 内存版(单测 / 不落盘运行) */
75
- export function createMemoryRuleStore(seed = []) {
76
- let rules = [...seed];
77
- return {
78
- async load() {
79
- return [...rules];
80
- },
81
- async save(next) {
82
- rules = [...next];
83
- },
84
- };
85
- }
@@ -12,7 +12,7 @@
12
12
  import { isAbsolute, join, resolve } from "node:path";
13
13
  import { isReadOnlyCommand } from "./read-only.js";
14
14
  /** 不问的工具:只读类 */
15
- export const READ_ONLY_TOOLS = new Set(["read", "grep", "find", "ls"]);
15
+ const READ_ONLY_TOOLS = new Set(["read", "grep", "find", "ls"]);
16
16
  const SUMMARY_CHARS = 200;
17
17
  const RISK_NOTES = {
18
18
  bash: "跑命令会修改你的机器(装包、删文件、改配置都算),确认后才能执行",
@@ -48,7 +48,7 @@ const ENV_ASSIGNMENT_RE = /^[A-Za-z_][A-Za-z0-9_]*=/;
48
48
  * 以及路径前缀。
49
49
  * 例:`FOO=1 npm run build` → `npm`;`sudo -u root npm ci` → `npm`;`/usr/bin/git push -f` → `git`
50
50
  */
51
- export function commandExecutable(command) {
51
+ function commandExecutable(command) {
52
52
  const tokens = command.trim().split(/\s+/).filter(Boolean);
53
53
  let index = 0;
54
54
  skipLeading: for (;;) {
@@ -81,7 +81,7 @@ export function commandExecutable(command) {
81
81
  return base === "" ? "(空命令)" : base;
82
82
  }
83
83
  /** 目标路径所在目录(相对路径按工作区根解析) */
84
- export function targetDirectory(path, workspace) {
84
+ function targetDirectory(path, workspace) {
85
85
  if (path === "")
86
86
  return workspace;
87
87
  const absolute = isAbsolute(path) ? path : resolve(join(workspace, path));
@@ -9,8 +9,8 @@
9
9
  */
10
10
  import { readFile } from "node:fs/promises";
11
11
  import { join } from "node:path";
12
- export const PASSWORD_FILE = "password";
13
- export const ALLOWED_HOSTS_FILE = "allowed-hosts";
12
+ const PASSWORD_FILE = "password";
13
+ const ALLOWED_HOSTS_FILE = "allowed-hosts";
14
14
  const readTrimmed = async (path) => {
15
15
  try {
16
16
  const text = await readFile(path, "utf8");
@@ -23,7 +23,7 @@ const readTrimmed = async (path) => {
23
23
  }
24
24
  };
25
25
  /** 纯读取:把两个文件都试一遍(不存在就给空值) */
26
- export async function readStoredOptions(dataDir) {
26
+ async function readStoredOptions(dataDir) {
27
27
  const password = await readTrimmed(join(dataDir, PASSWORD_FILE));
28
28
  const rawHosts = await readFile(join(dataDir, ALLOWED_HOSTS_FILE), "utf8").catch(() => "");
29
29
  const allowedHosts = rawHosts
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `omaw` 的「查询类命令」:status / version / upgrade。
2
+ * `aw` 的「查询类命令」:status / version / upgrade。
3
3
  *
4
4
  * 为什么单独成文件:cli.ts 已经要管参数解析、前后台切换、停止/重启,
5
5
  * 再加状态查询就过 400 行护栏了(ADR-0010:一个文件只干一件事)。
@@ -35,14 +35,14 @@ function formatUptime(ms) {
35
35
  return `${minutes} 分 ${total % 60} 秒`;
36
36
  return `${total} 秒`;
37
37
  }
38
- /** `omaw status`:进程状态 + 服务端自报的事实(引擎 / 会话数 / 工作区) */
38
+ /** `aw status`:进程状态 + 服务端自报的事实(引擎 / 会话数 / 工作区) */
39
39
  export async function showStatus(options) {
40
40
  const { dataDir, probeHost } = options;
41
41
  const lock = await readLock(dataDir);
42
42
  console.log(`oh-my-agent-web v${packageMeta().version} · 数据目录 ${dataDir}`);
43
43
  if (!lock) {
44
44
  console.log("状态:未运行(没有 server.lock)");
45
- console.log(" 启动:omaw -d");
45
+ console.log(" 启动:aw -d");
46
46
  return;
47
47
  }
48
48
  if (!defaultIsAlive(lock.pid)) {
@@ -55,7 +55,7 @@ export async function showStatus(options) {
55
55
  console.log(` 地址 http://${probeHost}:${lock.port}`);
56
56
  if (health) {
57
57
  const running = health.version ?? "unknown";
58
- console.log(` 版本 v${running}(磁盘上是 v${onDisk}${running === onDisk ? "" : ",重启后生效:omaw restart -d"})`);
58
+ console.log(` 版本 v${running}(磁盘上是 v${onDisk}${running === onDisk ? "" : ",重启后生效:aw restart -d"})`);
59
59
  console.log(` 引擎 ${health.engine} · 协议 v${health.protocolVersion} · 会话 ${health.sessions} · 对话 ${health.conversations}`);
60
60
  console.log(` 工作区 ${health.workspace}`);
61
61
  }
@@ -63,12 +63,12 @@ export async function showStatus(options) {
63
63
  console.log(" ⚠ 进程在,但 /api/health 没通(可能还在启动,或端口没监听成功)");
64
64
  }
65
65
  }
66
- /** `omaw version` */
66
+ /** `aw version` */
67
67
  export function showVersion() {
68
68
  console.log(`oh-my-agent-web v${packageMeta().version}(node ${process.version} · ${process.platform}/${process.arch})`);
69
69
  }
70
70
  /**
71
- * `omaw upgrade`:升级**全局安装**的这个包。
71
+ * `aw upgrade`:升级**全局安装**的这个包。
72
72
  *
73
73
  * 三条底线:
74
74
  * 1. 只跑 `npm i -g <包名>@latest`,不做别的(不替你重启,重启会打断正在跑的回合);
@@ -80,8 +80,8 @@ export async function upgrade() {
80
80
  const meta = packageMeta();
81
81
  if (meta.private) {
82
82
  console.error(`这个包是 private(${meta.name}@${meta.version}),没有发布到 npm,无法从 npm 升级。`);
83
- console.error(" · 本地 / git 安装:回到源码目录重新 `npm i -g .`,然后 `omaw restart -d`");
84
- console.error(" · 想用 npm 升级:先把 package.json 的 name 改成自己 scope 下的名字(如 @you/omaw)并发布");
83
+ console.error(" · 本地 / git 安装:回到源码目录重新 `npm i -g .`,然后 `aw restart -d`");
84
+ console.error(" · 想用 npm 升级:先把 package.json 的 name 改成自己 scope 下的名字(如 @you/aw)并发布");
85
85
  return 1;
86
86
  }
87
87
  console.log(`当前 v${meta.version},正在安装 npm 上的最新版 ${meta.name} …`);
@@ -99,6 +99,6 @@ export async function upgrade() {
99
99
  console.error("升级失败(网络 / 权限 / 包名不存在都可能)。");
100
100
  return code;
101
101
  }
102
- console.log(`已升级到 v${packageMeta().version}(新进程才用新版:omaw restart -d)`);
102
+ console.log(`已升级到 v${packageMeta().version}(新进程才用新版:aw restart -d)`);
103
103
  return 0;
104
104
  }
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * 命令行入口(ADR-0031):`oh-my-agent-web` / 简写 **`omaw`** / **`agent-web`** [命令] [选项]。
3
+ * 命令行入口(ADR-0031):`oh-my-agent-web` / 简写 **`aw`** / **`agent-web`** [命令] [选项]。
4
4
  * 三个 bin 指向同一个文件,完全等价(`agent-web` 给不想记缩写的人用)。
5
5
  *
6
6
  * 命令:start(默认)/ stop / restart / status / version / upgrade / help
@@ -15,8 +15,11 @@
15
15
  * -P, --password <pwd> AGENT_WEB_PASSWORD 非本机访问时的登录密码
16
16
  * --allow-host <h> AGENT_WEB_ALLOWED_HOSTS 额外允许的域名(可重复)
17
17
  * --data-dir <dir> AGENT_WEB_DATA_DIR 默认 ~/.oh-my-agent-web
18
- * -d, --detach 启动后转入后台(关掉终端也继续跑)
18
+ * -f, --foreground 前台运行(日志直接在终端,Ctrl-C 退出)
19
+ * -d, --detach 后台运行(**现在就是默认**,留着给老脚本)
20
+ * --no-open 不要在浏览器里打开页面
19
21
  *
22
+ * 默认行为:**后台起来 + 打开浏览器**(终端立刻还给你);`-f` 才留在前台。
20
23
  * 后台模式只是「spawn 一个脱离终端的自己 + 等它写好 server.lock」:
21
24
  * 真正的事实源仍然是锁文件,所以 stop / restart 对前台、后台一视同仁。
22
25
  *
@@ -34,13 +37,13 @@ import { migrateLegacyDataDir } from "./util/data-dir.js";
34
37
  import { DEFAULT_DATA_DIR_NAME } from "./util/env.js";
35
38
  import { fetchHealth, showStatus, showVersion, upgrade } from "./cli-info.js";
36
39
  import { defaultIsAlive, readLock } from "./session/lock.js";
37
- const USAGE = `用法: agent-web [命令] [选项] (agent-web / omaw / oh-my-agent-web 三个命令完全等价)
40
+ const USAGE = `用法: agent-web [命令] [选项] (agent-web / aw / oh-my-agent-web 三个命令完全等价)
38
41
 
39
42
  命令:
40
- start 启动(默认;前台运行,Ctrl-C 退出)
41
- start -d 启动并转入后台(关掉终端也继续跑,日志写到 <dataDir>/daemon.log)
43
+ start 启动(默认;后台运行并打开页面,终端立刻还给你)
44
+ start -f 前台运行(日志直接打在终端,Ctrl-C 退出)
42
45
  stop 停止正在跑的实例(读 dataDir/server.lock 里的 pid)
43
- restart 先停再起(带 -d 则后台起)
46
+ restart 先停再起(默认仍后台;带 -f 则前台)
44
47
  status 看状态:pid / 端口 / 已运行多久 / 引擎 / 会话数 / 工作区
45
48
  version 看版本号(-v / --version 同义)
46
49
  upgrade 升级全局安装(npm i -g <包名>@latest;private 包会拒绝并提示怎么做)
@@ -52,20 +55,25 @@ const USAGE = `用法: agent-web [命令] [选项] (agent-web / omaw /
52
55
  -P, --password <pwd> 访问密码 AGENT_WEB_PASSWORD
53
56
  --allow-host <h> 额外允许的域名 AGENT_WEB_ALLOWED_HOSTS(可多次 / 逗号分隔)
54
57
  --data-dir <dir> 数据目录 AGENT_WEB_DATA_DIR(默认 ~/.oh-my-agent-web)
55
- -d, --detach 启动后转入后台(日志写到 <dataDir>/daemon.log)
58
+ -f, --foreground 前台运行(Ctrl-C 退出)
59
+ -d, --detach 后台运行(现在就是默认,留着给老脚本)
60
+ --no-open 不要在浏览器里打开页面
56
61
 
57
62
  没给 --password / --allow-host 时,会读数据目录里的 password / allowed-hosts 兜底
58
- (设一次就一直在,重启不必重复输入)。
63
+ (设一次就一直在,重启不必重复输入)。远程第一次打开时若还没设密码,页面会让你先设一个。
59
64
 
60
65
  例:
61
- omaw -d -H 0.0.0.0 -P 你的密码 --allow-host agent.example.com # 后台起,供外网访问
62
- omaw status && omaw restart -d # 看完状态再重启
63
- omaw stop # 停掉(前台/后台都行)`;
64
- export function parseArgs(argv) {
66
+ aw # 后台起 + 打开页面
67
+ aw -f # 前台起(Ctrl-C 退出)
68
+ aw -H 0.0.0.0 --allow-host agent.example.com # 后台起,供外网访问(首次打开会先让你设密码)
69
+ aw status && aw restart # 看完状态再重启
70
+ aw stop # 停掉(前台/后台都行)`;
71
+ function parseArgs(argv) {
65
72
  const env = {};
66
73
  let command = "start";
67
74
  let help = false;
68
- let detach = false;
75
+ let foreground = false;
76
+ let open = true;
69
77
  for (let i = 0; i < argv.length; i += 1) {
70
78
  const arg = argv[i] ?? "";
71
79
  const takeValue = () => {
@@ -86,7 +94,13 @@ export function parseArgs(argv) {
86
94
  else if (arg === "-h" || arg === "--help" || arg === "help")
87
95
  help = true;
88
96
  else if (arg === "-d" || arg === "--detach" || arg === "--daemon")
89
- detach = true;
97
+ foreground = false;
98
+ else if (arg === "-f" || arg === "--foreground" || arg === "--fg")
99
+ foreground = true;
100
+ else if (arg === "--no-open")
101
+ open = false;
102
+ else if (arg === "--open")
103
+ open = true;
90
104
  else if (arg === "-p" || arg === "--port")
91
105
  env.AGENT_WEB_PORT = takeValue();
92
106
  else if (arg === "-H" || arg === "--host")
@@ -104,7 +118,7 @@ export function parseArgs(argv) {
104
118
  else
105
119
  throw new Error(`未知命令:${arg}`);
106
120
  }
107
- return { command, env, help, detach };
121
+ return { command, env, help, foreground, open };
108
122
  }
109
123
  function dataDir() {
110
124
  const fromEnv = process.env.AGENT_WEB_DATA_DIR;
@@ -223,12 +237,24 @@ async function waitForHttp(host, port, deadline) {
223
237
  }
224
238
  return false;
225
239
  }
226
- /** 拼出「停止」的准确命令:沿用本次参数(去掉 -d 与命令词),密码一律打成 *** */
240
+ /** 拼出「停止」的准确命令:沿用本次参数(去掉前后台/开页开关与命令词),密码一律打成 *** */
227
241
  function stopHint(rawArgv) {
228
242
  const rest = [];
243
+ const IGNORED = new Set([
244
+ "-d",
245
+ "--detach",
246
+ "--daemon",
247
+ "-f",
248
+ "--foreground",
249
+ "--fg",
250
+ "--open",
251
+ "--no-open",
252
+ "start",
253
+ "restart",
254
+ ]);
229
255
  for (let i = 0; i < rawArgv.length; i += 1) {
230
256
  const arg = rawArgv[i] ?? "";
231
- if (["-d", "--detach", "--daemon", "start", "restart"].includes(arg))
257
+ if (IGNORED.has(arg))
232
258
  continue;
233
259
  // 行命令会留在终端滚动区和 shell 历史里,密码不能跟着回显
234
260
  if (arg === "-P" || arg === "--password") {
@@ -243,10 +269,65 @@ function stopHint(rawArgv) {
243
269
  const prefix = base === "cli.js" ? `node ${entry}` : base;
244
270
  return [prefix, "stop", ...rest].join(" ");
245
271
  }
246
- async function startDetached(rawArgv) {
272
+ /** 后台子进程要带上的开关(它自己不能再往后台上套娃,也不要再开一次浏览器) */
273
+ const CHILD_FLAGS = ["-f", "--no-open"];
274
+ /** 子进程要把这些去掉:前后台/开页开关 + 命令词(它只需要「起」,不需要再重启一次) */
275
+ const PROCESS_FLAGS = new Set([
276
+ "-d",
277
+ "--detach",
278
+ "--daemon",
279
+ "-f",
280
+ "--foreground",
281
+ "--fg",
282
+ "--open",
283
+ "--no-open",
284
+ "start",
285
+ "restart",
286
+ ]);
287
+ /**
288
+ * 打开系统浏览器。
289
+ *
290
+ * 什么时候**不**开:`--no-open`、没有终端(CI / 被别的程序拉起)、Linux 上没有图形会话。
291
+ * 打不开也不报错 —— URL 已经打在终端里了,复制一下就能用。
292
+ */
293
+ function openBrowser(url) {
294
+ if (!process.stdout.isTTY || process.env.CI)
295
+ return;
296
+ try {
297
+ if (process.platform === "darwin") {
298
+ spawn("open", [url], { stdio: "ignore", detached: true }).unref();
299
+ }
300
+ else if (process.platform === "win32") {
301
+ // `start` 的第一个参数是窗口标题,必须留一个空串,否则带引号的 URL 会被当成标题
302
+ spawn("cmd", ["/c", "start", "", url], { stdio: "ignore", detached: true, windowsHide: true }).unref();
303
+ }
304
+ else if (process.env.DISPLAY || process.env.WAYLAND_DISPLAY) {
305
+ spawn("xdg-open", [url], { stdio: "ignore", detached: true }).unref();
306
+ }
307
+ }
308
+ catch {
309
+ // 没装 xdg-open / 权限不够:忽略
310
+ }
311
+ }
312
+ /** 前台模式:等服务真的在听,再开浏览器(起不来就不开,日志里已经有原因了) */
313
+ function openWhenReady() {
314
+ const probeHost = probeHostOf();
315
+ const deadline = Date.now() + 20_000;
316
+ void (async () => {
317
+ while (Date.now() < deadline) {
318
+ const lock = await readLock(dataDir());
319
+ if (lock && (await fetchHealth(probeHost, lock.port, 1000))) {
320
+ openBrowser(`http://${probeHost}:${lock.port}`);
321
+ return;
322
+ }
323
+ await sleep(200);
324
+ }
325
+ })();
326
+ }
327
+ async function startDetached(rawArgv, open) {
247
328
  const script = fileURLToPath(import.meta.url);
248
- // 把自己再起一遍,但去掉 -d(否则子进程会继续往后台上套娃)
249
- const args = rawArgv.filter((arg) => arg !== "-d" && arg !== "--detach" && arg !== "--daemon");
329
+ // 把自己再起一遍,但去掉前后台/开页开关 —— 子进程固定「前台 + 不开页」(开页是父进程的事)
330
+ const args = [...rawArgv.filter((arg) => !PROCESS_FLAGS.has(arg)), ...CHILD_FLAGS];
250
331
  const log = daemonLogPath();
251
332
  mkdirSync(dirname(log), { recursive: true });
252
333
  const fd = openSync(log, "a");
@@ -279,6 +360,8 @@ async function startDetached(rawArgv) {
279
360
  console.log(` → 打开 http://${probeHost}:${lock.port}`);
280
361
  console.log(` → 日志 ${log}`);
281
362
  console.log(` → 停止:${stopHint(rawArgv)}`);
363
+ if (open)
364
+ openBrowser(`http://${probeHost}:${lock.port}`);
282
365
  return;
283
366
  }
284
367
  }
@@ -330,11 +413,13 @@ async function main() {
330
413
  // 等端口真正释放再起,否则会撞 EADDRINUSE
331
414
  await sleep(400);
332
415
  }
333
- // start / restart:默认**在前台**跑(日志与退出码都在同一条线上)
334
- if (parsed.detach) {
335
- await startDetached(process.argv.slice(2));
416
+ // start / restart:默认**后台**跑(终端立刻还给你),`-f` 才留在前台
417
+ if (!parsed.foreground) {
418
+ await startDetached(process.argv.slice(2), parsed.open);
336
419
  return;
337
420
  }
421
+ if (parsed.open)
422
+ openWhenReady();
338
423
  await import("./index.js");
339
424
  }
340
425
  // 被当脚本执行时才 main(被 import 时什么都不做)
@@ -1,9 +1,19 @@
1
+ import { createClaudeEngine } from "./claude.js";
2
+ import { probeCliVersion, probePackageVersion } from "./cli-version.js";
1
3
  import { createCodexEngine } from "./codex.js";
2
4
  import { createDshEngine } from "./dsh.js";
3
5
  import { createFakeEngine } from "./fake.js";
4
6
  import { createOpencodeEngine } from "./opencode.js";
5
7
  import { createPiSdkEngine } from "./pi-sdk.js";
8
+ import { piSdkVersion } from "./pi-version.js";
6
9
  import { createEngineRegistry, FAKE_CAPABILITIES, PI_CAPABILITIES } from "./registry.js";
10
+ /**
11
+ * ACP 适配器(claude-code-acp)的版本:它不认 `--version`(直接去握手),
12
+ * 所以读 bin 旁边的 package.json —— 与握手给的 `agentInfo.version` 是同一个数。
13
+ */
14
+ function claudeVersionOf(bin) {
15
+ return Promise.resolve(probePackageVersion(bin ?? "claude-code-acp"));
16
+ }
7
17
  export function createBuiltinEngineRegistry(options) {
8
18
  return createEngineRegistry([
9
19
  {
@@ -12,6 +22,8 @@ export function createBuiltinEngineRegistry(options) {
12
22
  badge: "PI",
13
23
  description: "完整插件与技能生态、工具延迟加载、审批门禁、目标模式与子代理",
14
24
  providerConfig: true, // 模型 / provider 配置由本工作台管理(models-store.json + auth.json)
25
+ // 版本同步就有(SDK 导出的 VERSION),不用探测
26
+ version: () => piSdkVersion(),
15
27
  capabilities: PI_CAPABILITIES,
16
28
  create: () => createPiSdkEngine({
17
29
  onNotice: (text, level) => {
@@ -19,6 +31,7 @@ export function createBuiltinEngineRegistry(options) {
19
31
  options.onNotice(text, level);
20
32
  },
21
33
  ...(options.skillPaths ? { skillPaths: options.skillPaths } : {}),
34
+ ...(options.thinkCount ? { thinkCount: options.thinkCount } : {}),
22
35
  }),
23
36
  },
24
37
  ...(options.codex
@@ -30,6 +43,8 @@ export function createBuiltinEngineRegistry(options) {
30
43
  // 能力如实写:审批是命令/文件级的(比 pi 粗一档),没有 provider 面板
31
44
  description: options.codex.description ?? "codex 官方 agent:独立 loop 与沙箱,会话与 Codex Desktop / CLI 共用",
32
45
  providerConfig: false,
46
+ // 版本号跟本机的 codex CLI 走(`codex --version` → `codex-cli 0.162.1`)
47
+ version: () => probeCliVersion(options.codex?.bin ?? "codex"),
33
48
  // steer: false —— 底层(JSON-RPC / ACP)没有「插进正在跑的这一轮」的语义,宿主会降级成排队
34
49
  capabilities: {
35
50
  approvals: true,
@@ -38,8 +53,19 @@ export function createBuiltinEngineRegistry(options) {
38
53
  thinkingLevel: true,
39
54
  resume: true,
40
55
  steer: false,
56
+ // 真回合探针:临时 thread 发一句 ping,量 TTFT / 出字速度(不偷 codex 的凭证,见 port.ts)
57
+ modelProbe: true,
58
+ // codex 自带多代理(spawn_agent 那一套):显示成面板卡片 + 按会话注入模板
59
+ subagents: true,
60
+ // 它的隔离是「沙箱 + 审批」,不是逐工具白名单 → 模板里的工具勾选对 codex 不生效
61
+ subagentToolWhitelist: false,
41
62
  },
42
- create: () => createCodexEngine({ client: options.codex?.client, log: options.onLog }),
63
+ create: () => createCodexEngine({
64
+ client: options.codex?.client,
65
+ log: options.onLog,
66
+ ...(options.codex?.subagentPolicy ? { subagentPolicy: options.codex.subagentPolicy } : {}),
67
+ ...(options.thinkCount ? { thinkCount: options.thinkCount } : {}),
68
+ }),
43
69
  },
44
70
  ]
45
71
  : []),
@@ -52,6 +78,8 @@ export function createBuiltinEngineRegistry(options) {
52
78
  description: options.dsh.description ??
53
79
  "DeepSeek 官方 agent(ACP):模型与思考档可切、权限逐次确认,会话与 dsh web/tui 共用",
54
80
  providerConfig: false,
81
+ // 版本号跟本机的 dsh CLI 走(`dsh --version` → `0.2.0-rc.2`)
82
+ version: () => probeCliVersion(options.dsh?.bin ?? "dsh"),
55
83
  // steer: false —— 底层(JSON-RPC / ACP)没有「插进正在跑的这一轮」的语义,宿主会降级成排队
56
84
  capabilities: {
57
85
  approvals: true,
@@ -75,6 +103,8 @@ export function createBuiltinEngineRegistry(options) {
75
103
  description: options.opencode.description ??
76
104
  "opencode 官方 agent(ACP):自带工具与 MCP/插件生态,模型与思考档可切,会话与 opencode CLI / TUI 共用;审批取决于 opencode 自身 permission 配置",
77
105
  providerConfig: false,
106
+ // 版本号跟本机的 opencode CLI 走(`opencode --version` → `1.19.0`)
107
+ version: () => probeCliVersion(options.opencode?.bin ?? "opencode"),
78
108
  // 没有子代理面板(ACP 不暴露 task 子代理的运行视图)→ subagents 不写,界面就不出现那块
79
109
  // steer: false —— 底层(JSON-RPC / ACP)没有「插进正在跑的这一轮」的语义,宿主会降级成排队
80
110
  capabilities: {
@@ -95,12 +125,45 @@ export function createBuiltinEngineRegistry(options) {
95
125
  },
96
126
  ]
97
127
  : []),
128
+ ...(options.claude
129
+ ? [
130
+ {
131
+ name: "claude",
132
+ label: "Claude Code",
133
+ // 短标签沿用 DSH / OC 的风格:长名字会把顶栏那枚胶囊撑开
134
+ badge: "CC",
135
+ // 能力如实写:模型可切(session/set_model),**没有思考档**(协议不暴露),没有子代理面板
136
+ description: options.claude.description ??
137
+ "Claude Code(ACP):Anthropic 官方 agent,模型可切、工具逐次确认,会话与 claude CLI 共用;思考档与子代理面板不暴露",
138
+ providerConfig: false,
139
+ // 版本 = ACP 适配器(claude-code-acp)的版本,与握手给的 "agentInfo.version" 一致
140
+ version: () => claudeVersionOf(options.claude?.bin),
141
+ // steer: false —— 底层(JSON-RPC / ACP)没有「插进正在跑的这一轮」的语义,宿主会降级成排队
142
+ capabilities: {
143
+ approvals: true,
144
+ tools: true,
145
+ modelSwitch: true,
146
+ thinkingLevel: false, // ACP 面里 Claude 不暴露 thinking/effort
147
+ resume: true,
148
+ steer: false,
149
+ },
150
+ create: () => createClaudeEngine({
151
+ client: options.claude?.client,
152
+ bin: options.claude?.bin,
153
+ args: options.claude?.args,
154
+ model: options.claude?.model,
155
+ log: options.onLog,
156
+ }),
157
+ },
158
+ ]
159
+ : []),
98
160
  {
99
161
  name: "fake",
100
162
  label: "假引擎",
101
163
  badge: "FAKE",
102
164
  description: "零 token 的脚本化引擎:没有真实模型,只用来验证协议、工具与审批流程",
103
165
  providerConfig: false,
166
+ version: () => "fake",
104
167
  hidden: true, // 测试引擎:AGENT_WEB_ENGINE=fake 仍可选,但不进普通用户的切换面板
105
168
  capabilities: FAKE_CAPABILITIES,
106
169
  create: () => createFakeEngine(),
@@ -0,0 +1,34 @@
1
+ /**
2
+ * 「模型目录」探针(dsh / opencode / claude 三个 ACP 引擎共用)。
3
+ *
4
+ * 为什么需要它:这三个引擎**只在 `session/new` / `session/resume` 的返回里**给出模型表
5
+ * (opencode / dsh 是 `configOptions`,claude 是 `models`),没有会话就问不到。
6
+ * 于是刚切到引擎、还没开会话时,模型下拉是空的 —— 界面上就是「模型未就绪」,
7
+ * 用户以为功能坏了(实测复现:`prewarm` 只连上子进程,`listModels()` 回 0 条;
8
+ * 建完会话立刻变成 219 / 3 / 5 条)。
9
+ *
10
+ * 做法:在**临时目录**里开一个一次性会话,把目录抄下来,然后立刻关掉并删掉:
11
+ * - 临时目录由 `isScratchCwd` 判定 → 这些探针会话不会进侧栏(三个引擎的清单都按这条过滤);
12
+ * - 删掉是免得在引擎自己的历史里留一条空会话(claude 删 JSONL、opencode 有 `session delete`、
13
+ * dsh 删会话目录,各自见调用处)。
14
+ *
15
+ * 代价(如实记在这里,免得以后有人以为它是免费的):
16
+ * - 每个引擎实例**只探一次**(`reloadModels` / `dispose` 会重置),不是每帧都建会话;
17
+ * - 时间:claude / dsh ~1s,opencode 的 `session/new` 要 4~6s(它自己启动就要扫配置),
18
+ * 所以这一步放在切引擎的那条路径上(界面本来就在等),不放在发消息的路径上。
19
+ */
20
+ import { mkdtemp, rm } from "node:fs/promises";
21
+ import { tmpdir } from "node:os";
22
+ import { join } from "node:path";
23
+ /** 探针目录前缀(`isScratchCwd` 判定 → 侧栏不显示这类目录) */
24
+ const PROBE_CWD_PREFIX = "agent-web-models-";
25
+ /** 在临时工作区里跑一段逻辑,跑完把目录删掉(失败也删) */
26
+ export async function withProbeCwd(run) {
27
+ const cwd = await mkdtemp(join(tmpdir(), PROBE_CWD_PREFIX));
28
+ try {
29
+ return await run(cwd);
30
+ }
31
+ finally {
32
+ await rm(cwd, { recursive: true, force: true }).catch(() => undefined);
33
+ }
34
+ }