@maudecode/talaria-web 1.21.0 → 1.23.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (390) 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 +225 -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.d.ts +1 -1
  8. package/dist/api/raw-routes.js +15 -3
  9. package/dist/api/raw-routes.js.map +1 -1
  10. package/dist/api/router.d.ts +41 -0
  11. package/dist/api/router.js +2 -0
  12. package/dist/api/router.js.map +1 -1
  13. package/dist/api/sessions-router.d.ts +590 -160
  14. package/dist/api/sessions-router.js +14 -14
  15. package/dist/api/sessions-router.js.map +1 -1
  16. package/dist/api/settings-router.d.ts +948 -1
  17. package/dist/api/settings-router.js +54 -2
  18. package/dist/api/settings-router.js.map +1 -1
  19. package/dist/api/sse-routes.d.ts +1 -1
  20. package/dist/api/sse-routes.js +11 -3
  21. package/dist/api/sse-routes.js.map +1 -1
  22. package/dist/api/tools-raw.js +27 -136
  23. package/dist/api/tools-raw.js.map +1 -1
  24. package/dist/api/tools-router.d.ts +109 -24
  25. package/dist/api/tools-router.js +69 -9
  26. package/dist/api/tools-router.js.map +1 -1
  27. package/dist/app.js +5 -1
  28. package/dist/app.js.map +1 -1
  29. package/dist/auth/gate.js +5 -1
  30. package/dist/auth/gate.js.map +1 -1
  31. package/dist/cli/ctl.d.ts +5 -2
  32. package/dist/cli/ctl.js +90 -42
  33. package/dist/cli/ctl.js.map +1 -1
  34. package/dist/config/agent-config.d.ts +6 -1
  35. package/dist/config/agent-config.js +75 -3
  36. package/dist/config/agent-config.js.map +1 -1
  37. package/dist/feedback.d.ts +21 -0
  38. package/dist/feedback.js +328 -0
  39. package/dist/feedback.js.map +1 -0
  40. package/dist/fs/stat.d.ts +2 -0
  41. package/dist/fs/stat.js +4 -0
  42. package/dist/fs/stat.js.map +1 -1
  43. package/dist/http/context.d.ts +10 -1
  44. package/dist/http/context.js +0 -6
  45. package/dist/http/context.js.map +1 -1
  46. package/dist/http/pinned.js +19 -8
  47. package/dist/http/pinned.js.map +1 -1
  48. package/dist/providers/catalog.d.ts +10 -2
  49. package/dist/providers/catalog.js +113 -15
  50. package/dist/providers/catalog.js.map +1 -1
  51. package/dist/redact.js +9 -3
  52. package/dist/redact.js.map +1 -1
  53. package/dist/release.d.ts +14 -1
  54. package/dist/release.js +40 -2
  55. package/dist/release.js.map +1 -1
  56. package/dist/runtime.js +29 -5
  57. package/dist/runtime.js.map +1 -1
  58. package/dist/server.js +1 -0
  59. package/dist/server.js.map +1 -1
  60. package/dist/sessions/anchor.d.ts +3 -2
  61. package/dist/sessions/anchor.js +11 -10
  62. package/dist/sessions/anchor.js.map +1 -1
  63. package/dist/sessions/cli-sessions.js +11 -6
  64. package/dist/sessions/cli-sessions.js.map +1 -1
  65. package/dist/sessions/compress.d.ts +10 -9
  66. package/dist/sessions/compress.js +31 -20
  67. package/dist/sessions/compress.js.map +1 -1
  68. package/dist/sessions/gateway-sessions.d.ts +11 -0
  69. package/dist/sessions/gateway-sessions.js +79 -24
  70. package/dist/sessions/gateway-sessions.js.map +1 -1
  71. package/dist/sessions/gateway-watcher.js +8 -4
  72. package/dist/sessions/gateway-watcher.js.map +1 -1
  73. package/dist/sessions/groups.d.ts +60 -0
  74. package/dist/sessions/groups.js +339 -0
  75. package/dist/sessions/groups.js.map +1 -0
  76. package/dist/sessions/list.d.ts +8 -3
  77. package/dist/sessions/list.js +17 -6
  78. package/dist/sessions/list.js.map +1 -1
  79. package/dist/sessions/merge.d.ts +14 -5
  80. package/dist/sessions/merge.js +42 -13
  81. package/dist/sessions/merge.js.map +1 -1
  82. package/dist/sessions/pending.d.ts +1 -1
  83. package/dist/sessions/pending.js +15 -6
  84. package/dist/sessions/pending.js.map +1 -1
  85. package/dist/sessions/prefix-index.d.ts +1 -14
  86. package/dist/sessions/prefix-index.js +1 -50
  87. package/dist/sessions/prefix-index.js.map +1 -1
  88. package/dist/sessions/reasoning-display.d.ts +2 -0
  89. package/dist/sessions/reasoning-display.js +31 -0
  90. package/dist/sessions/reasoning-display.js.map +1 -0
  91. package/dist/sessions/relay.d.ts +34 -2
  92. package/dist/sessions/relay.js +84 -7
  93. package/dist/sessions/relay.js.map +1 -1
  94. package/dist/sessions/service.d.ts +7 -2
  95. package/dist/sessions/service.js +106 -64
  96. package/dist/sessions/service.js.map +1 -1
  97. package/dist/sessions/session.d.ts +6 -0
  98. package/dist/sessions/session.js +13 -0
  99. package/dist/sessions/session.js.map +1 -1
  100. package/dist/sessions/shares.d.ts +3 -1
  101. package/dist/sessions/shares.js +33 -11
  102. package/dist/sessions/shares.js.map +1 -1
  103. package/dist/sessions/source-kind.js +2 -0
  104. package/dist/sessions/source-kind.js.map +1 -1
  105. package/dist/sessions/state-db.d.ts +2 -1
  106. package/dist/sessions/state-db.js +12 -5
  107. package/dist/sessions/state-db.js.map +1 -1
  108. package/dist/sessions/storage/json-documents.js +2 -0
  109. package/dist/sessions/storage/json-documents.js.map +1 -1
  110. package/dist/sessions/store.d.ts +1 -0
  111. package/dist/sessions/store.js +10 -3
  112. package/dist/sessions/store.js.map +1 -1
  113. package/dist/sessions/streams.d.ts +7 -2
  114. package/dist/sessions/streams.js +16 -6
  115. package/dist/sessions/streams.js.map +1 -1
  116. package/dist/sessions/titles.d.ts +2 -0
  117. package/dist/sessions/titles.js +11 -0
  118. package/dist/sessions/titles.js.map +1 -1
  119. package/dist/sessions/todo.js +10 -1
  120. package/dist/sessions/todo.js.map +1 -1
  121. package/dist/sessions/turn.js +50 -21
  122. package/dist/sessions/turn.js.map +1 -1
  123. package/dist/settings.js +6 -1
  124. package/dist/settings.js.map +1 -1
  125. package/dist/sidecar/client.d.ts +3 -0
  126. package/dist/sidecar/client.js +2 -1
  127. package/dist/sidecar/client.js.map +1 -1
  128. package/dist/sidecar/discover.js +3 -2
  129. package/dist/sidecar/discover.js.map +1 -1
  130. package/dist/spa.js +1 -1
  131. package/dist/spa.js.map +1 -1
  132. package/dist/test/group-fixture.d.ts +66 -0
  133. package/dist/test/group-fixture.js +89 -0
  134. package/dist/test/group-fixture.js.map +1 -0
  135. package/dist/test/harness.d.ts +3 -0
  136. package/dist/test/harness.js +3 -0
  137. package/dist/test/harness.js.map +1 -1
  138. package/dist/tools/connectors.d.ts +9 -1
  139. package/dist/tools/connectors.js +48 -0
  140. package/dist/tools/connectors.js.map +1 -1
  141. package/dist/tools/gateway-restart.d.ts +1 -1
  142. package/dist/tools/insights.d.ts +3 -0
  143. package/dist/tools/insights.js +6 -7
  144. package/dist/tools/insights.js.map +1 -1
  145. package/dist/tools/skills.d.ts +6 -0
  146. package/dist/tools/skills.js +17 -0
  147. package/dist/tools/skills.js.map +1 -1
  148. package/dist/tools/update-notifications.d.ts +2 -0
  149. package/dist/tools/update-notifications.js +7 -3
  150. package/dist/tools/update-notifications.js.map +1 -1
  151. package/dist/tools/updates.d.ts +26 -9
  152. package/dist/tools/updates.js +144 -47
  153. package/dist/tools/updates.js.map +1 -1
  154. package/dist/workspace/fs.d.ts +2 -0
  155. package/dist/workspace/fs.js +63 -1
  156. package/dist/workspace/fs.js.map +1 -1
  157. package/dist/workspace/media-refs.js +10 -3
  158. package/dist/workspace/media-refs.js.map +1 -1
  159. package/dist/workspace/upload.d.ts +1 -1
  160. package/dist/workspace/upload.js +52 -37
  161. package/dist/workspace/upload.js.map +1 -1
  162. package/dist/workspace/workspaces.d.ts +5 -1
  163. package/dist/workspace/workspaces.js +32 -6
  164. package/dist/workspace/workspaces.js.map +1 -1
  165. package/package.json +4 -3
  166. package/sidecar/agent_importer.json +8 -0
  167. package/sidecar/scripts/agent_importer_contract.py +19 -0
  168. package/sidecar/scripts/suite_runner.py +55 -8
  169. package/sidecar/scripts/test.sh +3 -1
  170. package/sidecar/talaria_sidecar/__init__.py +1 -1
  171. package/sidecar/talaria_sidecar/home.py +12 -5
  172. package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
  173. package/sidecar/talaria_sidecar/methods/agent_imports.py +346 -0
  174. package/sidecar/talaria_sidecar/methods/chat.py +141 -58
  175. package/sidecar/talaria_sidecar/methods/commands.py +6 -4
  176. package/sidecar/talaria_sidecar/methods/config.py +8 -3
  177. package/sidecar/talaria_sidecar/methods/connectors.py +138 -4
  178. package/sidecar/talaria_sidecar/methods/dashboard_plugins.py +21 -0
  179. package/sidecar/talaria_sidecar/methods/delegation.py +126 -0
  180. package/sidecar/talaria_sidecar/methods/gateway.py +5 -0
  181. package/sidecar/talaria_sidecar/methods/gateway_sessions.py +35 -6
  182. package/sidecar/talaria_sidecar/methods/groups.py +120 -0
  183. package/sidecar/talaria_sidecar/methods/memory.py +27 -3
  184. package/sidecar/talaria_sidecar/methods/nous_account.py +130 -0
  185. package/sidecar/talaria_sidecar/methods/oauth.py +25 -4
  186. package/sidecar/talaria_sidecar/methods/outbound_drafts.py +1 -19
  187. package/sidecar/talaria_sidecar/methods/plugin_catalog.py +73 -2
  188. package/sidecar/talaria_sidecar/methods/providers.py +51 -1
  189. package/sidecar/talaria_sidecar/methods/skill_revisions.py +177 -0
  190. package/sidecar/talaria_sidecar/methods/skills.py +10 -0
  191. package/sidecar/talaria_sidecar/methods/tts.py +67 -0
  192. package/sidecar/talaria_sidecar/rpc.py +11 -2
  193. package/static/dist/FILES.txt +154 -143
  194. package/static/dist/THIRD_PARTY_LICENSES.md +1 -1
  195. package/static/dist/assets/AppShell-jwd95ofg.js +8 -0
  196. package/static/dist/assets/{Button-nuaikybd.js → Button-nrcj7shj.js} +1 -1
  197. package/static/dist/assets/ChatPage-fmgk8xpk.js +2 -0
  198. package/static/dist/assets/Composer-oya4ggu7.js +5 -0
  199. package/static/dist/assets/ConnectTargets-rg90uiui.js +1 -0
  200. package/static/dist/assets/Dialog-gk0uatl8.js +1 -0
  201. package/static/dist/assets/ExtensionRoute-ji4xce9l.js +1 -0
  202. package/static/dist/assets/HubRoute-2pgy4aa2.js +2 -0
  203. package/static/dist/assets/Markdown-d6br42ts.js +2 -0
  204. package/static/dist/assets/{MarkdownRenderer-ccgqrza3.js → MarkdownRenderer-k8rmb83g.js} +6 -6
  205. package/static/dist/assets/OAuthSignIn-pbls9ljk.js +1 -0
  206. package/static/dist/assets/QueueCard-liib58ru.js +1 -0
  207. package/static/dist/assets/ResourceSwitch-gtdv2jgs.js +1 -0
  208. package/static/dist/assets/RightPanel-b89e4kx3.js +1 -0
  209. package/static/dist/assets/Select-l6qybenp.js +1 -0
  210. package/static/dist/assets/SessionListPanel-l4vy95j8.js +1 -0
  211. package/static/dist/assets/SettingsLayout-chw7pl2m.js +1 -0
  212. package/static/dist/assets/TerminalPanel-qrd0gkx7.js +16 -0
  213. package/static/dist/assets/Toaster-ihzgaq9z.js +1 -0
  214. package/static/dist/assets/_app-cn1au4zq.js +1 -0
  215. package/static/dist/assets/{_app.ext._extensionId-n797jp72.js → _app.ext._extensionId-m4npkrr0.js} +1 -1
  216. package/static/dist/assets/_app.groups-c8ycbp5n.js +1 -0
  217. package/static/dist/assets/{_app.index-ne43mdw2.js → _app.index-f1fckz3h.js} +1 -1
  218. package/static/dist/assets/{_app.insights-fqonovb4.js → _app.insights-ezivk7ad.js} +1 -1
  219. package/static/dist/assets/{_app.kanban-kysliv19.js → _app.kanban-zbkeov0f.js} +1 -1
  220. package/static/dist/assets/{_app.logs-febut82r.js → _app.logs-dqam5zyz.js} +1 -1
  221. package/static/dist/assets/{_app.memory-ha86oj1c.js → _app.memory-gzxkm3a0.js} +1 -1
  222. package/static/dist/assets/{_app.profiles-cnu3gxxq.js → _app.profiles-j28l31hl.js} +1 -1
  223. package/static/dist/assets/{_app.session._sessionId-ifzjpcgn.js → _app.session._sessionId-55egsufs.js} +1 -1
  224. package/static/dist/assets/{_app.settings-nmakdiaf.js → _app.settings-f5ltpieb.js} +1 -1
  225. package/static/dist/assets/_app.settings._section-dda0vfot.js +3 -0
  226. package/static/dist/assets/{_app.skills-f3wad8yo.js → _app.skills-kuc81hq0.js} +1 -1
  227. package/static/dist/assets/_app.tasks-gyy6vj5l.js +1 -0
  228. package/static/dist/assets/{_app.todos-bml1m3r1.js → _app.todos-d0xoy68t.js} +1 -1
  229. package/static/dist/assets/{_app.workspaces-f3d066lx.js → _app.workspaces-pawp2gxk.js} +1 -1
  230. package/static/dist/assets/_delete-qnxuo87s.js +1 -0
  231. package/static/dist/assets/{abnfDiagram-VCTEODGH-i53jc4yp.js → abnfDiagram-VCTEODGH-mhav7n5a.js} +1 -1
  232. package/static/dist/assets/{architecture-7GRP2DOG-e3bv5m6f.js → architecture-7GRP2DOG-ozbanukt.js} +1 -1
  233. package/static/dist/assets/{architectureDiagram-5GKGNRK7-pakekxu2.js → architectureDiagram-5GKGNRK7-febtor5m.js} +1 -1
  234. package/static/dist/assets/archive-gyk9c34a.js +1 -0
  235. package/static/dist/assets/autoReadClaim-nlyv8tu9.js +1 -0
  236. package/static/dist/assets/{blockDiagram-I7D4REHJ-bc6bo1n6.js → blockDiagram-I7D4REHJ-ocais2jf.js} +1 -1
  237. package/static/dist/assets/{c4Diagram-7LVT6UL2-btvwk2qx.js → c4Diagram-7LVT6UL2-cgpfjx5f.js} +1 -1
  238. package/static/dist/assets/channel-els5ri1f.js +1 -0
  239. package/static/dist/assets/{chunk-4HAMMTFA-dlwab11c.js → chunk-4HAMMTFA-hvqdiiow.js} +1 -1
  240. package/static/dist/assets/{chunk-75Z2AOVW-dp5gh7i8.js → chunk-75Z2AOVW-hxbulvtb.js} +1 -1
  241. package/static/dist/assets/{chunk-DU6HZSFF-bi06zl1c.js → chunk-DU6HZSFF-kkp6a7gp.js} +3 -3
  242. package/static/dist/assets/{chunk-F27PBJKO-kvv1rnlh.js → chunk-F27PBJKO-ljz0gssc.js} +1 -1
  243. package/static/dist/assets/{chunk-GMAD6QVW-ji0rx9by.js → chunk-GMAD6QVW-bo0enyxn.js} +1 -1
  244. package/static/dist/assets/{chunk-GVQU2GXP-nmz0j82p.js → chunk-GVQU2GXP-cnq7l2v6.js} +1 -1
  245. package/static/dist/assets/{chunk-IMKFNOWR-kmi1qx88.js → chunk-IMKFNOWR-gjnkrfu6.js} +1 -1
  246. package/static/dist/assets/{chunk-L3NEJ4N5-xhb7mebj.js → chunk-L3NEJ4N5-gjfp71qi.js} +1 -1
  247. package/static/dist/assets/{chunk-OSK3NFVY-5a9sgscq.js → chunk-OSK3NFVY-ieq5r94b.js} +1 -1
  248. package/static/dist/assets/{chunk-P2QGCYS3-dfezp4vu.js → chunk-P2QGCYS3-hdefjk56.js} +1 -1
  249. package/static/dist/assets/{chunk-POPQ4Y6H-gd2vq3c0.js → chunk-POPQ4Y6H-mcbt8wpr.js} +1 -1
  250. package/static/dist/assets/{chunk-PWAF6VOD-wvw2nksf.js → chunk-PWAF6VOD-mx2drja9.js} +1 -1
  251. package/static/dist/assets/{chunk-SHT3W25Y-f58q97px.js → chunk-SHT3W25Y-j7dd9i66.js} +1 -1
  252. package/static/dist/assets/{chunk-SVP7TREG-abgwpyz9.js → chunk-SVP7TREG-jcnt3lxt.js} +1 -1
  253. package/static/dist/assets/{chunk-TICWLB2K-h4sbs8ds.js → chunk-TICWLB2K-lrwv5hx9.js} +1 -1
  254. package/static/dist/assets/classDiagram-ZZMXUADV-krpt9pdd.js +1 -0
  255. package/static/dist/assets/classDiagram-v2-VYDZK3BY-krpt9pdd.js +1 -0
  256. package/static/dist/assets/completionAudio-fhxf0h9j.js +1 -0
  257. package/static/dist/assets/{copy-m9h56qa8.js → copy-gyzrqmyr.js} +1 -1
  258. package/static/dist/assets/{cynefin-OW5HDTMX-g081vffs.js → cynefin-OW5HDTMX-heq36srt.js} +1 -1
  259. package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-odxvjesk.js → cynefinDiagram-5FMLGOSQ-crrmq338.js} +1 -1
  260. package/static/dist/assets/{dagre-GXQ25YYZ-cpdwzjs4.js → dagre-GXQ25YYZ-m7fpnt6k.js} +1 -1
  261. package/static/dist/assets/{diagram-S7CK7UJ4-clv5mxvu.js → diagram-S7CK7UJ4-ebm1ebv6.js} +1 -1
  262. package/static/dist/assets/{diagram-UQ7AKVKN-m2ia1mbf.js → diagram-UQ7AKVKN-hc0ypsp1.js} +1 -1
  263. package/static/dist/assets/{diagram-VSXAHHWV-ea6zv636.js → diagram-VSXAHHWV-mu8q5n4b.js} +1 -1
  264. package/static/dist/assets/{diagram-VX7I27RA-oqlef685.js → diagram-VX7I27RA-i488wdlb.js} +1 -1
  265. package/static/dist/assets/{diagram-Z3DM3KII-kjubcbwu.js → diagram-Z3DM3KII-gz3nwvq9.js} +1 -1
  266. package/static/dist/assets/{dist-carq1azj.js → dist-ge7hom9n.js} +4 -4
  267. package/static/dist/assets/{dist-crc46di9.js → dist-kpg5ccm9.js} +1 -1
  268. package/static/dist/assets/{ebnfDiagram-PWID7BFC-fg0zri3e.js → ebnfDiagram-PWID7BFC-lwskecx4.js} +1 -1
  269. package/static/dist/assets/{erDiagram-RLTQ6QDP-eb7pmk3x.js → erDiagram-RLTQ6QDP-c0bysigr.js} +1 -1
  270. package/static/dist/assets/{eventmodeling-NTZA5JFV-gvi1fxhn.js → eventmodeling-NTZA5JFV-k19e509j.js} +1 -1
  271. package/static/dist/assets/feedbackVideo.worker-JBVT3sDD.js +480 -0
  272. package/static/dist/assets/flowDiagram-HODETNUW-n4m8e6kq.js +1 -0
  273. package/static/dist/assets/folder-plus-m1ywe0cv.js +1 -0
  274. package/static/dist/assets/{ganttDiagram-EL5Y4UJY-jmisowrc.js → ganttDiagram-EL5Y4UJY-mz1p8yzt.js} +1 -1
  275. package/static/dist/assets/getPseudoElementBounds-ci0x9gki.js +1 -0
  276. package/static/dist/assets/{gitGraph-4MIJSDKK-m7kt3u6e.js → gitGraph-4MIJSDKK-h0uskiog.js} +1 -1
  277. package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-nq7vfzh2.js → gitGraphDiagram-WWUBYQGX-e4d9um1j.js} +1 -1
  278. package/static/dist/assets/group_discussions-fk5hry4l.js +1 -0
  279. package/static/dist/assets/{highlighted-body-KPVGNVTW-gcu017ay.js → highlighted-body-KPVGNVTW-d47n6oqu.js} +1 -1
  280. package/static/dist/assets/{host-ipp4e84c.js → host-f7eudvft.js} +1 -1
  281. package/static/dist/assets/index-lab8g9gs.css +2 -0
  282. package/static/dist/assets/index-n5rg2q2r.js +13 -0
  283. package/static/dist/assets/{info-A6RAGUB7-idazwfqe.js → info-A6RAGUB7-k04of9c6.js} +1 -1
  284. package/static/dist/assets/{infoDiagram-27XIBGKW-bb9jh8gl.js → infoDiagram-27XIBGKW-l98n1tgz.js} +1 -1
  285. package/static/dist/assets/{ishikawaDiagram-5VMMS53U-nqc132rv.js → ishikawaDiagram-5VMMS53U-gkznu2d6.js} +1 -1
  286. package/static/dist/assets/{journeyDiagram-3NMN7TZE-fpvnhy44.js → journeyDiagram-3NMN7TZE-mt23qvx1.js} +1 -1
  287. package/static/dist/assets/{kanban-definition-UXKFOSKX-ndzqm6fu.js → kanban-definition-UXKFOSKX-ab93l7y5.js} +1 -1
  288. package/static/dist/assets/katex-ic33v820.js +257 -0
  289. package/static/dist/assets/katex-qcv7xo0t.css +1 -0
  290. package/static/dist/assets/{lazyRouteComponent-javud235.js → lazyRouteComponent-dt9efiqk.js} +1 -1
  291. package/static/dist/assets/{line-g625e7cj.js → line-dkm9r3as.js} +1 -1
  292. package/static/dist/assets/link-2-ozyjqwhh.js +1 -0
  293. package/static/dist/assets/{link-gfdaho08.js → link-dip3g6zs.js} +1 -1
  294. package/static/dist/assets/{locale-cs-n41hptse.js → locale-cs-brltdkcp.js} +1 -1
  295. package/static/dist/assets/{locale-de-k7ht47ew.js → locale-de-h6e9oq6v.js} +1 -1
  296. package/static/dist/assets/{locale-es-ih078lco.js → locale-es-oklmh80f.js} +1 -1
  297. package/static/dist/assets/{locale-fr-ojabnf41.js → locale-fr-b17ozoi2.js} +1 -1
  298. package/static/dist/assets/{locale-it-kb96jufy.js → locale-it-whe01eef.js} +1 -1
  299. package/static/dist/assets/{locale-ja-jpsp3xs1.js → locale-ja-g1s0rtpc.js} +1 -1
  300. package/static/dist/assets/{locale-pl-dbap4leu.js → locale-pl-d9p3lqcb.js} +1 -1
  301. package/static/dist/assets/{locale-pt-nci7w72x.js → locale-pt-l75on74f.js} +1 -1
  302. package/static/dist/assets/{locale-ru-soghe4cs.js → locale-ru-wl3mbsaw.js} +1 -1
  303. package/static/dist/assets/{locale-tr-ks00mu68.js → locale-tr-cz87itrq.js} +1 -1
  304. package/static/dist/assets/{locale-vi-ij7twfm6.js → locale-vi-elqj1y4k.js} +1 -1
  305. package/static/dist/assets/{locale-zh-Hant-feakwr7a.js → locale-zh-Hant-hf8gqm7d.js} +1 -1
  306. package/static/dist/assets/{locale-zh-ren0vjyv.js → locale-zh-cdckwb0u.js} +1 -1
  307. package/static/dist/assets/login-go5xfzkx.js +1 -0
  308. package/static/dist/assets/mermaid-HWGCJPDP-o7wh7c74.js +1 -0
  309. package/static/dist/assets/{mermaid-parser.core-plzkuzov.js → mermaid-parser.core-c4ywstu7.js} +3 -3
  310. package/static/dist/assets/{mindmap-definition-YA3MSWOX-ojrgs569.js → mindmap-definition-YA3MSWOX-mnvkgz5u.js} +1 -1
  311. package/static/dist/assets/onboarding-hbbpvp6w.js +1 -0
  312. package/static/dist/assets/{packet-AYTQ26CC-gyaf9s8h.js → packet-AYTQ26CC-dfm2ygox.js} +1 -1
  313. package/static/dist/assets/{pegDiagram-XKGWAZYB-bf6xj8mq.js → pegDiagram-XKGWAZYB-kfbyls4v.js} +1 -1
  314. package/static/dist/assets/{pie-WAS4IAKB-f5ymzelo.js → pie-WAS4IAKB-jniw7r2b.js} +1 -1
  315. package/static/dist/assets/{pieDiagram-E7YTZNPT-d0qjl3zi.js → pieDiagram-E7YTZNPT-cir9nlg4.js} +1 -1
  316. package/static/dist/assets/presence-ivo54s19.js +1 -0
  317. package/static/dist/assets/prompt_queue_cancelled-cebcuagu.js +1 -0
  318. package/static/dist/assets/{quadrantDiagram-AXDQQJYC-oqy1mras.js → quadrantDiagram-AXDQQJYC-2nnb2u4m.js} +1 -1
  319. package/static/dist/assets/queryKeys-mbi6o5vh.js +9 -0
  320. package/static/dist/assets/{radar-RG4KPBEZ-b6jcjvda.js → radar-RG4KPBEZ-jfvtqhq2.js} +1 -1
  321. package/static/dist/assets/{railroad-74A4TZTK-e3a6lk5k.js → railroad-74A4TZTK-hnm0ikx7.js} +1 -1
  322. package/static/dist/assets/{railroad-abnf-HS5TGJTU-ff5hocrx.js → railroad-abnf-HS5TGJTU-hcfz5gyc.js} +1 -1
  323. package/static/dist/assets/{railroad-ebnf-LZEXJU2U-exkhyw4n.js → railroad-ebnf-LZEXJU2U-ocbvmcwo.js} +1 -1
  324. package/static/dist/assets/{railroad-peg-WCYAUIDC-jefk9b7c.js → railroad-peg-WCYAUIDC-kkbzgo8d.js} +1 -1
  325. package/static/dist/assets/{railroadDiagram-O6MQD6OU-e4amks1i.js → railroadDiagram-O6MQD6OU-ox5v7853.js} +1 -1
  326. package/static/dist/assets/rename-ccd52yws.js +1 -0
  327. package/static/dist/assets/{requirementDiagram-BXWQKSXE-c77k1e2y.js → requirementDiagram-BXWQKSXE-e1iq54h4.js} +1 -1
  328. package/static/dist/assets/{sankeyDiagram-P5KCCOFB-oa1cc52s.js → sankeyDiagram-P5KCCOFB-mmza9r2u.js} +1 -1
  329. package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-j20gqt22.js → sequenceDiagram-WJ2MYXX4-dioic69h.js} +1 -1
  330. package/static/dist/assets/{share._token-kmfoflw6.js → share._token-i51g4ccy.js} +1 -1
  331. package/static/dist/assets/sliders-horizontal-nj1dipq4.js +1 -0
  332. package/static/dist/assets/{stateDiagram-D77RDMKH-co2ghkdp.js → stateDiagram-D77RDMKH-do5wvqjw.js} +1 -1
  333. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-f0sedx52.js +1 -0
  334. package/static/dist/assets/{swimlanes-42K2YHIH-m30cktqe.js → swimlanes-42K2YHIH-mhjzgtgl.js} +1 -1
  335. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-n0x444kc.js +8 -0
  336. package/static/dist/assets/{timeline-definition-24CTP7MA-liuep6fl.js → timeline-definition-24CTP7MA-i5820ppg.js} +1 -1
  337. package/static/dist/assets/toast-esv4ma70.js +56 -0
  338. package/static/dist/assets/{treeView-Q6P3EWNA-np5d2jv2.js → treeView-Q6P3EWNA-btp8feob.js} +1 -1
  339. package/static/dist/assets/{treemap-WGGIJYW6-cmpltxh1.js → treemap-WGGIJYW6-lv71dt7p.js} +1 -1
  340. package/static/dist/assets/tts-bq7ax6xp.js +1 -0
  341. package/static/dist/assets/{useForm-f1udzsjf.js → useForm-d5wxx0l0.js} +1 -1
  342. package/static/dist/assets/{useParams-ierevvts.js → useParams-gk1u0y6m.js} +1 -1
  343. package/static/dist/assets/{useSearch-i7nr9ypl.js → useSearch-nsrme1pf.js} +1 -1
  344. package/static/dist/assets/useTranscript-m9ve9mml.js +1 -0
  345. package/static/dist/assets/{vennDiagram-4TSXK5OY-b2g42qrg.js → vennDiagram-4TSXK5OY-bffv7e11.js} +1 -1
  346. package/static/dist/assets/{wardley-WFR3VGLG-lb0eqbff.js → wardley-WFR3VGLG-f1m16ivx.js} +1 -1
  347. package/static/dist/assets/{wardleyDiagram-VM6X3IG4-ermwaqha.js → wardleyDiagram-VM6X3IG4-ous4pq48.js} +1 -1
  348. package/static/dist/assets/{xychartDiagram-S5SC5T6Z-dll0jldk.js → xychartDiagram-S5SC5T6Z-jxgbp4as.js} +1 -1
  349. package/static/dist/index.html +1 -1
  350. package/static/dist/sw.js +1 -1
  351. package/static/dist/assets/AppShell-c5989995.js +0 -8
  352. package/static/dist/assets/ChatPage-bfg4khph.js +0 -6
  353. package/static/dist/assets/ConnectTargets-k5wo9yo8.js +0 -1
  354. package/static/dist/assets/Dialog-o8vo2aws.js +0 -1
  355. package/static/dist/assets/ExtensionRoute-l1yjrso4.js +0 -1
  356. package/static/dist/assets/Field-j7o8cfps.js +0 -1
  357. package/static/dist/assets/HubRoute-efnwe4xz.js +0 -2
  358. package/static/dist/assets/Markdown-clqf5m22.js +0 -2
  359. package/static/dist/assets/OAuthSignIn-h7bjaqp6.js +0 -1
  360. package/static/dist/assets/ResourceSwitch-oai2zqaj.js +0 -1
  361. package/static/dist/assets/RightPanel-ho161a45.js +0 -1
  362. package/static/dist/assets/Select-d1g5a8bm.js +0 -1
  363. package/static/dist/assets/SessionListPanel-i7kaokhi.js +0 -1
  364. package/static/dist/assets/SettingsLayout-lnecm87w.js +0 -1
  365. package/static/dist/assets/States-easntmry.js +0 -1
  366. package/static/dist/assets/TerminalPanel-lxhwd4jl.js +0 -16
  367. package/static/dist/assets/Toaster-l4sciy8t.js +0 -1
  368. package/static/dist/assets/_app-mqexqbg8.js +0 -1
  369. package/static/dist/assets/_app.settings._section-djglnjmi.js +0 -3
  370. package/static/dist/assets/_app.tasks-l10py1c0.js +0 -1
  371. package/static/dist/assets/archive-ihqi7z2x.js +0 -1
  372. package/static/dist/assets/channel-jn4f8443.js +0 -1
  373. package/static/dist/assets/classDiagram-ZZMXUADV-dzrw66se.js +0 -1
  374. package/static/dist/assets/classDiagram-v2-VYDZK3BY-dzrw66se.js +0 -1
  375. package/static/dist/assets/completionAudio-ce86msbu.js +0 -1
  376. package/static/dist/assets/flowDiagram-HODETNUW-abekab15.js +0 -1
  377. package/static/dist/assets/folder-plus-ksaons88.js +0 -1
  378. package/static/dist/assets/index-d6vbxlra.js +0 -13
  379. package/static/dist/assets/index-ey6qo0zx.css +0 -2
  380. package/static/dist/assets/katex-cbdd1ohw.css +0 -1
  381. package/static/dist/assets/katex-pbij2oo6.js +0 -257
  382. package/static/dist/assets/login-iv6yq4xf.js +0 -1
  383. package/static/dist/assets/mermaid-HWGCJPDP-cwj5cl6k.js +0 -1
  384. package/static/dist/assets/onboarding-ff4emacu.js +0 -1
  385. package/static/dist/assets/queryKeys-x8fjxhut.js +0 -9
  386. package/static/dist/assets/refresh-cw-lfp4u1oh.js +0 -1
  387. package/static/dist/assets/sliders-horizontal-g5jkzd9k.js +0 -1
  388. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-faw1opgh.js +0 -1
  389. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-hfil433g.js +0 -8
  390. package/static/dist/assets/toast-zmb35u3r.js +0 -56
