@rho-workspace/rho 0.5.3 → 0.5.5

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 (277) hide show
  1. package/README.md +24 -5
  2. package/dist/browser-access.js +3 -2
  3. package/dist/capabilities.d.ts +29 -2
  4. package/dist/capabilities.js +79 -2
  5. package/dist/cli.js +237 -99
  6. package/dist/contract.generated.d.ts +441 -16
  7. package/dist/contract.generated.js +1 -1
  8. package/dist/contracts/owners.generated.js +459 -10
  9. package/dist/conversation-presentation.d.ts +3 -1
  10. package/dist/conversation-presentation.js +16 -6
  11. package/dist/conversations.d.ts +10 -6
  12. package/dist/conversations.js +17 -13
  13. package/dist/discovery.d.ts +1 -0
  14. package/dist/discovery.js +3 -3
  15. package/dist/extension-contract.d.ts +99 -0
  16. package/dist/extension-contract.js +52 -0
  17. package/dist/extension-directory.d.ts +9 -0
  18. package/dist/extension-directory.js +11 -0
  19. package/dist/extension-worker.d.ts +1 -0
  20. package/dist/extension-worker.js +36 -0
  21. package/dist/extensions.d.ts +157 -0
  22. package/dist/extensions.js +349 -0
  23. package/dist/installs.d.ts +11 -0
  24. package/dist/installs.js +179 -0
  25. package/dist/plugin-cli.d.ts +1 -0
  26. package/dist/plugin-cli.js +139 -0
  27. package/dist/plugin-inventory.json +18 -0
  28. package/dist/plugins.d.ts +3 -3
  29. package/dist/project-notes.js +2 -0
  30. package/dist/project-work.d.ts +3 -1
  31. package/dist/project-work.js +15 -2
  32. package/dist/protocol.d.ts +7 -0
  33. package/dist/protocol.js +11 -7
  34. package/dist/screen-check-skill.d.ts +2 -0
  35. package/dist/screen-check-skill.js +39 -0
  36. package/dist/server.d.ts +6 -1
  37. package/dist/server.js +105 -15
  38. package/dist/web/.vite/manifest.json +65 -65
  39. package/dist/web/assets/{canvas-BXhC8RDZ.js → canvas-D8rDdfDz.js} +1 -1
  40. package/dist/web/assets/choices-b0XhrGQV.js +1 -0
  41. package/dist/web/assets/{console-e1P5qYQ5.js → console-BSjLGe0T.js} +1 -1
  42. package/dist/web/assets/{dist-Dd3iAGFy.js → dist-Buk0lwzI.js} +2 -2
  43. package/dist/web/assets/dist-Cw8hRIlT.js +1 -0
  44. package/dist/web/assets/dist-Tzoe_zm-.js +3 -0
  45. package/dist/web/assets/{dist-DEorbOW0.js → dist-foH9IKc6.js} +1 -1
  46. package/dist/web/assets/explore-in-BVUy3ZW_.js +1 -0
  47. package/dist/web/assets/graph-1OdIJ9l7.js +2 -0
  48. package/dist/web/assets/index-CsC3sTRq.css +1 -0
  49. package/dist/web/assets/{index-BYuKpyoi.js → index-CxmItXqa.js} +220 -121
  50. package/dist/web/assets/{notes-meaning-B3HFbh84.js → notes-meaning-pVbbQUGP.js} +1 -1
  51. package/dist/web/assets/overview-B9UtVJ32.js +6 -0
  52. package/dist/web/assets/preview-CksD_5KS.js +1 -0
  53. package/dist/web/assets/{review-BVh9jWMB.js → review-CMxQlQ-C.js} +1 -1
  54. package/dist/web/assets/{skills-DPaEYkln.js → skills-BFNSyrnD.js} +1 -1
  55. package/dist/web/assets/{skills-work-dvjfy_tu.js → skills-work-BGPSnF_I.js} +1 -1
  56. package/dist/web/assets/{studio-process-BYm8mjRJ.js → studio-process-CsRoTHOh.js} +2 -2
  57. package/dist/web/assets/{timeline-events-CDZ7wiJX.js → timeline-events-BbuuFWHV.js} +1 -1
  58. package/dist/web/assets/{work-turns-BeCt303c.js → work-turns-BvJlIMmA.js} +1 -1
  59. package/dist/web/assets/workspace-CWlkjLNx.js +5 -0
  60. package/dist/web/composition.json +80 -80
  61. package/dist/web/index.html +3 -3
  62. package/dist/web/renderer.json +1 -1
  63. package/dist/web-assets.d.ts +2 -1
  64. package/dist/web-assets.js +3 -2
  65. package/dist/work-contract.js +2 -2
  66. package/docs/FRONTEND.md +30 -14
  67. package/docs/HEADLESS.md +60 -14
  68. package/docs/PLUGIN-AUTHORING.md +4 -0
  69. package/docs/THIRD-PARTY-PLUGINS.md +190 -0
  70. package/example/pa14-qs/.rho/rho/conversations/c-67f77cfebaba2cea.json +1 -1
  71. package/example/pa14-qs/.rho/rho/conversations/c-91aa2e46b1ac1a6c.json +1 -1
  72. package/example/pa14-qs/.rho/rho/conversations/c-fa24dfd80dfe4bcb.json +1 -1
  73. package/node_modules/@hono/node-server/dist/index.js +11 -0
  74. package/node_modules/@hono/node-server/dist/index.mjs +11 -0
  75. package/node_modules/@hono/node-server/dist/listener.js +11 -0
  76. package/node_modules/@hono/node-server/dist/listener.mjs +11 -0
  77. package/node_modules/@hono/node-server/dist/request.js +11 -0
  78. package/node_modules/@hono/node-server/dist/request.mjs +11 -0
  79. package/node_modules/@hono/node-server/dist/server.js +11 -0
  80. package/node_modules/@hono/node-server/dist/server.mjs +11 -0
  81. package/node_modules/@hono/node-server/dist/vercel.js +11 -0
  82. package/node_modules/@hono/node-server/dist/vercel.mjs +11 -0
  83. package/node_modules/@hono/node-server/package.json +1 -1
  84. package/node_modules/@rho-workspace/plugin-agent/README.md +42 -1
  85. package/node_modules/@rho-workspace/plugin-agent/dist/acp-conversation.js +14 -3
  86. package/node_modules/@rho-workspace/plugin-agent/dist/activity-normalize.d.ts +1 -0
  87. package/node_modules/@rho-workspace/plugin-agent/dist/activity-normalize.js +2 -2
  88. package/node_modules/@rho-workspace/plugin-agent/dist/conversation.d.ts +1 -0
  89. package/node_modules/@rho-workspace/plugin-agent/dist/conversation.js +1 -0
  90. package/node_modules/@rho-workspace/plugin-agent/dist/dsh-session.d.ts +6 -0
  91. package/node_modules/@rho-workspace/plugin-agent/dist/dsh-session.js +174 -0
  92. package/node_modules/@rho-workspace/plugin-agent/dist/external-session-contract.d.ts +82 -0
  93. package/node_modules/@rho-workspace/plugin-agent/dist/external-session-contract.js +15 -0
  94. package/node_modules/@rho-workspace/plugin-agent/dist/external-session.d.ts +73 -0
  95. package/node_modules/@rho-workspace/plugin-agent/dist/external-session.js +190 -0
  96. package/node_modules/@rho-workspace/plugin-agent/dist/generic-acp.d.ts +11 -0
  97. package/node_modules/@rho-workspace/plugin-agent/dist/generic-acp.js +7 -0
  98. package/node_modules/@rho-workspace/plugin-agent/dist/index.d.ts +373 -0
  99. package/node_modules/@rho-workspace/plugin-agent/dist/index.js +1 -0
  100. package/node_modules/@rho-workspace/plugin-agent/dist/mcp.js +5 -1
  101. package/node_modules/@rho-workspace/plugin-agent/dist/operations.d.ts +33 -0
  102. package/node_modules/@rho-workspace/plugin-agent/dist/operations.js +3 -0
  103. package/node_modules/@rho-workspace/plugin-agent/dist/presentation.d.ts +14 -0
  104. package/node_modules/@rho-workspace/plugin-agent/dist/presentation.js +2 -0
  105. package/node_modules/@rho-workspace/plugin-agent/dist/ui-external-session.d.ts +15 -0
  106. package/node_modules/@rho-workspace/plugin-agent/dist/ui-external-session.js +37 -0
  107. package/node_modules/@rho-workspace/plugin-agent/package.json +5 -1
  108. package/node_modules/@rho-workspace/plugin-annotations/package.json +1 -1
  109. package/node_modules/@rho-workspace/plugin-documents/README.md +4 -0
  110. package/node_modules/@rho-workspace/plugin-documents/dist/index.d.ts +3 -0
  111. package/node_modules/@rho-workspace/plugin-documents/dist/index.js +1 -1
  112. package/node_modules/@rho-workspace/plugin-documents/package.json +1 -1
  113. package/node_modules/@rho-workspace/plugin-kernel/README.md +71 -10
  114. package/node_modules/@rho-workspace/plugin-kernel/dist/code.d.ts +2 -0
  115. package/node_modules/@rho-workspace/plugin-kernel/dist/code.js +4 -1
  116. package/node_modules/@rho-workspace/plugin-kernel/dist/contract.js +5 -0
  117. package/node_modules/@rho-workspace/plugin-kernel/dist/index.d.ts +7 -0
  118. package/node_modules/@rho-workspace/plugin-kernel/dist/index.js +55 -8
  119. package/node_modules/@rho-workspace/plugin-kernel/dist/lineage.d.ts +9 -6
  120. package/node_modules/@rho-workspace/plugin-kernel/dist/lineage.js +18 -41
  121. package/node_modules/@rho-workspace/plugin-kernel/dist/operations.js +5 -2
  122. package/node_modules/@rho-workspace/plugin-kernel/dist/script-flow.d.ts +39 -0
  123. package/node_modules/@rho-workspace/plugin-kernel/dist/script-flow.js +661 -0
  124. package/node_modules/@rho-workspace/plugin-kernel/dist/ui-base.d.ts +3 -0
  125. package/node_modules/@rho-workspace/plugin-kernel/dist/ui-base.js +10 -0
  126. package/node_modules/@rho-workspace/plugin-kernel/dist/ui-flow.d.ts +38 -4
  127. package/node_modules/@rho-workspace/plugin-kernel/dist/ui-flow.js +194 -97
  128. package/node_modules/@rho-workspace/plugin-kernel/dist/ui-notebook-work.js +1 -2
  129. package/node_modules/@rho-workspace/plugin-kernel/dist/ui-notebook.d.ts +10 -2
  130. package/node_modules/@rho-workspace/plugin-kernel/dist/ui-notebook.js +79 -18
  131. package/node_modules/@rho-workspace/plugin-kernel/dist/ui-styles.js +115 -45
  132. package/node_modules/@rho-workspace/plugin-kernel/dist/ui.d.ts +1 -1
  133. package/node_modules/@rho-workspace/plugin-kernel/dist/ui.js +1 -1
  134. package/node_modules/@rho-workspace/plugin-kernel/dist/value-graph.d.ts +63 -0
  135. package/node_modules/@rho-workspace/plugin-kernel/dist/value-graph.js +344 -0
  136. package/node_modules/@rho-workspace/plugin-kernel/package.json +1 -1
  137. package/node_modules/@rho-workspace/plugin-kernel/runners/runner.R +6 -3
  138. package/node_modules/@rho-workspace/plugin-kernel/runners/runner.py +6 -3
  139. package/node_modules/express-rate-limit/dist/index.cjs +1 -1
  140. package/node_modules/express-rate-limit/dist/index.mjs +1 -1
  141. package/node_modules/express-rate-limit/package.json +1 -1
  142. package/node_modules/ip-address/dist/ipv6.js +1 -1
  143. package/node_modules/ip-address/package.json +1 -1
  144. package/node_modules/playwright-core/LICENSE +202 -0
  145. package/node_modules/playwright-core/NOTICE +5 -0
  146. package/node_modules/playwright-core/ThirdPartyNotices.txt +13 -0
  147. package/node_modules/playwright-core/bin/install_media_pack.ps1 +5 -0
  148. package/node_modules/playwright-core/bin/install_webkit_wsl.ps1 +33 -0
  149. package/node_modules/playwright-core/bin/reinstall_chrome_beta_linux.sh +42 -0
  150. package/node_modules/playwright-core/bin/reinstall_chrome_beta_mac.sh +13 -0
  151. package/node_modules/playwright-core/bin/reinstall_chrome_beta_win.ps1 +24 -0
  152. package/node_modules/playwright-core/bin/reinstall_chrome_stable_linux.sh +42 -0
  153. package/node_modules/playwright-core/bin/reinstall_chrome_stable_mac.sh +12 -0
  154. package/node_modules/playwright-core/bin/reinstall_chrome_stable_win.ps1 +24 -0
  155. package/node_modules/playwright-core/bin/reinstall_msedge_beta_linux.sh +48 -0
  156. package/node_modules/playwright-core/bin/reinstall_msedge_beta_mac.sh +11 -0
  157. package/node_modules/playwright-core/bin/reinstall_msedge_beta_win.ps1 +23 -0
  158. package/node_modules/playwright-core/bin/reinstall_msedge_dev_linux.sh +48 -0
  159. package/node_modules/playwright-core/bin/reinstall_msedge_dev_mac.sh +11 -0
  160. package/node_modules/playwright-core/bin/reinstall_msedge_dev_win.ps1 +23 -0
  161. package/node_modules/playwright-core/bin/reinstall_msedge_stable_linux.sh +48 -0
  162. package/node_modules/playwright-core/bin/reinstall_msedge_stable_mac.sh +11 -0
  163. package/node_modules/playwright-core/bin/reinstall_msedge_stable_win.ps1 +24 -0
  164. package/node_modules/playwright-core/browsers.json +52 -0
  165. package/node_modules/playwright-core/cli.js +22 -0
  166. package/node_modules/playwright-core/index.js +17 -0
  167. package/node_modules/playwright-core/index.mjs +28 -0
  168. package/node_modules/playwright-core/lib/bootstrap.js +149 -0
  169. package/node_modules/playwright-core/lib/coreBundle.js +78041 -0
  170. package/node_modules/playwright-core/lib/entry/cliDaemon.js +5 -0
  171. package/node_modules/playwright-core/lib/entry/dashboardApp.js +3 -0
  172. package/node_modules/playwright-core/lib/entry/mcp.js +10 -0
  173. package/node_modules/playwright-core/lib/entry/oopBrowserDownload.js +3 -0
  174. package/node_modules/playwright-core/lib/package.js +50 -0
  175. package/node_modules/playwright-core/lib/server/chromium/appIcon.png +0 -0
  176. package/node_modules/playwright-core/lib/server/electron/loader.js +123 -0
  177. package/node_modules/playwright-core/lib/serverRegistry.js +1939 -0
  178. package/node_modules/playwright-core/lib/serverRegistry.js.LICENSE +65 -0
  179. package/node_modules/playwright-core/lib/tools/cli-client/channelSessions.js +141 -0
  180. package/node_modules/playwright-core/lib/tools/cli-client/cli.js +7 -0
  181. package/node_modules/playwright-core/lib/tools/cli-client/help.json +808 -0
  182. package/node_modules/playwright-core/lib/tools/cli-client/minimist.js +128 -0
  183. package/node_modules/playwright-core/lib/tools/cli-client/output.js +352 -0
  184. package/node_modules/playwright-core/lib/tools/cli-client/program.js +422 -0
  185. package/node_modules/playwright-core/lib/tools/cli-client/registry.js +180 -0
  186. package/node_modules/playwright-core/lib/tools/cli-client/session.js +261 -0
  187. package/node_modules/playwright-core/lib/tools/dashboard/appIcon.png +0 -0
  188. package/node_modules/playwright-core/lib/tools/utils/extension.js +112 -0
  189. package/node_modules/playwright-core/lib/tools/utils/socketConnection.js +108 -0
  190. package/node_modules/playwright-core/lib/utilsBundle.js +86078 -0
  191. package/node_modules/playwright-core/lib/utilsBundle.js.LICENSE +1890 -0
  192. package/node_modules/playwright-core/lib/vite/dashboard/assets/codeMirrorModule--QdMvsKi.css +1 -0
  193. package/node_modules/playwright-core/lib/vite/dashboard/assets/codeMirrorModule-CQ8RZGBm.js +32 -0
  194. package/node_modules/playwright-core/lib/vite/dashboard/assets/codicon-DCmgc-ay.ttf +0 -0
  195. package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-1bWoP6pv.svg +1 -0
  196. package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-beta-k3eOH_eK.svg +1 -0
  197. package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-nightly-Cp5nfeDT.svg +1 -0
  198. package/node_modules/playwright-core/lib/vite/dashboard/assets/index-AwgwcYie.css +1 -0
  199. package/node_modules/playwright-core/lib/vite/dashboard/assets/index-hRvO4yU2.js +12 -0
  200. package/node_modules/playwright-core/lib/vite/dashboard/assets/safari-na3_-uQk.svg +1 -0
  201. package/node_modules/playwright-core/lib/vite/dashboard/index.html +29 -0
  202. package/node_modules/playwright-core/lib/vite/dashboard/playwright-logo.svg +24 -0
  203. package/node_modules/playwright-core/lib/vite/htmlReport/index.html +16 -0
  204. package/node_modules/playwright-core/lib/vite/htmlReport/report.css +2 -0
  205. package/node_modules/playwright-core/lib/vite/htmlReport/report.js +32 -0
  206. package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule--QdMvsKi.css +1 -0
  207. package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-DK72OMRi.js +32 -0
  208. package/node_modules/playwright-core/lib/vite/recorder/assets/codicon-DCmgc-ay.ttf +0 -0
  209. package/node_modules/playwright-core/lib/vite/recorder/assets/index-BZpYJZ-P.css +1 -0
  210. package/node_modules/playwright-core/lib/vite/recorder/assets/index-Cn0mmDuv.js +129 -0
  211. package/node_modules/playwright-core/lib/vite/recorder/index.html +29 -0
  212. package/node_modules/playwright-core/lib/vite/recorder/playwright-logo.svg +9 -0
  213. package/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-BEB2e8M3.js +32 -0
  214. package/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-D-g2vrvf.js +184 -0
  215. package/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-L3liM589.js +1 -0
  216. package/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-B794rD2Z.js +7 -0
  217. package/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.-QdMvsKi.css +1 -0
  218. package/node_modules/playwright-core/lib/vite/traceViewer/codicon.DCmgc-ay.ttf +0 -0
  219. package/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.C8BeHuxd.css +1 -0
  220. package/node_modules/playwright-core/lib/vite/traceViewer/index.B_TqY17P.css +1 -0
  221. package/node_modules/playwright-core/lib/vite/traceViewer/index.html +44 -0
  222. package/node_modules/playwright-core/lib/vite/traceViewer/index.oNry3Hjv.js +1 -0
  223. package/node_modules/playwright-core/lib/vite/traceViewer/manifest.webmanifest +16 -0
  224. package/node_modules/playwright-core/lib/vite/traceViewer/playwright-logo.svg +9 -0
  225. package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.B_Jk1wbt.js +1 -0
  226. package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.html +10 -0
  227. package/node_modules/playwright-core/lib/vite/traceViewer/sw.bundle.js +4 -0
  228. package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CYwC_gH1.js +5 -0
  229. package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CyMfwkXJ.css +1 -0
  230. package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +18 -0
  231. package/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.kHJ-D0s7.css +1 -0
  232. package/node_modules/playwright-core/lib/webp_codec.LICENSE +173 -0
  233. package/node_modules/playwright-core/lib/webp_codec.wasm +0 -0
  234. package/node_modules/playwright-core/lib/xdg-open +1267 -0
  235. package/node_modules/playwright-core/package.json +34 -0
  236. package/package.json +13 -7
  237. package/protocol/index.json +75 -21
  238. package/protocol/operations/agent.sessions.list.json +100 -0
  239. package/protocol/operations/agent.sessions.read.json +266 -0
  240. package/protocol/operations/conversation.settings.check.json +147 -4
  241. package/protocol/operations/conversation.settings.read.json +145 -3
  242. package/protocol/operations/conversation.settings.update.json +166 -4
  243. package/protocol/operations/conversation.start.json +2 -1
  244. package/protocol/operations/extension.call.json +132 -0
  245. package/protocol/operations/extension.content.json +78 -0
  246. package/protocol/operations/extension.describe.json +195 -0
  247. package/protocol/operations/extension.install.json +198 -0
  248. package/protocol/operations/extension.list.json +123 -0
  249. package/protocol/operations/extension.manage.json +55 -0
  250. package/protocol/operations/extension.read.json +112 -0
  251. package/protocol/operations/kernel.interrupt.json +20 -0
  252. package/protocol/operations/kernel.read.json +30 -0
  253. package/protocol/operations/kernel.run.json +36 -1
  254. package/protocol/operations/kernel.stop.json +10 -0
  255. package/protocol/operations/project.work.read.json +3 -2
  256. package/protocol/operations/session.grant.json +9 -0
  257. package/scripts/browser-checks.mjs +53 -0
  258. package/scripts/screen-check.mjs +193 -0
  259. package/scripts/third-party-acceptance.mjs +77 -0
  260. package/vendor/composition.json +111 -111
  261. package/vendor/rho-workspace-plugin-agent-0.4.29.tgz +0 -0
  262. package/vendor/rho-workspace-plugin-annotations-0.5.2.tgz +0 -0
  263. package/vendor/rho-workspace-plugin-documents-0.4.11.tgz +0 -0
  264. package/vendor/rho-workspace-plugin-kernel-0.5.5.tgz +0 -0
  265. package/web/theme.css +17 -2
  266. package/dist/web/assets/choices-B-y2OYvE.js +0 -1
  267. package/dist/web/assets/dist-CBM2oq6g.js +0 -1
  268. package/dist/web/assets/dist-CH56yNDn.js +0 -3
  269. package/dist/web/assets/explore-in-muWXOGOY.js +0 -1
  270. package/dist/web/assets/graph-CK-df5lh.js +0 -2
  271. package/dist/web/assets/index-DBd77u4a.css +0 -1
  272. package/dist/web/assets/overview-D1Ev2OC_.js +0 -6
  273. package/dist/web/assets/preview-HbmQKUGY.js +0 -1
  274. package/dist/web/assets/workspace-d5Hw_ClZ.js +0 -5
  275. package/vendor/rho-workspace-plugin-agent-0.4.27.tgz +0 -0
  276. package/vendor/rho-workspace-plugin-documents-0.4.10.tgz +0 -0
  277. package/vendor/rho-workspace-plugin-kernel-0.5.2.tgz +0 -0
