@maudecode/talaria-web 1.20.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 (410) hide show
  1. package/_release.json +8 -8
  2. package/dist/api/automation-router.d.ts +319 -14
  3. package/dist/api/automation-router.js +21 -1
  4. package/dist/api/automation-router.js.map +1 -1
  5. package/dist/api/chat-router.d.ts +615 -69
  6. package/dist/api/chat-router.js +22 -170
  7. package/dist/api/chat-router.js.map +1 -1
  8. package/dist/api/git-router.d.ts +50 -0
  9. package/dist/api/git-router.js +17 -4
  10. package/dist/api/git-router.js.map +1 -1
  11. package/dist/api/groups-router.d.ts +764 -0
  12. package/dist/api/groups-router.js +79 -0
  13. package/dist/api/groups-router.js.map +1 -0
  14. package/dist/api/raw-routes.js +3 -1
  15. package/dist/api/raw-routes.js.map +1 -1
  16. package/dist/api/router.d.ts +1 -1
  17. package/dist/api/sessions-router.d.ts +1675 -167
  18. package/dist/api/sessions-router.js +67 -53
  19. package/dist/api/sessions-router.js.map +1 -1
  20. package/dist/api/settings-router.d.ts +911 -1
  21. package/dist/api/settings-router.js +53 -2
  22. package/dist/api/settings-router.js.map +1 -1
  23. package/dist/api/sse-routes.d.ts +1 -1
  24. package/dist/api/sse-routes.js +15 -5
  25. package/dist/api/sse-routes.js.map +1 -1
  26. package/dist/api/tools-raw.js +27 -136
  27. package/dist/api/tools-raw.js.map +1 -1
  28. package/dist/api/tools-router.d.ts +588 -51
  29. package/dist/api/tools-router.js +98 -16
  30. package/dist/api/tools-router.js.map +1 -1
  31. package/dist/app.js +5 -1
  32. package/dist/app.js.map +1 -1
  33. package/dist/auth/gate.js +5 -1
  34. package/dist/auth/gate.js.map +1 -1
  35. package/dist/config/agent-config.d.ts +6 -1
  36. package/dist/config/agent-config.js +75 -3
  37. package/dist/config/agent-config.js.map +1 -1
  38. package/dist/feedback.d.ts +18 -0
  39. package/dist/feedback.js +145 -0
  40. package/dist/feedback.js.map +1 -0
  41. package/dist/fs/stat.d.ts +2 -0
  42. package/dist/fs/stat.js +4 -0
  43. package/dist/fs/stat.js.map +1 -1
  44. package/dist/http/context.d.ts +9 -0
  45. package/dist/http/context.js +0 -6
  46. package/dist/http/context.js.map +1 -1
  47. package/dist/http/pinned.js +19 -8
  48. package/dist/http/pinned.js.map +1 -1
  49. package/dist/providers/catalog.d.ts +9 -1
  50. package/dist/providers/catalog.js +86 -6
  51. package/dist/providers/catalog.js.map +1 -1
  52. package/dist/redact.d.ts +2 -0
  53. package/dist/redact.js +26 -5
  54. package/dist/redact.js.map +1 -1
  55. package/dist/runtime.js +62 -11
  56. package/dist/runtime.js.map +1 -1
  57. package/dist/server.js +2 -0
  58. package/dist/server.js.map +1 -1
  59. package/dist/sessions/admission.d.ts +21 -0
  60. package/dist/sessions/admission.js +176 -0
  61. package/dist/sessions/admission.js.map +1 -0
  62. package/dist/sessions/anchor.d.ts +5 -2
  63. package/dist/sessions/anchor.js +14 -13
  64. package/dist/sessions/anchor.js.map +1 -1
  65. package/dist/sessions/background-tasks.d.ts +19 -1
  66. package/dist/sessions/background-tasks.js +36 -6
  67. package/dist/sessions/background-tasks.js.map +1 -1
  68. package/dist/sessions/cli-sessions.js +11 -6
  69. package/dist/sessions/cli-sessions.js.map +1 -1
  70. package/dist/sessions/compress.d.ts +10 -9
  71. package/dist/sessions/compress.js +31 -20
  72. package/dist/sessions/compress.js.map +1 -1
  73. package/dist/sessions/events.d.ts +3 -0
  74. package/dist/sessions/events.js +4 -0
  75. package/dist/sessions/events.js.map +1 -1
  76. package/dist/sessions/gateway-sessions.d.ts +61 -2
  77. package/dist/sessions/gateway-sessions.js +223 -35
  78. package/dist/sessions/gateway-sessions.js.map +1 -1
  79. package/dist/sessions/gateway-watcher.js +8 -4
  80. package/dist/sessions/gateway-watcher.js.map +1 -1
  81. package/dist/sessions/groups.d.ts +60 -0
  82. package/dist/sessions/groups.js +339 -0
  83. package/dist/sessions/groups.js.map +1 -0
  84. package/dist/sessions/list.d.ts +7 -2
  85. package/dist/sessions/list.js +13 -4
  86. package/dist/sessions/list.js.map +1 -1
  87. package/dist/sessions/merge.d.ts +15 -5
  88. package/dist/sessions/merge.js +49 -15
  89. package/dist/sessions/merge.js.map +1 -1
  90. package/dist/sessions/pending.d.ts +1 -1
  91. package/dist/sessions/pending.js +15 -6
  92. package/dist/sessions/pending.js.map +1 -1
  93. package/dist/sessions/prefix-index.d.ts +9 -14
  94. package/dist/sessions/prefix-index.js +14 -52
  95. package/dist/sessions/prefix-index.js.map +1 -1
  96. package/dist/sessions/prompt-queues.d.ts +36 -0
  97. package/dist/sessions/prompt-queues.js +318 -0
  98. package/dist/sessions/prompt-queues.js.map +1 -0
  99. package/dist/sessions/reasoning-display.d.ts +2 -0
  100. package/dist/sessions/reasoning-display.js +31 -0
  101. package/dist/sessions/reasoning-display.js.map +1 -0
  102. package/dist/sessions/relay.d.ts +16 -2
  103. package/dist/sessions/relay.js +119 -24
  104. package/dist/sessions/relay.js.map +1 -1
  105. package/dist/sessions/service.d.ts +33 -3
  106. package/dist/sessions/service.js +304 -70
  107. package/dist/sessions/service.js.map +1 -1
  108. package/dist/sessions/session.d.ts +6 -0
  109. package/dist/sessions/session.js +14 -0
  110. package/dist/sessions/session.js.map +1 -1
  111. package/dist/sessions/shares.d.ts +1 -1
  112. package/dist/sessions/shares.js +13 -4
  113. package/dist/sessions/shares.js.map +1 -1
  114. package/dist/sessions/state-db.d.ts +2 -1
  115. package/dist/sessions/state-db.js +7 -3
  116. package/dist/sessions/state-db.js.map +1 -1
  117. package/dist/sessions/storage/json-documents.js +3 -0
  118. package/dist/sessions/storage/json-documents.js.map +1 -1
  119. package/dist/sessions/store.d.ts +2 -0
  120. package/dist/sessions/store.js +11 -3
  121. package/dist/sessions/store.js.map +1 -1
  122. package/dist/sessions/streams.d.ts +4 -0
  123. package/dist/sessions/streams.js +12 -0
  124. package/dist/sessions/streams.js.map +1 -1
  125. package/dist/sessions/todo.js +10 -1
  126. package/dist/sessions/todo.js.map +1 -1
  127. package/dist/sessions/tool-display.d.ts +8 -1
  128. package/dist/sessions/tool-display.js +39 -4
  129. package/dist/sessions/tool-display.js.map +1 -1
  130. package/dist/sessions/turn.d.ts +4 -1
  131. package/dist/sessions/turn.js +53 -22
  132. package/dist/sessions/turn.js.map +1 -1
  133. package/dist/settings.js +2 -1
  134. package/dist/settings.js.map +1 -1
  135. package/dist/spa.js +1 -1
  136. package/dist/spa.js.map +1 -1
  137. package/dist/test/group-fixture.d.ts +66 -0
  138. package/dist/test/group-fixture.js +89 -0
  139. package/dist/test/group-fixture.js.map +1 -0
  140. package/dist/test/harness.d.ts +3 -0
  141. package/dist/test/harness.js +3 -0
  142. package/dist/test/harness.js.map +1 -1
  143. package/dist/tools/connectors.d.ts +9 -1
  144. package/dist/tools/connectors.js +48 -0
  145. package/dist/tools/connectors.js.map +1 -1
  146. package/dist/tools/kanban.d.ts +10 -1
  147. package/dist/tools/kanban.js +73 -1
  148. package/dist/tools/kanban.js.map +1 -1
  149. package/dist/tools/mcp.d.ts +3 -1
  150. package/dist/tools/mcp.js +73 -0
  151. package/dist/tools/mcp.js.map +1 -1
  152. package/dist/tools/memory.d.ts +28 -0
  153. package/dist/tools/memory.js +120 -14
  154. package/dist/tools/memory.js.map +1 -1
  155. package/dist/tools/skills.d.ts +6 -0
  156. package/dist/tools/skills.js +17 -0
  157. package/dist/tools/skills.js.map +1 -1
  158. package/dist/tools/update-notifications.d.ts +12 -10
  159. package/dist/tools/update-notifications.js +124 -14
  160. package/dist/tools/update-notifications.js.map +1 -1
  161. package/dist/tools/updates.d.ts +38 -12
  162. package/dist/tools/updates.js +180 -44
  163. package/dist/tools/updates.js.map +1 -1
  164. package/dist/workspace/fs.d.ts +2 -0
  165. package/dist/workspace/fs.js +63 -1
  166. package/dist/workspace/fs.js.map +1 -1
  167. package/dist/workspace/managed-buffers.d.ts +58 -0
  168. package/dist/workspace/managed-buffers.js +76 -0
  169. package/dist/workspace/managed-buffers.js.map +1 -0
  170. package/dist/workspace/rollback.js +1 -1
  171. package/dist/workspace/rollback.js.map +1 -1
  172. package/dist/workspace/upload.d.ts +1 -1
  173. package/dist/workspace/upload.js +52 -37
  174. package/dist/workspace/upload.js.map +1 -1
  175. package/dist/workspace/workspaces.d.ts +5 -1
  176. package/dist/workspace/workspaces.js +32 -6
  177. package/dist/workspace/workspaces.js.map +1 -1
  178. package/package.json +2 -2
  179. package/sidecar/agent_dependency.json +4 -4
  180. package/sidecar/agent_importer.json +8 -0
  181. package/sidecar/agent_support.json +20 -8
  182. package/sidecar/scripts/agent_importer_contract.py +19 -0
  183. package/sidecar/scripts/test.sh +3 -1
  184. package/sidecar/talaria_sidecar/__init__.py +1 -1
  185. package/sidecar/talaria_sidecar/agent_mutation.py +87 -9
  186. package/sidecar/talaria_sidecar/agent_services.py +83 -23
  187. package/sidecar/talaria_sidecar/home.py +12 -5
  188. package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
  189. package/sidecar/talaria_sidecar/methods/agent_imports.py +346 -0
  190. package/sidecar/talaria_sidecar/methods/chat.py +141 -58
  191. package/sidecar/talaria_sidecar/methods/commands.py +6 -4
  192. package/sidecar/talaria_sidecar/methods/config.py +8 -3
  193. package/sidecar/talaria_sidecar/methods/connectors.py +138 -4
  194. package/sidecar/talaria_sidecar/methods/dashboard_plugins.py +25 -4
  195. package/sidecar/talaria_sidecar/methods/delegation.py +126 -0
  196. package/sidecar/talaria_sidecar/methods/gateway.py +5 -0
  197. package/sidecar/talaria_sidecar/methods/gateway_sessions.py +142 -23
  198. package/sidecar/talaria_sidecar/methods/groups.py +120 -0
  199. package/sidecar/talaria_sidecar/methods/kanban.py +48 -0
  200. package/sidecar/talaria_sidecar/methods/managed_buffers.py +110 -0
  201. package/sidecar/talaria_sidecar/methods/mcp.py +55 -0
  202. package/sidecar/talaria_sidecar/methods/memory.py +27 -3
  203. package/sidecar/talaria_sidecar/methods/nous_account.py +130 -0
  204. package/sidecar/talaria_sidecar/methods/oauth.py +25 -4
  205. package/sidecar/talaria_sidecar/methods/outbound_drafts.py +147 -0
  206. package/sidecar/talaria_sidecar/methods/plugin_catalog.py +73 -2
  207. package/sidecar/talaria_sidecar/methods/providers.py +51 -1
  208. package/sidecar/talaria_sidecar/methods/skill_revisions.py +177 -0
  209. package/sidecar/talaria_sidecar/methods/skills.py +10 -0
  210. package/sidecar/talaria_sidecar/methods/tts.py +67 -0
  211. package/sidecar/talaria_sidecar/rpc.py +11 -2
  212. package/static/dist/FILES.txt +155 -144
  213. package/static/dist/THIRD_PARTY_LICENSES.md +1 -1
  214. package/static/dist/assets/AppShell-hvysbd9x.js +8 -0
  215. package/static/dist/assets/Button-dlsibkzq.js +1 -0
  216. package/static/dist/assets/ChatPage-jikwrwfd.js +2 -0
  217. package/static/dist/assets/Composer-mnke8ss2.js +5 -0
  218. package/static/dist/assets/ConnectTargets-ejplzo4q.js +1 -0
  219. package/static/dist/assets/Dialog-rksfyyq5.js +1 -0
  220. package/static/dist/assets/{ExtensionRoute-zrhjn0wm.js → ExtensionRoute-igk5h0rj.js} +1 -1
  221. package/static/dist/assets/HubRoute-eooassx4.js +2 -0
  222. package/static/dist/assets/Markdown-m5pzgvtx.js +2 -0
  223. package/static/dist/assets/{MarkdownRenderer-fbswsgld.js → MarkdownRenderer-74ysuomr.js} +18 -18
  224. package/static/dist/assets/OAuthSignIn-i4flsuq2.js +1 -0
  225. package/static/dist/assets/QueueCard-msz9wd1m.js +1 -0
  226. package/static/dist/assets/ResourceSwitch-b70zg4xd.js +1 -0
  227. package/static/dist/assets/RightPanel-f5v61m8j.js +1 -0
  228. package/static/dist/assets/Select-f3l897pt.js +1 -0
  229. package/static/dist/assets/SessionListPanel-brnzacw9.js +1 -0
  230. package/static/dist/assets/SettingsLayout-f4akmrt2.js +1 -0
  231. package/static/dist/assets/States-msohx3c0.js +1 -0
  232. package/static/dist/assets/TerminalPanel-bnu5xawo.js +16 -0
  233. package/static/dist/assets/Toaster-d6col3ch.js +1 -0
  234. package/static/dist/assets/_app-lrjgufcy.js +1 -0
  235. package/static/dist/assets/{_app.ext._extensionId-na62087t.js → _app.ext._extensionId-k3xitxr1.js} +1 -1
  236. package/static/dist/assets/_app.groups-bt4wdwnd.js +1 -0
  237. package/static/dist/assets/{_app.index-jypgdqoi.js → _app.index-n3otiki0.js} +1 -1
  238. package/static/dist/assets/{_app.insights-kabejdtm.js → _app.insights-b0mzlh6m.js} +1 -1
  239. package/static/dist/assets/{_app.kanban-m19kfcmx.js → _app.kanban-d923g7j5.js} +1 -1
  240. package/static/dist/assets/{_app.logs-iw4m96sz.js → _app.logs-donegue1.js} +1 -1
  241. package/static/dist/assets/{_app.memory-jq5frwcg.js → _app.memory-hdnled08.js} +1 -1
  242. package/static/dist/assets/{_app.profiles-kwa3m7rp.js → _app.profiles-fumot3jk.js} +1 -1
  243. package/static/dist/assets/{_app.session._sessionId-o5lyeipj.js → _app.session._sessionId-crmdl2ux.js} +1 -1
  244. package/static/dist/assets/{_app.settings-ckl8hdll.js → _app.settings-jui8iesz.js} +1 -1
  245. package/static/dist/assets/_app.settings._section-do84hsqw.js +3 -0
  246. package/static/dist/assets/{_app.skills-n016rihd.js → _app.skills-heqcn21m.js} +1 -1
  247. package/static/dist/assets/_app.tasks-jiudtutt.js +1 -0
  248. package/static/dist/assets/{_app.todos-btew5v2s.js → _app.todos-bl6kk2zq.js} +1 -1
  249. package/static/dist/assets/{_app.workspaces-lnfk95ch.js → _app.workspaces-frvh3o7w.js} +1 -1
  250. package/static/dist/assets/_delete-qnxuo87s.js +1 -0
  251. package/static/dist/assets/{abnfDiagram-VCTEODGH-dttheegb.js → abnfDiagram-VCTEODGH-dppg3yfx.js} +1 -1
  252. package/static/dist/assets/{architecture-7GRP2DOG-ic435919.js → architecture-7GRP2DOG-gbajejh3.js} +1 -1
  253. package/static/dist/assets/{architectureDiagram-5GKGNRK7-8r3glgiq.js → architectureDiagram-5GKGNRK7-bzrxyais.js} +1 -1
  254. package/static/dist/assets/archive-yhdyz231.js +1 -0
  255. package/static/dist/assets/autoReadClaim-nlyv8tu9.js +1 -0
  256. package/static/dist/assets/{blockDiagram-I7D4REHJ-jenyos6c.js → blockDiagram-I7D4REHJ-gmk9koab.js} +1 -1
  257. package/static/dist/assets/{c4Diagram-7LVT6UL2-nzlx7yc4.js → c4Diagram-7LVT6UL2-bfqiqqcz.js} +1 -1
  258. package/static/dist/assets/channel-grt3rob4.js +1 -0
  259. package/static/dist/assets/{chunk-4HAMMTFA-g0cru63b.js → chunk-4HAMMTFA-i9s6h4wv.js} +1 -1
  260. package/static/dist/assets/{chunk-75Z2AOVW-7rkswkjw.js → chunk-75Z2AOVW-gnt6pnso.js} +1 -1
  261. package/static/dist/assets/{chunk-DU6HZSFF-fn73jzdo.js → chunk-DU6HZSFF-gq439wot.js} +3 -3
  262. package/static/dist/assets/{chunk-F27PBJKO-oey8e72m.js → chunk-F27PBJKO-wvx90pbt.js} +1 -1
  263. package/static/dist/assets/{chunk-GMAD6QVW-l2a32wiy.js → chunk-GMAD6QVW-mc2gu30k.js} +1 -1
  264. package/static/dist/assets/{chunk-GVQU2GXP-o9dwzh8h.js → chunk-GVQU2GXP-f8fxa2ym.js} +1 -1
  265. package/static/dist/assets/{chunk-IMKFNOWR-eblwj20b.js → chunk-IMKFNOWR-lnsl31hj.js} +1 -1
  266. package/static/dist/assets/{chunk-L3NEJ4N5-o2mufspb.js → chunk-L3NEJ4N5-kbfdidwu.js} +1 -1
  267. package/static/dist/assets/{chunk-OSK3NFVY-ijp9pw29.js → chunk-OSK3NFVY-dvxsduj6.js} +1 -1
  268. package/static/dist/assets/{chunk-P2QGCYS3-hs9hqwl4.js → chunk-P2QGCYS3-ivo3i1wf.js} +1 -1
  269. package/static/dist/assets/{chunk-POPQ4Y6H-bp9rsqez.js → chunk-POPQ4Y6H-i0gfu0hx.js} +1 -1
  270. package/static/dist/assets/{chunk-PWAF6VOD-brkprov6.js → chunk-PWAF6VOD-jb0fdyb0.js} +1 -1
  271. package/static/dist/assets/{chunk-SHT3W25Y-gmpv0pxz.js → chunk-SHT3W25Y-b7ou2xsp.js} +1 -1
  272. package/static/dist/assets/{chunk-SVP7TREG-oldm08fu.js → chunk-SVP7TREG-e2v7me5v.js} +1 -1
  273. package/static/dist/assets/{chunk-TICWLB2K-ooqilfv7.js → chunk-TICWLB2K-e5zlai62.js} +1 -1
  274. package/static/dist/assets/classDiagram-ZZMXUADV-nnsp49jr.js +1 -0
  275. package/static/dist/assets/classDiagram-v2-VYDZK3BY-nnsp49jr.js +1 -0
  276. package/static/dist/assets/completionAudio-c5at041z.js +1 -0
  277. package/static/dist/assets/{copy-jo006coq.js → copy-jj3oiw13.js} +1 -1
  278. package/static/dist/assets/{cynefin-OW5HDTMX-d47op7qs.js → cynefin-OW5HDTMX-n5n1ck70.js} +1 -1
  279. package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-doa7594y.js → cynefinDiagram-5FMLGOSQ-b33npu4f.js} +1 -1
  280. package/static/dist/assets/{dagre-GXQ25YYZ-os768dp4.js → dagre-GXQ25YYZ-miwvae07.js} +1 -1
  281. package/static/dist/assets/{diagram-S7CK7UJ4-fyi81nxd.js → diagram-S7CK7UJ4-j8fphsp4.js} +1 -1
  282. package/static/dist/assets/{diagram-UQ7AKVKN-j3exggaq.js → diagram-UQ7AKVKN-5cixo166.js} +1 -1
  283. package/static/dist/assets/{diagram-VSXAHHWV-omo1en2l.js → diagram-VSXAHHWV-ii3bf92m.js} +1 -1
  284. package/static/dist/assets/{diagram-VX7I27RA-oro2cyu0.js → diagram-VX7I27RA-b3cfgx81.js} +1 -1
  285. package/static/dist/assets/{diagram-Z3DM3KII-byt9vdb7.js → diagram-Z3DM3KII-bey9lqr8.js} +1 -1
  286. package/static/dist/assets/{dist-etpoyscq.js → dist-cb8um5dh.js} +4 -4
  287. package/static/dist/assets/{dist-crc46di9.js → dist-kpg5ccm9.js} +1 -1
  288. package/static/dist/assets/{ebnfDiagram-PWID7BFC-ee3haugh.js → ebnfDiagram-PWID7BFC-2m4wrnqc.js} +1 -1
  289. package/static/dist/assets/{erDiagram-RLTQ6QDP-d1e93yff.js → erDiagram-RLTQ6QDP-kak5te0n.js} +1 -1
  290. package/static/dist/assets/{eventmodeling-NTZA5JFV-i1ihb40c.js → eventmodeling-NTZA5JFV-ojcc486t.js} +1 -1
  291. package/static/dist/assets/flowDiagram-HODETNUW-kxr9sicu.js +1 -0
  292. package/static/dist/assets/folder-plus-fk6j7tlv.js +1 -0
  293. package/static/dist/assets/{ganttDiagram-EL5Y4UJY-m7g0jud4.js → ganttDiagram-EL5Y4UJY-k6nfmhrh.js} +1 -1
  294. package/static/dist/assets/getPseudoElementBounds-mwj3vqni.js +1 -0
  295. package/static/dist/assets/{gitGraph-4MIJSDKK-hzdj07yy.js → gitGraph-4MIJSDKK-bxq1lbhy.js} +1 -1
  296. package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-dm9agbna.js → gitGraphDiagram-WWUBYQGX-ehvoankt.js} +1 -1
  297. package/static/dist/assets/group_discussions-fk5hry4l.js +1 -0
  298. package/static/dist/assets/{highlighted-body-KPVGNVTW-mmgz2czu.js → highlighted-body-KPVGNVTW-nr4nfdq7.js} +1 -1
  299. package/static/dist/assets/{host-fr6lni1p.js → host-dhgqiozp.js} +1 -1
  300. package/static/dist/assets/index-hx7z4wdj.js +13 -0
  301. package/static/dist/assets/index-o8ebuc5x.css +2 -0
  302. package/static/dist/assets/{info-A6RAGUB7-lll1dqmm.js → info-A6RAGUB7-dtsj0ybw.js} +1 -1
  303. package/static/dist/assets/{infoDiagram-27XIBGKW-k7esc639.js → infoDiagram-27XIBGKW-jena90ao.js} +1 -1
  304. package/static/dist/assets/{ishikawaDiagram-5VMMS53U-lgr6k9wo.js → ishikawaDiagram-5VMMS53U-kia6bn5u.js} +1 -1
  305. package/static/dist/assets/{journeyDiagram-3NMN7TZE-ndmbxuqk.js → journeyDiagram-3NMN7TZE-ipsa6icb.js} +1 -1
  306. package/static/dist/assets/{kanban-definition-UXKFOSKX-mjxym0jp.js → kanban-definition-UXKFOSKX-moxx59kp.js} +1 -1
  307. package/static/dist/assets/katex-ic33v820.js +257 -0
  308. package/static/dist/assets/katex-qcv7xo0t.css +1 -0
  309. package/static/dist/assets/{lazyRouteComponent-cwb6l7xh.js → lazyRouteComponent-i74ujrfk.js} +1 -1
  310. package/static/dist/assets/{line-mtr5k6il.js → line-lgrkg9no.js} +1 -1
  311. package/static/dist/assets/link-2-fp49mwl2.js +1 -0
  312. package/static/dist/assets/{link-iadw4ghw.js → link-es18mse1.js} +2 -2
  313. package/static/dist/assets/{locale-cs-n41hptse.js → locale-cs-brltdkcp.js} +1 -1
  314. package/static/dist/assets/{locale-de-k7ht47ew.js → locale-de-h6e9oq6v.js} +1 -1
  315. package/static/dist/assets/{locale-es-ih078lco.js → locale-es-oklmh80f.js} +1 -1
  316. package/static/dist/assets/{locale-fr-ojabnf41.js → locale-fr-b17ozoi2.js} +1 -1
  317. package/static/dist/assets/{locale-it-kb96jufy.js → locale-it-whe01eef.js} +1 -1
  318. package/static/dist/assets/{locale-ja-jpsp3xs1.js → locale-ja-g1s0rtpc.js} +1 -1
  319. package/static/dist/assets/{locale-pl-dbap4leu.js → locale-pl-d9p3lqcb.js} +1 -1
  320. package/static/dist/assets/{locale-pt-nci7w72x.js → locale-pt-l75on74f.js} +1 -1
  321. package/static/dist/assets/{locale-ru-soghe4cs.js → locale-ru-wl3mbsaw.js} +1 -1
  322. package/static/dist/assets/{locale-tr-ks00mu68.js → locale-tr-cz87itrq.js} +1 -1
  323. package/static/dist/assets/{locale-vi-ij7twfm6.js → locale-vi-elqj1y4k.js} +1 -1
  324. package/static/dist/assets/{locale-zh-Hant-feakwr7a.js → locale-zh-Hant-hf8gqm7d.js} +1 -1
  325. package/static/dist/assets/{locale-zh-ren0vjyv.js → locale-zh-cdckwb0u.js} +1 -1
  326. package/static/dist/assets/login-o6gfl4gy.js +1 -0
  327. package/static/dist/assets/mermaid-HWGCJPDP-n2kbzsao.js +1 -0
  328. package/static/dist/assets/{mermaid-parser.core-k91vdnvi.js → mermaid-parser.core-fflovwf2.js} +3 -3
  329. package/static/dist/assets/{mindmap-definition-YA3MSWOX-ehztfea4.js → mindmap-definition-YA3MSWOX-ic8l9whi.js} +1 -1
  330. package/static/dist/assets/onboarding-bxm7gdrr.js +1 -0
  331. package/static/dist/assets/{packet-AYTQ26CC-ok2xrblz.js → packet-AYTQ26CC-ntci3088.js} +1 -1
  332. package/static/dist/assets/{pegDiagram-XKGWAZYB-ec3rpj0m.js → pegDiagram-XKGWAZYB-e3btz04w.js} +1 -1
  333. package/static/dist/assets/{pie-WAS4IAKB-iwkir23u.js → pie-WAS4IAKB-o2dc7l36.js} +1 -1
  334. package/static/dist/assets/{pieDiagram-E7YTZNPT-fcpqpekh.js → pieDiagram-E7YTZNPT-gro55rrw.js} +1 -1
  335. package/static/dist/assets/presence-lq6m12ga.js +1 -0
  336. package/static/dist/assets/prompt_queue_cancelled-cebcuagu.js +1 -0
  337. package/static/dist/assets/{quadrantDiagram-AXDQQJYC-lqsync43.js → quadrantDiagram-AXDQQJYC-fxulmd7d.js} +1 -1
  338. package/static/dist/assets/queryKeys-cnvn6pea.js +9 -0
  339. package/static/dist/assets/{radar-RG4KPBEZ-hje4jbk6.js → radar-RG4KPBEZ-9pcospra.js} +1 -1
  340. package/static/dist/assets/{railroad-74A4TZTK-gll9n6qi.js → railroad-74A4TZTK-nzdga12j.js} +1 -1
  341. package/static/dist/assets/{railroad-abnf-HS5TGJTU-o6h6lpn4.js → railroad-abnf-HS5TGJTU-ep3ytel0.js} +1 -1
  342. package/static/dist/assets/{railroad-ebnf-LZEXJU2U-juaefj6o.js → railroad-ebnf-LZEXJU2U-iw7uc1wy.js} +1 -1
  343. package/static/dist/assets/{railroad-peg-WCYAUIDC-cgfhvpah.js → railroad-peg-WCYAUIDC-fo3b3gme.js} +1 -1
  344. package/static/dist/assets/{railroadDiagram-O6MQD6OU-lp3w7awf.js → railroadDiagram-O6MQD6OU-lxpxf8f0.js} +1 -1
  345. package/static/dist/assets/{refresh-cw-iccm7a4e.js → refresh-cw-d14h79x5.js} +1 -1
  346. package/static/dist/assets/rename-ccd52yws.js +1 -0
  347. package/static/dist/assets/{requirementDiagram-BXWQKSXE-ha8eibcf.js → requirementDiagram-BXWQKSXE-lrozucd2.js} +1 -1
  348. package/static/dist/assets/{sankeyDiagram-P5KCCOFB-oxf31pr9.js → sankeyDiagram-P5KCCOFB-iusxy9mq.js} +1 -1
  349. package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-jiq8lhyo.js → sequenceDiagram-WJ2MYXX4-o987q8ti.js} +1 -1
  350. package/static/dist/assets/{share._token-o76ki0ly.js → share._token-bfw7f9m5.js} +1 -1
  351. package/static/dist/assets/sliders-horizontal-l65hoa3x.js +1 -0
  352. package/static/dist/assets/{stateDiagram-D77RDMKH-f3fslnon.js → stateDiagram-D77RDMKH-hh23v7y5.js} +1 -1
  353. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-jukr0d8h.js +1 -0
  354. package/static/dist/assets/{swimlanes-42K2YHIH-k1hmk7jf.js → swimlanes-42K2YHIH-gj2t3rm8.js} +1 -1
  355. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-ik9m9j2m.js +8 -0
  356. package/static/dist/assets/{timeline-definition-24CTP7MA-n3ncfy9r.js → timeline-definition-24CTP7MA-84s2w2sr.js} +1 -1
  357. package/static/dist/assets/toast-gqqcvxiy.js +56 -0
  358. package/static/dist/assets/{treeView-Q6P3EWNA-l3y2pejt.js → treeView-Q6P3EWNA-cmuj50l7.js} +1 -1
  359. package/static/dist/assets/{treemap-WGGIJYW6-jown9axh.js → treemap-WGGIJYW6-dpjlg0xc.js} +1 -1
  360. package/static/dist/assets/tts-jw84jlqy.js +1 -0
  361. package/static/dist/assets/{useForm-o4uu74mx.js → useForm-lxzhiqao.js} +1 -1
  362. package/static/dist/assets/{useParams-ftsveu84.js → useParams-dt6gkoq7.js} +1 -1
  363. package/static/dist/assets/{useSearch-crqueqga.js → useSearch-o8t49csk.js} +1 -1
  364. package/static/dist/assets/useTranscript-gx9umcc2.js +1 -0
  365. package/static/dist/assets/{vennDiagram-4TSXK5OY-dh59uq1c.js → vennDiagram-4TSXK5OY-g9g5o8vy.js} +1 -1
  366. package/static/dist/assets/{wardley-WFR3VGLG-e6d74b0j.js → wardley-WFR3VGLG-ij35obrl.js} +1 -1
  367. package/static/dist/assets/{wardleyDiagram-VM6X3IG4-hnofh78f.js → wardleyDiagram-VM6X3IG4-f9my7q7w.js} +1 -1
  368. package/static/dist/assets/{xychartDiagram-S5SC5T6Z-bpymjelb.js → xychartDiagram-S5SC5T6Z-olqx1s1p.js} +1 -1
  369. package/static/dist/index.html +1 -1
  370. package/static/dist/sw.js +1 -1
  371. package/static/dist/assets/AppShell-3isgvoro.js +0 -8
  372. package/static/dist/assets/Button-f4n6dapf.js +0 -1
  373. package/static/dist/assets/ChatPage-k7b6xfax.js +0 -4
  374. package/static/dist/assets/ConnectTargets-juqvt5lm.js +0 -1
  375. package/static/dist/assets/Dialog-hta5utr3.js +0 -1
  376. package/static/dist/assets/Field-b6ez48oz.js +0 -1
  377. package/static/dist/assets/HubRoute-iwvso2tz.js +0 -2
  378. package/static/dist/assets/Markdown-jdeth7zq.js +0 -2
  379. package/static/dist/assets/OAuthSignIn-bpd8hom3.js +0 -1
  380. package/static/dist/assets/ResourceSwitch-odi6dy2o.js +0 -1
  381. package/static/dist/assets/RightPanel-eu6enlo6.js +0 -1
  382. package/static/dist/assets/Select-kr07928w.js +0 -1
  383. package/static/dist/assets/SessionListPanel-etb2i54l.js +0 -1
  384. package/static/dist/assets/SettingsLayout-eu3xxp23.js +0 -1
  385. package/static/dist/assets/States-icab19yg.js +0 -1
  386. package/static/dist/assets/TerminalPanel-hngmsr8b.js +0 -16
  387. package/static/dist/assets/Toaster-nb2uzjw1.js +0 -1
  388. package/static/dist/assets/_app-ga1moszl.js +0 -1
  389. package/static/dist/assets/_app.settings._section-dtdjk98a.js +0 -3
  390. package/static/dist/assets/_app.tasks-n8zuqsqe.js +0 -1
  391. package/static/dist/assets/archive-il7c6053.js +0 -1
  392. package/static/dist/assets/cancel-bsnfo36u.js +0 -1
  393. package/static/dist/assets/channel-dsbtk01y.js +0 -1
  394. package/static/dist/assets/classDiagram-ZZMXUADV-fvgcv24g.js +0 -1
  395. package/static/dist/assets/classDiagram-v2-VYDZK3BY-fvgcv24g.js +0 -1
  396. package/static/dist/assets/flowDiagram-HODETNUW-jahfpiod.js +0 -1
  397. package/static/dist/assets/folder-plus-ivoglq98.js +0 -1
  398. package/static/dist/assets/index-73649w7o.js +0 -10
  399. package/static/dist/assets/index-lhf1fsdf.css +0 -2
  400. package/static/dist/assets/katex-cbdd1ohw.css +0 -1
  401. package/static/dist/assets/katex-pbij2oo6.js +0 -257
  402. package/static/dist/assets/login-lkhtzfcl.js +0 -1
  403. package/static/dist/assets/mermaid-HWGCJPDP-jx0q5dm2.js +0 -1
  404. package/static/dist/assets/onboarding-f67z0a2r.js +0 -1
  405. package/static/dist/assets/queryKeys-ed4n2egc.js +0 -9
  406. package/static/dist/assets/refresh-i9ya9dvl.js +0 -1
  407. package/static/dist/assets/sliders-horizontal-ddcs88av.js +0 -1
  408. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-hlt6yphw.js +0 -1
  409. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-hd5t9tnt.js +0 -8
  410. package/static/dist/assets/toast-liparz2j.js +0 -56
