@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
@@ -6,6 +6,8 @@ Config edits and health probes live in the server; this namespace only reads
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
+ import hashlib
10
+ import json
9
11
  import logging
10
12
 
11
13
  from ..home import profile_home_param, scoped_home
@@ -48,7 +50,60 @@ def registry_tools() -> list[dict]:
48
50
  return out
49
51
 
50
52
 
53
+ def cost_policies(servers: dict) -> dict:
54
+ """Read declarations using the native parser; inspect cached packages without discovering or connecting."""
55
+ try:
56
+ from tools.mcp_tool_costs import CONFIG_KEYS, _parse_cost, config_cost_policy
57
+ from hermes_cli.plugins import get_plugin_manager
58
+
59
+ portable = get_plugin_manager().get_portable_mcp_servers()
60
+ except (ImportError, AttributeError):
61
+ return {"supported": False, "policies": []}
62
+ rows = []
63
+ for name in sorted(servers.keys() | portable.keys()):
64
+ native, package = servers.get(name), portable.get(name, {})
65
+ if not isinstance(package, dict):
66
+ package = {}
67
+ editable = isinstance(native, dict) and ("command" in native or "url" in native)
68
+ own = {k: native[k] for k in CONFIG_KEYS if isinstance(native, dict) and k in native}
69
+ # The native loader rejects a malformed package before it can donate inherited keys.
70
+ if native is not None:
71
+ try:
72
+ config_cost_policy(name, package)
73
+ except (ValueError, TypeError):
74
+ package = {}
75
+ inherited = {k: package[k] for k in CONFIG_KEYS if k in package}
76
+ effective = {**inherited, **own}
77
+ sources = {k: "native" if k in own else "package" if k in inherited else "default" for k in CONFIG_KEYS}
78
+ row = {"name": name, "source": "native_override" if native is not None and package else "native" if native is not None else "package",
79
+ "valid": True, "can_edit": editable, "error": None,
80
+ "package_revision": hashlib.sha256(json.dumps(inherited, sort_keys=True, default=str).encode()).hexdigest(),
81
+ "overrides": {"tool_costs": None, "spend_limit": None, "cost_unit": None},
82
+ "tool_costs": [], "spend_limit": 0, "cost_unit": "credits", "budget_enabled": False, "field_sources": sources}
83
+ try:
84
+ # Validate the native entry separately as the Agent does before package inheritance.
85
+ config_cost_policy(name, own)
86
+ policy = config_cost_policy(name, effective)
87
+ costs = effective.get("tool_costs") or {}
88
+ row["tool_costs"] = [{"name": tool, "low": cost.low, "high": cost.high, "note": cost.note}
89
+ for tool, value in costs.items() for cost in [_parse_cost(value, "declared cost")]]
90
+ row["spend_limit"] = effective.get("spend_limit") or 0
91
+ row["cost_unit"] = (effective.get("cost_unit") or "credits").strip()
92
+ row["budget_enabled"] = policy is not None and policy.limit > 0
93
+ row["overrides"] = {k: own.get(k) for k in CONFIG_KEYS}
94
+ except (ValueError, TypeError):
95
+ # Native errors may quote arbitrary config values, including mistakenly pasted credentials.
96
+ row.update(valid=False, error="Invalid declared cost policy. Check tool estimates, limit and unit.")
97
+ rows.append(row)
98
+ return {"supported": True, "policies": rows}
99
+
100
+
51
101
  def register(registry_) -> None:
102
+ @registry_.method("mcp.cost_policies")
103
+ def policies(ctx: CallContext, params: dict) -> dict:
104
+ with scoped_home(profile_home_param(params), load_plugins=False):
105
+ return cost_policies(params.get("servers") or {})
106
+
52
107
  @registry_.method("mcp.status")
53
108
  def status_(ctx: CallContext, params: dict) -> dict:
54
109
  with scoped_home(profile_home_param(params)):
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from contextlib import contextmanager
5
6
  import errno
6
7
  import hashlib
7
8
  import os
@@ -45,22 +46,45 @@ def _digest(path: Path) -> str | None:
45
46
  return hashlib.sha256(handle.read()).hexdigest()
46
47
 
47
48
 
