@maudecode/talaria-web 1.21.0 → 1.22.0

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 (360) hide show
  1. package/_release.json +4 -4
  2. package/dist/api/automation-router.d.ts +14 -14
  3. package/dist/api/chat-router.d.ts +217 -68
  4. package/dist/api/groups-router.d.ts +764 -0
  5. package/dist/api/groups-router.js +79 -0
  6. package/dist/api/groups-router.js.map +1 -0
  7. package/dist/api/raw-routes.js +3 -1
  8. package/dist/api/raw-routes.js.map +1 -1
  9. package/dist/api/sessions-router.d.ts +559 -160
  10. package/dist/api/sessions-router.js +9 -12
  11. package/dist/api/sessions-router.js.map +1 -1
  12. package/dist/api/settings-router.d.ts +911 -1
  13. package/dist/api/settings-router.js +53 -2
  14. package/dist/api/settings-router.js.map +1 -1
  15. package/dist/api/sse-routes.d.ts +1 -1
  16. package/dist/api/sse-routes.js +11 -3
  17. package/dist/api/sse-routes.js.map +1 -1
  18. package/dist/api/tools-raw.js +27 -136
  19. package/dist/api/tools-raw.js.map +1 -1
  20. package/dist/api/tools-router.d.ts +109 -24
  21. package/dist/api/tools-router.js +64 -7
  22. package/dist/api/tools-router.js.map +1 -1
  23. package/dist/app.js +5 -1
  24. package/dist/app.js.map +1 -1
  25. package/dist/auth/gate.js +5 -1
  26. package/dist/auth/gate.js.map +1 -1
  27. package/dist/config/agent-config.d.ts +6 -1
  28. package/dist/config/agent-config.js +75 -3
  29. package/dist/config/agent-config.js.map +1 -1
  30. package/dist/feedback.d.ts +18 -0
  31. package/dist/feedback.js +145 -0
  32. package/dist/feedback.js.map +1 -0
  33. package/dist/fs/stat.d.ts +2 -0
  34. package/dist/fs/stat.js +4 -0
  35. package/dist/fs/stat.js.map +1 -1
  36. package/dist/http/context.d.ts +7 -0
  37. package/dist/http/context.js +0 -6
  38. package/dist/http/context.js.map +1 -1
  39. package/dist/http/pinned.js +19 -8
  40. package/dist/http/pinned.js.map +1 -1
  41. package/dist/providers/catalog.d.ts +9 -1
  42. package/dist/providers/catalog.js +86 -6
  43. package/dist/providers/catalog.js.map +1 -1
  44. package/dist/redact.js +9 -3
  45. package/dist/redact.js.map +1 -1
  46. package/dist/runtime.js +27 -4
  47. package/dist/runtime.js.map +1 -1
  48. package/dist/server.js +1 -0
  49. package/dist/server.js.map +1 -1
  50. package/dist/sessions/anchor.d.ts +3 -2
  51. package/dist/sessions/anchor.js +11 -10
  52. package/dist/sessions/anchor.js.map +1 -1
  53. package/dist/sessions/cli-sessions.js +11 -6
  54. package/dist/sessions/cli-sessions.js.map +1 -1
  55. package/dist/sessions/compress.d.ts +10 -9
  56. package/dist/sessions/compress.js +31 -20
  57. package/dist/sessions/compress.js.map +1 -1
  58. package/dist/sessions/gateway-sessions.d.ts +11 -0
  59. package/dist/sessions/gateway-sessions.js +79 -24
  60. package/dist/sessions/gateway-sessions.js.map +1 -1
  61. package/dist/sessions/gateway-watcher.js +8 -4
  62. package/dist/sessions/gateway-watcher.js.map +1 -1
  63. package/dist/sessions/groups.d.ts +60 -0
  64. package/dist/sessions/groups.js +339 -0
  65. package/dist/sessions/groups.js.map +1 -0
  66. package/dist/sessions/list.d.ts +7 -2
  67. package/dist/sessions/list.js +13 -4
  68. package/dist/sessions/list.js.map +1 -1
  69. package/dist/sessions/merge.d.ts +14 -5
  70. package/dist/sessions/merge.js +42 -13
  71. package/dist/sessions/merge.js.map +1 -1
  72. package/dist/sessions/pending.d.ts +1 -1
  73. package/dist/sessions/pending.js +15 -6
  74. package/dist/sessions/pending.js.map +1 -1
  75. package/dist/sessions/prefix-index.d.ts +1 -14
  76. package/dist/sessions/prefix-index.js +1 -50
  77. package/dist/sessions/prefix-index.js.map +1 -1
  78. package/dist/sessions/reasoning-display.d.ts +2 -0
  79. package/dist/sessions/reasoning-display.js +31 -0
  80. package/dist/sessions/reasoning-display.js.map +1 -0
  81. package/dist/sessions/relay.d.ts +4 -1
  82. package/dist/sessions/relay.js +44 -7
  83. package/dist/sessions/relay.js.map +1 -1
  84. package/dist/sessions/service.d.ts +7 -2
  85. package/dist/sessions/service.js +104 -64
  86. package/dist/sessions/service.js.map +1 -1
  87. package/dist/sessions/session.d.ts +6 -0
  88. package/dist/sessions/session.js +13 -0
  89. package/dist/sessions/session.js.map +1 -1
  90. package/dist/sessions/shares.d.ts +1 -1
  91. package/dist/sessions/shares.js +13 -4
  92. package/dist/sessions/shares.js.map +1 -1
  93. package/dist/sessions/state-db.d.ts +2 -1
  94. package/dist/sessions/state-db.js +7 -3
  95. package/dist/sessions/state-db.js.map +1 -1
  96. package/dist/sessions/storage/json-documents.js +2 -0
  97. package/dist/sessions/storage/json-documents.js.map +1 -1
  98. package/dist/sessions/store.d.ts +1 -0
  99. package/dist/sessions/store.js +10 -3
  100. package/dist/sessions/store.js.map +1 -1
  101. package/dist/sessions/streams.d.ts +2 -0
  102. package/dist/sessions/streams.js +12 -0
  103. package/dist/sessions/streams.js.map +1 -1
  104. package/dist/sessions/todo.js +10 -1
  105. package/dist/sessions/todo.js.map +1 -1
  106. package/dist/sessions/turn.js +33 -9
  107. package/dist/sessions/turn.js.map +1 -1
  108. package/dist/settings.js +2 -1
  109. package/dist/settings.js.map +1 -1
  110. package/dist/spa.js +1 -1
  111. package/dist/spa.js.map +1 -1
  112. package/dist/test/group-fixture.d.ts +66 -0
  113. package/dist/test/group-fixture.js +89 -0
  114. package/dist/test/group-fixture.js.map +1 -0
  115. package/dist/test/harness.d.ts +3 -0
  116. package/dist/test/harness.js +3 -0
  117. package/dist/test/harness.js.map +1 -1
  118. package/dist/tools/connectors.d.ts +9 -1
  119. package/dist/tools/connectors.js +48 -0
  120. package/dist/tools/connectors.js.map +1 -1
  121. package/dist/tools/gateway-restart.d.ts +1 -1
  122. package/dist/tools/skills.d.ts +6 -0
  123. package/dist/tools/skills.js +17 -0
  124. package/dist/tools/skills.js.map +1 -1
  125. package/dist/tools/updates.d.ts +16 -9
  126. package/dist/tools/updates.js +63 -25
  127. package/dist/tools/updates.js.map +1 -1
  128. package/dist/workspace/fs.d.ts +2 -0
  129. package/dist/workspace/fs.js +63 -1
  130. package/dist/workspace/fs.js.map +1 -1
  131. package/dist/workspace/upload.d.ts +1 -1
  132. package/dist/workspace/upload.js +52 -37
  133. package/dist/workspace/upload.js.map +1 -1
  134. package/dist/workspace/workspaces.d.ts +5 -1
  135. package/dist/workspace/workspaces.js +32 -6
  136. package/dist/workspace/workspaces.js.map +1 -1
  137. package/package.json +2 -2
  138. package/sidecar/agent_importer.json +8 -0
  139. package/sidecar/scripts/agent_importer_contract.py +19 -0
  140. package/sidecar/scripts/test.sh +3 -1
  141. package/sidecar/talaria_sidecar/__init__.py +1 -1
  142. package/sidecar/talaria_sidecar/home.py +12 -5
  143. package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
  144. package/sidecar/talaria_sidecar/methods/agent_imports.py +346 -0
  145. package/sidecar/talaria_sidecar/methods/chat.py +141 -58
  146. package/sidecar/talaria_sidecar/methods/commands.py +6 -4
  147. package/sidecar/talaria_sidecar/methods/config.py +8 -3
  148. package/sidecar/talaria_sidecar/methods/connectors.py +138 -4
  149. package/sidecar/talaria_sidecar/methods/dashboard_plugins.py +21 -0
  150. package/sidecar/talaria_sidecar/methods/delegation.py +126 -0
  151. package/sidecar/talaria_sidecar/methods/gateway.py +5 -0
  152. package/sidecar/talaria_sidecar/methods/gateway_sessions.py +35 -6
  153. package/sidecar/talaria_sidecar/methods/groups.py +120 -0
  154. package/sidecar/talaria_sidecar/methods/memory.py +27 -3
  155. package/sidecar/talaria_sidecar/methods/nous_account.py +130 -0
  156. package/sidecar/talaria_sidecar/methods/oauth.py +25 -4
  157. package/sidecar/talaria_sidecar/methods/outbound_drafts.py +1 -19
  158. package/sidecar/talaria_sidecar/methods/plugin_catalog.py +73 -2
  159. package/sidecar/talaria_sidecar/methods/providers.py +51 -1
  160. package/sidecar/talaria_sidecar/methods/skill_revisions.py +177 -0
  161. package/sidecar/talaria_sidecar/methods/skills.py +10 -0
  162. package/sidecar/talaria_sidecar/methods/tts.py +67 -0
  163. package/sidecar/talaria_sidecar/rpc.py +11 -2
  164. package/static/dist/FILES.txt +155 -143
  165. package/static/dist/THIRD_PARTY_LICENSES.md +1 -1
  166. package/static/dist/assets/AppShell-hvysbd9x.js +8 -0
  167. package/static/dist/assets/{Button-nuaikybd.js → Button-dlsibkzq.js} +1 -1
  168. package/static/dist/assets/ChatPage-jikwrwfd.js +2 -0
  169. package/static/dist/assets/Composer-mnke8ss2.js +5 -0
  170. package/static/dist/assets/ConnectTargets-ejplzo4q.js +1 -0
  171. package/static/dist/assets/Dialog-rksfyyq5.js +1 -0
  172. package/static/dist/assets/{ExtensionRoute-l1yjrso4.js → ExtensionRoute-igk5h0rj.js} +1 -1
  173. package/static/dist/assets/HubRoute-eooassx4.js +2 -0
  174. package/static/dist/assets/Markdown-m5pzgvtx.js +2 -0
  175. package/static/dist/assets/{MarkdownRenderer-ccgqrza3.js → MarkdownRenderer-74ysuomr.js} +6 -6
  176. package/static/dist/assets/OAuthSignIn-i4flsuq2.js +1 -0
  177. package/static/dist/assets/QueueCard-msz9wd1m.js +1 -0
  178. package/static/dist/assets/ResourceSwitch-b70zg4xd.js +1 -0
  179. package/static/dist/assets/RightPanel-f5v61m8j.js +1 -0
  180. package/static/dist/assets/Select-f3l897pt.js +1 -0
  181. package/static/dist/assets/SessionListPanel-brnzacw9.js +1 -0
  182. package/static/dist/assets/SettingsLayout-f4akmrt2.js +1 -0
  183. package/static/dist/assets/States-msohx3c0.js +1 -0
  184. package/static/dist/assets/TerminalPanel-bnu5xawo.js +16 -0
  185. package/static/dist/assets/Toaster-d6col3ch.js +1 -0
  186. package/static/dist/assets/_app-lrjgufcy.js +1 -0
  187. package/static/dist/assets/{_app.ext._extensionId-n797jp72.js → _app.ext._extensionId-k3xitxr1.js} +1 -1
  188. package/static/dist/assets/_app.groups-bt4wdwnd.js +1 -0
  189. package/static/dist/assets/{_app.index-ne43mdw2.js → _app.index-n3otiki0.js} +1 -1
  190. package/static/dist/assets/{_app.insights-fqonovb4.js → _app.insights-b0mzlh6m.js} +1 -1
  191. package/static/dist/assets/{_app.kanban-kysliv19.js → _app.kanban-d923g7j5.js} +1 -1
  192. package/static/dist/assets/{_app.logs-febut82r.js → _app.logs-donegue1.js} +1 -1
  193. package/static/dist/assets/{_app.memory-ha86oj1c.js → _app.memory-hdnled08.js} +1 -1
  194. package/static/dist/assets/{_app.profiles-cnu3gxxq.js → _app.profiles-fumot3jk.js} +1 -1
  195. package/static/dist/assets/{_app.session._sessionId-ifzjpcgn.js → _app.session._sessionId-crmdl2ux.js} +1 -1
  196. package/static/dist/assets/{_app.settings-nmakdiaf.js → _app.settings-jui8iesz.js} +1 -1
  197. package/static/dist/assets/_app.settings._section-do84hsqw.js +3 -0
  198. package/static/dist/assets/{_app.skills-f3wad8yo.js → _app.skills-heqcn21m.js} +1 -1
  199. package/static/dist/assets/_app.tasks-jiudtutt.js +1 -0
  200. package/static/dist/assets/{_app.todos-bml1m3r1.js → _app.todos-bl6kk2zq.js} +1 -1
  201. package/static/dist/assets/{_app.workspaces-f3d066lx.js → _app.workspaces-frvh3o7w.js} +1 -1
  202. package/static/dist/assets/_delete-qnxuo87s.js +1 -0
  203. package/static/dist/assets/{abnfDiagram-VCTEODGH-i53jc4yp.js → abnfDiagram-VCTEODGH-dppg3yfx.js} +1 -1
  204. package/static/dist/assets/{architecture-7GRP2DOG-e3bv5m6f.js → architecture-7GRP2DOG-gbajejh3.js} +1 -1
  205. package/static/dist/assets/{architectureDiagram-5GKGNRK7-pakekxu2.js → architectureDiagram-5GKGNRK7-bzrxyais.js} +1 -1
  206. package/static/dist/assets/archive-yhdyz231.js +1 -0
  207. package/static/dist/assets/autoReadClaim-nlyv8tu9.js +1 -0
  208. package/static/dist/assets/{blockDiagram-I7D4REHJ-bc6bo1n6.js → blockDiagram-I7D4REHJ-gmk9koab.js} +1 -1
  209. package/static/dist/assets/{c4Diagram-7LVT6UL2-btvwk2qx.js → c4Diagram-7LVT6UL2-bfqiqqcz.js} +1 -1
  210. package/static/dist/assets/channel-grt3rob4.js +1 -0
  211. package/static/dist/assets/{chunk-4HAMMTFA-dlwab11c.js → chunk-4HAMMTFA-i9s6h4wv.js} +1 -1
  212. package/static/dist/assets/{chunk-75Z2AOVW-dp5gh7i8.js → chunk-75Z2AOVW-gnt6pnso.js} +1 -1
  213. package/static/dist/assets/{chunk-DU6HZSFF-bi06zl1c.js → chunk-DU6HZSFF-gq439wot.js} +3 -3
  214. package/static/dist/assets/{chunk-F27PBJKO-kvv1rnlh.js → chunk-F27PBJKO-wvx90pbt.js} +1 -1
  215. package/static/dist/assets/{chunk-GMAD6QVW-ji0rx9by.js → chunk-GMAD6QVW-mc2gu30k.js} +1 -1
  216. package/static/dist/assets/{chunk-GVQU2GXP-nmz0j82p.js → chunk-GVQU2GXP-f8fxa2ym.js} +1 -1
  217. package/static/dist/assets/{chunk-IMKFNOWR-kmi1qx88.js → chunk-IMKFNOWR-lnsl31hj.js} +1 -1
  218. package/static/dist/assets/{chunk-L3NEJ4N5-xhb7mebj.js → chunk-L3NEJ4N5-kbfdidwu.js} +1 -1
  219. package/static/dist/assets/{chunk-OSK3NFVY-5a9sgscq.js → chunk-OSK3NFVY-dvxsduj6.js} +1 -1
  220. package/static/dist/assets/{chunk-P2QGCYS3-dfezp4vu.js → chunk-P2QGCYS3-ivo3i1wf.js} +1 -1
  221. package/static/dist/assets/{chunk-POPQ4Y6H-gd2vq3c0.js → chunk-POPQ4Y6H-i0gfu0hx.js} +1 -1
  222. package/static/dist/assets/{chunk-PWAF6VOD-wvw2nksf.js → chunk-PWAF6VOD-jb0fdyb0.js} +1 -1
  223. package/static/dist/assets/{chunk-SHT3W25Y-f58q97px.js → chunk-SHT3W25Y-b7ou2xsp.js} +1 -1
  224. package/static/dist/assets/{chunk-SVP7TREG-abgwpyz9.js → chunk-SVP7TREG-e2v7me5v.js} +1 -1
  225. package/static/dist/assets/{chunk-TICWLB2K-h4sbs8ds.js → chunk-TICWLB2K-e5zlai62.js} +1 -1
  226. package/static/dist/assets/classDiagram-ZZMXUADV-nnsp49jr.js +1 -0
  227. package/static/dist/assets/classDiagram-v2-VYDZK3BY-nnsp49jr.js +1 -0
  228. package/static/dist/assets/completionAudio-c5at041z.js +1 -0
  229. package/static/dist/assets/{copy-m9h56qa8.js → copy-jj3oiw13.js} +1 -1
  230. package/static/dist/assets/{cynefin-OW5HDTMX-g081vffs.js → cynefin-OW5HDTMX-n5n1ck70.js} +1 -1
  231. package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-odxvjesk.js → cynefinDiagram-5FMLGOSQ-b33npu4f.js} +1 -1
  232. package/static/dist/assets/{dagre-GXQ25YYZ-cpdwzjs4.js → dagre-GXQ25YYZ-miwvae07.js} +1 -1
  233. package/static/dist/assets/{diagram-S7CK7UJ4-clv5mxvu.js → diagram-S7CK7UJ4-j8fphsp4.js} +1 -1
  234. package/static/dist/assets/{diagram-UQ7AKVKN-m2ia1mbf.js → diagram-UQ7AKVKN-5cixo166.js} +1 -1
  235. package/static/dist/assets/{diagram-VSXAHHWV-ea6zv636.js → diagram-VSXAHHWV-ii3bf92m.js} +1 -1
  236. package/static/dist/assets/{diagram-VX7I27RA-oqlef685.js → diagram-VX7I27RA-b3cfgx81.js} +1 -1
  237. package/static/dist/assets/{diagram-Z3DM3KII-kjubcbwu.js → diagram-Z3DM3KII-bey9lqr8.js} +1 -1
  238. package/static/dist/assets/{dist-carq1azj.js → dist-cb8um5dh.js} +4 -4
  239. package/static/dist/assets/{dist-crc46di9.js → dist-kpg5ccm9.js} +1 -1
  240. package/static/dist/assets/{ebnfDiagram-PWID7BFC-fg0zri3e.js → ebnfDiagram-PWID7BFC-2m4wrnqc.js} +1 -1
  241. package/static/dist/assets/{erDiagram-RLTQ6QDP-eb7pmk3x.js → erDiagram-RLTQ6QDP-kak5te0n.js} +1 -1
  242. package/static/dist/assets/{eventmodeling-NTZA5JFV-gvi1fxhn.js → eventmodeling-NTZA5JFV-ojcc486t.js} +1 -1
  243. package/static/dist/assets/flowDiagram-HODETNUW-kxr9sicu.js +1 -0
  244. package/static/dist/assets/folder-plus-fk6j7tlv.js +1 -0
  245. package/static/dist/assets/{ganttDiagram-EL5Y4UJY-jmisowrc.js → ganttDiagram-EL5Y4UJY-k6nfmhrh.js} +1 -1
  246. package/static/dist/assets/getPseudoElementBounds-mwj3vqni.js +1 -0
  247. package/static/dist/assets/{gitGraph-4MIJSDKK-m7kt3u6e.js → gitGraph-4MIJSDKK-bxq1lbhy.js} +1 -1
  248. package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-nq7vfzh2.js → gitGraphDiagram-WWUBYQGX-ehvoankt.js} +1 -1
  249. package/static/dist/assets/group_discussions-fk5hry4l.js +1 -0
  250. package/static/dist/assets/{highlighted-body-KPVGNVTW-gcu017ay.js → highlighted-body-KPVGNVTW-nr4nfdq7.js} +1 -1
  251. package/static/dist/assets/{host-ipp4e84c.js → host-dhgqiozp.js} +1 -1
  252. package/static/dist/assets/index-hx7z4wdj.js +13 -0
  253. package/static/dist/assets/index-o8ebuc5x.css +2 -0
  254. package/static/dist/assets/{info-A6RAGUB7-idazwfqe.js → info-A6RAGUB7-dtsj0ybw.js} +1 -1
  255. package/static/dist/assets/{infoDiagram-27XIBGKW-bb9jh8gl.js → infoDiagram-27XIBGKW-jena90ao.js} +1 -1
  256. package/static/dist/assets/{ishikawaDiagram-5VMMS53U-nqc132rv.js → ishikawaDiagram-5VMMS53U-kia6bn5u.js} +1 -1
  257. package/static/dist/assets/{journeyDiagram-3NMN7TZE-fpvnhy44.js → journeyDiagram-3NMN7TZE-ipsa6icb.js} +1 -1
  258. package/static/dist/assets/{kanban-definition-UXKFOSKX-ndzqm6fu.js → kanban-definition-UXKFOSKX-moxx59kp.js} +1 -1
  259. package/static/dist/assets/katex-ic33v820.js +257 -0
  260. package/static/dist/assets/katex-qcv7xo0t.css +1 -0
  261. package/static/dist/assets/{lazyRouteComponent-javud235.js → lazyRouteComponent-i74ujrfk.js} +1 -1
  262. package/static/dist/assets/{line-g625e7cj.js → line-lgrkg9no.js} +1 -1
  263. package/static/dist/assets/link-2-fp49mwl2.js +1 -0
  264. package/static/dist/assets/{link-gfdaho08.js → link-es18mse1.js} +2 -2
  265. package/static/dist/assets/{locale-cs-n41hptse.js → locale-cs-brltdkcp.js} +1 -1
  266. package/static/dist/assets/{locale-de-k7ht47ew.js → locale-de-h6e9oq6v.js} +1 -1
  267. package/static/dist/assets/{locale-es-ih078lco.js → locale-es-oklmh80f.js} +1 -1
  268. package/static/dist/assets/{locale-fr-ojabnf41.js → locale-fr-b17ozoi2.js} +1 -1
  269. package/static/dist/assets/{locale-it-kb96jufy.js → locale-it-whe01eef.js} +1 -1
  270. package/static/dist/assets/{locale-ja-jpsp3xs1.js → locale-ja-g1s0rtpc.js} +1 -1
  271. package/static/dist/assets/{locale-pl-dbap4leu.js → locale-pl-d9p3lqcb.js} +1 -1
  272. package/static/dist/assets/{locale-pt-nci7w72x.js → locale-pt-l75on74f.js} +1 -1
  273. package/static/dist/assets/{locale-ru-soghe4cs.js → locale-ru-wl3mbsaw.js} +1 -1
  274. package/static/dist/assets/{locale-tr-ks00mu68.js → locale-tr-cz87itrq.js} +1 -1
  275. package/static/dist/assets/{locale-vi-ij7twfm6.js → locale-vi-elqj1y4k.js} +1 -1
  276. package/static/dist/assets/{locale-zh-Hant-feakwr7a.js → locale-zh-Hant-hf8gqm7d.js} +1 -1
  277. package/static/dist/assets/{locale-zh-ren0vjyv.js → locale-zh-cdckwb0u.js} +1 -1
  278. package/static/dist/assets/login-o6gfl4gy.js +1 -0
  279. package/static/dist/assets/mermaid-HWGCJPDP-n2kbzsao.js +1 -0
  280. package/static/dist/assets/{mermaid-parser.core-plzkuzov.js → mermaid-parser.core-fflovwf2.js} +3 -3
  281. package/static/dist/assets/{mindmap-definition-YA3MSWOX-ojrgs569.js → mindmap-definition-YA3MSWOX-ic8l9whi.js} +1 -1
  282. package/static/dist/assets/onboarding-bxm7gdrr.js +1 -0
  283. package/static/dist/assets/{packet-AYTQ26CC-gyaf9s8h.js → packet-AYTQ26CC-ntci3088.js} +1 -1
  284. package/static/dist/assets/{pegDiagram-XKGWAZYB-bf6xj8mq.js → pegDiagram-XKGWAZYB-e3btz04w.js} +1 -1
  285. package/static/dist/assets/{pie-WAS4IAKB-f5ymzelo.js → pie-WAS4IAKB-o2dc7l36.js} +1 -1
  286. package/static/dist/assets/{pieDiagram-E7YTZNPT-d0qjl3zi.js → pieDiagram-E7YTZNPT-gro55rrw.js} +1 -1
  287. package/static/dist/assets/presence-lq6m12ga.js +1 -0
  288. package/static/dist/assets/prompt_queue_cancelled-cebcuagu.js +1 -0
  289. package/static/dist/assets/{quadrantDiagram-AXDQQJYC-oqy1mras.js → quadrantDiagram-AXDQQJYC-fxulmd7d.js} +1 -1
  290. package/static/dist/assets/queryKeys-cnvn6pea.js +9 -0
  291. package/static/dist/assets/{radar-RG4KPBEZ-b6jcjvda.js → radar-RG4KPBEZ-9pcospra.js} +1 -1
  292. package/static/dist/assets/{railroad-74A4TZTK-e3a6lk5k.js → railroad-74A4TZTK-nzdga12j.js} +1 -1
  293. package/static/dist/assets/{railroad-abnf-HS5TGJTU-ff5hocrx.js → railroad-abnf-HS5TGJTU-ep3ytel0.js} +1 -1
  294. package/static/dist/assets/{railroad-ebnf-LZEXJU2U-exkhyw4n.js → railroad-ebnf-LZEXJU2U-iw7uc1wy.js} +1 -1
  295. package/static/dist/assets/{railroad-peg-WCYAUIDC-jefk9b7c.js → railroad-peg-WCYAUIDC-fo3b3gme.js} +1 -1
  296. package/static/dist/assets/{railroadDiagram-O6MQD6OU-e4amks1i.js → railroadDiagram-O6MQD6OU-lxpxf8f0.js} +1 -1
  297. package/static/dist/assets/{refresh-cw-lfp4u1oh.js → refresh-cw-d14h79x5.js} +1 -1
  298. package/static/dist/assets/rename-ccd52yws.js +1 -0
  299. package/static/dist/assets/{requirementDiagram-BXWQKSXE-c77k1e2y.js → requirementDiagram-BXWQKSXE-lrozucd2.js} +1 -1
  300. package/static/dist/assets/{sankeyDiagram-P5KCCOFB-oa1cc52s.js → sankeyDiagram-P5KCCOFB-iusxy9mq.js} +1 -1
  301. package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-j20gqt22.js → sequenceDiagram-WJ2MYXX4-o987q8ti.js} +1 -1
  302. package/static/dist/assets/{share._token-kmfoflw6.js → share._token-bfw7f9m5.js} +2 -2
  303. package/static/dist/assets/sliders-horizontal-l65hoa3x.js +1 -0
  304. package/static/dist/assets/{stateDiagram-D77RDMKH-co2ghkdp.js → stateDiagram-D77RDMKH-hh23v7y5.js} +1 -1
  305. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-jukr0d8h.js +1 -0
  306. package/static/dist/assets/{swimlanes-42K2YHIH-m30cktqe.js → swimlanes-42K2YHIH-gj2t3rm8.js} +1 -1
  307. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-ik9m9j2m.js +8 -0
  308. package/static/dist/assets/{timeline-definition-24CTP7MA-liuep6fl.js → timeline-definition-24CTP7MA-84s2w2sr.js} +1 -1
  309. package/static/dist/assets/toast-gqqcvxiy.js +56 -0
  310. package/static/dist/assets/{treeView-Q6P3EWNA-np5d2jv2.js → treeView-Q6P3EWNA-cmuj50l7.js} +1 -1
  311. package/static/dist/assets/{treemap-WGGIJYW6-cmpltxh1.js → treemap-WGGIJYW6-dpjlg0xc.js} +1 -1
  312. package/static/dist/assets/tts-jw84jlqy.js +1 -0
  313. package/static/dist/assets/{useForm-f1udzsjf.js → useForm-lxzhiqao.js} +1 -1
  314. package/static/dist/assets/{useParams-ierevvts.js → useParams-dt6gkoq7.js} +1 -1
  315. package/static/dist/assets/{useSearch-i7nr9ypl.js → useSearch-o8t49csk.js} +1 -1
  316. package/static/dist/assets/useTranscript-gx9umcc2.js +1 -0
  317. package/static/dist/assets/{vennDiagram-4TSXK5OY-b2g42qrg.js → vennDiagram-4TSXK5OY-g9g5o8vy.js} +1 -1
  318. package/static/dist/assets/{wardley-WFR3VGLG-lb0eqbff.js → wardley-WFR3VGLG-ij35obrl.js} +1 -1
  319. package/static/dist/assets/{wardleyDiagram-VM6X3IG4-ermwaqha.js → wardleyDiagram-VM6X3IG4-f9my7q7w.js} +1 -1
  320. package/static/dist/assets/{xychartDiagram-S5SC5T6Z-dll0jldk.js → xychartDiagram-S5SC5T6Z-olqx1s1p.js} +1 -1
  321. package/static/dist/index.html +1 -1
  322. package/static/dist/sw.js +1 -1
  323. package/static/dist/assets/AppShell-c5989995.js +0 -8
  324. package/static/dist/assets/ChatPage-bfg4khph.js +0 -6
  325. package/static/dist/assets/ConnectTargets-k5wo9yo8.js +0 -1
  326. package/static/dist/assets/Dialog-o8vo2aws.js +0 -1
  327. package/static/dist/assets/Field-j7o8cfps.js +0 -1
  328. package/static/dist/assets/HubRoute-efnwe4xz.js +0 -2
  329. package/static/dist/assets/Markdown-clqf5m22.js +0 -2
  330. package/static/dist/assets/OAuthSignIn-h7bjaqp6.js +0 -1
  331. package/static/dist/assets/ResourceSwitch-oai2zqaj.js +0 -1
  332. package/static/dist/assets/RightPanel-ho161a45.js +0 -1
  333. package/static/dist/assets/Select-d1g5a8bm.js +0 -1
  334. package/static/dist/assets/SessionListPanel-i7kaokhi.js +0 -1
  335. package/static/dist/assets/SettingsLayout-lnecm87w.js +0 -1
  336. package/static/dist/assets/States-easntmry.js +0 -1
  337. package/static/dist/assets/TerminalPanel-lxhwd4jl.js +0 -16
  338. package/static/dist/assets/Toaster-l4sciy8t.js +0 -1
  339. package/static/dist/assets/_app-mqexqbg8.js +0 -1
  340. package/static/dist/assets/_app.settings._section-djglnjmi.js +0 -3
  341. package/static/dist/assets/_app.tasks-l10py1c0.js +0 -1
  342. package/static/dist/assets/archive-ihqi7z2x.js +0 -1
  343. package/static/dist/assets/channel-jn4f8443.js +0 -1
  344. package/static/dist/assets/classDiagram-ZZMXUADV-dzrw66se.js +0 -1
  345. package/static/dist/assets/classDiagram-v2-VYDZK3BY-dzrw66se.js +0 -1
  346. package/static/dist/assets/completionAudio-ce86msbu.js +0 -1
  347. package/static/dist/assets/flowDiagram-HODETNUW-abekab15.js +0 -1
  348. package/static/dist/assets/folder-plus-ksaons88.js +0 -1
  349. package/static/dist/assets/index-d6vbxlra.js +0 -13
  350. package/static/dist/assets/index-ey6qo0zx.css +0 -2
  351. package/static/dist/assets/katex-cbdd1ohw.css +0 -1
  352. package/static/dist/assets/katex-pbij2oo6.js +0 -257
  353. package/static/dist/assets/login-iv6yq4xf.js +0 -1
  354. package/static/dist/assets/mermaid-HWGCJPDP-cwj5cl6k.js +0 -1
  355. package/static/dist/assets/onboarding-ff4emacu.js +0 -1
  356. package/static/dist/assets/queryKeys-x8fjxhut.js +0 -9
  357. package/static/dist/assets/sliders-horizontal-g5jkzd9k.js +0 -1
  358. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-faw1opgh.js +0 -1
  359. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-hfil433g.js +0 -8
  360. package/static/dist/assets/toast-zmb35u3r.js +0 -56