@@ -17,8 +17,9 @@ from ..errors import InvalidParams
17
17
  from ..home import profile_home_param, scoped_home
18
18
 
19
19
  _ACTIONS = {"snapshot", "watch", "context_detail", "submit", "receipt", "cancel", "steer_queued", "replace_queued", "interrupt", "approval", "clarify",
20
- "clarify_lock", "discard", "mutate", "export"}
21
- _EXPORT_MAX_BYTES = 4 * 1024 * 1024
20
+ "clarify_lock", "discard", "mutate", "export", "checkpoint_diff", "terminal_info", "shell_exec", "react"}
21
+ _OWNER_READ_MAX_BYTES = 4 * 1024 * 1024
22
+ _REACTION_EMOJIS = {"👍", "👎", "❤️", "😂", "😮", "🎉"}
22
23
  # A restart's after-turn wait keeps the owner reachable only to finish its parked turn (the owner's
23
24
  # RESTART_WAIT_WS_METHODS); new work waits for the next owner.
24
25
  _RESTART_ACTIONS = {"snapshot", "watch", "receipt", "approval", "clarify", "clarify_lock", "interrupt"}
@@ -69,14 +70,40 @@ def _control_refusal(home: Path, sid: str) -> str | None:
69
70
  return None
70
71
 