49
+ @contextmanager
50
+ def file_lock(path: Path, *, parent_fd=None):
51
+ """The native memory lock, with an anchored opening for descriptor-relative publishers."""
52
+ if parent_fd is None:
53
+ from tools.memory_tool_store import MemoryStore
54
+ native = getattr(MemoryStore, '_file_lock', None)
55
+ with native(path) if native else _SAVE_LOCK:
56
+ yield
57
+ return
58
+ import fcntl
59
+ # Same persistent .lock inode and flock protocol as MemoryStore._file_lock; never re-resolve its parent.
60
+ with _SAVE_LOCK:
61
+ fd = os.open(path.name + '.lock', os.O_RDWR | os.O_CREAT | os.O_NOFOLLOW | os.O_NONBLOCK, 0o600, dir_fd=parent_fd)
62
+ with os.fdopen(fd, 'r+') as lock:
63
+ info = os.fstat(lock.fileno())
64
+ if not stat.S_ISREG(info.st_mode) or info.st_nlink != 1:
65
+ raise OSError('Unsafe memory lock')
66
+ os.fchmod(lock.fileno(), 0o600)
67
+ fcntl.flock(lock.fileno(), fcntl.LOCK_EX)
68
+ try:
69
+ yield
70
+ finally:
71
+ fcntl.flock(lock.fileno(), fcntl.LOCK_UN)
72
+
73
+
48
74
  def write(home: Path, section: str, base: str, content: str) -> dict:
49
75
  """Replace ``section``'s file with ``content`` only while its bytes still hash to ``base``, the server's read.
50
76
 
51
77
  Each file is checked and written under the Agent's own per-file lock (``<file>.lock``), so neither a memory tool
52
78
  call nor a concurrent save can land between the two; an Agent without the lock helper gets this sidecar's lock.
53
79
  """
54
- from tools.memory_tool_store import MemoryStore
55
80
  from utils import atomic_write_text
56
81
 
57
82
  if section not in ("memory", "user", "soul"):
58
83
  raise InvalidParams("section must be memory, user, or soul")
59
84
  # The files the server read: SOUL.md in the profile home, MEMORY.md / USER.md in its memories folder.
60
85
  path = home / "SOUL.md" if section == "soul" else home / "memories" / ("USER.md" if section == "user" else "MEMORY.md")
61
- file_lock = getattr(MemoryStore, "_file_lock", None)
62
86
  try:
63
- with file_lock(path) if file_lock else _SAVE_LOCK:
87
+ with file_lock(path):
64
88
  if _digest(path) != base:
65
89
  return {"written": False}
66
90
  atomic_write_text(path, content, tmp_prefix=".mem_", preserve_mode=True)