@@ -18,6 +18,7 @@ import importlib.util
18
18
  import inspect
19
19
  import json
20
20
  import logging
21
+ import math
21
22
  import os
22
23
  import queue
23
24
  import re
@@ -34,7 +35,7 @@ from ..errors import InvalidParams, RpcError
34
35
  from ..home import profile_home_param, scoped_home, session_workspace
35
36
  from ..rpc import CallContext
36
37
  from .checkpoints import checkpoint_kwargs
37
- from .providers import runtime_failure
38
+ from .providers import require_runtime_ownership, runtime_failure
38
39
  from .state_db import release_all, release_home, release_session_db
39
40
  from .toolsets import disabled_toolsets
40
41
 
@@ -69,6 +70,12 @@ class _Run:
69
70
  self.session_ended = False
70
71
  self.home = None # the profile home its agent runs under, set by ``start``
71
72
  self.mcp_discovery_outcome = "ok"
73
+ self.deadline: float | None = None
74
+
75
+ def prompt_cancelled(self, data: dict) -> bool:
76
+ # Vault answers racing a clear retain their existing unlock-then-release cleanup at turn end.
77
+ ended = self.session_ended and not str(data.get("kind") or "").startswith("vault_")
78
+ return self.ctx.cancelled or self.cancel.is_set() or self.finished.is_set() or ended
72
79
 