71
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
+
72
91
  def _snapshot(value: dict) -> dict:
73
92
  # Raw conversation rows contain private principal/admission metadata; the client needs
74
93
  # display rows, not those bindings or the inventory's system prompt and credential hint.
75
94
  message_keys = {"role", "content", "tool_calls", "tool_call_id", "name", "timestamp", "message_id", "row_id",
76
95
  "reasoning", "reasoning_content", "display_kind", "finish_reason"}
77
96
  info_keys = {"title", "model", "cwd", "source", "provider", "config_warning"}
78
- return {**value, "messages": [{k: v for k, v in row.items() if k in message_keys} for row in value["messages"]],
79
- "info": {k: v for k, v in value["info"].items() if k in info_keys}}
97
+ rows = []
98
+ for row in value["messages"]:
99
+ metadata = row.get("display_metadata")
100
+ reactions = metadata.get("reactions", []) if isinstance(metadata, dict) else []
101
+ rows.append({**{k: v for k, v in row.items() if k in message_keys}, "reactions": [
102
+ {"author": reaction["author"], "emoji": reaction["emoji"]} for reaction in reactions
103
+ if isinstance(reaction, dict) and reaction.get("author") in ("user", "agent")
104
+ and isinstance(reaction.get("emoji"), str) and reaction["emoji"] in _REACTION_EMOJIS] if isinstance(reactions, list) else []})
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 {})}
80
107
 