@@ -0,0 +1,130 @@
1
+ """Safe, read-only account/handoff projection shared by OAuth and provider status.
2
+
3
+ The Agent owns the record and retry worker. This bounded display cache only retains a
4
+ confirmed view across read failures, keyed by the current profile and account.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import hashlib
9
+ import re
10
+ import threading
11
+ from collections import OrderedDict
12
+ from pathlib import Path
13
+ from typing import Any
14
+
15
+ _LAST: OrderedDict[str, dict] = OrderedDict()
16
+ _LOCK = threading.RLock()
17
+ _STATES = {"idle", "pending", "retrying", "claimed", "gone", "failed", "paused"}
18
+ _ERRORS = {"client_not_allowed", "account_locked", "invalid_request", "retry_window_ended"}
19
+
20
+
21
+ def _identity(home: Path) -> tuple[str, str | None] | None:
22
+ from hermes_cli.anon_auth import is_guest_state
23
+ from hermes_cli.auth import get_provider_auth_state
24
+
25
+ state = get_provider_auth_state("nous")
26
+ if not isinstance(state, dict) or is_guest_state(state):
27
+ return None
28
+ return _token_identity(home, state.get("access_token"))
29
+
30
+
31
+ def _token_identity(home: Path, token: Any) -> tuple[str, str | None] | None:
32
+ from hermes_cli.auth_constants import _decode_jwt_claims
33
+
34
+ claims = _decode_jwt_claims(token)
35
+ subject = claims.get("sub")
36
+ if not isinstance(subject, str) or not subject:
37
+ return None
38
+ identity = hashlib.sha256(f"{home.resolve()}\0{subject}".encode()).hexdigest()
39
+ email = claims.get("email")
40
+ if not isinstance(email, str) or not re.fullmatch(r"[^@\s]+@[^@\s]+\.[^@\s]+", email) or len(email) > 254:
41
+ return identity, None
42
+ local, domain = email.rsplit("@", 1)
43
+ return identity, f"{local[0]}***@{domain}"
44
+
45
+
46
+ def _apps(value: Any) -> list[str]:
47
+ if not isinstance(value, (list, tuple)):
48
+ return []
49
+ return list(dict.fromkeys(v for v in value[:100] if isinstance(v, str) and re.fullmatch(r"[a-z0-9][a-z0-9_-]{0,63}", v)))
50
+
51
+
52
+ def read(home: Path, completed: Any = None) -> dict | None:
53
+ """One sequential read owner for flow/provider consumers, called under scoped_home."""
54
+ with _LOCK:
55
+ return _read(home, completed)
56
+
57
+
58
+ def _read(home: Path, completed: Any = None) -> dict | None:
59
+ """No provisioning, refresh, drain, or retry dispatch."""
60
+ try:
61
+ from hermes_cli import guest_handoff as native
62
+ except ImportError:
63
+ return None # older Agents keep generic success
64
+ try:
65
+ identity = _identity(home)
66
+ except Exception: # no current identity proof means no cached account may be displayed
67
+ return None
68
+ if identity is None:
69
+ return None
70
+ account_id, email = identity
71
+ try:
72
+ status = native.handoff_status()
73
+ if status.get("state") == "idle" and completed is not None:
74
+ status = getattr(completed, "handoff_status", None) or status
75
+ state = status.get("state") if status.get("state") in _STATES else "unknown"
76
+ # The public native read only fences *active* records. Prove terminal ownership
77
+ # with the same native selector/binding helper before exposing any old app names.
78
+ if state not in {"idle", "unknown", "paused"}:
79
+ records = native._load(native._records_path())["records"].values()
80
+ mine = [r for r in records if native._home_key() in r.get("profiles", ())]
81
+ record = max(mine, key=lambda r: str(r.get("updated_at") or "")) if mine else None
82
+ if record is not None and native._status_of(record) != status:
83
+ raise RuntimeError("Native handoff changed during read")
84
+ if record is None or native._account_bound_to(record) is None:
85
+ state = "paused" if record else "unknown"
86
+ safe = {"state": state, "account_email": email,
87
+ "connectors_kind": status.get("connectors_kind") if status.get("connectors_kind") in {"moved", "swapped", "none", "conflict"} else None,
88
+ "moved_toolkits": _apps(status.get("moved_toolkits")) if state == "claimed" else [],
89
+ "dropped_toolkits": _apps(status.get("dropped_toolkits")) if state == "claimed" else [],
90
+ "probably_moved": status.get("probably_moved") is True,
91
+ "error": status.get("error") if status.get("error") in _ERRORS else None}
92
+ lines = native.wire_status(safe)["lines"] if state != "unknown" else ["Connected-app transfer status is unknown."]
93
+ if _identity(home) != identity:
94
+ return None
95
+ view = {"account_id": account_id, "email": email, "state": state, "lines": lines,
96
+ "moved": safe["moved_toolkits"], "dropped": safe["dropped_toolkits"],
97
+ "reconnect": safe["dropped_toolkits"],
98
+ "poll_after": 5 if state in {"pending", "retrying", "unavailable"} else None}
99
+ with _LOCK:
100
+ _LAST[account_id] = view
101
+ _LAST.move_to_end(account_id)
102
+ while len(_LAST) > 32:
103
+ _LAST.popitem(last=False)
104
+ return view
105
+ except Exception: # raw native records and exception details never cross this boundary
106
+ try:
107
+ if _identity(home) != identity:
108
+ return None
109
+ except Exception:
110
+ return None
111
+ with _LOCK:
112
+ previous = _LAST.get(account_id)
113
+ return {**(previous or {"account_id": account_id, "email": email, "moved": [], "dropped": [], "lines": []}),
114
+ "state": "unavailable", "reconnect": [], "poll_after": 5,
115
+ "lines": [*(previous or {}).get("lines", []), "Connected-app transfer status is unavailable."]}
116
+
117
+
118
+ def connector_headers(home: Path, account_id: str, url: str) -> dict:
119
+ """The native connector client reads this on every request, never provisioning or refreshing."""
120
+ from tools.managed_gateway_auth import managed_gateway_auth_headers
121
+ from tools.managed_tool_gateway import peek_nous_access_token
122
+
123
+ def token():
124
+ value = peek_nous_access_token()
125
+ current, bearer = _identity(home), _token_identity(home, value)
126
+ if current is None or bearer is None or current[0] != account_id or bearer[0] != account_id:
127
+ return None
128
+ return value
129
+
130
+ return managed_gateway_auth_headers(url, token_reader=token)
@@ -26,7 +26,7 @@ from typing import Any, Callable
26
26
  from ..errors import InvalidParams, RpcError
27
27
  from ..home import profile_home_param, scoped_home
28
28
  from ..rpc import CallContext
29
- from . import plugin_oauth
29
+ from . import plugin_oauth, nous_account
30
30
 
31
31
  log = logging.getLogger("talaria_sidecar.oauth")
32
32
 
@@ -65,6 +65,7 @@ class Flow:
65
65
  # The credential is saved but the sign-in has not finished (a guest sign-in still settles the account): a cancel or
66
66
  # an expiry can no longer undo it, and the flow stays pending until its own ending.
67
67
  committed: bool = False
68
+ nous_account_id: str | None = None
68
69
  # A browser sign-in also finishes from the redirect address the user pastes (``oauth.submit_redirect``).
69
70
  submit: Callable[[str], None] | None = None
70
71
  # Set once a browser sign-in's loopback listener is closed, so a replacing start can bind the same fixed port.
@@ -78,7 +79,12 @@ class Flow:
78
79
  self.status, self.error, self.ended_at = status, error, time.time()
79
80
 
80
81
  def view(self) -> dict:
81
- return {"flow_id": self.flow_id, "provider": self.provider, "status": self.status, "error": self.error}
82
+ view = {"flow_id": self.flow_id, "provider": self.provider, "status": self.status, "error": self.error}
83
+ if self.status == "approved" and self.nous_account_id:
84
+ with scoped_home(self.home):
85
+ account = nous_account.read(self.home)
86
+ view["nous_account"] = account if account and account["account_id"] == self.nous_account_id else None
87
+ return view
82
88
 
83
89
 
84
90
  class _Watched:
@@ -156,6 +162,9 @@ def _nous_begin() -> tuple[dict, dict]:
156
162
 
157
163
  # v0.22.0 replaced ``guest_enabled`` with ``guest_allowed``; 0.21.x releases only have the former.
158
164
  guest_gate = getattr(anon_auth, "guest_allowed", None) or getattr(anon_auth, "guest_enabled")
165
+ native = getattr(anon_auth, "run_sign_in", None)
166
+ if native and "replace_account" in inspect.signature(native).parameters:
167
+ return _nous_sign_in(anon_auth)
159
168
  if guest_gate() and anon_auth.has_guest():
160
169
  # The native sign-in owns the guest's connector handoff for its installed protocol.
161
170
  if hasattr(anon_auth, "run_sign_in"):
@@ -191,6 +200,9 @@ def _nous_sign_in(anon_auth: Any) -> tuple[dict, dict]:
191
200
  yield may_persist
192
201
  if may_persist:
193
202
  flow.committed = True
203
+ # Bind before native handoff/settlement releases this account to another sign-in.
204
+ account = nous_account.read(flow.home)
205
+ state["nous_account_id"] = account["account_id"] if account else None
194
206
 
195
207
  def scope():
196
208
  flow = state.get("flow")
@@ -198,11 +210,13 @@ def _nous_sign_in(anon_auth: Any) -> tuple[dict, dict]:
198
210
 
199
211
  # Older Agents use promotion; v0.22.5 removed its option in favor of plain device login and native handoff.
200
212
  options = {"cancel_wins_after_promotion": True} if "cancel_wins_after_promotion" in inspect.signature(anon_auth.run_sign_in).parameters else {}
213
+ if "replace_account" in inspect.signature(anon_auth.run_sign_in).parameters:
214
+ options["replace_account"] = True
201
215
  sign_in = anon_auth.run_sign_in(cancelled=cancelled, persist_guard=persist_guard, scope=scope, **options)
202
216
  first = next(sign_in)
203
217
  if first.kind != "code":
204
218
  sign_in.close()
205
- raise RpcError(first.copy_terminal, condition="oauth_unsupported" if first.precondition else "oauth_failed")
219
+ raise RpcError(first.copy, condition="oauth_unsupported" if first.precondition else "oauth_failed")
206
220
  state["sign_in"] = sign_in
207
221
  return {"user_code": first.code, "verification_url": first.link, "expires_in": first.expires_in, "interval": first.interval}, state
208
222
 
@@ -226,6 +240,8 @@ def _nous_wait(flow: Flow, state: dict) -> dict:
226
240
  sign_in.close()
227
241
  if not ended.ok:
228
242
  raise _SignInEnded(ended)
243
+ with scoped_home(flow.home):
244
+ nous_account.read(flow.home, ended)
229
245
  return {}
230
246
  from hermes_cli.auth import _poll_for_token
231
247
 
@@ -406,6 +422,7 @@ def _run(flow: Flow, wait: Callable[[Flow, dict], dict], save: Callable[[Flow, d
406
422
  return
407
423
  with scoped_home(flow.home):
408
424
  save(flow, state, tokens)
425
+ flow.nous_account_id = state.get("nous_account_id")
409
426
  flow.end("approved")
410
427
  log.info("oauth: %s sign-in approved (flow=%s)", flow.provider, flow.flow_id[:6])
411
428
  except _Stop:
@@ -556,7 +573,11 @@ def register(reg) -> None:
556
573
 
557
574
  @reg.method("oauth.poll", requires_agent=False)
558
575
  def poll_(ctx: CallContext, params: dict) -> dict:
559
- return poll(profile_home_param(params), _flow_id(params))
576
+ home, flow_id = profile_home_param(params), _flow_id(params)
577
+ flow = _flow(home, flow_id)
578
+ if flow.status == "approved" and flow.nous_account_id:
579
+ reg.runtime.ensure_current() # a retired worker cannot promise background progress
580
+ return poll(home, flow_id)
560
581
 
561
582
  @reg.method("oauth.cancel", requires_agent=False)
562
583
  def cancel_(ctx: CallContext, params: dict) -> dict:
@@ -0,0 +1,147 @@
1
+ """Typed adapter for the reviewed Draft Cards store. Never imports producer code or sends."""
2
+ from __future__ import annotations
3
+
4
+ import hashlib
5
+ import hmac
6
+ import secrets
7
+ import json
8
+ from pathlib import Path
9
+ import re
10
+ import sqlite3
11
+ import time
12
+ from contextlib import closing
13
+
14
+ from .. import home as homes
15
+ from ..errors import InvalidParams, RpcError
16
+ from . import dashboard_plugins
17
+
18
+ _KEY = secrets.token_bytes(32)
19
+ _PIN = '6e6ca659ece59b0ce1c28b3b188393491ee5a415'
20
+ _SOURCES = {'__init__.py': '73388811ddc4f7cfc336c2a278dad9c0c032b0b15e4183ce8a157e61ce13695b',
21
+ 'plugin.yaml': 'e2a8a92847f4519f1636ec542ae2b1599050d9552fb9a61d9c6d6afaca83d6eb',
22
+ 'dashboard/manifest.json': '14e906a0e492669f0950e091f42d98ad607486dbea524b2a8e8691faa2139941',
23
+ 'dashboard/plugin_api.py': '98e38f618c23ec266f1e171a958ffcf048339ea3c7876d1faa2d0b8910839336',
24
+ 'store.py': '5815d4b106082b7f51d1b7e9d239fc655fa247a5e61ff7465e8c1865476eaaf6',
25
+ 'tools.py': '29271409488efc39206a048dc2852effc156d9ca5d91721632e1219840f14db1'}
26
+ _STATES = {'draft', 'approved', 'sent', 'saved', 'failed', 'discarded'}
27
+
28
+
29
+ def _producer(home):
30
+ return dashboard_plugins.reviewed_producer(home, 'draft-cards', _PIN, _SOURCES)
31
+
32
+
33
+ def _database(home):
34
+ """Only the producer's existing profile-local database; never creates storage on a read."""
35
+ path = Path(home) / 'plugin-data' / 'draft-cards' / 'drafts.db'
36
+ # No caller supplies a filesystem path, and no symlink may redirect native state.
37
+ for parent in [path, *path.parents]:
38
+ if parent.is_symlink():
39
+ raise OSError('Symlink in draft storage')
40
+ if not path.is_file():
41
+ raise OSError('Draft storage unavailable')
42
+ connection = sqlite3.connect(path.as_uri() + '?mode=rw', uri=True, timeout=5)
43
+ connection.row_factory = sqlite3.Row
44
+ return connection
45
+
46
+
47
+ def _fields(value):
48
+ if not isinstance(value, dict) or set(value) != {'from', 'to', 'cc', 'bcc', 'subject', 'body'}:
49
+ raise InvalidParams('Invalid draft fields')
50
+ for key, limit in [('from', 320), ('subject', 300), ('body', 20_000)]:
51
+ if not isinstance(value[key], str) or len(value[key]) > limit or '\x00' in value[key]:
52
+ raise InvalidParams('Invalid draft fields')
53
+ for key in ('to', 'cc', 'bcc'):
54
+ if not isinstance(value[key], list) or len(value[key]) > 50 or any(
55
+ not isinstance(item, str) or len(item) > 320 or re.search(r'[\r\n\x00]', item) for item in value[key]):
56
+ raise InvalidParams('Invalid draft recipients')
57
+ if not value['body'].strip():
58
+ raise InvalidParams('A draft body is required')
59
+ return value
60
+
61
+
62
+ def _snapshot(conn, home, params, producer):
63
+ row = conn.execute('SELECT * FROM drafts WHERE id = ?', (params['draft_id'],)).fetchone()
64
+ if row is None or not row['session_id'] or row['session_id'] != params['session_id']:
65
+ return None
66
+ versions = conn.execute('SELECT * FROM versions WHERE draft_id = ? ORDER BY v LIMIT 61', (params['draft_id'],)).fetchall()
67
+ if not versions or len(versions) > 60 or row['kind'] not in ('email', 'message') or row['status'] not in _STATES:
68
+ return None
69
+ matches = [v for v in versions if v['card'] == params['card']]
70
+ if not matches:
71
+ return None
72
+ selected, latest = matches[-1], versions[-1]
73
+ fields = _fields(json.loads(selected['fields']))
74
+ state = row['status'] if selected['v'] == latest['v'] else 'superseded'
75
+ if state == 'approved' and row['approved_version'] != latest['v']:
76
+ state = 'unavailable'
77
+ consumed = False
78
+ if conn.execute("SELECT 1 FROM sqlite_master WHERE type='table' AND name='talaria_outbound_approvals'").fetchone():
79
+ consumed = conn.execute('SELECT 1 FROM talaria_outbound_approvals WHERE draft_id = ? AND version = ?',
80
+ (row['id'], selected['v'])).fetchone() is not None
81
+ revision = hmac.new(_KEY, json.dumps([str(home), producer, dict(row), [dict(v) for v in versions], consumed],
82
+ sort_keys=True, separators=(',', ':')).encode(), hashlib.sha256).hexdigest()
83
+ editable = state in ('draft', 'failed', 'approved')
84
+ return {'producer': 'draft-cards', 'producer_revision': producer, 'draft_id': row['id'], 'card': selected['card'],
85
+ 'kind': row['kind'], 'platform': str(row['platform'])[:40], 'version': selected['v'], 'revision': revision,
86
+ 'fields': fields, 'state': state, 'can_edit': editable, 'can_approve': state in ('draft', 'failed') and not consumed,
87
+ 'masked': False}
88
+
89
+
90
+ def _conflict():
91
+ raise RpcError('The draft changed or this version was already approved. Review it again.', condition='outbound_draft_conflict')
92
+
93
+
94
+ def review(ctx, params):
95
+ ctx.check_cancelled()
96
+ action = params.get('action')
97
+ if (action not in ('read', 'edit', 'approve') or not isinstance(params.get('draft_id'), str)
98
+ or not re.fullmatch(r'd_[0-9a-f]{8}', params['draft_id']) or type(params.get('card')) is not int
99
+ or params['card'] < 1 or not isinstance(params.get('session_id'), str) or not params['session_id']):
100
+ raise InvalidParams('Invalid outbound draft reference')
101
+ home = homes.profile_home_param(params)
102
+ with homes.scoped_home(home, load_plugins=False):
103
+ producer = _producer(home)
104
+ if producer is None:
105
+ if action != 'read':
106
+ _conflict()
107
+ return {'draft': None}
108
+ try:
109
+ with closing(_database(home)) as conn, conn:
110
+ # The native tool writers share this database lock. Check and mutation have one snapshot.
111
+ conn.execute('BEGIN IMMEDIATE' if action != 'read' else 'BEGIN')
112
+ draft = _snapshot(conn, home, params, producer)
113
+ if action == 'read':
114
+ ctx.check_cancelled()
115
+ return {'draft': draft if _producer(home) == producer else None}
116
+ if (draft is None or params.get('version') != draft['version'] or params.get('revision') != draft['revision']
117
+ or not draft['can_' + action] or _producer(home) != producer):
118
+ _conflict()
119
+ now = time.time()
120
+ if action == 'edit':
121
+ fields = _fields(params.get('fields'))
122
+ if fields == draft['fields']:
123
+ return {'draft': draft}
124
+ if conn.execute('SELECT count(*) FROM versions WHERE draft_id = ?', (draft['draft_id'],)).fetchone()[0] >= 60:
125
+ _conflict()
126
+ conn.execute("INSERT INTO versions (draft_id,v,card,author,note,fields,created_at) VALUES (?,?,?,'user','Edited in Talaria',?,?)",
127
+ (draft['draft_id'], draft['version'] + 1, draft['card'], json.dumps(fields, ensure_ascii=False), now))
128
+ conn.execute("UPDATE drafts SET status='draft', approved_version=NULL, status_detail='', status_at=?, updated_at=? WHERE id=?",
129
+ (now, now, draft['draft_id']))
130
+ else:
131
+ # Additive native-store state: Stable producer readers ignore this per-version receipt.
132
+ conn.execute('CREATE TABLE IF NOT EXISTS talaria_outbound_approvals (draft_id TEXT NOT NULL, version INTEGER NOT NULL, revision TEXT NOT NULL, PRIMARY KEY(draft_id,version))')
133
+ conn.execute('INSERT INTO talaria_outbound_approvals VALUES (?,?,?)', (draft['draft_id'], draft['version'], draft['revision']))
134
+ conn.execute("UPDATE drafts SET status='approved', approved_version=?, status_detail='', status_at=?, updated_at=? WHERE id=?",
135
+ (draft['version'], now, now, draft['draft_id']))
136
+ ctx.check_cancelled()
137
+ if _producer(home) != producer:
138
+ _conflict()
139
+ return {'draft': _snapshot(conn, home, params, producer)}
140
+ except (OSError, sqlite3.Error, ValueError, KeyError, TypeError, InvalidParams) as exc:
141
+ if action != 'read':
142
+ raise RpcError('Draft outcome unconfirmed; read its state before trying again.', condition='outbound_draft_unconfirmed') from exc
143
+ return {'draft': None}
144
+
145
+
146
+ def register(registry):
147
+ registry.method('drafts.review')(review)
@@ -27,9 +27,14 @@ def _agent():
27
27
  return None