73
80
 
74
81
  class _ClarifyEntry:
@@ -80,7 +87,77 @@ class _ClarifyEntry:
80
87
  self.deadline: float | None = None
81
88
 
82
89
 
90
+ def _ask_clarify(run: _Run, data: dict, timeout: int, emit=None) -> str | None:
91
+ """Park on a prompt the server relays until it is answered, the turn is cancelled or ends, or ``timeout``
92
+ passes (``<= 0``: no limit). The answer, or None, goes back to the Agent and is never emitted."""
93
+ emit = emit or run.ctx.emit
94
+ entry = _ClarifyEntry(data)
95
+ entry.data["clarify_id"] = entry.clarify_id
96
+ entry.deadline = None if timeout <= 0 else time.monotonic() + float(timeout)
97
+ if run.deadline is not None:
98
+ entry.deadline = min(entry.deadline, run.deadline) if entry.deadline is not None else run.deadline
99
+ with run.lock:
100
+ if run.prompt_cancelled(data) or (entry.deadline is not None and entry.deadline <= time.monotonic()):
101
+ return None
102
+ run.clarify_entries[entry.clarify_id] = entry
103
+ try:
104
+ emit("clarify", dict(entry.data))
105
+ while not entry.event.is_set():
106
+ if run.prompt_cancelled(data):
107
+ break
108
+ if entry.deadline is None:
109
+ entry.event.wait(0.1)
110
+ continue
111
+ remaining = entry.deadline - time.monotonic()
112
+ if remaining <= 0:
113
+ break
114
+ entry.event.wait(min(0.1, remaining))
115
+ finally:
116
+ with run.lock:
117
+ run.clarify_entries.pop(entry.clarify_id, None)
118
+ emit("clarify_resolved", {"clarify_id": entry.clarify_id, "session_id": run.session_id})
119
+ return None if run.prompt_cancelled(data) else entry.result
120
+
121
+
122
+ def _clarify_callback(run: _Run, params: dict, emit=None):
123
+ def clarify_callback(question, choices=None, multi_select=False, questions=None):
124
+ # TAL-351: remove the legacy signature path when v0.21.6 is the oldest supported Agent.
125
+ structured = isinstance(question, list)
126
+ if structured:
127
+ questions, question = question, ""
128
+ choices_list = [str(c) for c in (choices or [])]
129
+ # Predecessor `_clarify_timeout_seconds`: the Agent's own resolver over this profile's config
130
+ # (``clarify.timeout`` else ``agent.clarify_timeout`` else 3600); ``<= 0`` waits until answered or cancelled.
131
+ # The advertised ``timeout_seconds`` is what the clients count down, so it is the same number.
132
+ timeout = _clarify_timeout(params)
133
+ if run.deadline is not None:
134
+ remaining = max(1, math.ceil(run.deadline - time.monotonic()))
135
+ timeout = min(timeout, remaining) if timeout > 0 else remaining
136
+ data = {"question": str(question or ""), "choices_offered": choices_list, "session_id": run.session_id, "kind": "clarify", "requested_at": time.time(), "timeout_seconds": timeout}
137
+ if multi_select:
138
+ data["multi_select"] = True
139
+ # Batch prompts (the Agent's ``_run_batch``) carry no top-level question and expect the ``{"answers": {...}}`` reply.
140
+ if isinstance(questions, list) and questions:
141
+ data["questions"] = questions
142
+ answer = _ask_clarify(run, data, timeout, emit)
143
+ if answer is not None:
144
+ if structured:
145
+ return {"answers": json.loads(answer)["answers"], "outcome": "submitted"}
146
+ return answer
147
+ cancelled = run.prompt_cancelled(data)
148
+ if structured:
149
+ return {"answers": {}, "outcome": "cancelled" if cancelled else "timed_out",
150
+ "notice": "The user cancelled the clarification." if cancelled else "The clarification timed out."}
151
+ # Older tools have no outcome field. A batch understands timed_out; a single needs an empty answer.
152
+ if questions:
153
+ return json.dumps({"answers": {}, "timed_out": not cancelled})
154
+ return ""
155
+
156
+ return clarify_callback
157
+
158
+
83
159
  _RUNS: dict[str, _Run] = {}