81
108
 
82
109
  def _refused(state: str, reason: str) -> dict:
@@ -84,7 +111,7 @@ def _refused(state: str, reason: str) -> dict:
84
111
 
85
112
 
86
113
  @asynccontextmanager
87
- async def _connect(home: Path, endpoint):
114
+ async def _connect(home: Path, endpoint, *, subject: str | None = None):
88
115
  from hermes_constants import get_hermes_home
89
116
  from hermes_cli.gateway_client import GatewayClientError, connect_gateway
90
117
 
@@ -92,7 +119,7 @@ async def _connect(home: Path, endpoint):
92
119
  # override is mandatory so its bootstrap ticket cannot fall back to the launch profile.
93
120
  if Path(get_hermes_home()).resolve() != home:
94
121
  raise GatewayClientError("profile_mismatch")
95
- async with connect_gateway(endpoint) as client:
122
+ async with connect_gateway(endpoint, **({"subject": subject} if subject is not None else {})) as client:
96
123
  websocket = client.websocket
97
124
  # GatewayClient queues server requests as events. Drain continuously, including
98
125
  # during an RPC, so unsupported sudo/secure inputs never wait for a Web renderer.
@@ -120,16 +147,20 @@ def _served(home: Path, endpoint, *, allow_verified: bool = False) -> bool:
120
147
  return False