@@ -1,3 +1,4 @@
1
+ import { installText, readInstall } from './installs.js';
1
2
  // Only an explicit Agent limit report qualifies; an unknown outcome never does.
2
3
  export function agentTurnLimit(turn) {
3
4
  return turn.state === 'failed' && (turn.result?.kind === 'error_max_turns' || turn.result?.reason === 'max_turn_requests' || /^(?:Internal error: )?Reached maximum number of turns \(\d+\)\.?$/i.test(turn.problem ?? ''));
@@ -181,6 +182,10 @@ export function toolActivity(step, location, root, key) {
181
182
  targetKind = detail || location ? 'path' : 'text';
182
183
  }
183
184
  else if (action === 'execute') {
185
+ // A command that installs packages reads as what it installed; its command stays in the details.
186
+ const installed = detail ? readInstall(detail, step.output?.text) : null;
187
+ if (installed)
188
+ return { key: key ?? step.id ?? tool + '-' + step.at, step, action, verb: outcome === 'returned' ? 'Installed' : outcome === 'requested' ? 'Installing' : 'Tried to install', target: installText(installed), targetKind: 'packages', installed, title, location, outcome };
184
189
  target = detail ? displayCommand(detail) : (title && title.toLowerCase() !== tool.toLowerCase() ? title : null);
185
190
  targetKind = detail ? 'command' : 'text';
186
191
  }
@@ -199,17 +204,22 @@ export function toolActivity(step, location, root, key) {
199
204
  return { key: key ?? step.id ?? tool + '-' + step.at, step, action, verb, target, targetKind, title, location, outcome };
200
205
  }
201
206
  // Files are counted once however often the Agent touched them: nine edits of one script
202
- // are "Edited 1 file". Other actions count each time they happened.
207
+ // are "Edited 1 file". Other actions count each time they happened. Installs are named by
208
+ // their packages, "Installed pandas, statsmodels", and are not counted as commands.
203
209
  const onFiles = new Set(['read', 'edit', 'write', 'delete', 'move']);
204
210
  export function activitySummary(activities) {
205
211
  const seen = new Map();
206
212
  activities.forEach((activity, i) => {
207
- const file = onFiles.has(activity.action) ? activity.location ?? activity.step.detail ?? activity.target : null;
208
- const named = seen.get(activity.action) ?? new Set();
209
- named.add(file ?? '#' + i);
210
- seen.set(activity.action, named);
213
+ const group = activity.installed ? activity.verb : activity.action;
214
+ const file = activity.installed ? activity.installed.packages.map(p => p.name).join('\n') || 'packages from ' + activity.installed.from
215
+ : onFiles.has(activity.action) ? activity.location ?? activity.step.detail ?? activity.target : null;
216
+ const named = seen.get(group) ?? new Set();
217
+ for (const name of activity.installed ? file.split('\n') : [file ?? '#' + i])
218
+ named.add(name);
219
+ seen.set(group, named);
211
220
  });
212
- const parts = [...seen].map(([action, named]) => summaries[action][named.size === 1 ? 0 : 1].replace('{n}', String(named.size)));
221
+ const listed = (names) => names.length > 3 ? names.slice(0, 3).join(', ') + ` and ${names.length - 3} more` : names.join(', ');
222
+ const parts = [...seen].map(([group, named]) => group in summaries ? summaries[group][named.size === 1 ? 0 : 1].replace('{n}', String(named.size)) : group + ' ' + listed([...named]));
213
223
  const count = (outcome) => activities.filter(a => a.outcome === outcome).length;
214
224
  const failed = count('failed'), refused = count('rejected'), unknown = count('unknown');
215
225
  if (failed)
@@ -41,7 +41,11 @@ interface Permission extends AgentPermission {
41
41
  }
42
42
  export interface ConversationConfig {
43
43
  settingsFile?: string;
44
- backend?: 'claude-acp' | 'codex-acp' | 'claude-cli';
44
+ backend?: 'claude-acp' | 'codex-acp' | 'claude-cli' | 'acp';
45
+ acpCommand?: {
46
+ file: string;
47
+ args: string[];
48
+ };
45
49
  codex?: string;
46
50
  codexAcp?: Omit<CodexAcpOptions, 'cwd' | 'mcpServers' | 'limits' | 'sessionId' | 'codex' | 'observation' | 'observe'>;
47
51
  claudeAcp?: Omit<ClaudeAcpOptions, 'cwd' | 'mcpServers' | 'limits' | 'sessionId' | 'claude' | 'observation' | 'observe'>;
@@ -125,7 +129,7 @@ interface FileChange {
125
129
  modifiedAt: string | null;
126
130
  bytes: number | null;
127
131
  }
128
- type Backend = 'claude-acp' | 'codex-acp';
132
+ type Backend = 'claude-acp' | 'codex-acp' | 'acp';
129
133
  type Choices = Record<string, string | boolean>;
130
134
  interface Offered {
131
135
  at: string;
@@ -406,7 +410,7 @@ export declare class Conversations {
406
410
  private sessionObservation;
407
411
  work(project: Project, limit?: number): Promise<{
408
412
  sessions: {
409
- backend: "claude-acp" | "codex-acp" | "claude-cli";
413
+ backend: "acp" | "claude-acp" | "codex-acp" | "claude-cli";
410
414
  sessionId: string | null;
411
415
  observation: SessionObservation | null;
412
416
  state: string;
@@ -574,7 +578,7 @@ export declare class Conversations {
574
578
  version: number;
575
579
  updatedAt: string | null;
576
580
  backend: Backend | null;
577
- serviceBackend: "claude-acp" | "codex-acp" | "claude-cli";
581
+ serviceBackend: "acp" | "claude-acp" | "codex-acp" | "claude-cli";
578
582
  choices: Partial<Record<Backend, Choices>>;
579
583
  offered: Partial<Record<Backend, Offered>>;
580
584
  checking: Backend[];
@@ -593,7 +597,7 @@ export declare class Conversations {
593
597
  version: number;
594
598
  updatedAt: string | null;
595
599
  backend: Backend | null;
596
- serviceBackend: "claude-acp" | "codex-acp" | "claude-cli";
600
+ serviceBackend: "acp" | "claude-acp" | "codex-acp" | "claude-cli";
597
601
  choices: Partial<Record<Backend, Choices>>;
598
602
  offered: Partial<Record<Backend, Offered>>;
599
603
  checking: Backend[];
@@ -607,7 +611,7 @@ export declare class Conversations {
607
611
  version: number;
608
612
  updatedAt: string | null;
609
613
  backend: Backend | null;
610
- serviceBackend: "claude-acp" | "codex-acp" | "claude-cli";
614
+ serviceBackend: "acp" | "claude-acp" | "codex-acp" | "claude-cli";
611
615
  choices: Partial<Record<Backend, Choices>>;
612
616
  offered: Partial<Record<Backend, Offered>>;
613
617
  checking: Backend[];
@@ -1,7 +1,7 @@
1
1
  import { projectPath, commandFiles, buildWorkGraph } from './work-graph.js';
2
2
  import { notesInstructions, projectNotesPath } from './project-notes.js';
3
3
  import { addSpend, describeLimits, describable, fileFacts, figureContext, raster } from './file-describer.js';
4
- import { agentTurnLimit, toolActivity } from './conversation-presentation.js';
4
+ import { agentTurnLimit, displayCommand, toolActivity } from './conversation-presentation.js';
5
5
  import { privateEvidencePath } from './evidence-capture.js';
6
6
  import * as fs from 'node:fs/promises';
7
7
  import path from 'node:path';
@@ -127,7 +127,7 @@ function compare(before, after, limit) {
127
127
  changed.sort((a, b) => (a.modifiedAt ?? '\uffff').localeCompare(b.modifiedAt ?? '\uffff') || a.path.localeCompare(b.path));
128
128
  return { changed: changed.slice(0, limit), omitted: Math.max(0, changed.length - limit), partial };
129
129
  }
130
- const backends = ['claude-acp', 'codex-acp'];
130
+ const backends = ['claude-acp', 'codex-acp', 'acp'];
131
131
  const blankSettings = () => ({ format: 1, version: 0, updatedAt: null, backend: null, choices: {}, offered: {} });
132
132
  const acpDriver = (driver) => !!driver && 'configure' in driver;
133
133
  const identity = /^c-[a-f0-9]{16}$/;
@@ -196,7 +196,7 @@ export class Conversations {
196
196
  this.limits = { ...conversationLimits, ...limits };
197
197
  for (const key of ['maxCaptureFiles', 'maxCaptureBytes', 'maxSnapshotBytes', 'maxCaptureConcurrent'])
198
198
  requireThat(Number.isSafeInteger(this.limits[key]) && this.limits[key] > 0 && this.limits[key] <= conversationLimits[key], 'invalid', 'Capture budgets must be positive integers no greater than the supported defaults.', { limit: key, maximum: conversationLimits[key] });
199
- requireThat(!config.backend || ['claude-acp', 'codex-acp', 'claude-cli'].includes(config.backend), 'invalid', 'Choose claude-acp, codex-acp or claude-cli.');
199
+ requireThat(!config.backend || ['claude-acp', 'codex-acp', 'claude-cli', 'acp'].includes(config.backend), 'invalid', 'Choose claude-acp, codex-acp, claude-cli or acp.');
200
200
  }
201
201
  serial(key, run) {
202
202
  const task = (this.queues.get(key) ?? Promise.resolve()).catch(() => { }).then(run);
@@ -408,7 +408,7 @@ export class Conversations {
408
408
  const saved = await this.readSettings();
409
409
  const backend = input.backend ?? saved.backend ?? this.config.backend ?? 'claude-acp';
410
410
  const config = backend === 'claude-cli' ? undefined : input.config ?? saved.choices[backend];
411
- const at = now(), c = { format: 2, backend, id: 'c-' + randomBytes(8).toString('hex'), changeId: input.changeId, requestDigest, createdAt: at, createdBy: by, updatedAt: at, version: 0, client: backend === 'codex-acp' ? 'codex' : 'claude-code', nativeSessionId: backend === 'claude-cli' ? randomUUID() : null, confirmed: false, principal: null, title: titleOf(text), turns: [], ...(config && Object.keys(config).length ? { config } : {}) };
411
+ const at = now(), c = { format: 2, backend, id: 'c-' + randomBytes(8).toString('hex'), changeId: input.changeId, requestDigest, createdAt: at, createdBy: by, updatedAt: at, version: 0, client: backend === 'codex-acp' ? 'codex' : backend === 'acp' ? 'acp' : 'claude-code', nativeSessionId: backend === 'claude-cli' ? randomUUID() : null, confirmed: false, principal: null, title: titleOf(text), turns: [], ...(config && Object.keys(config).length ? { config } : {}) };
412
412
  for (const id of input.contexts ?? [])
413
413
  await this.ports.context?.(project, id);
414
414
  const turn = this.turn(input.changeId, requestDigest, by, text, 1);
@@ -468,7 +468,7 @@ export class Conversations {
468
468
  this.choices({ [input.configId]: input.value });
469
469
  return this.serial(project.id, async () => {
470
470
  const c = await this.load(project, input.conversationId), last = c.turns.at(-1);
471
- requireThat(c.backend === 'claude-acp' || c.backend === 'codex-acp', 'unavailable', 'This conversation\'s Agent has no settings Rho can change.');
471
+ requireThat(c.backend === 'claude-acp' || c.backend === 'codex-acp' || c.backend === 'acp', 'unavailable', 'This conversation\'s Agent has no settings Rho can change.');
472
472
  requireThat(!c.trashedAt, 'conflict', 'This conversation is in the trash. Restore it to change its settings.');
473
473
  requireThat(!last || last.state !== 'running' || !this.liveTurn(project, c, last), 'conflict', 'The Agent is answering. Change its settings after this message.');
474
474
  const driver = this.drivers.get(project.id + '/' + c.id)?.driver;
@@ -558,9 +558,9 @@ export class Conversations {
558
558
  return this.settings();
559
559
  }
560
560
  async inspect(project, backend) {
561
- const { ClaudeAcpConversation, CodexAcpConversation } = await import('@rho-workspace/plugin-agent/conversation');
561
+ const { ClaudeAcpConversation, CodexAcpConversation, GenericAcpConversation } = await import('@rho-workspace/plugin-agent/conversation');
562
562
  const common = { cwd: project.root, mcpServers: [], limits: this.limits };
563
- const driver = backend === 'codex-acp' ? new CodexAcpConversation({ ...this.config.codexAcp, ...common, codex: this.config.codex }) : new ClaudeAcpConversation({ preferred: claudePreferred, ...this.config.claudeAcp, ...common, claude: this.binary });
563
+ const driver = backend === 'codex-acp' ? new CodexAcpConversation({ ...this.config.codexAcp, ...common, codex: this.config.codex }) : backend === 'acp' ? new GenericAcpConversation({ ...common, command: this.config.acpCommand }) : new ClaudeAcpConversation({ preferred: claudePreferred, ...this.config.claudeAcp, ...common, claude: this.binary });
564
564
  let found;
565
565
  try {
566
566
  found = await driver.inspect();
@@ -750,11 +750,11 @@ export class Conversations {
750
750
  const grant = await this.ports.grant(project, c);
751
751
  c.principal = grant.principal;
752
752
  const mcpServers = [{ name: 'rho', command: process.execPath, args: [fileURLToPath(import.meta.resolve('@rho-workspace/plugin-agent/mcp'))], env: [{ name: 'RHO_AGENT_CONNECTION', value: grant.file }] }];
753
- const { ClaudeAcpConversation, CodexAcpConversation, ClaudeCliConversation } = await import('@rho-workspace/plugin-agent/conversation');
753
+ const { ClaudeAcpConversation, CodexAcpConversation, ClaudeCliConversation, GenericAcpConversation } = await import('@rho-workspace/plugin-agent/conversation');
754
754
  const observed = { observation: c.observation, observe: (observation) => {
755
755
  requireThat(observation.sessionId === c.acpSessionId, 'conflict', 'The observed Agent session differs from the retained identity.');
756
756
  requireThat(Buffer.byteLength(JSON.stringify(observation)) <= this.limits.maxObservationBytes, 'limit', 'The session projection exceeded maxObservationBytes; prior observations were kept.');
757
- const config = observation.config, learned = config && config.at !== c.observation?.config?.at && config.origin === 'initial' && !c.config && (c.backend === 'claude-acp' || c.backend === 'codex-acp');
757
+ const config = observation.config, learned = config && config.at !== c.observation?.config?.at && config.origin === 'initial' && !c.config && (c.backend === 'claude-acp' || c.backend === 'codex-acp' || c.backend === 'acp');
758
758
  c.observation = observation;
759
759
  this.schedule(project, c);
760
760
  for (const live of this.live.values())
@@ -775,7 +775,9 @@ export class Conversations {
775
775
  ? new CodexAcpConversation({ ...this.config.codexAcp, ...common, codex: this.config.codex })
776
776
  : c.backend === 'claude-acp'
777
777
  ? new ClaudeAcpConversation({ preferred: claudePreferred, ...this.config.claudeAcp, ...common, claude: this.binary, plugins: [...this.config.claudeAcp?.plugins ?? [], ...await this.ports.claudePlugins?.(project) ?? []] })
778
- : new ClaudeCliConversation({ cwd: project.root, binary: this.binary, sessionId: c.nativeSessionId, confirmed: c.confirmed, projectName: project.name ?? path.basename(project.root), mcpServers, limits: this.limits, instructions });
778
+ : c.backend === 'acp'
779
+ ? new GenericAcpConversation({ ...common, command: this.config.acpCommand })
780
+ : new ClaudeCliConversation({ cwd: project.root, binary: this.binary, sessionId: c.nativeSessionId, confirmed: c.confirmed, projectName: project.name ?? path.basename(project.root), mcpServers, limits: this.limits, instructions });
779
781
  this.drivers.set(key, { driver, project, conversation: c });
780
782
  return driver;
781
783
  });
@@ -895,8 +897,9 @@ export class Conversations {
895
897
  const action = toolActivity(incoming, null, project.root);
896
898
  if (action.targetKind === 'path' && (action.location || incoming.detail))
897
899
  this.capture(live, action.location ?? incoming.detail, 'step:' + incoming.id + ':' + incoming.outcome);
898
- if (action.targetKind === 'command' && action.target)
899
- for (const file of commandFiles(action.target, project))
900
+ // A command's files are captured however its row reads (an install names its packages).
901
+ if (action.action === 'execute' && incoming.detail)
902
+ for (const file of commandFiles(displayCommand(incoming.detail), project))
900
903
  this.capture(live, file, 'command:' + incoming.id + ':' + incoming.outcome);
901
904
  }
902
905
  if (!kept)
@@ -1068,7 +1071,8 @@ export class Conversations {
1068
1071
  const given = shown.get(file);
1069
1072
  const reason = !enabled ? 'Descriptions by a model are switched off; the facts are kept.'
1070
1073
  : c.backend === 'codex-acp' ? 'Descriptions are written with Claude Code; this session\'s Agent is Codex.'
1071
- : !this.ports.describe ? 'No model is set up to describe files.' : null;
1074
+ : c.backend === 'acp' ? 'Descriptions are written with Claude Code; this session\'s Agent is a configured ACP agent.'
1075
+ : !this.ports.describe ? 'No model is set up to describe files.' : null;
1072
1076
  const keep = (why) => { if (record.reason !== why) {
1073
1077
  record.state = record.sentence ? 'described' : 'facts';
1074
1078
  record.reason = why;
@@ -1,5 +1,6 @@
1
1
  import type { Principal, Project } from './state.js';
2
2
  import type { Plugins } from './plugins.js';
3
+ export declare const applicationEffects: Record<string, string>;
3
4
  export declare class Discovery {
4
5
  plugins: Plugins;
5
6
  project: Project;
package/dist/discovery.js CHANGED
@@ -3,11 +3,11 @@ import { Capabilities, rhoWorkspaceSkill } from './capabilities.js';
3
3
  import { protocol } from './protocol.js';
4
4
  import { schemaClosure } from './protocol-discovery.js';
5
5
  const size = (v) => Buffer.byteLength(JSON.stringify(v)) + 64;
6
- const applicationEffects = Object.fromEntries([
7
- ...['protocol.describe', 'protocol.validate', 'project.entries', 'capability.list', 'skill.list', 'skill.read', 'action.lookup', 'action.observe', 'action.output', 'action.list', 'composition.read', 'observation.snapshot', 'interaction.selection.read', 'workspace.observe', 'task.map.read'].map(m => [m, 'read']),
6
+ export const applicationEffects = Object.fromEntries([
7
+ ...['extension.list', 'extension.describe', 'extension.content', 'extension.read', 'protocol.describe', 'protocol.validate', 'project.entries', 'capability.list', 'skill.list', 'skill.read', 'action.lookup', 'action.observe', 'action.output', 'action.list', 'composition.read', 'observation.snapshot', 'interaction.selection.read', 'workspace.observe', 'task.map.read'].map(m => [m, 'read']),
8
8
  ...['composition.put', 'view.close', 'capability.configure', 'composition.apply', 'composition.undo', 'view.protection.set', 'interaction.selection.set', 'selection.set', 'workspace.enhance', 'task.map.declare', 'task.map.present', 'task.map.respond'].map(m => [m, 'metadata']),
9
9
  ...['workspace.save', 'workspace.export', 'workspace.restore', 'action.export'].map(m => [m, 'file']),
10
- ...['action.retry', 'action.cancel'].map(m => [m, 'native']),
10
+ ...['extension.call', 'extension.install', 'extension.manage', 'action.retry', 'action.cancel'].map(m => [m, 'native']),
11
11
  ]);
12
12
  const rhoSummary = 'The project in Rho: its folder\'s entries, managed runs, the views on the person\'s screen and what is selected in them, the task map, and which plugins are on.';
13
13
  const metadata = new Set(['capability.index', 'capability.resolve', 'binding.activity.append', 'task.routing.put', 'feedback.submit', 'view.observe', 'view.observations.read']);
@@ -0,0 +1,99 @@
1
+ export declare const extensionOperations: {
2
+ 'extension.list': {
3
+ type: string;
4
+ properties: Record<string, unknown>;
5
+ required: string[];
6
+ additionalProperties: boolean;
7
+ };
8
+ 'extension.describe': {
9
+ type: string;
10
+ properties: Record<string, unknown>;
11
+ required: string[];
12
+ additionalProperties: boolean;
13
+ };
14
+ 'extension.content': {
15
+ type: string;
16
+ properties: Record<string, unknown>;
17
+ required: string[];
18
+ additionalProperties: boolean;
19
+ };
20
+ 'extension.call': {
21
+ type: string;
22
+ properties: Record<string, unknown>;
23
+ required: string[];
24
+ additionalProperties: boolean;
25
+ };
26
+ 'extension.read': {
27
+ type: string;
28
+ properties: Record<string, unknown>;
29
+ required: string[];
30
+ additionalProperties: boolean;
31
+ };
32
+ 'extension.install': {
33
+ type: string;
34
+ properties: Record<string, unknown>;
35
+ required: string[];
36
+ additionalProperties: boolean;
37
+ };
38
+ 'extension.manage': {
39
+ type: string;
40
+ properties: Record<string, unknown>;
41
+ required: string[];
42
+ additionalProperties: boolean;
43
+ };
44
+ };
45
+ export declare const extensionResponses: {
46
+ 'extension.list': {
47
+ type: string;
48
+ properties: Record<string, unknown>;
49
+ required: string[];
50
+ additionalProperties: boolean;
51
+ };
52
+ 'extension.describe': {
53
+ type: string;
54
+ properties: Record<string, unknown>;
55
+ required: string[];
56
+ additionalProperties: boolean;
57
+ };
58
+ 'extension.install': {
59
+ type: string;
60
+ properties: Record<string, unknown>;
61
+ required: string[];
62
+ additionalProperties: boolean;
63
+ };
64
+ 'extension.content': {
65
+ type: string;
66
+ properties: Record<string, unknown>;
67
+ required: string[];
68
+ additionalProperties: boolean;
69
+ };
70
+ 'extension.call': {
71
+ type: string;
72
+ properties: Record<string, unknown>;
73
+ required: string[];
74
+ additionalProperties: boolean;
75
+ };
76
+ 'extension.read': {
77
+ type: string;
78
+ properties: Record<string, unknown>;
79
+ required: string[];
80
+ additionalProperties: boolean;
81
+ };
82
+ 'extension.manage': {
83
+ type: string;
84
+ properties: Record<string, unknown>;
85
+ required: string[];
86
+ additionalProperties: boolean;
87
+ };
88
+ };
89
+ export declare const extensionDescriptions: {
90
+ 'extension.list': string;
91
+ 'extension.describe': string;
92
+ 'extension.content': string;
93
+ 'extension.call': string;
94
+ 'extension.read': string;
95
+ 'extension.install': string;
96
+ 'extension.manage': string;
97
+ };
98
+ export declare const extensionManagement: string[];
99
+ export declare const extensionMethods: string[];
@@ -0,0 +1,52 @@
1
+ // Installed third-party packages are application assembly, independent of the
2
+ // functional owners compiled into Rho. This protocol never imports their code.
3
+ const str = { type: 'string', minLength: 1, maxLength: 1024 };
4
+ const id = { type: 'string', pattern: '^[a-z0-9][a-z0-9._-]{0,95}$' };
5
+ const sha = { type: 'string', pattern: '^[a-f0-9]{64}$' };
6
+ const object = (properties, required = Object.keys(properties)) => ({ type: 'object', properties, required, additionalProperties: false });
7
+ const json = { type: 'object', additionalProperties: true };
8
+ const project = { projectId: str };
9
+ const selected = { ...project, pluginId: id, expectedDigest: sha };
10
+ export const extensionOperations = {
11
+ 'extension.list': object(project, []),
12
+ 'extension.describe': object({ ...project, pluginId: id }),
13
+ 'extension.content': object({ ...selected, kind: { enum: ['skill', 'view'] }, entryId: id }),
14
+ 'extension.call': object({ ...selected, operation: id, input: json, changeId: { type: 'string', pattern: '^[A-Za-z0-9._:@-]{1,128}$' } }),
15
+ 'extension.read': object({ ...project, invocationId: sha }),
16
+ 'extension.install': object({ source: str, replace: id, expectedDigest: sha }, ['source']),
17
+ 'extension.manage': object({ pluginId: id, expectedDigest: sha, action: { enum: ['enable', 'disable', 'remove'] } }),
18
+ };
19
+ const array = (items) => ({ type: 'array', items });
20
+ const text = { type: 'string' };
21
+ const entry = { ...object({ id, title: text, file: text }), additionalProperties: true };
22
+ const manifest = { ...object({ apiVersion: { const: 1 }, id, title: text, description: text, host: text,
23
+ operations: array({ ...object({ id, title: text, description: text, effect: { enum: ['read', 'native'] }, methods: array(text), inputSchema: json }), additionalProperties: true }),
24
+ skills: array(entry), views: array(entry),
25
+ }, ['apiVersion', 'id', 'title', 'description', 'operations', 'skills', 'views']), additionalProperties: true };
26
+ const installed = { id, name: text, version: text, title: text, description: text, digest: sha, source: text, sourceCommit: text, enabled: { type: 'boolean' }, installedAt: text };
27
+ const installedRequired = Object.keys(installed).filter(key => key !== 'sourceCommit');
28
+ const descriptor = object({ ...installed, manifest }, [...installedRequired, 'manifest']);
29
+ const receipt = object({ invocationId: sha, caller: text, projectId: text, pluginId: id, packageDigest: sha, operation: id, requestDigest: sha,
30
+ state: { enum: ['pending', 'completed', 'unknown'] }, startedAt: text, endedAt: text, value: {}, error: text,
31
+ delegations: array(object({ method: text, request: {}, state: { enum: ['pending', 'completed', 'unknown'] }, value: {}, error: text }, ['method', 'request', 'state'])),
32
+ }, ['invocationId', 'caller', 'projectId', 'pluginId', 'packageDigest', 'operation', 'requestDigest', 'state', 'startedAt']);
33
+ export const extensionResponses = {
34
+ 'extension.list': object({ plugins: array(object(installed, installedRequired)), directory: array(object({ id, title: text, version: text, description: text, source: text, repository: text, needs: text })), limits: json }),
35
+ 'extension.describe': descriptor,
36
+ 'extension.install': descriptor,
37
+ 'extension.content': object({ pluginId: id, packageDigest: sha, entryId: id, kind: { enum: ['skill', 'view'] }, title: text, text }),
38
+ 'extension.call': receipt,
39
+ 'extension.read': receipt,
40
+ 'extension.manage': object({ pluginId: id, action: { enum: ['enable', 'disable', 'remove'] }, retained: text }),
41
+ };
42
+ export const extensionDescriptions = {
43
+ 'extension.list': 'List independently installed third-party packages and their enabled state, source and immutable digest. Reads start no plugin or runtime. Use extension.describe to discover operation schemas and Skills.',
44
+ 'extension.describe': 'Read an installed third-party package manifest, operation input schemas, required Rho methods, Skills and views. Read its Skill with extension.content before use. Disabled packages cannot run.',
45
+ 'extension.content': 'Read one installed plugin Skill or standalone HTML view at its selected package digest. Reading starts no plugin or runtime.',
46
+ 'extension.call': 'Call an enabled third-party operation at the described package digest, under the caller and project. The package runs trusted native code. Non-read calls retain the original changeId: repeat observes that invocation, including unknown outcomes, without re-execution. Completion is not scientific validity.',
47
+ 'extension.read': 'Observe an original plugin invocation, including pending or unknown outcomes. Never reruns code. Only its original caller can read it.',
48
+ 'extension.install': 'Local person/operator: install a trusted prebuilt npm package, local folder, tarball, or Git source. Lifecycle scripts are disabled. Replacement stages and checks the new package before switching; use replace and expectedDigest for updates.',
49
+ 'extension.manage': 'Local person/operator: enable, disable or remove an installed third-party package at the expected digest. Existing native tasks, R sessions, project files and retained receipts are preserved.',
50
+ };
51
+ export const extensionManagement = ['extension.install', 'extension.manage'];
52
+ export const extensionMethods = Object.keys(extensionOperations);
@@ -0,0 +1,9 @@
1
+ export declare const extensionDirectory: {
2
+ id: string;
3
+ title: string;
4
+ version: string;
5
+ description: string;
6
+ source: string;
7
+ repository: string;
8
+ needs: string;
9
+ }[];
@@ -0,0 +1,11 @@
1
+ // Optional discovery, not a registration requirement. Packages keep their own
2
+ // repositories/releases; an entry records the source a maintainer actually checked.
3
+ export const extensionDirectory = [
4
+ {
5
+ id: 'yulab.sclet', title: 'sclet', version: '0.1.0',
6
+ description: 'State-aware single-cell analysis using sclet in your shared R Notebook.',
7
+ source: 'git+https://github.com/YuLab-SMU/Rho-plugins.git#82df99f8cbab868c30949692172c6cf11db2f5c0:examples/sclet-plugin',
8
+ repository: 'https://github.com/YuLab-SMU/Rho-plugins/tree/82df99f8cbab868c30949692172c6cf11db2f5c0/examples/sclet-plugin',
9
+ needs: 'R, sclet, SingleCellExperiment and jsonlite in the selected R environment.',
10
+ },
11
+ ];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,36 @@
1
+ // A process boundary for crashes/timeouts, NOT an OS sandbox. An installed host
2
+ // has this user's native permissions. Never send it service credentials.
3
+ import { pathToFileURL } from 'node:url';
4
+ const pending = new Map();
5
+ let next = 0;
6
+ process.on('message', async (message) => {
7
+ if (message.type === 'reply') {
8
+ const waiter = pending.get(message.id);
9
+ pending.delete(message.id);
10
+ if (message.error)
11
+ waiter?.reject(new Error(message.error));
12
+ else
13
+ waiter?.resolve(message.value);
14
+ }
15
+ if (message.type !== 'start')
16
+ return;
17
+ try {
18
+ const plugin = await import(pathToFileURL(message.entry).href);
19
+ if (typeof plugin.run !== 'function')
20
+ throw new Error('The plugin host must export run(operation, input, context).');
21
+ const value = await plugin.run(message.operation, message.input, {
22
+ projectRoot: message.projectRoot,
23
+ invocationId: message.invocationId,
24
+ changeId: (label) => 'ext:' + message.invocationId + ':' + String(label).slice(0, 40),
25
+ call: (method, body) => new Promise((resolve, reject) => {
26
+ const id = ++next;
27
+ pending.set(id, { resolve, reject });
28
+ process.send?.({ type: 'call', id, method, body });
29
+ }),
30
+ });
31
+ process.send?.({ type: 'result', value }, () => process.exit(0));
32
+ }
33
+ catch (error) {
34
+ process.send?.({ type: 'error', error: error instanceof Error ? error.message : String(error) }, () => process.exit(1));
35
+ }
36
+ });
@@ -0,0 +1,157 @@
1
+ export declare const extensionLimits: {
2
+ maxPlugins: number;
3
+ maxVersions: number;
4
+ maxFiles: number;
5
+ maxPackageBytes: number;
6
+ maxContentBytes: number;
7
+ maxReceipts: number;
8
+ maxResultBytes: number;
9
+ maxRunning: number;
10
+ maxDelegations: number;
11
+ timeoutMs: number;
12
+ installTimeoutMs: number;
13
+ };
14
+ export interface ExtensionManifest {
15
+ apiVersion: 1;
16
+ id: string;
17
+ title: string;
18
+ description: string;
19
+ host?: string;
20
+ operations: {
21
+ id: string;
22
+ title: string;
23
+ description: string;
24
+ effect: 'read' | 'native';
25
+ methods: string[];
26
+ inputSchema: Record<string, any>;
27
+ }[];
28
+ skills: {
29
+ id: string;
30
+ title: string;
31
+ file: string;
32
+ }[];
33
+ views: {
34
+ id: string;
35
+ title: string;
36
+ file: string;
37
+ }[];
38
+ }
39
+ export interface InstalledExtension {
40
+ id: string;
41
+ name: string;
42
+ version: string;
43
+ title: string;
44
+ description: string;
45
+ digest: string;
46
+ source: string;
47
+ sourceCommit?: string;
48
+ enabled: boolean;
49
+ installedAt: string;
50
+ manifest: ExtensionManifest;
51
+ }
52
+ interface Receipt {
53
+ invocationId: string;
54
+ caller: string;
55
+ projectId: string;
56
+ pluginId: string;
57
+ packageDigest: string;
58
+ operation: string;
59
+ requestDigest: string;
60
+ state: 'pending' | 'completed' | 'unknown';
61
+ startedAt: string;
62
+ endedAt?: string;
63
+ value?: unknown;
64
+ error?: string;
65
+ delegations?: {
66
+ method: string;
67
+ request: unknown;
68
+ state: 'pending' | 'completed' | 'unknown';
69
+ value?: unknown;
70
+ error?: string;
71
+ }[];
72
+ }
73
+ export declare class Extensions {
74
+ readonly directory: string;
75
+ readonly limits: typeof extensionLimits;
76
+ private installed;
77
+ private running;
78
+ private workers;
79
+ private receiptCount;
80
+ private mutation;
81
+ constructor(directory: string, limits?: Partial<typeof extensionLimits>);
82
+ init(): Promise<void>;
83
+ close(): void;
84
+ private serial;
85
+ list(): {
86
+ directory: {
87
+ id: string;
88
+ title: string;
89
+ version: string;
90
+ description: string;
91
+ source: string;
92
+ repository: string;
93
+ needs: string;
94
+ }[];
95
+ plugins: {
96
+ id: string;
97
+ name: string;
98
+ version: string;
99
+ title: string;
100
+ description: string;
101
+ digest: string;
102
+ source: string;
103
+ sourceCommit?: string;
104
+ enabled: boolean;
105
+ installedAt: string;
106
+ }[];
107
+ limits: {
108
+ maxPlugins: number;
109
+ maxVersions: number;
110
+ maxFiles: number;
111
+ maxPackageBytes: number;
112
+ maxContentBytes: number;
113
+ maxReceipts: number;
114
+ maxResultBytes: number;
115
+ maxRunning: number;
116
+ maxDelegations: number;
117
+ timeoutMs: number;
118
+ installTimeoutMs: number;
119
+ };
120
+ };
121
+ describe(id: string): InstalledExtension;
122
+ private selected;
123
+ private root;
124
+ private file;
125
+ private hash;
126
+ check(root: string): Promise<{
127
+ name: string;
128
+ version: string;
129
+ manifest: ExtensionManifest;
130
+ }>;
131
+ private verify;
132
+ content(id: string, expected: string, kind: 'skill' | 'view', entryId: string): Promise<{
133
+ pluginId: string;
134
+ packageDigest: string;
135
+ entryId: string;
136
+ kind: "skill" | "view";
137
+ title: string;
138
+ text: string;
139
+ }>;
140
+ install(source: string, replace?: string, expected?: string): Promise<InstalledExtension>;
141
+ manage(id: string, expected: string, action: 'enable' | 'disable' | 'remove'): Promise<{
142
+ pluginId: string;
143
+ action: "remove" | "enable" | "disable";
144
+ retained: string;
145
+ }>;
146
+ read(invocationId: string, caller: string, projectId: string): Promise<Receipt>;
147
+ call(b: {
148
+ pluginId: string;
149
+ expectedDigest: string;
150
+ operation: string;
151
+ input: any;
152
+ changeId: string;
153
+ projectId: string;
154
+ }, caller: string, projectRoot: string, dispatch: (method: string, body: any, effect: 'read' | 'native') => Promise<unknown>): Promise<Receipt>;
155
+ private execute;
156
+ }
157
+ export {};