@@ -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
 
@@ -0,0 +1,126 @@
1
+ """Native delegation registry and route validation for the auxiliary settings owner."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import asdict
6
+
7
+ from ..errors import InvalidParams, RpcError
8
+ from ..home import profile_home_param, scoped_home
9
+
10
+
11
+ def registry(config: dict) -> dict:
12
+ section = config.get("delegation")
13
+ section = section if isinstance(section, dict) else {}
14
+ try:
15
+ from agent.delegation_backends import list_backends
16
+ from agent.delegation_registry import backend_status, configured_backend
17
+ except ImportError:
18
+ configured = section.get("backend") or "default"
19
+ return {"supported": False, "configured": configured, "effective": None, "available": False,
20
+ "blocked_reason": "Update Hermes Agent to configure delegation modes.", "fixed_model_active": configured == "default",
21
+ "backends": [{"name": configured, "label": configured, "description": "", "available": False, "reason": "Update Hermes Agent."}], "profiles": []}
22
+ from hermes_cli.plugins import discover_plugins
23
+ from hermes_cli.profiles import list_profile_names
24
+
25
+ discover_plugins()
26
+ configured = configured_backend(section)
27
+ provider, reason = backend_status(configured)
28
+ available = provider is not None and not reason
29
+ backends = [asdict(info) for info in list_backends()]
30
+ if not any(info["name"] == configured for info in backends):
31
+ backends.append({"name": configured, "label": configured, "description": "", "available": False, "reason": reason})
32
+ return {"supported": True, "configured": configured, "effective": configured if available else None,
33
+ "available": available, "blocked_reason": reason or None, "fixed_model_active": available and configured == "default",
34
+ "backends": backends, "profiles": list_profile_names()}
35
+
36
+
37
+ def clean_routes(backend: str, block: dict, current: dict) -> dict:
38
+ """Use native canonical keys and parsers, preserving credentials by original route identity."""
39
+ from agent.delegation_provider import ROUTE_KEYS
40
+ from agent.fast_mode import STATIC_TIERS, parse_exact_service_tier
41
+ from hermes_constants import parse_reasoning_effort
42
+ from hermes_cli.profiles import list_profile_names
43
+
44
+ field = "options" if backend == "model_map" else "profiles"
45
+ entries = block.get(field)
46
+ if not isinstance(entries, list):
47
+ raise InvalidParams(f"{field} must be a list")
48
+ stored = {row.get("name"): row for row in current.get(field, []) if isinstance(row, dict)}
49
+ known = set(list_profile_names()) if backend == "profile_routes" else set()
50
+ rows, names = [], []
51
+ for entry in entries:
52
+ if not isinstance(entry, dict):
53
+ raise InvalidParams("Each route must be an object")
54
+ name = str(entry.get("name") or "").strip()
55
+ if not name or name in names:
56
+ raise InvalidParams("Route names must be nonempty and unique")
57
+ names.append(name)
58
+ if backend == "profile_routes":
59
+ if name not in known:
60
+ raise InvalidParams(f"Profile {name!r} does not exist")
61
+ rows.append({"name": name, **({"description": entry["description"].strip()} if entry.get("description", "").strip() else {})})
62
+ continue
63
+ source = stored.get(entry.get("original_name") or name, {})
64
+ # Hidden canonical values survive both edits and renames.
65
+ row = {key: value for key, value in source.items() if key in ROUTE_KEYS}
66
+ row.update({key: value for key, value in entry.items() if key in ROUTE_KEYS or key in {"name", "description"}})
67
+ row["name"] = name
68
+ for key in ("description", "model", "provider", "base_url", "api_mode"):
69
+ if key in row:
70
+ row[key] = str(row[key] or "").strip()
71
+ if not row.get("description") or not row.get("model") or not (row.get("provider") or row.get("base_url")):
72
+ raise InvalidParams(f"Route {name!r} needs a description, model and provider or endpoint")
73
+ for key in ("reasoning_effort", "service_tier"):
74
+ if key in entry and not entry[key]:
75
+ row.pop(key, None)
76
+ effort = row.get("reasoning_effort")
77
+ if effort and parse_reasoning_effort(effort) is None:
78
+ raise InvalidParams("Unknown reasoning effort")
79
+ tier = row.get("service_tier")
80
+ if tier:
81
+ try:
82
+ parsed = parse_exact_service_tier(tier)
83
+ except ValueError:
84
+ raise InvalidParams("Unknown service tier") from None
85
+ if parsed is None or parsed and parsed not in STATIC_TIERS:
86
+ raise InvalidParams("Unknown service tier")
87
+ if entry.get("api_key_clear"):
88
+ row.pop("api_key", None)
89
+ elif not entry.get("api_key") and source.get("api_key"):
90
+ row["api_key"] = source["api_key"]
91
+ if "fallback_providers" in row and not isinstance(row["fallback_providers"], list):
92
+ raise InvalidParams("fallback_providers must be a list")
93
+ rows.append(row)
94
+ default = (block.get("default") or "").strip()
95
+ if default and default not in names:
96
+ raise InvalidParams("The default route must be in the list")
97
+ return {field: rows, **({"default": default} if default else {})}
98
+
99
+
100
+ def register(methods) -> None:
101
+ @methods.method("aux.delegation")
102
+ def describe(ctx, params):
103
+ with scoped_home(profile_home_param(params)):
104
+ return registry(params.get("config") or {})
105
+
106
+ @methods.method("aux.delegation_routes")
107
+ def validate(ctx, params):
108
+ with scoped_home(profile_home_param(params)):
109
+ return {"block": clean_routes(params["backend"], params["block"], params["current"])}
110
+
111
+ @methods.method("aux.delegation_backend")
112
+ def select(ctx, params):
113
+ from hermes_cli.config import get_config_path
114
+ from agent.delegation_backends import set_global_backend
115
+ from .config import config_path_param
116
+
117
+ with scoped_home(profile_home_param(params)):
118
+ if get_config_path().resolve() != config_path_param(params).resolve():
119
+ raise RpcError("Native delegation config path differs from the configured file", condition="config_write_refused")
120
+ try:
121
+ set_global_backend(params["backend"])
122
+ except PermissionError:
123
+ raise RpcError("config.yaml is package-managed. Update the declarative configuration instead.", condition="config_write_refused") from None
124
+ except ValueError as exc:
125
+ raise InvalidParams(str(exc)) from None
126
+ return {"ok": True}
@@ -243,6 +243,11 @@ def _run(action: str, home: Path, cli_profile: str | None, ctx: CallContext, *,
243
243
 
244
244
 
245
245
  def register(registry) -> None:
246
+ @registry.method("gateway.groups")
247
+ def groups_(ctx: CallContext, params: dict) -> dict:
248
+ from .groups import groups
249
+ return groups(ctx, params)
250
+
246
251
  @registry.method("gateway.session")
247
252
  def session_(ctx: CallContext, params: dict) -> dict:
248
253
  from .gateway_sessions import session
@@ -70,6 +70,24 @@ def _control_refusal(home: Path, sid: str) -> str | None:
70
70
  return None
71
71
 
72
72
 
73
+ def _todo_state(value: object) -> dict | None:
74
+ if not isinstance(value, dict) or not isinstance(value.get("todos"), list):
75
+ return None
76
+ fields = {"id": (str, int, float), "text": (str,), "content": (str,), "title": (str,),
77
+ "status": (str,), "done": (bool,), "completed": (bool,)}
78
+ revision = value.get("revision", 0)
79
+ if type(revision) is not int or revision < 0:
80
+ return None
81
+ return {"todos": [{k: v for k, v in item.items() if k in fields and type(v) in fields[k]}
82
+ for item in value["todos"] if isinstance(item, dict)], "revision": revision}
83
+
84
+
85
+ def _event(value: dict) -> dict:
86
+ if value.get("type") == "todo.updated":
87
+ return {**value, "payload": _todo_state(value.get("payload")) or {}}
88
+ return value
89
+
90
+
73
91
  def _snapshot(value: dict) -> dict:
74
92
  # Raw conversation rows contain private principal/admission metadata; the client needs
75
93
  # display rows, not those bindings or the inventory's system prompt and credential hint.
@@ -84,7 +102,8 @@ def _snapshot(value: dict) -> dict:
84
102
  {"author": reaction["author"], "emoji": reaction["emoji"]} for reaction in reactions
85
103
  if isinstance(reaction, dict) and reaction.get("author") in ("user", "agent")
86
104
  and isinstance(reaction.get("emoji"), str) and reaction["emoji"] in _REACTION_EMOJIS] if isinstance(reactions, list) else []})
87
- return {**value, "messages": rows, "info": {k: v for k, v in value["info"].items() if k in info_keys}}
105
+ return {**value, "messages": rows, "info": {k: v for k, v in value["info"].items() if k in info_keys},
106
+ **({"todo_state": _todo_state(value["todo_state"])} if "todo_state" in value else {})}
88
107
 
89
108
 
90
109
  def _refused(state: str, reason: str) -> dict:
@@ -92,7 +111,7 @@ def _refused(state: str, reason: str) -> dict:
92
111
 
93
112
 
94
113
  @asynccontextmanager
95
- async def _connect(home: Path, endpoint):
114
+ async def _connect(home: Path, endpoint, *, subject: str | None = None):
96
115
  from hermes_constants import get_hermes_home
97
116
  from hermes_cli.gateway_client import GatewayClientError, connect_gateway
98
117
 
@@ -100,7 +119,7 @@ async def _connect(home: Path, endpoint):
100
119
  # override is mandatory so its bootstrap ticket cannot fall back to the launch profile.
101
120
  if Path(get_hermes_home()).resolve() != home:
102
121
  raise GatewayClientError("profile_mismatch")
103
- async with connect_gateway(endpoint) as client:
122
+ async with connect_gateway(endpoint, **({"subject": subject} if subject is not None else {})) as client:
104
123
  websocket = client.websocket
105
124
  # GatewayClient queues server requests as events. Drain continuously, including
106
125
  # during an RPC, so unsupported sudo/secure inputs never wait for a Web renderer.
@@ -211,7 +230,17 @@ async def _run(ctx, params: dict, home: Path, discovery) -> dict:
211
230
  subscription = value["subscription_id"]
212
231
  if reason := await asyncio.to_thread(_control_refusal, home, sid):
213
232
  raise GatewayRPCError(reason)
214
- return {**_snapshot(value), "reactions_supported": _verified_contract(endpoint, "message-reactions-v1")}
233
+ gauge = None
234
+ if params["action"] in {"snapshot", "watch"}:
235
+ try:
236
+ stats = await client.rpc("session.stats", session_id=sid)
237
+ after = await client.rpc("session.resume", session_id=sid)
238
+ if all(value.get(k) == after.get(k) for k in ("session_id", "authority_epoch", "replay_epoch", "execution_generation")):
239
+ gauge = stats.get("plan_gauge")
240
+ except GatewayRPCError:
241
+ pass # older owners have no stats capability
242
+ return {**_snapshot(value), "plan_gauge": gauge,
243
+ "reactions_supported": _verified_contract(endpoint, "message-reactions-v1")}
215
244
 
216
245
  receipt = lock = None
217
246
  try:
@@ -332,7 +361,7 @@ async def _run(ctx, params: dict, home: Path, discovery) -> dict:
332
361
  if (event.get("type") in {"session.updated", "notification.show", "notification.clear"}
333
362
  or event.get("execution_generation") == snap["execution_generation"]
334
363
  or event.get("type") == "session.info" and event.get("payload", {}).get("execution_generation") == snap["execution_generation"]):
335
- ctx.emit("event", event)
364
+ ctx.emit("event", _event(event))
336
365
  sequence = replay["last_sequence"]
337
366
  ctx.emit("replay_complete", {})
338
367
  checked = asyncio.get_running_loop().time()
@@ -386,7 +415,7 @@ async def _run(ctx, params: dict, home: Path, discovery) -> dict:
386
415
  if event.get("seq", 0) <= sequence:
387
416
  continue
388
417
  sequence = event["seq"]
389
- ctx.emit("event", event)
418
+ ctx.emit("event", _event(event))
390
419
  return result
391
420
  except GatewayRPCError as exc:
392
421
  if params["action"] == "react" and str(exc) not in {"invalid_params", "not_found", "permission_denied", "profile_mismatch"}:
@@ -0,0 +1,120 @@
1
+ """Closed native room operations. Attach only, with the Web principal's own grant."""
2
+ from __future__ import annotations
3
+
4
+ import asyncio
5
+ import json
6
+ from contextlib import suppress
7
+
8
+ from ..errors import InvalidParams
9
+ from ..home import profile_home_param
10
+ from .gateway_sessions import _connect, _owner_refusal, _with_owner
11
+
12
+ _FIELDS = {
13
+ "capabilities": set(), "list": {"offset", "limit"}, "state": {"room_id", "include_disbanded"},
14
+ "log": {"room_id", "since_seq", "limit"}, "create": {"room_id", "name", "members"},
15
+ "rename": {"room_id", "event_id", "name"}, "disband": {"room_id", "cancel_id"},
16
+ "send": {"room_id", "event_id", "payload"}, "stop": {"room_id", "cancel_id"},
17
+ "retry": {"room_id", "member_id", "task_id", "execution_generation"},
18
+ "discard": {"room_id", "member_id", "task_id", "execution_generation"},
19
+ "approve": {"room_id", "member_id", "task_id", "execution_generation", "request_id", "choice"},
20
+ "attachment.upload": {"room_id", "upload_id", "kind", "name", "mime", "data_base64"},
21
+ "attachment.download": {"room_id", "event_id", "attachment_id"},
22
+ }
23
+ _READS = {"capabilities", "list", "state", "log", "attachment.download"}
24
+ _REFUSALS = {"permission_denied", "profile_mismatch", "invalid_params", "stale_generation", "unknown_execution",
25
+ "unsupported_operation", "session_busy", "runtime_coordination_required"}
26
+
27
+
28
+ def _result(state, reason=None, value=None):
29
+ return {"state": state, "reason": reason, "value": value}
30
+
31
+
32
+ async def _run(home, subject, operation, discovery, expected_room=None):
33
+ from hermes_cli.gateway_client import GatewayRPCError
34
+ action = operation["action"]
35
+ method = "groups." + action
36
+ dispatched = False
37
+ try:
38
+ if discovery.state != "ready":
39
+ return _result("unavailable", "stopped")
40
+ principal = 'auth:v1:' + json.dumps(['talaria', '', subject], separators=(',', ':'))
41
+ async with _connect(home, discovery.endpoint, subject=principal) as (client, _):
42
+ # A native 15 MB attachment expands to 20 MB on the wire. Keep the shared native client,
43
+ # with a bounded receiver large enough for the owner's supported attachment envelope.
44
+ client.websocket.protocol.max_size = 24 * 1024 * 1024
45
+ described = await client.rpc("runtime.describe")
46
+ if _owner_refusal(described, discovery.endpoint):
47
+ return _result("unavailable", "unsupported")
48
+ caps = await client.rpc("groups.capabilities")
49
+ if (not isinstance(caps, dict) or type(caps.get("driver")) is not bool
50
+ or not isinstance(caps.get("methods"), list) or method not in caps["methods"]):
51
+ return _result("unavailable", "unsupported")
52
+ if not caps["driver"]:
53
+ return _result("unavailable", "driver_inactive")
54
+ if action == "capabilities":
55
+ return _result("available", value=caps)
56
+ if expected_room is not None:
57
+ current = await client.rpc("groups.state", room_id=expected_room["room_id"], include_disbanded=True)
58
+ room = current.get("room", {})
59
+ if (room.get("room_id") != expected_room["room_id"]
60
+ or room.get("authority_gateway_id") != expected_room["gateway_id"]
61
+ or room.get("authority_epoch") != expected_room["authority_epoch"]):
62
+ return _result("refused", "refused")
63
+ if action == "send":
64
+ operation = {**operation, "payload": {**operation["payload"], "thread_id": operation["event_id"]}}
65
+ dispatched = True
66
+ value = await client.rpc(method, **{k: v for k, v in operation.items() if k != "action"}, _timeout=25)
67
+ if not isinstance(value, dict) or len(json.dumps(value).encode()) > 24 * 1024 * 1024:
68
+ return _result("unknown" if action not in _READS else "unavailable", "unknown")
69
+ return _result("available", value=value)
70
+ except GatewayRPCError as exc:
71
+ reason = str(exc)
72
+ # Native errors are data, never public diagnostics. Only fixed refusals prove non-admission.
73
+ if reason in _REFUSALS:
74
+ return _result("refused", "refused")
75
+ if "method" in reason.lower() and not dispatched:
76
+ return _result("unavailable", "unsupported")
77
+ return _result("unknown" if dispatched and action not in _READS else "unavailable", "unknown")
78
+ except Exception: # noqa: BLE001 - a transport loss never authorizes automatic replay
79
+ return _result("unknown" if dispatched and action not in _READS else "unavailable", "unknown")
80
+
81
+
82
+ async def _read(ctx, coroutine):
83
+ task = asyncio.create_task(coroutine)
84
+ try:
85
+ while not task.done():
86
+ await asyncio.wait({task}, timeout=.1)
87
+ ctx.check_cancelled()
88
+ return await task
89
+ finally:
90
+ if not task.done():
91
+ task.cancel()
92
+ with suppress(asyncio.CancelledError):
93
+ await task
94
+
95
+
96
+ def groups(ctx, params):
97
+ operation, subject = params.get("operation"), params.get("subject")
98
+ if (set(params) - {"profile_home", "subject", "operation", "expected_room"}
99
+ or not {"profile_home", "subject", "operation"} <= set(params)
100
+ or not isinstance(subject, str) or not 0 < len(subject) <= 128
101
+ or not isinstance(operation, dict) or operation.get("action") not in _FIELDS
102
+ or set(operation) - (_FIELDS[operation["action"]] | {"action"})):
103
+ raise InvalidParams("invalid room operation")
104
+ expected = params.get("expected_room")
105
+ if expected is not None and (not isinstance(expected, dict)
106
+ or set(expected) != {"room_id", "gateway_id", "authority_epoch"}
107
+ or expected.get("room_id") != operation.get("room_id")
108
+ or not isinstance(expected.get("gateway_id"), str)
109
+ or type(expected.get("authority_epoch")) is not int):
110
+ raise InvalidParams("invalid room authority fence")
111
+ home = profile_home_param(params).resolve()
112
+ ctx.check_cancelled()
113
+ def run(discovery):
114
+ coroutine = _run(home, subject, operation, discovery, expected)
115
+ return _read(ctx, coroutine) if operation["action"] in _READS else coroutine
116
+ result = _with_owner(home, run)
117
+ if "value" not in result:
118
+ return _result("unavailable", "stopped" if result["state"] == "stopped" else "unsupported"
119
+ if result["state"] == "incompatible" else "unavailable")
120
+ return result