121
148
  if not allow_verified:
122
149
  return True
150
+ return not _verified_contract(endpoint, "served-local-policy-v1")
151
+
152
+
153
+ def _verified_contract(endpoint, name: str) -> bool:
123
154
  # The private control socket reports the owner's boot revision, not the sidecar's
124
155
  # checkout or a version label. The authenticated describe must still match below.
125
156
  try:
126
157
  record = json.loads((Path(__file__).resolve().parents[2] / "agent_support.json").read_text())
127
- revisions = record["nativeContracts"]["served-local-policy-v1"]
128
- return (not isinstance(revisions, list)
129
- or not all(isinstance(sha, str) and re.fullmatch(r"[0-9a-f]{40}", sha) for sha in revisions)
130
- or getattr(endpoint, "code_sha", None) not in revisions)
158
+ revisions = record["nativeContracts"][name]
159
+ return (isinstance(revisions, list)
160
+ and all(isinstance(sha, str) and re.fullmatch(r"[0-9a-f]{40}", sha) for sha in revisions)
161
+ and getattr(endpoint, "code_sha", None) in revisions)
131
162
  except (OSError, ValueError, KeyError, TypeError):
132
- return True
163
+ return False
133
164
 
134
165
 
135
166
  def _owner_refusal(described: dict, endpoint) -> dict | None:
@@ -145,12 +176,41 @@ def _owner_refusal(described: dict, endpoint) -> dict | None:
145
176
  return None
146
177
 
147
178
 
179
+ def _checkpoint_read(value, *, no_baseline=False):
180
+ comparison = {"provenance": "native_owner", "baseline": "earliest_retained_project_checkpoint",
181
+ "available": False, "reason": None, "truncated": False}
182
+ result = {"diff": "", "stat": "", "comparison": comparison}
183
+ reason = None
184
+ if not isinstance(value, dict) or type(value.get("enabled")) is not bool:
185
+ reason = "invalid_payload"
186
+ elif not value["enabled"]:
187
+ reason = "checkpoints_disabled"
188
+ elif value.get("success") is not True:
189
+ reason = "checkpoint_unavailable"
190
+ elif (not isinstance(value.get("diff"), str) or not isinstance(value.get("stat"), str)
191
+ or type(value.get("diff_lines")) is not int or value["diff_lines"] < 0
192
+ or type(value.get("empty")) is not bool):
193
+ reason = "invalid_payload"
194
+ elif sum(len(json.dumps(value[key], ensure_ascii=False).encode("utf-8", "surrogatepass")) - 2
195
+ for key in ("stat", "diff")) > _OWNER_READ_MAX_BYTES:
196
+ reason = "payload_too_large"
197
+ elif no_baseline:
198
+ reason = "no_baseline"
199
+ else:
200
+ result.update(diff=value["diff"], stat=value["stat"])
201
+ comparison.update(available=True, truncated=value["diff_lines"] > len(value["diff"].splitlines()))
202
+ comparison["reason"] = reason
203
+ return result
204
+
205
+
148
206
  async def _run(ctx, params: dict, home: Path, discovery) -> dict:
149
207
  from hermes_cli.gateway_client import GatewayRPCError
150
208
 
151
209
  endpoint = discovery.endpoint
152
210
  if _served(home, endpoint, allow_verified=True):
153
211
  return _refused("incompatible", "served_profile_sessions_unavailable")
212
+ if params["action"] in {"terminal_info", "shell_exec"} and not _verified_contract(endpoint, "captured-command-v1"):
213
+ return _refused("incompatible", "owner_shell_unsupported")
154
214
  restarting = discovery.state != "ready"
155
215
  if restarting and params["action"] not in _RESTART_ACTIONS:
156
216
  return _refused("refused", "gateway_restarting")
@@ -170,10 +230,32 @@ async def _run(ctx, params: dict, home: Path, discovery) -> dict:
170
230
  subscription = value["subscription_id"]
171
231
  if reason := await asyncio.to_thread(_control_refusal, home, sid):
172
232
  raise GatewayRPCError(reason)
173
- return _snapshot(value)
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")}
174
244
 
175
245
  receipt = lock = None
176
246
  try:
247
+ if params["action"] == "checkpoint_diff":
248
+ # The owner resolves the physical target and frozen policy. Never resume or read host files.
249
+ value = await asyncio.wait_for(client.rpc("rollback.session_diff", session_id=sid, _timeout=25), timeout=30)
250
+ read = _checkpoint_read(value)
251
+ if read["comparison"]["available"] and value["empty"] and not value["diff"] and not value["stat"]:
252
+ listed = await asyncio.wait_for(client.rpc("rollback.list", session_id=sid, _timeout=3), timeout=3)
253
+ if (not isinstance(listed, dict) or listed.get("enabled") is not True
254
+ or not isinstance(listed.get("checkpoints"), list)):
255
+ read = _checkpoint_read(None)
256
+ elif not listed["checkpoints"]:
257
+ read = _checkpoint_read(value, no_baseline=True)
258
+ return {"state": "attached", "reason": None, "snapshot": None, "receipt": None, "checkpoint_diff": read}
177
259
  if params["action"] == "export":
178
260
  # No resume/subscription or mutation: the owner selects its current physical target.
179
261
  fmt = "md" if params["format"] == "markdown" else params["format"]
@@ -183,14 +265,34 @@ async def _run(ctx, params: dict, home: Path, discovery) -> dict:
183
265
  or not isinstance(value.get("filename"), str) or not 0 < len(value["filename"]) <= 255
184
266
  or not isinstance(value.get("content"), str)):
185
267
  return _refused("refused", "owner_export_invalid")
186
- if len(value["content"]) > _EXPORT_MAX_BYTES or len(value["content"].encode("utf-8")) > _EXPORT_MAX_BYTES:
268
+ if len(value["content"]) > _OWNER_READ_MAX_BYTES or len(value["content"].encode("utf-8")) > _OWNER_READ_MAX_BYTES:
187
269
  return _refused("refused", "export_too_large")
188
270
  return {"state": "attached", "reason": None, "snapshot": None, "receipt": None,
189
271
  "export": {"format": params["format"], "filename": value["filename"], "content": value["content"]}}
190
272
  snap = await snapshot()
191
273
  action = params["action"]
192
274
  common = {"session_id": sid}