160
+ _COMMAND_RUNS: dict[str, _Run] = {}
84
161
  _RUNS_BY_SESSION: dict[str, str] = {}
85
162
  # Which run owns the Agent's gateway approval callback per session; register/unregister happen under one lock so a
86
163
  # stale run can never remove a successor's callback, and a successor can never leak a predecessor's.
@@ -108,6 +185,43 @@ _MCP_REDISCOVERY_WAIT = 2.0
108
185
  _MCP_PENDING_KEY = "mcp_discovery_pending"
109
186
 
110
187
 
188
+ def cancel_command_choices(session_id: str | None = None, home=None) -> None:
189
+ with _RUNS_LOCK:
190
+ for run in _COMMAND_RUNS.values():
191
+ if (session_id is None or run.session_id == session_id) and (home is None or run.home == os.path.realpath(home)):
192
+ run.session_ended = True
193
+ run.cancel.set()
194
+
195
+
196
+ @contextlib.contextmanager
197
+ def command_choices(ctx: CallContext, params: dict):
198
+ session_id = str(params.get("session_id") or "").strip()
199
+ try:
200
+ from hermes_cli.plugin_choices import bind_asker, clarify_asker
201
+ except ImportError:
202
+ yield
203
+ return
204
+ if not session_id:
205
+ yield
206
+ return
207
+ run = _Run(uuid.uuid4().hex, session_id, ctx)
208
+ # The server's commands.exec budget includes every question and the handler's final output.
209
+ run.deadline = time.monotonic() + 120
210
+ run.home = os.path.realpath(profile_home_param(params))
211
+ with _MCP_DISCOVERY_LOCK, _RUNS_LOCK:
212
+ ctx.check_cancelled()
213
+ if _DRAINING.is_set() or run.home in _MCP_CLOSING_HOMES:
214
+ raise RpcError("Command profile is unavailable", condition="profile_unavailable")
215
+ _COMMAND_RUNS[run.stream_id] = run
216
+ try:
217
+ with bind_asker(clarify_asker(_clarify_callback(run, params))):
218
+ yield
219
+ finally:
220
+ run.finished.set()
221
+ with _RUNS_LOCK:
222
+ _COMMAND_RUNS.pop(run.stream_id, None)
223
+
224
+
111
225
  def _snippet(raw: Any, limit: int = _TOOL_RESULT_SNIPPET_MAX) -> str:
112
226
  """Predecessor ``_tool_result_snippet``: the ``output``/``result``/``error`` of a dict (or JSON) result, hard-cut."""
113
227
  if limit <= 0:
@@ -313,8 +427,14 @@ def _usage(agent: Any) -> dict:
313
427
  value = getattr(agent, name, None)
314
428
  return value if isinstance(value, (int, float)) else 0
315
429
 
430
+ try:
431
+ from gateway.session_usage import plan_gauge
432
+ gauge = plan_gauge(agent)
433
+ except Exception: # noqa: BLE001 - older Agent capability omission clears a complete snapshot
434
+ gauge = None
316
435
  cost = getattr(agent, "session_estimated_cost_usd", None)
317
436
  return {
437
+ **({"plan_gauge": gauge} if gauge is not None else {}),
318
438
  "prompt_tokens": int(_num("session_prompt_tokens")),
319
439
  "completion_tokens": int(_num("session_completion_tokens")),
320
440
  "cache_read_tokens": int(_num("session_cache_read_tokens")),
@@ -695,6 +815,7 @@ def _cancel_checkpoint(run: _Run) -> list | None:
695
815
 
696
816
 
697
817
  def _resolve_runtime(provider: str | None, model: str) -> dict:
818
+ require_runtime_ownership(provider)
698
819
  try:
699
820
  from hermes_cli.runtime_provider import resolve_runtime_provider
700
821
  except Exception as exc: # noqa: BLE001
@@ -1363,6 +1484,7 @@ def _cancel_background_reviews(entries, message: str) -> list[threading.Thread]:
1363
1484
 
1364
1485
  def evict_all_agents() -> int:
1365
1486
  """Drop every cached agent (credentials or environment changed underneath them)."""
1487
+ cancel_command_choices()
1366
1488
  with _AGENT_CACHE_LOCK:
1367
1489
  entries = list(_AGENT_CACHE.values())
1368
1490
  _AGENT_CACHE.clear()
@@ -1387,7 +1509,7 @@ def drain_agents(timeout: float = _AGENT_RELEASE_TIMEOUT) -> None:
1387
1509
  run.cancel.set()
1388
1510
  while time.monotonic() < deadline:
1389
1511
  with _AGENT_CACHE_LOCK, _RUNS_LOCK:
1390
- if not _COMMITTING and not any(_RUNS.get(run.stream_id) is run for run in runs):
1512
+ if not _COMMITTING and not _COMMAND_RUNS and not any(_RUNS.get(run.stream_id) is run for run in runs):
1391
1513
  break
1392
1514
  time.sleep(0.05)
1393
1515
  # A turn may have cached its agent after the first eviction.
@@ -1440,6 +1562,7 @@ def release_profile_agents(home):
1440
1562
  raise RpcError("The profile is already being deleted.", condition="profile_unavailable")
1441
1563
  _MCP_CLOSING_HOMES.add(target)
1442
1564
  try:
1565
+ cancel_command_choices(home=home)
1443
1566
  with _AGENT_CACHE_LOCK:
1444
1567
  entries = [_AGENT_CACHE.pop(sid) for sid, entry in list(_AGENT_CACHE.items()) if os.path.realpath(entry[2]) == target]
1445
1568
  started = _release_agents_locked(entries)
@@ -1470,8 +1593,8 @@ def release_profile_agents(home):
1470
1593
 
1471
1594
 
1472
1595
  def _holding_home(target: str) -> bool:
1473
- with _AGENT_CACHE_LOCK:
1474
- return any(os.path.realpath(home) == target for _agent, home in _COMMITTING)
1596
+ with _AGENT_CACHE_LOCK, _RUNS_LOCK:
1597
+ return any(os.path.realpath(home) == target for _agent, home in _COMMITTING) or any(run.home == target for run in _COMMAND_RUNS.values())
1475
1598
 
1476
1599
 
1477
1600
  def _acquire_session_db(home):
@@ -1889,28 +2012,7 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
1889
2012
  emit("notice_clear", {"key": str(key)})
1890
2013
 
1891
2014
  def ask(data: dict, timeout: int) -> str | None:
1892
- """Park on a prompt the server relays until it is answered, the turn is cancelled or ends, or ``timeout``
1893
- passes (``<= 0``: no limit). The answer, or None, goes back to the Agent and is never emitted."""
1894
- entry = _ClarifyEntry(data)
1895
- entry.data["clarify_id"] = entry.clarify_id
1896
- entry.deadline = None if timeout <= 0 else time.monotonic() + float(timeout)
1897
- with run.lock:
1898
- run.clarify_entries[entry.clarify_id] = entry
1899
- emit("clarify", dict(entry.data))
1900
- while not entry.event.is_set():
1901
- if run.cancel.is_set() or run.finished.is_set() or (data.get("kind") == "skill_secret" and (ctx.cancelled or run.session_ended)):
1902
- break
1903
- if entry.deadline is None:
1904
- entry.event.wait(1.0)
1905
- continue
1906
- remaining = entry.deadline - time.monotonic()
1907
- if remaining <= 0:
1908
- break
1909
- entry.event.wait(min(1.0, remaining))
1910
- with run.lock:
1911
- run.clarify_entries.pop(entry.clarify_id, None)
1912
- emit("clarify_resolved", {"clarify_id": entry.clarify_id, "session_id": session_id})
1913
- return entry.result
2015
+ return _ask_clarify(run, data, timeout, emit)
1914
2016
 
1915
2017
  def ask_vault(kind: str, fields: dict) -> str:
1916
2018
  """TAL-887: a password-manager prompt; an empty answer is the Agent's decline, as are expiry and cancel."""
@@ -1966,35 +2068,7 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
1966
2068
  except Exception: # noqa: BLE001 - never expose an exception that could include the submitted value
1967
2069
  return {**skipped, "success": False}
1968
2070
 
1969
- def clarify_callback(question, choices=None, multi_select=False, questions=None):
1970
- # TAL-351: remove the legacy signature path when v0.21.6 is the oldest supported Agent.
1971
- structured = isinstance(question, list)
1972
- if structured:
1973
- questions, question = question, ""
1974
- choices_list = [str(c) for c in (choices or [])]
1975
- # Predecessor `_clarify_timeout_seconds`: the Agent's own resolver over this profile's config
1976
- # (``clarify.timeout`` else ``agent.clarify_timeout`` else 3600); ``<= 0`` waits until answered or cancelled.
1977
- # The advertised ``timeout_seconds`` is what the clients count down, so it is the same number.
1978
- timeout = _clarify_timeout(params)
1979
- data = {"question": str(question or ""), "choices_offered": choices_list, "session_id": session_id, "kind": "clarify", "requested_at": time.time(), "timeout_seconds": timeout}
1980
- if multi_select:
1981
- data["multi_select"] = True
1982
- # Batch prompts (the Agent's ``_run_batch``) carry no top-level question and expect the ``{"answers": {...}}`` reply.
1983
- if isinstance(questions, list) and questions:
1984
- data["questions"] = questions
1985
- answer = ask(data, timeout)
1986
- if answer is not None:
1987
- if structured:
1988
- return {"answers": json.loads(answer)["answers"], "outcome": "submitted"}
1989
- return answer
1990
- cancelled = run.cancel.is_set() or run.finished.is_set()
1991
- if structured:
1992
- return {"answers": {}, "outcome": "cancelled" if cancelled else "timed_out",
1993
- "notice": "The user cancelled the clarification." if cancelled else "The clarification timed out."}
1994
- # Older tools have no outcome field. A batch understands timed_out; a single needs an empty answer.
1995
- if questions:
1996
- return json.dumps({"answers": {}, "timed_out": not cancelled})
1997
- return ""
2071
+ clarify_callback = _clarify_callback(run, params, emit)
1998
2072
 
1999
2073
  # TAL-818: a ``manage_connections`` card (a hosted connect, an MCP install) opened; the server turns the frame into
2000
2074
  # the chat's connect card, which reads and answers the operation through ``connectors.operation`` / ``respond``.
@@ -2465,10 +2539,18 @@ def refine(params: dict) -> str:
2465
2539
  return f"⚗ Reviewing this conversation in the background{tail} — any memory/skill updates will be reported when done."
2466
2540
 
2467
2541
 
2468
- def _run_for(params: dict) -> _Run | None:
2542
+ def _run_for(params: dict, *, include_commands: bool = False) -> _Run | None:
2469
2543
  stream_id = str(params.get("stream_id") or "").strip()
2470
2544
  session_id = str(params.get("session_id") or "").strip()
2471
2545
  with _RUNS_LOCK:
2546
+ clarify_id = str(params.get("clarify_id") or "")
2547
+ home = params.get("profile_home")
2548
+ if include_commands and clarify_id and session_id and home:
2549
+ for run in _COMMAND_RUNS.values():
2550
+ if run.session_id == session_id and run.home == os.path.realpath(home):
2551
+ with run.lock:
2552
+ if clarify_id in run.clarify_entries:
2553
+ return run
2472
2554
  if stream_id:
2473
2555
  return _RUNS.get(stream_id)
2474
2556
  if session_id:
@@ -2619,6 +2701,7 @@ def register(registry) -> None:
2619
2701
  in place."""
2620
2702
  session_id = str(params.get("session_id") or "").strip()
2621
2703
  end_session = params.get("clear_session") is True
2704
+ cancel_command_choices(session_id)
2622
2705
  with _AGENT_CACHE_LOCK:
2623
2706
  with _RUNS_LOCK:
2624
2707
  run = _RUNS.get(_RUNS_BY_SESSION.get(session_id) or "")
@@ -2775,7 +2858,7 @@ def register(registry) -> None:
2775
2858
  def clarify_respond_(ctx: CallContext, params: dict) -> dict:
2776
2859
  response = str(params.get("response") or "")
2777
2860
  clarify_id = str(params.get("clarify_id") or "").strip()
2778
- run = _run_for(params)
2861
+ run = _run_for(params, include_commands=True)
2779
2862
  if run is None:
2780
2863
  return {"ok": False}
2781
2864
  if params.get("session_id") and str(params["session_id"]) != run.session_id:
@@ -2786,7 +2869,7 @@ def register(registry) -> None:
2786
2869
  if target is None or target.event.is_set() or (target.deadline is not None and time.monotonic() >= target.deadline):
2787
2870
  return {"ok": False}
2788
2871
  skill_secret = target.data.get("kind") == "skill_secret"
2789
- if skill_secret and (not clarify_id or ctx.cancelled or run.ctx.cancelled or run.cancel.is_set() or run.finished.is_set() or run.session_ended):
2872
+ if ctx.cancelled or run.prompt_cancelled(target.data) or (skill_secret and not clarify_id):
2790
2873
  return {"ok": False}
2791
2874
  # Secure answers are as typed; an empty reply is their decline. Only the first answer releases a waiter.
2792
2875
  secure = str(target.data.get("kind") or "").startswith("vault_") or skill_secret
@@ -239,7 +239,7 @@ def forget_profile_mcp_tools(home) -> None:
239
239
  _MCP_DISCOVERED_HOMES.discard(os.path.realpath(home))
240
240
 
241
241
 
242
- def _reload_mcp() -> str:
242
+ def _reload_mcp(*, log_errors=True) -> str:
243
243
  with _RELOAD_MCP_LOCK:
244
244
  try:
245
245
  from agent.secret_scope import is_multiplex_active
@@ -274,7 +274,8 @@ def _reload_mcp() -> str:
274
274
  with _lock:
275
275
  tools = [name for name in tools if _mcp_tool_server_names.get(name) in connected]
276
276
  except Exception as exc: # noqa: BLE001
277
- log.warning("Failed to reload MCP servers", exc_info=True)
277
+ if log_errors:
278
+ log.warning("Failed to reload MCP servers", exc_info=True)
278
279
  raise RpcError("Failed to reload MCP servers", condition="mcp_reload_failed") from exc
279
280
  added, removed, reconnected = connected - old, old - connected, connected & old
280
281
  lines = ["Reloaded MCP servers from configuration."]
@@ -846,11 +847,12 @@ def register(registry) -> None:
846
847
  output = execute_agent_command(command, session_id, workspace)
847
848
  source = "agent"
848
849
  if output is None:
849
- from .chat import _turn_identity
850
+ from .chat import _turn_identity, command_choices
850
851
 
851
852
  # TAL-904: a chat's plugin command runs under that chat's session and workspace, as its turns do.
852
- with _turn_identity(session_id, workspace) if session_id else contextlib.nullcontext():
853
+ with command_choices(ctx, params), (_turn_identity(session_id, workspace) if session_id else contextlib.nullcontext()):
853
854
  output = execute_plugin_command(command, session_id)
855
+ ctx.check_cancelled()
854
856
  source = "plugin"
855
857
  if output is None:
856
858
  raise RpcError(f"unknown command {parse_slash_command(command)[0]!r}", condition="command_not_found")
@@ -133,9 +133,8 @@ def _split_config_aliases(text: str, desired: dict) -> str:
133
133
  return output.getvalue()
134
134
 
135
135
 
136
- def write_config(path: Path, config: dict, *, profile_home: Path | None = None) -> dict:
137
- if not isinstance(config, dict):
138
- raise InvalidParams("config must be an object")
136
+ def assert_config_writable(profile_home: Path | None = None) -> None:
137
+ """Verify the owning profile policy before any config publication; uncertainty refuses."""
139
138
  try:
140
139
  managed = importlib.import_module("hermes_constants").get_managed_system(profile_home)
141
140
  except Exception as exc: # noqa: BLE001
@@ -144,6 +143,12 @@ def write_config(path: Path, config: dict, *, profile_home: Path | None = None)
144
143
  if managed is not None:
145
144
  raise RpcError("config.yaml is package-managed. Update the declarative configuration instead.",
146
145
  condition="config_write_refused")
146
+
147
+
148
+ def write_config(path: Path, config: dict, *, profile_home: Path | None = None) -> dict:
149
+ if not isinstance(config, dict):
150
+ raise InvalidParams("config must be an object")
151
+ assert_config_writable(profile_home)
147
152
  target = _write_target(path)
148
153
  target.parent.mkdir(parents=True, exist_ok=True)
149
154
  try:
@@ -8,10 +8,13 @@ registers its session operation here through ``remember_chat_operation``. Every
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
+ import contextvars
12
+ import hashlib
11
13
  import inspect
12
14
  import json
13
15
  import logging
14
16
  import threading
17
+ import uuid
15
18
  from collections import OrderedDict
16
19
  from pathlib import Path
17
20
  from types import SimpleNamespace
@@ -130,11 +133,46 @@ def accounts() -> dict:
130
133
  client = _portal()
131
134
  if not _authenticated(client):
132
135
  return {"accounts": []}
136
+ rows = client.list_accounts()
137
+ try:
138
+ from hermes_cli.free_tier_offer import note_connected_apps_seen
139
+ note_connected_apps_seen([{"connected": row.get("active") is True and not row.get("disabled")} for row in rows])
140
+ except ImportError:
141
+ pass # older Agents have no connector offer
142
+ except Exception:
143
+ log.warning("connector sighting unavailable")
133
144
  return {"accounts": [{
134
145
  "connection_id": row["connectionId"], "connector": row["connector"], "status": row["status"],
135
146
  "status_reason": row.get("statusReason"), "label": row["label"], "alias": row.get("alias"),
136
147
  "active": bool(row["active"]), "disabled": bool(row.get("disabled")), "created_at": row["createdAt"],
137
- } for row in client.list_accounts()]}
148
+ } for row in rows]}
149
+
150
+
151
+ def sign_in_offer(home: Path, claim_guest: str | None = None) -> dict:
152
+ """Native policy and CAS only. The server owns retention of the delivered page result."""
153
+ result = {"available": False, "guest_id": None, "eligible": False, "due_in": None, "claimed": False}
154
+ try:
155
+ from hermes_cli import anon_auth, free_tier_offer as native
156
+ if not all(callable(getattr(native, name, None)) for name in ("_eligible", "offer_due_in", "claim_offer")):
157
+ return result
158
+
159
+ def identity():
160
+ state = anon_auth.current_nous_state()
161
+ token = state.get("anon_token") if anon_auth.is_guest_state(state) else None
162
+ return hashlib.sha256(f"{home.resolve()}\0{token}".encode()).hexdigest() if isinstance(token, str) and token else None
163
+
164
+ guest = identity()
165
+ result["guest_id"] = guest
166
+ eligible = bool(native._eligible(native.CONNECTOR))
167
+ due = native.offer_due_in(native.CONNECTOR)
168
+ claimed = bool(guest and guest == claim_guest and native.claim_offer(native.CONNECTOR)) if claim_guest else False
169
+ if identity() != guest:
170
+ return {**result, "guest_id": None}
171
+ return {"available": True, "guest_id": guest, "eligible": eligible, "due_in": due, "claimed": claimed}
172
+ except ImportError:
173
+ return result
174
+ except Exception:
175
+ return result # retain a proven identity, never raw auth or native error details
138
176
 
139
177
 
140
178
  def remove(connection_id: str) -> dict:
@@ -201,6 +239,14 @@ def _operation(home: Path, op_id: str):
201
239
  operation = _OPS.get((str(home), op_id))
202
240
  if operation is None:
203
241
  raise RpcError("No connection operation with that id.", condition="operation_not_found")
242
+ account_id = getattr(operation, "_talaria_nous_account_id", None)
243
+ if account_id:
244
+ from . import nous_account
245
+ current = nous_account.read(home)
246
+ if not current or current["account_id"] != account_id:
247
+ from tools.connectors.contract import SettleReason
248
+ operation.settle(SettleReason.interrupt)
249
+ raise RpcError("No connection operation for the current account.", condition="operation_not_found")
204
250
  return operation
205
251
 
206
252
 
@@ -226,8 +272,62 @@ def operation_view(operation) -> dict:
226
272
  }
227
273
 
228
274
 
229
- def connect(home: Path, connector: str) -> dict:
275
+ def _reconnect(home: Path, connector: str, account_id: str) -> dict:
276
+ """Compose the existing native operation with an account-fenced, noncreating client.
277
+
278
+ Native account.find_or_start_operation always uses a creating, live-credential client
279
+ in its later thread. Its lifecycle primitives accept the client this result needs.
280
+ """
281
+ from tools.connectors import account, live, managed
282
+ from tools.connectors.gateway.client import ConnectorClient
283
+ from tools.connectors.operation import ConnectionOperation, Target
284
+ from tools.connectors.run import drive_operation
285
+ from tools.connectors.contract import SettleReason
286
+ from . import nous_account
287
+
288
+ with account._start_lock:
289
+ existing = live.find_target(connector, profile_home=str(home))
290
+ if existing is not None:
291
+ if getattr(existing, "_talaria_nous_account_id", None) != account_id:
292
+ raise RpcError("Finish the current app connection before reconnecting this result.", condition="operation_refused")
293
+ _remember(home, existing)
294
+ return operation_view(_operation(home, existing.op_id))
295
+ client = ConnectorClient(initiator="desktop_page", header_provider=lambda url: nous_account.connector_headers(home, account_id, url))
296
+ operation = ConnectionOperation([Target(connector, "connector", "connect")], session_key=f"account:{uuid.uuid4().hex}")
297
+ operation._talaria_nous_account_id = account_id
298
+ operation._talaria_nous_client = client
299
+ start = account.AccountOperationStart(operation=operation, started=True)
300
+ context = contextvars.copy_context()
301
+
302
+ def run():
303
+ try:
304
+ drive_operation(operation, managed.managed_kind(client, "connect", force=False),
305
+ connection_callback=lambda payload: start.done.set(),
306
+ tick_seconds=managed.WATCH_TICK_SECONDS, with_urls_in_result=False)
307
+ except Exception:
308
+ start.failed = True
309
+ operation.settle(SettleReason.interrupt)
310
+ finally:
311
+ live.close(operation)
312
+ start.done.set()
313
+
314
+ live.open(operation)
315
+ _remember(home, operation)
316
+ try:
317
+ threading.Thread(target=lambda: context.run(run), daemon=True, name="connector-account-operation").start()
318
+ except Exception:
319
+ live.close(operation)
320
+ operation.settle(SettleReason.interrupt)
321
+ raise
322
+ if account.wait_for_prepare(start) and start.failed:
323
+ raise RpcError("Connector request failed.", condition="connectors_failed")
324
+ return operation_view(_operation(home, operation.op_id))
325
+
326
+
327
+ def connect(home: Path, connector: str, *, account_id: str | None = None) -> dict:
230
328
  """Start (or rejoin) the account-owned connect for ``connector`` and wait for its authorization link."""
329
+ if account_id is not None:
330
+ return _reconnect(home, connector, account_id)
231
331
  from tools.connectors import account
232
332
  from tools.connectors.contract import TargetState
233
333
  from tools.connectors.run import reissue
@@ -236,8 +336,11 @@ def connect(home: Path, connector: str) -> dict:
236
336
  page = {"initiator": "desktop_page"} if "initiator" in inspect.signature(account.find_or_start_operation).parameters else {}
237
337
  start = account.find_or_start_operation([connector], action="connect", profile_home=str(home), **page)
238
338
  _remember(home, start.operation)
339
+ _operation(home, start.operation.op_id)
239
340
  if not start.started:
240
341
  target = start.operation.target(connector)
342
+ if target is not None and target.state in (TargetState.failed, TargetState.expired) and getattr(start.operation, "_talaria_nous_client", None) is not None:
343
+ return respond(home, start.operation.op_id, "retry", connector, {})
241
344
  if target is not None and target.state in (TargetState.failed, TargetState.expired) and reissue(start.operation, [connector], **page):
242
345
  raise RpcError("This connection cannot be started again.", condition="operation_refused")
243
346
  return operation_view(start.operation)
@@ -262,7 +365,14 @@ def respond(home: Path, op_id: str, action: str, target: str | None, env: dict)
262
365
  if action == "continue":
263
366
  apply_answer(operation, json.dumps({"settled_by": SettleReason.continue_.value}))
264
367
  elif action == "retry" and row.kind == "connector":
265
- if reissue(operation, [row.name]):
368
+ client = getattr(operation, "_talaria_nous_client", None)
369
+ if client is not None:
370
+ from tools.connectors.managed import mint
371
+ from tools.connectors.contract import Actor, TargetState
372
+ if row.state not in (TargetState.failed, TargetState.expired):
373
+ raise RpcError("This target cannot be run again.", condition="operation_refused")
374
+ mint(client, operation, [row.name], reinitiate=True, actor=Actor.user)
375
+ elif reissue(operation, [row.name]):
266
376
  raise RpcError("This target cannot be run again.", condition="operation_refused")
267
377
  else:
268
378
  status = "skipped" if action == "skip" else "approved"
@@ -288,6 +398,21 @@ def remember_chat_operation(home: Path, payload: dict) -> dict | None:
288
398
 
289
399
 
290
400
  def register(registry_) -> None:
401
+ @registry_.method("connectors.offer.status")
402
+ def offer_status_(ctx: CallContext, params: dict) -> dict:
403
+ home = profile_home_param(params)
404
+ with scoped_home(home):
405
+ return sign_in_offer(home)
406
+
407
+ @registry_.method("connectors.offer.claim")
408
+ def offer_claim_(ctx: CallContext, params: dict) -> dict:
409
+ guest = params.get("guest_id")
410
+ if not isinstance(guest, str) or not guest:
411
+ raise InvalidParams("guest_id is required")
412
+ home = profile_home_param(params)
413
+ with scoped_home(home):
414
+ return sign_in_offer(home, guest)
415
+
291
416
  def page(params: dict, read, *, creates_guest: bool = False):
292
417
  with scoped_home(profile_home_param(params)):
293
418
  _open(creates_guest=creates_guest)
@@ -346,7 +471,16 @@ def register(registry_) -> None:
346
471
  def connect_(ctx: CallContext, params: dict) -> dict:
347
472
  slug = _slug(params)
348
473
  home = profile_home_param(params)
349
- return page(params, lambda: connect(home, slug), creates_guest=True)
474
+ def begin():
475
+ if "nous_account_id" in params:
476
+ from . import nous_account
477
+ view = nous_account.read(home)
478
+ if not view or view["account_id"] != params["nous_account_id"] or slug not in view["reconnect"]:
479
+ raise RpcError("The signed-in account or reconnect result changed. Refresh before reconnecting.", condition="connectors_forbidden")
480
+ if not any(row["slug"] == slug for row in catalog()["connectors"]):
481
+ raise RpcError("This app is not available to reconnect.", condition="connectors_forbidden")
482
+ return connect(home, slug, account_id=params.get("nous_account_id"))
483
+ return page(params, begin, creates_guest="nous_account_id" not in params)
350
484
 
351
485
  def gated() -> None:
352
486
  # Reads and moves on an operation pass the availability gate too, without creating an identity.
@@ -190,6 +190,27 @@ def _plugins(home):
190
190
  return out
191
191
 
192
192
 
193
+ def reviewed_producer(home, name, pin, sources):
194
+ """Verify a store adapter's native registration, profile consent and reviewed bytes without loading it."""
195
+ try:
196
+ from hermes_cli import plugins_cmd as pc
197
+ metadata = pc._read_install_metadata()
198
+ for plugin in _plugins(home):
199
+ if plugin['name'] != name or not plugin['enabled'] or plugin.get('source') != 'user':
200
+ continue
201
+ root, anchor = Path(plugin['_dir']).parent, Path(plugin['_asset_anchor'])
202
+ record = metadata.get(root.name, {})
203
+ catalog = record.get('catalog', {})
204
+ if catalog.get('name') != name or catalog.get('sha') != pin or record.get('revision') != pin:
205
+ continue
206
+ if all((body := _read(root, file, anchor=anchor)) is not None
207
+ and hashlib.sha256(body).hexdigest() == digest for file, digest in sources.items()):
208
+ return pin
209
+ except (ImportError, AttributeError, OSError, ValueError, TypeError):
210
+ pass
211
+ return None
212
+
213
+
193
214
  def _named(home, name, *, require_panel=True):
194
215
  return next((p for p in _plugins(home) if p['name'] == name and p['enabled'] and (p['panel'] if require_panel else p.get('_api_file'))), None)
195
216