28
28
 
29
29
 
30
- def _refusal(entry, pc, native):
30
+ def _refusal(entry, pc, native, *, removed_entries=None):
31
31
  try:
32
- native.raise_if_removed(entry.name, entry.repo)
32
+ if removed_entries is None:
33
+ native.raise_if_removed(entry.name, entry.repo)
34
+ else:
35
+ from hermes_cli.plugin_catalog import match_removed
36
+ if any(match_removed(value, removed_entries) for value in (entry.name, entry.repo)):
37
+ return 'This plugin was removed from the catalog.'
33
38
  native._refuse_unsupported_catalog_platform(entry)
34
39
  from hermes_cli.plugins_manifest import requires_hermes_error
35
40
  reason = requires_hermes_error({'requires_hermes': entry.requires_hermes})
@@ -196,3 +201,69 @@ def install(name, consent_token=None):
196
201
  del _receipts[next(iter(_receipts))]
197
202
  _receipts[token] = {'identity': identity, 'preview': preview, 'expires': time.monotonic() + 300}
198
203
  return {'name': name, 'status': 'needs_consent', 'findings': findings, 'consent_token': token}
204
+
205
+
206
+ def reviewed_importer(home):
207
+ """Load only attested native structured APIs, sharing catalog identity and enablement policy."""
208
+ import sys
209
+ import types
210
+ from . import dashboard_plugins
211
+ reviewed = json.loads((Path(__file__).parents[2] / 'agent_importer.json').read_text())
212
+ pin, hashes = reviewed['revision'], reviewed['files']
213
+ # Older Agent config imports initialize files. Check the pure in-tree catalog before importing their command owner.
214
+ try:
215
+ from hermes_cli import plugin_catalog as source
216
+ entry = next((e for e in source.load_catalog() if e.name == 'agent-importers'), None)
217
+ if entry is None or entry.sha != pin:
218
+ return None
219
+ except (ImportError, AttributeError, OSError, ValueError):
220
+ return None
221
+ agent = _agent()
222
+ if agent is None or not dashboard_plugins._supported():
223
+ return None
224
+ source, pc, native = agent
225
+ try:
226
+ from pm.workspace import enabled_plugin_dirs
227
+ from hermes_cli.web_server_dashboard import _plugin_enable_sets_here
228
+ enabled, disabled = _plugin_enable_sets_here()
229
+ if not (Path(home) / 'plugins').is_dir():
230
+ return None
231
+ metadata = pc._read_install_metadata()
232
+ matches = [(key, record) for key, record in metadata.items() if isinstance(record, dict)
233
+ and isinstance(record.get('catalog'), dict) and record['catalog'].get('name') == 'agent-importers']
234
+ if len(matches) != 1:
235
+ return None
236
+ key, record = matches[0]
237
+ root = pc._sanitize_plugin_name(key, Path(home) / 'plugins')
238
+ # Admission must not fetch or populate the profile's live catalog cache on a preview.
239
+ cached = dashboard_plugins._read(Path(home), 'cache/plugin-catalog.json', 4 * 1024 * 1024, anchor=Path(home))
240
+ if cached is None and ((Path(home) / 'cache/plugin-catalog.json').exists() or (Path(home) / 'cache/plugin-catalog.json').is_symlink()):
241
+ return None
242
+ cached = json.loads(cached) if cached is not None else {}
243
+ if not isinstance(cached, dict) or not isinstance(cached.get('entries', []), list):
244
+ return None
245
+ if any(e.get('name') == 'agent-importers' and e.get('sha') != pin for e in cached.get('entries', []) if isinstance(e, dict)):
246
+ return None
247
+ removed = source.load_removed_list() + source._removed_from_list(cached.get('removed'))
248
+ if (entry is None or entry.sha != pin or _refusal(entry, pc, native, removed_entries=removed)
249
+ or record.get('revision') != pin or record['catalog'].get('sha') != pin
250
+ or record['catalog'].get('repo') != entry.repo
251
+ or key not in enabled or key in disabled or root.resolve() not in {p.resolve() for p in enabled_plugin_dirs()}):
252
+ return None
253
+ bodies = {name: dashboard_plugins._read(root, name, anchor=Path(home)) for name in hashes}
254
+ if any(body is None or hashlib.sha256(body).hexdigest() != hashes[name] for name, body in bodies.items()):
255
+ return None
256
+ # Compile verified bytes, never re-open a mutable plugin path or execute its entry point.
257
+ package = '_talaria_reviewed_agent_importers'
258
+ if package not in sys.modules:
259
+ module = types.ModuleType(package)
260
+ module.__path__ = []
261
+ sys.modules[package] = module
262
+ for name in ('importer', 'sync'):
263
+ module = types.ModuleType(f'{package}.{name}')
264
+ module.__package__ = package
265
+ sys.modules[module.__name__] = module
266
+ exec(compile(bodies[name + '.py'], f'<reviewed-agent-importers/{name}.py>', 'exec'), module.__dict__)
267
+ return sys.modules[package + '.importer'].AgentImporter, sys.modules[package + '.sync'], [pin, str(root), _tree_digest(root)]
268
+ except (ImportError, AttributeError, OSError, ValueError, TypeError, RpcError):
269
+ return None