193
- if action == "context_detail":
275
+ if action == "terminal_info":
276
+ return {"state": "attached", "reason": None, "snapshot": snap, "receipt": None}
277
+ if action == "shell_exec":
278
+ if (snap.get("session_id") != params["session_id"] or snap.get("authority_epoch") != params["authority_epoch"]
279
+ or snap.get("replay_epoch") != params["replay_epoch"]):
280
+ return _refused("refused", "owner_changed")
281
+ # The native owner alone authorizes the frozen backend/cwd and redacts before bounding.
282
+ # No second observation may discard a received result; no retry after an unknown ACK.
283
+ value = await client.rpc("shell.exec", **common, command=params["command"], _timeout=35)
284
+ if (not isinstance(value, dict) or not isinstance(value.get("stdout"), str)
285
+ or not isinstance(value.get("stderr"), str) or type(value.get("code")) is not int
286
+ or len(value["stdout"]) > 4000 or len(value["stderr"]) > 2000):
287
+ return _refused("refused", "owner_shell_invalid")
288
+ return {"state": "attached", "reason": None, "snapshot": None, "receipt": None, "shell": value}
289
+ if action == "react":
290
+ if not snap["reactions_supported"]:
291
+ return _refused("refused", "owner_reactions_unsupported")
292
+ if not any(row.get("row_id") == params["row_id"] and row.get("role") in {"user", "assistant"} for row in snap["messages"]):
293
+ return _refused("refused", "reaction_row_unavailable")
294
+ await client.rpc("message.react", **common, row_id=params["row_id"], emoji=params["emoji"], author="user")
295
+ elif action == "context_detail":
194
296
  from ..context_detail import project, unavailable
195
297
  # The authorized native read owns lookup and prompt interpretation. No local Agent is built.
196
298
  try:
@@ -259,21 +361,27 @@ async def _run(ctx, params: dict, home: Path, discovery) -> dict:
259
361
  if (event.get("type") in {"session.updated", "notification.show", "notification.clear"}
260
362
  or event.get("execution_generation") == snap["execution_generation"]
261
363
  or event.get("type") == "session.info" and event.get("payload", {}).get("execution_generation") == snap["execution_generation"]):
262
- ctx.emit("event", event)
364
+ ctx.emit("event", _event(event))
263
365
  sequence = replay["last_sequence"]
264
366
  ctx.emit("replay_complete", {})
265
367
  checked = asyncio.get_running_loop().time()
266
368
  while not ctx.cancelled:
369
+ now = asyncio.get_running_loop().time()
370
+ if not restarting and now - checked >= 2:
371
+ checked = now
372
+ if restarting := await asyncio.to_thread(_restart_wait, home):
373
+ result["restarting"] = True
374
+ ctx.emit("restarting", {"restarting": True})
375
+ if not restarting and snap.get("reactions_supported"):
376
+ # The owner emits no reaction event. Read through this viewer's existing subscription,
377
+ # including while tokens keep the event queue busy.
378
+ current = await snapshot()
379
+ if [(r.get("row_id"), r.get("reactions")) for r in current["messages"] if r.get("reactions")] != [(r.get("row_id"), r.get("reactions")) for r in snap["messages"] if r.get("reactions")]:
380
+ ctx.emit("snapshot", {**result, "snapshot": current, "reaction_only": True})
381
+ snap = current
267
382
  try:
268
383
  frame = await asyncio.wait_for(events.get(), .25)
269
384
  except TimeoutError:
270
- now = asyncio.get_running_loop().time()
271
- if not restarting and now - checked >= 2:
272
- checked = now
273
- # One-way for this owner: its next state is exit, which ends the watch.
274
- if restarting := await asyncio.to_thread(_restart_wait, home):
275
- result["restarting"] = True
276
- ctx.emit("restarting", {"restarting": True})
277
385
  continue
278
386
  if isinstance(frame, Exception):
279
387
  raise frame
@@ -307,9 +415,11 @@ async def _run(ctx, params: dict, home: Path, discovery) -> dict:
307
415
  if event.get("seq", 0) <= sequence:
308
416
  continue
309
417
  sequence = event["seq"]
310
- ctx.emit("event", event)
418
+ ctx.emit("event", _event(event))
311
419
  return result
312
420
  except GatewayRPCError as exc:
421
+ if params["action"] == "react" and str(exc) not in {"invalid_params", "not_found", "permission_denied", "profile_mismatch"}:
422
+ return _refused("unavailable", "reaction_outcome_unknown")
313
423
  if params["action"] == "export":
314
424
  return _refused("refused", "export_too_large" if str(exc) == "export_too_large" else "owner_export_unsupported")
315
425
  return {**_refused("incompatible" if str(exc) in _CONTROL_REFUSALS else "refused", str(exc)), "receipt": receipt}
@@ -330,9 +440,18 @@ def session(ctx, params: dict) -> dict:
330
440
  if action not in _ACTIONS or not isinstance(sid, str) or not sid.strip() or sid != sid.strip():
331
441
  raise InvalidParams("session_id and supported action are required")
332
442
  params = {**params, "action": action}
443
+ if action == "react" and (set(params) - {"profile_home", "session_id", "action", "row_id", "emoji"}
444
+ or type(params.get("row_id")) is not int or params["row_id"] <= 0
445
+ or "emoji" not in params or (params["emoji"] is not None and (not isinstance(params["emoji"], str) or params["emoji"] not in _REACTION_EMOJIS))):
446
+ raise InvalidParams("react requires a visible row and an allowed emoji")
333
447
  if action == "export" and (params.get("format") not in ("json", "markdown", "html")
334
448
  or not isinstance(params.get("redact"), bool)):
335
449
  raise InvalidParams("export requires an allowed format and redaction policy")
450
+ if action == "shell_exec" and (not isinstance(params.get("command"), str)
451
+ or not params["command"].strip() or len(params["command"]) > 8192
452
+ or type(params.get("authority_epoch")) is not int or params["authority_epoch"] < 0
453
+ or not isinstance(params.get("replay_epoch"), str) or not params["replay_epoch"]):
454
+ raise InvalidParams("captured command requires command and owner identity")
336
455
  required = {
337
456
  "submit": ("request_id", "text"), "receipt": ("admission_id",), "cancel": ("admission_id",),
338
457
  "steer_queued": ("admission_id",), "replace_queued": ("admission_id", "submission_id", "text"),
@@ -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
@@ -10,8 +10,11 @@ from __future__ import annotations
10
10
  import functools
11
11
  import json
12
12
  import secrets
13
+ import sqlite3
13
14
  import time
15
+ from contextlib import closing
14
16
  from dataclasses import asdict, is_dataclass
17
+ from pathlib import Path
15
18
  from typing import Any, NamedTuple
16
19
 
17
20
  from ..errors import InvalidParams, RpcError
@@ -463,6 +466,50 @@ def _bool(params: dict, key: str, default: bool = False) -> bool:
463
466
 
464
467
  # ── payload builders ──────────────────────────────────────────────────────
465
468
 
469
+ def inspection_payload(params: dict) -> dict:
470
+ """Read only the selected profile's live boards, with shared global row/edge budgets."""
471
+ kb = _kb()
472
+ selected = _board(params)
473
+ boards = sorted((b for b in kb.list_boards(include_archived=False) if not b.get("archived")), key=lambda b: b["slug"])
474
+ if selected is not None:
475
+ boards = [b for b in boards if b["slug"] == selected]
476
+ if not boards:
477
+ raise NotFound("board not found")
478
+ nodes, edges = [], []
479
+ truncated = len(boards) > 32
480
+ for meta in boards[:32]:
481
+ slug = _normalize_existing_slug(meta["slug"])
482
+ if len(nodes) >= 500:
483
+ truncated = True
484
+ break
485
+ # An inspection must never initialize a missing database or run migrations.
486
+ uri = Path(kb.kanban_db_path(slug)).resolve().as_uri() + "?mode=ro"
487
+ with closing(sqlite3.connect(uri, uri=True)) as conn:
488
+ conn.row_factory = sqlite3.Row
489
+ conn.execute("BEGIN")
490
+ where = "" if _bool(params, "include_archived") else "WHERE t.status != 'archived'"
491
+ rows = conn.execute(
492
+ f"SELECT t.*, r.started_at AS current_run_started_at FROM tasks t LEFT JOIN task_runs r ON r.id = t.current_run_id {where} ORDER BY t.id LIMIT ?",
493
+ (501 - len(nodes),),
494
+ ).fetchall()
495
+ remaining = 500 - len(nodes)
496
+ truncated = truncated or len(rows) > remaining
497
+ rows = rows[:remaining]
498
+ ids = [row["id"] for row in rows]
499
+ for row in rows:
500
+ task = _task_dict(dict(row), conn)
501
+ nodes.append({"key": f"{slug}/{row['id']}", "board": slug, "board_name": meta.get("name") or slug, "task": task})
502
+ if ids:
503
+ placeholders = ",".join("?" for _ in ids)
504
+ links = conn.execute(
505
+ f"SELECT parent_id, child_id FROM task_links WHERE parent_id IN ({placeholders}) AND child_id IN ({placeholders}) ORDER BY parent_id, child_id LIMIT ?",
506
+ [*ids, *ids, 2001 - len(edges)],
507
+ ).fetchall()
508
+ remaining_edges = 2000 - len(edges)
509
+ truncated = truncated or len(links) > remaining_edges
510
+ edges.extend({"source": f"{slug}/{r['parent_id']}", "target": f"{slug}/{r['child_id']}"} for r in links[:remaining_edges])
511
+ return {"nodes": nodes, "edges": edges, "generated_at": time.time(), "truncated": truncated, "read_only": False}
512
+
466
513
  def board_payload(params: dict) -> dict:
467
514
  board = _board(params)
468
515
  kb = _kb()
@@ -852,6 +899,7 @@ def normalize_board_payload(params: dict) -> dict:
852
899
 
853
900
 
854
901
  METHODS = {
902
+ "kanban.inspection": inspection_payload,
855
903
  "kanban.normalize_board": normalize_board_payload,
856
904
  "kanban.board": board_payload,
857
905
  "kanban.boards": list_boards_payload,
@@ -0,0 +1,110 @@
1
+ """Fixed devkit REST adapter through the existing admitted Agent plugin host."""
2
+ import base64
3
+ import json
4
+ import re
5
+
6
+ from .. import home as homes
7
+ from ..errors import InvalidParams, RpcError
8
+ from . import dashboard_plugins as host
9
+
10
+ MAX_BYTES = 256 * 1024
11
+ _NAME = re.compile(r'[A-Za-z0-9][A-Za-z0-9._-]{0,127}')
12
+ _DIR = re.compile(r'[A-Za-z0-9_-]{1,96}')
13
+
14
+
15
+ def _text_size(value):
16
+ try:
17
+ return len(value.encode('utf-8')) if isinstance(value, str) else -1
18
+ except UnicodeError:
19
+ return -1
20
+
21
+
22
+ def _bad():
23
+ raise RpcError('Managed buffer response is invalid or too large', condition='buffer_invalid')
24
+
25
+
26
+ def _json(ctx, plugin, home, method, path, content=None):
27
+ # Recheck admission for each native call, including the PUT after inventory.
28
+ current = host._named(home, 'devkit', require_panel=False)
29
+ if current is None or current['_dir'] != plugin['_dir'] or current.get('_api_file') != plugin.get('_api_file'):
30
+ raise RpcError('Managed buffer backend changed or was disabled', condition='buffer_unavailable')
31
+ body = b'' if content is None else json.dumps({'content': content}, ensure_ascii=False).encode()
32
+ result = host._request(ctx, plugin, dict(profile_home=str(home), authenticated=True, method=method, path=path, query='',
33
+ headers={'content-type': 'application/json'}, body=base64.b64encode(body).decode()), require_panel=False)
34
+ if result['status'] != 200:
35
+ # A PUT may have committed before an error or malformed acknowledgement. Never replay it here.
36
+ raise RpcError('Managed buffer unavailable' if method == 'GET' else 'Save outcome is unknown; reload before retrying',
37
+ condition='buffer_unavailable' if method == 'GET' else 'buffer_save_unconfirmed')
38
+ try:
39
+ value = json.loads(base64.b64decode(result['body']))
40
+ except (ValueError, UnicodeDecodeError):
41
+ _bad()
42
+ if not isinstance(value, dict):
43
+ _bad()
44
+ return value
45
+
46
+
47
+ def run(ctx, params):
48
+ action, sid = params.get('action'), params.get('session_id')
49
+ if action not in ('list', 'read', 'save') or not isinstance(sid, str) or not 0 < len(sid) <= 200:
50
+ raise InvalidParams('Invalid managed buffer request')
51
+ name = params.get('name')
52
+ if action != 'list' and (not isinstance(name, str) or not _NAME.fullmatch(name)):
53
+ raise InvalidParams('Invalid managed buffer name')
54
+ content = params.get('content')
55
+ if action == 'save' and (not 0 <= _text_size(content) <= MAX_BYTES):
56
+ raise InvalidParams('Managed buffer content is too large or is not text')
57
+ home = homes.profile_home_param(params)
58
+ with homes.scoped_home(home, load_plugins=False):
59
+ plugin = host._named(home, 'devkit', require_panel=False) if host._supported() else None
60
+ result = {'supported': plugin is not None, 'buffers': [], 'write_semantics': 'unconditional'}
61
+ if plugin is None:
62
+ if action != 'list':
63
+ raise RpcError('Enable and approve the devkit backend in Plugins', condition='buffer_unavailable')
64
+ return result
65
+ rows = _json(ctx, plugin, home, 'GET', '/sessions').get('sessions')
66
+ if not isinstance(rows, list) or len(rows) > 2000 or any(not isinstance(r, dict) for r in rows):
67
+ _bad()
68
+ matches = [r for r in rows if r.get('session_id') == sid]
69
+ if len(matches) > 1:
70
+ _bad()
71
+ row = matches[0] if matches else None
72
+ if row is None:
73
+ if action != 'list':
74
+ raise RpcError('Managed buffer no longer exists', condition='buffer_unavailable')
75
+ return result
76
+ directory, buffers = row.get('dir'), row.get('buffers')
77
+ if not isinstance(directory, str) or not _DIR.fullmatch(directory) or not isinstance(buffers, list) or len(buffers) > 200:
78
+ _bad()
79
+ seen = set()
80
+ for buf in buffers:
81
+ if not isinstance(buf, dict):
82
+ _bad()
83
+ key, size = buf.get('name'), buf.get('bytes')
84
+ if not isinstance(key, str) or not _NAME.fullmatch(key) or key in seen or type(size) is not int or not 0 <= size <= MAX_BYTES:
85
+ _bad()
86
+ seen.add(key)
87
+ result['buffers'].append({'name': key, 'size': size, 'read_only': row.get('exists') is not True})
88
+ if action == 'list':
89
+ return result
90
+ selected = next((b for b in result['buffers'] if b['name'] == name), None)
91
+ if selected is None:
92
+ raise RpcError('Managed buffer no longer exists', condition='buffer_unavailable')
93
+ if action == 'save' and selected['read_only']:
94
+ raise RpcError('Managed session is deleted or cannot be verified', condition='buffer_read_only')
95
+ value = _json(ctx, plugin, home, 'PUT' if action == 'save' else 'GET', f'/sessions/{directory}/buffers/{name}', content if action == 'save' else None)
96
+ if action == 'read':
97
+ text = value.get('content')
98
+ if value.get('session_id') != sid or value.get('name') != name or not 0 <= _text_size(text) <= MAX_BYTES:
99
+ _bad()
100
+ result.update(content=text, size=_text_size(text), read_only=selected['read_only'])
101
+ else:
102
+ # Native PUT acknowledgements may omit session_id, but an explicit identity must match.
103
+ if value.get('session_id', sid) != sid or value.get('name') != name or type(value.get('bytes')) is not int or not 0 <= value['bytes'] <= MAX_BYTES:
104
+ raise RpcError('Save outcome is unknown; reload before retrying', condition='buffer_save_unconfirmed')
105
+ result.update(size=value['bytes'], read_only=False)
106
+ return result
107
+
108
+
109
+ def register(registry):
110
+ registry.method('plugins.buffers')(run)