@maudecode/talaria-web 1.14.0 → 1.16.1

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 (348) hide show
  1. package/_release.json +4 -4
  2. package/dist/api/automation-router.d.ts +123 -7
  3. package/dist/api/automation-router.js +5 -0
  4. package/dist/api/automation-router.js.map +1 -1
  5. package/dist/api/chat-router.d.ts +728 -55
  6. package/dist/api/chat-router.js +122 -126
  7. package/dist/api/chat-router.js.map +1 -1
  8. package/dist/api/git-router.js +44 -32
  9. package/dist/api/git-router.js.map +1 -1
  10. package/dist/api/raw-routes.js +5 -58
  11. package/dist/api/raw-routes.js.map +1 -1
  12. package/dist/api/router.d.ts +6 -3
  13. package/dist/api/router.js +10 -8
  14. package/dist/api/router.js.map +1 -1
  15. package/dist/api/sessions-router.d.ts +2583 -297
  16. package/dist/api/sessions-router.js +92 -47
  17. package/dist/api/sessions-router.js.map +1 -1
  18. package/dist/api/settings-router.d.ts +188 -17
  19. package/dist/api/settings-router.js +75 -26
  20. package/dist/api/settings-router.js.map +1 -1
  21. package/dist/api/sse-routes.js +1 -1
  22. package/dist/api/sse-routes.js.map +1 -1
  23. package/dist/api/tools-router.d.ts +13 -1
  24. package/dist/api/tools-router.js +10 -8
  25. package/dist/api/tools-router.js.map +1 -1
  26. package/dist/app.d.ts +3620 -343
  27. package/dist/auth/gate.js +8 -4
  28. package/dist/auth/gate.js.map +1 -1
  29. package/dist/auth/oidc.d.ts +6 -2
  30. package/dist/auth/oidc.js +16 -11
  31. package/dist/auth/oidc.js.map +1 -1
  32. package/dist/auth/store.d.ts +14 -2
  33. package/dist/auth/store.js +26 -1
  34. package/dist/auth/store.js.map +1 -1
  35. package/dist/cli/dotenv.d.ts +1 -1
  36. package/dist/cli/dotenv.js +36 -3
  37. package/dist/cli/dotenv.js.map +1 -1
  38. package/dist/config/agent-config.js +20 -15
  39. package/dist/config/agent-config.js.map +1 -1
  40. package/dist/http/addresses.d.ts +6 -0
  41. package/dist/http/addresses.js +26 -0
  42. package/dist/http/addresses.js.map +1 -1
  43. package/dist/http/context.d.ts +7 -5
  44. package/dist/http/context.js.map +1 -1
  45. package/dist/links.d.ts +26 -0
  46. package/dist/links.js +60 -0
  47. package/dist/links.js.map +1 -0
  48. package/dist/onboarding.d.ts +1 -1
  49. package/dist/onboarding.js +28 -8
  50. package/dist/onboarding.js.map +1 -1
  51. package/dist/profiles/profiles.js +1 -1
  52. package/dist/profiles/profiles.js.map +1 -1
  53. package/dist/providers/catalog.d.ts +85 -1
  54. package/dist/providers/catalog.js +280 -43
  55. package/dist/providers/catalog.js.map +1 -1
  56. package/dist/providers/tables.d.ts +3 -1
  57. package/dist/providers/tables.js +9 -4
  58. package/dist/providers/tables.js.map +1 -1
  59. package/dist/redact.js +493 -13
  60. package/dist/redact.js.map +1 -1
  61. package/dist/runtime.js +104 -42
  62. package/dist/runtime.js.map +1 -1
  63. package/dist/sessions/anchor.d.ts +22 -3
  64. package/dist/sessions/anchor.js +87 -42
  65. package/dist/sessions/anchor.js.map +1 -1
  66. package/dist/sessions/background-tasks.d.ts +140 -0
  67. package/dist/sessions/background-tasks.js +352 -0
  68. package/dist/sessions/background-tasks.js.map +1 -0
  69. package/dist/sessions/background-updates.d.ts +56 -0
  70. package/dist/sessions/background-updates.js +152 -0
  71. package/dist/sessions/background-updates.js.map +1 -0
  72. package/dist/sessions/cli-sessions.d.ts +6 -0
  73. package/dist/sessions/cli-sessions.js +20 -7
  74. package/dist/sessions/cli-sessions.js.map +1 -1
  75. package/dist/sessions/completions.d.ts +17 -1
  76. package/dist/sessions/completions.js +84 -13
  77. package/dist/sessions/completions.js.map +1 -1
  78. package/dist/sessions/compress.d.ts +33 -0
  79. package/dist/sessions/compress.js +66 -0
  80. package/dist/sessions/compress.js.map +1 -0
  81. package/dist/sessions/list.d.ts +22 -4
  82. package/dist/sessions/list.js +113 -33
  83. package/dist/sessions/list.js.map +1 -1
  84. package/dist/sessions/merge.d.ts +81 -7
  85. package/dist/sessions/merge.js +391 -26
  86. package/dist/sessions/merge.js.map +1 -1
  87. package/dist/sessions/pending.d.ts +5 -0
  88. package/dist/sessions/pending.js +30 -10
  89. package/dist/sessions/pending.js.map +1 -1
  90. package/dist/sessions/relay.d.ts +7 -1
  91. package/dist/sessions/relay.js +17 -4
  92. package/dist/sessions/relay.js.map +1 -1
  93. package/dist/sessions/service.d.ts +118 -6
  94. package/dist/sessions/service.js +564 -65
  95. package/dist/sessions/service.js.map +1 -1
  96. package/dist/sessions/session.d.ts +22 -1
  97. package/dist/sessions/session.js +29 -2
  98. package/dist/sessions/session.js.map +1 -1
  99. package/dist/sessions/shares.js +11 -2
  100. package/dist/sessions/shares.js.map +1 -1
  101. package/dist/sessions/source-kind.d.ts +22 -0
  102. package/dist/sessions/source-kind.js +54 -0
  103. package/dist/sessions/source-kind.js.map +1 -0
  104. package/dist/sessions/state-db.d.ts +10 -1
  105. package/dist/sessions/state-db.js +48 -17
  106. package/dist/sessions/state-db.js.map +1 -1
  107. package/dist/sessions/store.d.ts +2 -0
  108. package/dist/sessions/store.js +24 -5
  109. package/dist/sessions/store.js.map +1 -1
  110. package/dist/sessions/streams.d.ts +3 -0
  111. package/dist/sessions/streams.js +0 -5
  112. package/dist/sessions/streams.js.map +1 -1
  113. package/dist/sessions/titles.d.ts +4 -0
  114. package/dist/sessions/titles.js +16 -0
  115. package/dist/sessions/titles.js.map +1 -1
  116. package/dist/sessions/turn-context.d.ts +1 -0
  117. package/dist/sessions/turn-context.js +3 -0
  118. package/dist/sessions/turn-context.js.map +1 -1
  119. package/dist/sessions/turn.d.ts +85 -7
  120. package/dist/sessions/turn.js +518 -170
  121. package/dist/sessions/turn.js.map +1 -1
  122. package/dist/settings.d.ts +2 -0
  123. package/dist/settings.js +13 -0
  124. package/dist/settings.js.map +1 -1
  125. package/dist/sidecar/fake.js +2 -0
  126. package/dist/sidecar/fake.js.map +1 -1
  127. package/dist/tools/commands.d.ts +15 -0
  128. package/dist/tools/commands.js +52 -0
  129. package/dist/tools/commands.js.map +1 -0
  130. package/dist/tools/crons.d.ts +38 -4
  131. package/dist/tools/crons.js +323 -36
  132. package/dist/tools/crons.js.map +1 -1
  133. package/dist/tools/mcp.js +5 -1
  134. package/dist/tools/mcp.js.map +1 -1
  135. package/dist/tools/update-notifications.d.ts +1 -1
  136. package/dist/tools/update-notifications.js +1 -1
  137. package/dist/tools/update-notifications.js.map +1 -1
  138. package/dist/tools/updates.d.ts +8 -3
  139. package/dist/tools/updates.js +133 -34
  140. package/dist/tools/updates.js.map +1 -1
  141. package/dist/workspace/git.d.ts +57 -10
  142. package/dist/workspace/git.js +302 -195
  143. package/dist/workspace/git.js.map +1 -1
  144. package/dist/workspace/media-refs.d.ts +13 -0
  145. package/dist/workspace/media-refs.js +165 -0
  146. package/dist/workspace/media-refs.js.map +1 -0
  147. package/dist/workspace/media.d.ts +19 -0
  148. package/dist/workspace/media.js +61 -2
  149. package/dist/workspace/media.js.map +1 -1
  150. package/dist/workspace/workspaces.d.ts +7 -1
  151. package/dist/workspace/workspaces.js +30 -21
  152. package/dist/workspace/workspaces.js.map +1 -1
  153. package/dist/workspace/worktrees.d.ts +2 -4
  154. package/dist/workspace/worktrees.js +28 -33
  155. package/dist/workspace/worktrees.js.map +1 -1
  156. package/package.json +4 -2
  157. package/sidecar/scripts/replay_sidecar.py +3 -1
  158. package/sidecar/talaria_sidecar/__init__.py +1 -1
  159. package/sidecar/talaria_sidecar/home.py +59 -2
  160. package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
  161. package/sidecar/talaria_sidecar/methods/aux.py +19 -3
  162. package/sidecar/talaria_sidecar/methods/chat.py +360 -27
  163. package/sidecar/talaria_sidecar/methods/commands.py +46 -31
  164. package/sidecar/talaria_sidecar/methods/config.py +14 -10
  165. package/sidecar/talaria_sidecar/methods/cron.py +22 -14
  166. package/sidecar/talaria_sidecar/methods/oauth.py +439 -0
  167. package/sidecar/talaria_sidecar/methods/process.py +318 -0
  168. package/sidecar/talaria_sidecar/methods/profiles.py +5 -4
  169. package/sidecar/talaria_sidecar/methods/providers.py +161 -1
  170. package/sidecar/talaria_sidecar/methods/runtime.py +2 -0
  171. package/sidecar/talaria_sidecar/methods/text.py +1 -20
  172. package/sidecar/talaria_sidecar/methods/usage.py +7 -1
  173. package/static/dist/FILES.txt +124 -119
  174. package/static/dist/assets/AppShell-ef3bhu32.js +1 -0
  175. package/static/dist/assets/Button-nx37f4u1.js +1 -0
  176. package/static/dist/assets/ChatPage-cdcpykcg.js +25 -0
  177. package/static/dist/assets/Dialog-mec42hvb.js +1 -0
  178. package/static/dist/assets/HubRoute-ecb65zpr.js +2 -0
  179. package/static/dist/assets/{Markdown-emii55o0.js → Markdown-jqrx5es0.js} +125 -125
  180. package/static/dist/assets/RightPanel-d1lgvk4s.js +1 -0
  181. package/static/dist/assets/Select-h4ivl9qf.js +1 -0
  182. package/static/dist/assets/SessionListPanel-ewib046b.js +1 -0
  183. package/static/dist/assets/SettingsLayout-lx654cup.js +1 -0
  184. package/static/dist/assets/States-ehsb6lzo.js +1 -0
  185. package/static/dist/assets/Toaster-mch1pa3t.js +1 -0
  186. package/static/dist/assets/_app-m8d961ef.js +1 -0
  187. package/static/dist/assets/_app.ext._extensionId-mp33hbzi.js +1 -0
  188. package/static/dist/assets/_app.index-dk6zp7e0.js +1 -0
  189. package/static/dist/assets/_app.insights-jt2fk4i1.js +1 -0
  190. package/static/dist/assets/_app.kanban-if4pubeu.js +1 -0
  191. package/static/dist/assets/_app.logs-mfaw9m6g.js +1 -0
  192. package/static/dist/assets/_app.memory-h3eaarb6.js +1 -0
  193. package/static/dist/assets/_app.profiles-ienktovs.js +1 -0
  194. package/static/dist/assets/{_app.session._sessionId-m6nx9g8t.js → _app.session._sessionId-hh27wwu2.js} +1 -1
  195. package/static/dist/assets/_app.settings-dpoe0bzf.js +1 -0
  196. package/static/dist/assets/_app.settings._section-l468ynag.js +3 -0
  197. package/static/dist/assets/_app.skills-kcbncc8i.js +1 -0
  198. package/static/dist/assets/_app.tasks-occdrqhw.js +2 -0
  199. package/static/dist/assets/_app.todos-nnke2rcc.js +1 -0
  200. package/static/dist/assets/_app.workspaces-cdlatvwj.js +1 -0
  201. package/static/dist/assets/{abnfDiagram-VCTEODGH-kejgxpil.js → abnfDiagram-VCTEODGH-oity9y4o.js} +1 -1
  202. package/static/dist/assets/architecture-7GRP2DOG-hj11333z.js +1 -0
  203. package/static/dist/assets/architectureDiagram-5GKGNRK7-ceaepnyr.js +36 -0
  204. package/static/dist/assets/back-eu64w89w.js +1 -0
  205. package/static/dist/assets/{blockDiagram-I7D4REHJ-l1466a7j.js → blockDiagram-I7D4REHJ-n79st4pj.js} +1 -1
  206. package/static/dist/assets/{c4Diagram-7LVT6UL2-gg3aq3sr.js → c4Diagram-7LVT6UL2-mi73akzk.js} +1 -1
  207. package/static/dist/assets/cancel-mzvkdjg8.js +1 -0
  208. package/static/dist/assets/channel-mbvmf2vv.js +1 -0
  209. package/static/dist/assets/{chunk-4HAMMTFA-oalgz5l8.js → chunk-4HAMMTFA-laat0g1q.js} +1 -1
  210. package/static/dist/assets/{chunk-75Z2AOVW-babmzhdg.js → chunk-75Z2AOVW-0psy1tr2.js} +1 -1
  211. package/static/dist/assets/{chunk-DU6HZSFF-lagu350c.js → chunk-DU6HZSFF-lw8uox8x.js} +3 -3
  212. package/static/dist/assets/{chunk-F27PBJKO-gjh2tqif.js → chunk-F27PBJKO-erassib3.js} +1 -1
  213. package/static/dist/assets/{chunk-GMAD6QVW-ipdgja5c.js → chunk-GMAD6QVW-fz7rx619.js} +1 -1
  214. package/static/dist/assets/{chunk-GVQU2GXP-j8wkn1at.js → chunk-GVQU2GXP-gdb0epb2.js} +1 -1
  215. package/static/dist/assets/{chunk-IMKFNOWR-k00laqyk.js → chunk-IMKFNOWR-ea8t26qw.js} +1 -1
  216. package/static/dist/assets/{chunk-L3NEJ4N5-fju6xqj4.js → chunk-L3NEJ4N5-mb1gu9kz.js} +1 -1
  217. package/static/dist/assets/{chunk-OSK3NFVY-bi6widqt.js → chunk-OSK3NFVY-eovmccvy.js} +1 -1
  218. package/static/dist/assets/{chunk-P2QGCYS3-czr4tebv.js → chunk-P2QGCYS3-bo55zjo5.js} +1 -1
  219. package/static/dist/assets/{chunk-POPQ4Y6H-ktdhn67w.js → chunk-POPQ4Y6H-ja7ohe4h.js} +1 -1
  220. package/static/dist/assets/{chunk-PWAF6VOD-ii4dsu0d.js → chunk-PWAF6VOD-e30g9nus.js} +1 -1
  221. package/static/dist/assets/{chunk-SHT3W25Y-fpwk3xxp.js → chunk-SHT3W25Y-gbb798f7.js} +3 -3
  222. package/static/dist/assets/{chunk-SVP7TREG-k0jrvhei.js → chunk-SVP7TREG-h2iw8urm.js} +1 -1
  223. package/static/dist/assets/{chunk-TICWLB2K-nkqv9ot0.js → chunk-TICWLB2K-fd2cottz.js} +1 -1
  224. package/static/dist/assets/classDiagram-ZZMXUADV-o8r05k0n.js +1 -0
  225. package/static/dist/assets/classDiagram-v2-VYDZK3BY-o8r05k0n.js +1 -0
  226. package/static/dist/assets/{copied-gpchexqu.js → copied-h65fc3cx.js} +1 -1
  227. package/static/dist/assets/copy-e6977uwq.js +1 -0
  228. package/static/dist/assets/{cynefin-OW5HDTMX-lqxt8rs3.js → cynefin-OW5HDTMX-lbltyl5j.js} +1 -1
  229. package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-j9eqovf5.js → cynefinDiagram-5FMLGOSQ-lxhsqq4a.js} +1 -1
  230. package/static/dist/assets/{dagre-GXQ25YYZ-m9agobjm.js → dagre-GXQ25YYZ-gn4nnaln.js} +4 -4
  231. package/static/dist/assets/{diagram-S7CK7UJ4-itodc50c.js → diagram-S7CK7UJ4-pa3ij6q7.js} +3 -3
  232. package/static/dist/assets/{diagram-UQ7AKVKN-n1h2t4ah.js → diagram-UQ7AKVKN-zbipyz57.js} +1 -1
  233. package/static/dist/assets/{diagram-VSXAHHWV-lvtvm9bj.js → diagram-VSXAHHWV-d1kbpzgf.js} +1 -1
  234. package/static/dist/assets/{diagram-VX7I27RA-cgas58sy.js → diagram-VX7I27RA-i3ty14xl.js} +1 -1
  235. package/static/dist/assets/{diagram-Z3DM3KII-d0aeps1q.js → diagram-Z3DM3KII-m8xefqa7.js} +1 -1
  236. package/static/dist/assets/{ebnfDiagram-PWID7BFC-md345fyq.js → ebnfDiagram-PWID7BFC-nz7wj05l.js} +1 -1
  237. package/static/dist/assets/endpoints-k7yf25m0.js +64 -0
  238. package/static/dist/assets/{erDiagram-RLTQ6QDP-i4rua63b.js → erDiagram-RLTQ6QDP-fa41ib83.js} +1 -1
  239. package/static/dist/assets/eventmodeling-NTZA5JFV-kpbq09bx.js +1 -0
  240. package/static/dist/assets/flowDiagram-HODETNUW-lcz0kx14.js +1 -0
  241. package/static/dist/assets/{ganttDiagram-EL5Y4UJY-geqx7zgl.js → ganttDiagram-EL5Y4UJY-n7zq01qu.js} +1 -1
  242. package/static/dist/assets/{gitGraph-4MIJSDKK-leoq0z8t.js → gitGraph-4MIJSDKK-c89emb07.js} +1 -1
  243. package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-llrlxfha.js → gitGraphDiagram-WWUBYQGX-cz7cgr6w.js} +1 -1
  244. package/static/dist/assets/{highlighted-body-KPVGNVTW-kq2cnw6q.js → highlighted-body-KPVGNVTW-d9bapmni.js} +1 -1
  245. package/static/dist/assets/{index-f9bzglkn.js → index-h00usru2.js} +3 -3
  246. package/static/dist/assets/index-n73ijkv2.css +2 -0
  247. package/static/dist/assets/{info-A6RAGUB7-lkly4xj6.js → info-A6RAGUB7-ndddhhhh.js} +1 -1
  248. package/static/dist/assets/infoDiagram-27XIBGKW-ccxigcpf.js +2 -0
  249. package/static/dist/assets/{ishikawaDiagram-5VMMS53U-hq6jf374.js → ishikawaDiagram-5VMMS53U-le0z9vtc.js} +1 -1
  250. package/static/dist/assets/{journeyDiagram-3NMN7TZE-is03ntb0.js → journeyDiagram-3NMN7TZE-ivsmb26o.js} +1 -1
  251. package/static/dist/assets/{kanban-definition-UXKFOSKX-jmerizqb.js → kanban-definition-UXKFOSKX-h6n2awt0.js} +1 -1
  252. package/static/dist/assets/lazyRouteComponent-m1rtyk4t.js +1 -0
  253. package/static/dist/assets/{line-laaa8h99.js → line-dmzlfe0u.js} +1 -1
  254. package/static/dist/assets/link-budeg5t9.js +1 -0
  255. package/static/dist/assets/login-kjhasvp8.js +1 -0
  256. package/static/dist/assets/mermaid-HWGCJPDP-jb5d211j.js +1 -0
  257. package/static/dist/assets/{mermaid-parser.core-ix4junux.js → mermaid-parser.core-gwyd5bnq.js} +3 -3
  258. package/static/dist/assets/{mindmap-definition-YA3MSWOX-o0e0adl1.js → mindmap-definition-YA3MSWOX-l68gg5h7.js} +1 -1
  259. package/static/dist/assets/onboarding-cdfrk0pj.js +1 -0
  260. package/static/dist/assets/{packet-AYTQ26CC-kl8pwsyg.js → packet-AYTQ26CC-gpv06065.js} +1 -1
  261. package/static/dist/assets/{pegDiagram-XKGWAZYB-bbqn9jo4.js → pegDiagram-XKGWAZYB-e52vs23u.js} +1 -1
  262. package/static/dist/assets/{pie-WAS4IAKB-cpyo8bkr.js → pie-WAS4IAKB-l6m7nz5s.js} +1 -1
  263. package/static/dist/assets/{pieDiagram-E7YTZNPT-pelug34f.js → pieDiagram-E7YTZNPT-b4is36bq.js} +3 -3
  264. package/static/dist/assets/{quadrantDiagram-AXDQQJYC-jds4jmr5.js → quadrantDiagram-AXDQQJYC-m8vt2788.js} +1 -1
  265. package/static/dist/assets/{radar-RG4KPBEZ-e02oxnvd.js → radar-RG4KPBEZ-nxbrhiov.js} +1 -1
  266. package/static/dist/assets/{railroad-74A4TZTK-kz3delwo.js → railroad-74A4TZTK-222mu9hi.js} +1 -1
  267. package/static/dist/assets/railroad-abnf-HS5TGJTU-kdvgl6x8.js +1 -0
  268. package/static/dist/assets/railroad-ebnf-LZEXJU2U-itiqslby.js +1 -0
  269. package/static/dist/assets/railroad-peg-WCYAUIDC-go7kov78.js +1 -0
  270. package/static/dist/assets/{railroadDiagram-O6MQD6OU-jgroj2ew.js → railroadDiagram-O6MQD6OU-o8lldfzv.js} +1 -1
  271. package/static/dist/assets/{refresh-cw-k7yxjdn3.js → refresh-cw-kbcje1uy.js} +1 -1
  272. package/static/dist/assets/{requirementDiagram-BXWQKSXE-m4az5e0j.js → requirementDiagram-BXWQKSXE-ovyq38kv.js} +1 -1
  273. package/static/dist/assets/{sankeyDiagram-P5KCCOFB-vlryqiv3.js → sankeyDiagram-P5KCCOFB-ou7lznor.js} +1 -1
  274. package/static/dist/assets/{saved-brdfjjnw.js → saved-bazmcup9.js} +1 -1
  275. package/static/dist/assets/search-jen74z4u.js +1 -0
  276. package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-fr8hrpql.js → sequenceDiagram-WJ2MYXX4-ovckdzf2.js} +1 -1
  277. package/static/dist/assets/{settings_section_extensions_title-l0zodj0b.js → settings_section_extensions_title-ertwwhlx.js} +1 -1
  278. package/static/dist/assets/share._token-dan9hgvj.js +2 -0
  279. package/static/dist/assets/{sliders-horizontal-gcgscv8r.js → sliders-horizontal-f9cb3lh3.js} +1 -1
  280. package/static/dist/assets/{stateDiagram-D77RDMKH-bg548iyr.js → stateDiagram-D77RDMKH-fcrexkkv.js} +1 -1
  281. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-lcfraua2.js +1 -0
  282. package/static/dist/assets/{swimlanes-42K2YHIH-hk5z3efh.js → swimlanes-42K2YHIH-f7usu8fw.js} +1 -1
  283. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-ogmanni0.js +8 -0
  284. package/static/dist/assets/{timeline-definition-24CTP7MA-olvknr62.js → timeline-definition-24CTP7MA-gp1hvz9f.js} +1 -1
  285. package/static/dist/assets/todoStore-eqtid4r4.js +1 -0
  286. package/static/dist/assets/{treeView-Q6P3EWNA-ovvjsxtf.js → treeView-Q6P3EWNA-gb9gf11u.js} +1 -1
  287. package/static/dist/assets/{treemap-WGGIJYW6-ggkipj59.js → treemap-WGGIJYW6-nuvfnvvi.js} +1 -1
  288. package/static/dist/assets/{untitled-c8ydw0bu.js → untitled-htelhc68.js} +1 -1
  289. package/static/dist/assets/useForm-h2t0u14e.js +1 -0
  290. package/static/dist/assets/useLocale-hcsb84pj.js +1 -0
  291. package/static/dist/assets/useNavigate-nwy70ztj.js +1 -0
  292. package/static/dist/assets/{useParams-katwvzil.js → useParams-dqd64jv7.js} +1 -1
  293. package/static/dist/assets/{useSearch-c70sgau1.js → useSearch-j30pvsy7.js} +1 -1
  294. package/static/dist/assets/{vennDiagram-4TSXK5OY-hzld7zt7.js → vennDiagram-4TSXK5OY-fzchbzaj.js} +1 -1
  295. package/static/dist/assets/{wardley-WFR3VGLG-hxkeoms8.js → wardley-WFR3VGLG-m47mhfbh.js} +1 -1
  296. package/static/dist/assets/{wardleyDiagram-VM6X3IG4-ljh90mb1.js → wardleyDiagram-VM6X3IG4-odg6ct1z.js} +2 -2
  297. package/static/dist/assets/{xychartDiagram-S5SC5T6Z-mnuaes03.js → xychartDiagram-S5SC5T6Z-mrhms5um.js} +1 -1
  298. package/static/dist/index.html +1 -1
  299. package/static/dist/sw.js +1 -1
  300. package/static/dist/assets/AppShell-haqi9qzu.js +0 -1
  301. package/static/dist/assets/ChatPage-hjyu2g64.js +0 -22
  302. package/static/dist/assets/HubRoute-gkd8o1vg.js +0 -2
  303. package/static/dist/assets/RightPanel-o73y98dn.js +0 -1
  304. package/static/dist/assets/Select-jld3yeni.js +0 -1
  305. package/static/dist/assets/SessionListPanel-d3qx4ric.js +0 -1
  306. package/static/dist/assets/SettingsLayout-lnn2wo2s.js +0 -1
  307. package/static/dist/assets/States-e5hv6gdh.js +0 -1
  308. package/static/dist/assets/Toaster-c4jq1i26.js +0 -1
  309. package/static/dist/assets/_app-bnip2pjg.js +0 -1
  310. package/static/dist/assets/_app.ext._extensionId-egptzrfy.js +0 -1
  311. package/static/dist/assets/_app.index-vdz13ul5.js +0 -1
  312. package/static/dist/assets/_app.insights-lgjshsba.js +0 -1
  313. package/static/dist/assets/_app.kanban-c18cjrpz.js +0 -1
  314. package/static/dist/assets/_app.logs-ezhojgeg.js +0 -1
  315. package/static/dist/assets/_app.memory-i00paisv.js +0 -1
  316. package/static/dist/assets/_app.profiles-kd02bwjx.js +0 -1
  317. package/static/dist/assets/_app.settings-giaudkuk.js +0 -1
  318. package/static/dist/assets/_app.settings._section-k4qtlexk.js +0 -1
  319. package/static/dist/assets/_app.skills-bby2uvl8.js +0 -1
  320. package/static/dist/assets/_app.tasks-1kz9bosm.js +0 -2
  321. package/static/dist/assets/_app.todos-izjdbkm2.js +0 -1
  322. package/static/dist/assets/_app.workspaces-lfh2kw7s.js +0 -1
  323. package/static/dist/assets/architecture-7GRP2DOG-b4u9z1w3.js +0 -1
  324. package/static/dist/assets/architectureDiagram-5GKGNRK7-hqnf84fc.js +0 -36
  325. package/static/dist/assets/channel-o77ioktg.js +0 -1
  326. package/static/dist/assets/classDiagram-ZZMXUADV-f5rvod26.js +0 -1
  327. package/static/dist/assets/classDiagram-v2-VYDZK3BY-f5rvod26.js +0 -1
  328. package/static/dist/assets/endpoints-e58saih3.js +0 -9
  329. package/static/dist/assets/eventmodeling-NTZA5JFV-bn3fjsaq.js +0 -1
  330. package/static/dist/assets/flowDiagram-HODETNUW-li70d120.js +0 -1
  331. package/static/dist/assets/index-gkh7kum4.css +0 -2
  332. package/static/dist/assets/infoDiagram-27XIBGKW-oo7kdyte.js +0 -2
  333. package/static/dist/assets/lazyRouteComponent-b1h2ykmb.js +0 -1
  334. package/static/dist/assets/link-d8f0788e.js +0 -1
  335. package/static/dist/assets/login-llkohrcg.js +0 -1
  336. package/static/dist/assets/mermaid-HWGCJPDP-id0ffk2r.js +0 -1
  337. package/static/dist/assets/onboarding-lx0d6b38.js +0 -1
  338. package/static/dist/assets/railroad-abnf-HS5TGJTU-l13ped2z.js +0 -1
  339. package/static/dist/assets/railroad-ebnf-LZEXJU2U-c7rg3g4g.js +0 -1
  340. package/static/dist/assets/railroad-peg-WCYAUIDC-od0b8niq.js +0 -1
  341. package/static/dist/assets/search-gt2ueyvg.js +0 -1
  342. package/static/dist/assets/share._token-oyjv046r.js +0 -2
  343. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-j5v40qih.js +0 -1
  344. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-grzefo9c.js +0 -8
  345. package/static/dist/assets/tabBuild-idliyw9f.js +0 -56
  346. package/static/dist/assets/todoStore-j4c2xq0b.js +0 -1
  347. package/static/dist/assets/useForm-kd759sjk.js +0 -1
  348. package/static/dist/assets/useLocale-hds4xmja.js +0 -1
@@ -4,8 +4,13 @@ routing; the sidecar drains and formats registry events."""
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
+ import json
7
8
  import logging
8
9
  import queue
10
+ import sqlite3
11
+ import time
12
+ from contextlib import closing
13
+ from pathlib import Path
9
14
 
10
15
  from ..errors import InvalidParams
11
16
  from ..home import profile_home_param, scoped_home
@@ -89,6 +94,62 @@ def mark_consumed(process_id: str) -> bool:
89
94
  return False
90
95
 
91
96
 
97
+ def _delivery_api():
98
+ """The Agent's durable delivery ledger (``tools.async_delegation``), or None on an Agent without it."""
99
+ try:
100
+ from tools import async_delegation
101
+ except Exception: # noqa: BLE001
102
+ return None
103
+ names = ("claim_event_delivery", "complete_event_delivery", "release_event_delivery")
104
+ return async_delegation if all(callable(getattr(async_delegation, n, None)) for n in names) else None
105
+
106
+
107
+ def _ledger_event(evt: dict) -> dict:
108
+ return {k: v for k, v in evt.items() if k not in ("process_id", "consumed")}
109
+
110
+
111
+ def claim_delivery(evt: dict, consumer: str) -> str | None:
112
+ """Claim a durable delegation completion before delivering it (TAL-459).
113
+
114
+ ``None``: another consumer holds it, or the ledger already delivered or dropped it, so do not deliver.
115
+ ``""``: nothing durable to acknowledge (interim notice, legacy event, or an Agent without the ledger)."""
116
+ api = _delivery_api()
117
+ if api is None:
118
+ return ""
119
+ claim_id = api.claim_event_delivery(_ledger_event(evt), consumer)
120
+ return None if claim_id is None else str(claim_id)
121
+
122
+
123
+ def complete_delivery(evt: dict, claim_id: str) -> bool:
124
+ api = _delivery_api()
125
+ if api is None or not claim_id:
126
+ return False
127
+ api.complete_event_delivery(_ledger_event(evt), claim_id)
128
+ return True
129
+
130
+
131
+ def release_delivery(evt: dict, claim_id: str) -> bool:
132
+ api = _delivery_api()
133
+ if api is None or not claim_id:
134
+ return False
135
+ api.release_event_delivery(_ledger_event(evt), claim_id)
136
+ return True
137
+
138
+
139
+ def defer_delivery(evt: dict, claim_id: str) -> bool:
140
+ """Hand back a claim the target never admitted (its session was busy) without spending a delivery attempt;
141
+ the Agent drops a row for good after its attempt budget, so a busy session must not burn it."""
142
+ api = _delivery_api()
143
+ if api is None or not claim_id:
144
+ return False
145
+ defer = getattr(api, "defer_completion_delivery", None)
146
+ if not callable(defer) or evt.get("type") != "async_delegation" or not evt.get("delegation_id"):
147
+ api.release_event_delivery(_ledger_event(evt), claim_id)
148
+ return True
149
+ defer(str(evt["delegation_id"]), claim_id)
150
+ return True
151
+
152
+
92
153
  def format_notification(evt: dict) -> str:
93
154
  if evt.get("type") == "async_delegation":
94
155
  try:
@@ -122,6 +183,210 @@ def list_sessions() -> list[dict]:
122
183
  return out
123
184
 
124
185
 
186
+ _LEDGER_COLUMNS = "delegation_id, origin_ui_session_id, state, dispatched_at, completed_at, updated_at, task_json, result_json, event_json IS NOT NULL"
187
+ _LEDGER_LIMIT = 200
188
+
189
+
190
+ def _json(text) -> dict:
191
+ try:
192
+ value = json.loads(text) if text else {}
193
+ except (TypeError, ValueError):
194
+ return {}
195
+ return value if isinstance(value, dict) else {}
196
+
197
+
198
+ def _unit_goals(task: dict) -> list[str]:
199
+ goals = task.get("goals") if isinstance(task.get("goals"), list) else None
200
+ indexes = task.get("task_indexes") if isinstance(task.get("task_indexes"), list) else None
201
+ if goals and indexes:
202
+ return [str(goals[i]) for i in indexes if isinstance(i, int) and 0 <= i < len(goals)]
203
+ if goals:
204
+ return [str(g) for g in goals]
205
+ return [str(task.get("goal") or "")]
206
+
207
+
208
+ def _child_statuses(result: dict) -> list[str]:
209
+ """Per-subagent outcome of a finished unit: a batch carries ``results``, a single task is its own result."""
210
+ if isinstance(result.get("results"), list):
211
+ return [str(r.get("status") or "") for r in result["results"] if isinstance(r, dict)]
212
+ return [str(result.get("status") or "")] if result else []
213
+
214
+
215
+ def _ledger_rows(home: Path, session_ids: list[str]) -> list[dict]:
216
+ db_path = home / "state.db"
217
+ if not db_path.exists() or not session_ids:
218
+ return []
219
+ marks = ",".join("?" * len(session_ids))
220
+ with closing(sqlite3.connect(f"{db_path.resolve().as_uri()}?mode=ro", uri=True, timeout=5)) as conn:
221
+ try:
222
+ rows = conn.execute(f"SELECT {_LEDGER_COLUMNS} FROM async_delegations WHERE origin_ui_session_id IN ({marks}) ORDER BY dispatched_at DESC LIMIT ?", [*session_ids, _LEDGER_LIMIT]).fetchall()
223
+ except sqlite3.OperationalError: # an Agent that never delegated has no ledger table yet
224
+ return []
225
+ out = []
226
+ for delegation_id, origin, state, dispatched_at, completed_at, updated_at, task_json, result_json, has_event in rows:
227
+ task, result = _json(task_json), _json(result_json)
228
+ goals = _unit_goals(task)
229
+ out.append({
230
+ "delegation_id": delegation_id, "origin_ui_session_id": origin, "state": state, "dispatched_at": dispatched_at,
231
+ "completed_at": completed_at, "updated_at": updated_at, "goals": goals,
232
+ "child_statuses": _child_statuses(result), "has_result": bool(has_event),
233
+ })
234
+ return out
235
+
236
+
237
+ def _live_delegations() -> dict[str, dict]:
238
+ try:
239
+ from tools.async_delegation import list_async_delegations
240
+ items = list_async_delegations()
241
+ except Exception: # noqa: BLE001 - an Agent without the live registry reports from the ledger alone
242
+ return {}
243
+ return {str(i.get("delegation_id")): {"status": str(i.get("status") or "")} for i in items if isinstance(i, dict) and i.get("delegation_id")}
244
+
245
+
246
+ def _owned_processes(session_ids: set[str]) -> list[dict]:
247
+ """Notified or watched processes a WebUI session started (``session_key`` is the WebUI session id)."""
248
+ registry = _registry()
249
+ if registry is None:
250
+ return []
251
+ try:
252
+ with registry._lock:
253
+ sessions = [*registry._running.values(), *registry._finished.values()]
254
+ except Exception: # noqa: BLE001
255
+ return []
256
+ out = []
257
+ for proc in sessions:
258
+ if str(getattr(proc, "session_key", "") or "") not in session_ids:
259
+ continue
260
+ watched = bool(getattr(proc, "watch_patterns", None))
261
+ if not (getattr(proc, "notify_on_complete", False) or watched):
262
+ continue
263
+ exited = bool(getattr(proc, "exited", False))
264
+ out.append({
265
+ "process_id": str(proc.id), "session_key": str(proc.session_key), "command": str(getattr(proc, "command", "") or "")[:200],
266
+ "started_at": float(getattr(proc, "started_at", 0) or 0) or None, "exited": exited,
267
+ "exited_at": float(getattr(proc, "exited_at", 0) or 0) or None, "exit_code": getattr(proc, "exit_code", None),
268
+ "completion_reason": str(getattr(proc, "completion_reason", "") or ""), "watched": watched,
269
+ })
270
+ return out
271
+
272
+
273
+ _CHILD_WINDOW_S = 5
274
+
275
+
276
+ def _live_children() -> list[dict]:
277
+ """Running subagents from the Agent's in-process registry: the only place a child's session id sits next to its
278
+ delegation id. An Agent without the registry reports none."""
279
+ try:
280
+ from tools.delegate_tool_registry import _active_subagents, _active_subagents_lock
281
+ with _active_subagents_lock:
282
+ records = list(_active_subagents.values())
283
+ except Exception: # noqa: BLE001
284
+ return []
285
+ out = []
286
+ for r in records:
287
+ sid = getattr(r.get("agent"), "session_id", None)
288
+ if isinstance(sid, str) and sid:
289
+ out.append({"delegation_id": str(r.get("delegation_id") or ""), "goal": str(r.get("goal") or ""), "owner": str(r.get("owner_agent_session_id") or ""), "session_id": sid})
290
+ return out
291
+
292
+
293
+ def _ledger_children(home: Path, owners: list[str]) -> list[dict]:
294
+ """Finished subagents' own sessions in state.db: ``source = subagent`` rows tagged ``_delegate_from`` with their
295
+ parent's session, each with its start time and first user message (the child's goal, sent verbatim)."""
296
+ db_path = home / "state.db"
297
+ if not db_path.exists() or not owners:
298
+ return []
299
+ marks = ",".join("?" * len(owners))
300
+ with closing(sqlite3.connect(f"{db_path.resolve().as_uri()}?mode=ro", uri=True, timeout=5)) as conn:
301
+ try:
302
+ # Only these chats' children: a long-lived profile keeps every subagent session it ever ran.
303
+ rows = conn.execute(
304
+ "SELECT s.id, s.model_config, s.started_at, (SELECT m.content FROM messages m WHERE m.session_id = s.id AND m.role = 'user' ORDER BY m.id LIMIT 1)"
305
+ f" FROM sessions s WHERE s.source = 'subagent' AND json_extract(s.model_config, '$._delegate_from') IN ({marks})", owners).fetchall()
306
+ except sqlite3.OperationalError:
307
+ return []
308
+ out = []
309
+ for sid, model_config, started_at, first in rows:
310
+ owner = _json(model_config).get("_delegate_from")
311
+ goal = _goal_text(first)
312
+ if owner in owners and goal:
313
+ out.append({"owner": owner, "session_id": sid, "started_at": started_at, "goal": goal})
314
+ return out
315
+
316
+
317
+ _CONTENT_JSON_PREFIX = "\x00json:"
318
+ _IMAGE_HINTS = "\n\n[Image attached"
319
+
320
+
321
+ def _goal_text(content) -> str:
322
+ """The goal a child was sent, from its stored first message: a task with images is stored as multimodal parts
323
+ (native image input; the goal is the text part) or as the goal followed by image hints (text input)."""
324
+ if not isinstance(content, str):
325
+ return ""
326
+ if content.startswith(_CONTENT_JSON_PREFIX):
327
+ try:
328
+ parts = json.loads(content[len(_CONTENT_JSON_PREFIX):])
329
+ except ValueError:
330
+ return ""
331
+ texts = [p.get("text") for p in parts if isinstance(p, dict) and p.get("type") == "text"] if isinstance(parts, list) else []
332
+ return str(texts[0]) if texts and texts[0] else ""
333
+ return content.split(_IMAGE_HINTS, 1)[0]
334
+
335
+
336
+ def unit_children(unit: dict, owner: str, live: list[dict], finished: list[dict], now: float) -> list[dict]:
337
+ """The subagent sessions one delegation unit ran, as ``{goal, session_id}`` per task, in task order. A live registry
338
+ record is exact (same owner, the unit's call id, the task's goal) and is used once. Without one, finished sessions
339
+ of that owner whose goal matches, started in the unit's window, count only when there are exactly as many as tasks
340
+ with that goal; anything else stays unlinked rather than guessed."""
341
+ unit_id, goals = unit["delegation_id"], [g for g in unit["goals"] if g]
342
+ pool = [r for r in live if r["owner"] == owner and r["delegation_id"] and (unit_id == r["delegation_id"] or unit_id.startswith(f"{r['delegation_id']}-"))]
343
+ linked: list[str | None] = []
344
+ used: set[str] = set()
345
+ for goal in goals:
346
+ record = next((r for r in pool if r["goal"] == goal and r["session_id"] not in used), None)
347
+ if record:
348
+ used.add(record["session_id"])
349
+ linked.append(record["session_id"] if record else None)
350
+ start = (unit.get("dispatched_at") or 0) - _CHILD_WINDOW_S
351
+ end = (unit.get("completed_at") or now) + _CHILD_WINDOW_S
352
+ for goal in {g for g, sid in zip(goals, linked) if sid is None}:
353
+ slots = [i for i, (g, sid) in enumerate(zip(goals, linked)) if g == goal and sid is None]
354
+ matches = sorted((c for c in finished if c["owner"] == owner and c["goal"] == goal and c["session_id"] not in used and start <= (c["started_at"] or 0) <= end), key=lambda c: c["started_at"] or 0)
355
+ if len(matches) == len(slots):
356
+ for i, c in zip(slots, matches):
357
+ linked[i] = c["session_id"]
358
+ return [{"goal": g, "session_id": sid} for g, sid in zip(goals, linked) if sid]
359
+
360
+
361
+ def background_list(home: Path, session_ids: list[str]) -> dict:
362
+ """TAL-372: what the Agent knows about the background work of these WebUI sessions: delegations from the durable
363
+ ledger (with the live registry's status while they run) and notified processes from the process registry.
364
+ TAL-494: each delegation also names the subagent sessions it ran."""
365
+ live = _live_delegations()
366
+ delegations = _ledger_rows(home, session_ids)
367
+ live_children = _live_children()
368
+ finished = _ledger_children(home, session_ids) if delegations else []
369
+ now = time.time()
370
+ for row in delegations:
371
+ row["live_status"] = live.get(row["delegation_id"], {}).get("status")
372
+ row["children"] = unit_children(row, row["origin_ui_session_id"], live_children, finished, now)
373
+ return {"delegations": delegations, "processes": _owned_processes(set(session_ids))}
374
+
375
+
376
+ def delegation_result(home: Path, session_id: str, delegation_id: str) -> str:
377
+ """The full result of a finished delegation unit, as the Agent words it for its own notification."""
378
+ db_path = home / "state.db"
379
+ if not db_path.exists():
380
+ return ""
381
+ with closing(sqlite3.connect(f"{db_path.resolve().as_uri()}?mode=ro", uri=True, timeout=5)) as conn:
382
+ try:
383
+ row = conn.execute("SELECT event_json FROM async_delegations WHERE delegation_id = ? AND origin_ui_session_id = ?", (delegation_id, session_id)).fetchone()
384
+ except sqlite3.OperationalError:
385
+ return ""
386
+ event = _json(row[0]) if row else {}
387
+ return format_notification(event) if event else ""
388
+
389
+
125
390
  def register(registry_) -> None:
126
391
  @registry_.method("process.drain")
127
392
  def drain_(ctx: CallContext, params: dict) -> dict:
@@ -142,6 +407,39 @@ def register(registry_) -> None:
142
407
  raise InvalidParams("process_id is required")
143
408
  return {"ok": mark_consumed(process_id)}
144
409
 
410
+ def _delivery_params(params: dict) -> dict:
411
+ evt = params.get("event")
412
+ if not isinstance(evt, dict):
413
+ raise InvalidParams("event must be an object")
414
+ return evt
415
+
416
+ @registry_.method("process.claim_delivery")
417
+ def claim_(ctx: CallContext, params: dict) -> dict:
418
+ evt = _delivery_params(params)
419
+ consumer = str(params.get("consumer") or "").strip()
420
+ if not consumer:
421
+ raise InvalidParams("consumer is required")
422
+ with scoped_home(profile_home_param(params)):
423
+ return {"claim_id": claim_delivery(evt, consumer)}
424
+
425
+ @registry_.method("process.complete_delivery")
426
+ def complete_(ctx: CallContext, params: dict) -> dict:
427
+ evt = _delivery_params(params)
428
+ with scoped_home(profile_home_param(params)):
429
+ return {"ok": complete_delivery(evt, str(params.get("claim_id") or ""))}
430
+
431
+ @registry_.method("process.release_delivery")
432
+ def release_(ctx: CallContext, params: dict) -> dict:
433
+ evt = _delivery_params(params)
434
+ with scoped_home(profile_home_param(params)):
435
+ return {"ok": release_delivery(evt, str(params.get("claim_id") or ""))}
436
+
437
+ @registry_.method("process.defer_delivery")
438
+ def defer_(ctx: CallContext, params: dict) -> dict:
439
+ evt = _delivery_params(params)
440
+ with scoped_home(profile_home_param(params)):
441
+ return {"ok": defer_delivery(evt, str(params.get("claim_id") or ""))}
442
+
145
443
  @registry_.method("process.format_notification")
146
444
  def format_(ctx: CallContext, params: dict) -> dict:
147
445
  evt = params.get("event")
@@ -149,6 +447,26 @@ def register(registry_) -> None:
149
447
  raise InvalidParams("event must be an object")
150
448
  return {"text": format_notification(evt)}
151
449
 
450
+ def _session_ids(params: dict) -> list[str]:
451
+ ids = params.get("session_ids")
452
+ if not isinstance(ids, list) or not all(isinstance(i, str) and i for i in ids):
453
+ raise InvalidParams("session_ids must be a list of session ids")
454
+ return ids
455
+
456
+ @registry_.method("process.background_list")
457
+ def background_list_(ctx: CallContext, params: dict) -> dict:
458
+ ids = _session_ids(params)
459
+ with scoped_home(profile_home_param(params)) as home:
460
+ return background_list(home, ids)
461
+
462
+ @registry_.method("process.delegation_result")
463
+ def delegation_result_(ctx: CallContext, params: dict) -> dict:
464
+ sid, did = str(params.get("session_id") or ""), str(params.get("delegation_id") or "")
465
+ if not sid or not did:
466
+ raise InvalidParams("session_id and delegation_id are required")
467
+ with scoped_home(profile_home_param(params)) as home:
468
+ return {"text": delegation_result(home, sid, did)}
469
+
152
470
  @registry_.method("process.list")
153
471
  def list_(ctx: CallContext, params: dict) -> dict:
154
472
  with scoped_home(profile_home_param(params)):
@@ -18,6 +18,7 @@ from pathlib import Path
18
18
  from ..errors import InvalidParams, RpcError
19
19
  from ..home import profile_home_param
20
20
  from ..rpc import CallContext
21
+ from .config import yaml_parser
21
22
 
22
23
  log = logging.getLogger("talaria_sidecar.profiles")
23
24
  _PROFILE_ID_RE = re.compile(r"[a-z0-9][a-z0-9_-]{0,63}")
@@ -34,12 +35,12 @@ _TERMINAL_ENV = {
34
35
 
35
36
 
36
37
  def _yaml_load(path: Path):
37
- try:
38
- import yaml
39
- except Exception: # noqa: BLE001
38
+ """A YAML mapping, or None when the file is absent or malformed; a missing parser raises ``yaml_unavailable``."""
39
+ if not path.exists():
40
40
  return None
41
+ parser = yaml_parser()
41
42
  try:
42
- data = yaml.safe_load(path.read_text(encoding="utf-8")) if path.exists() else None
43
+ data = parser.safe_load(path.read_text(encoding="utf-8"))
43
44
  except Exception: # noqa: BLE001
44
45
  return None
45
46
  return data if isinstance(data, dict) else None
@@ -11,6 +11,9 @@ from __future__ import annotations
11
11
 
12
12
  import dataclasses
13
13
  import logging
14
+ import sys
15
+ import threading
16
+ from pathlib import Path
14
17
  from typing import Any
15
18
 
16
19
  from ..errors import InvalidParams, RpcError
@@ -61,19 +64,176 @@ def auth_status(provider_id: str | None) -> dict:
61
64
  return _plain(status) if isinstance(status, dict) else {"logged_in": bool(status)}
62
65
 
63
66
 
67
+ def _module_dir(module: Any) -> Path | None:
68
+ try:
69
+ return Path(module.__file__).resolve().parent
70
+ except Exception: # noqa: BLE001 - namespace/builtin modules have no file
71
+ return None
72
+
73
+
74
+ # The Agent's module-name prefix for provider plugins it imports from a ``$HERMES_HOME`` (``providers._import_plugin_dir``).
75
+ _USER_PLUGIN_MODULE = "_hermes_user_provider_"
76
+ _LOAD_LOCK = threading.Lock()
77
+ _LOAD_ATTEMPTED: set[Path] = set()
78
+
79
+
80
+ def _user_plugin_profiles() -> dict[int, tuple[Any, Path]]:
81
+ """Each registered profile a ``$HERMES_HOME`` provider plugin module holds, with that module's directory."""
82
+ from providers import list_providers
83
+ from providers.base import ProviderProfile
84
+
85
+ registered = {id(p) for p in list_providers()}
86
+ out: dict[int, tuple[Any, Path]] = {}
87
+ for name, module in list(sys.modules.items()):
88
+ directory = _module_dir(module) if module is not None and name.startswith(_USER_PLUGIN_MODULE) else None
89
+ if directory is None:
90
+ continue
91
+ for value in list(vars(module).values()):
92
+ if isinstance(value, ProviderProfile) and id(value) in registered:
93
+ out.setdefault(id(value), (value, directory))
94
+ return out
95
+
96
+
97
+ def _load_plugin(root: Path) -> None:
98
+ """Load the scoped profile's installed plugin that the process-wide discovery (run under the launch profile) skipped.
99
+
100
+ Uses the Agent's own loader, as ``hermes plugins dev`` does, once per directory. A plugin never displaces a provider
101
+ that is already registered, so one profile's plugin cannot change what another profile's provider id runs.
102
+ """
103
+ import providers
104
+
105
+ with _LOAD_LOCK:
106
+ if root in _LOAD_ATTEMPTED:
107
+ return
108
+ _LOAD_ATTEMPTED.add(root)
109
+ before = {p.name: p for p in providers.list_providers()}
110
+ try:
111
+ providers._import_plugin_dir(root, "user")
112
+ except Exception: # noqa: BLE001 - a broken plugin reads as not loaded
113
+ log.debug("loading provider plugin %s failed", root, exc_info=True)
114
+ return
115
+ after = {p.name: p for p in providers.list_providers()}
116
+ for name, previous in before.items():
117
+ if after.get(name) is not previous:
118
+ providers.register_provider(previous)
119
+
120
+
121
+ def installed_plugin_profiles() -> list[tuple[str, Any]]:
122
+ """``(manifest name, profile | None)`` for each enabled model-provider plugin installed in the scoped home.
123
+
124
+ Bundled providers are built-ins, not plugins. The Agent keeps one provider registry per process, so a profile
125
+ belongs to this home only when the plugin module that registered it lives in one of this home's plugin
126
+ directories; a plugin the launch discovery skipped is loaded here. ``None`` means the plugin could not be loaded:
127
+ it failed to import, its provider id is taken by another plugin, or another profile's plugin has the same directory
128
+ name. ponytail: ownership is read from the plugin module's globals, so a plugin that registers an unbound inline
129
+ profile also reads as not loaded; track registrations in the Agent if one ever does.
130
+ """
131
+ try:
132
+ from hermes_cli.plugins_discovery import _get_disabled_plugins, _get_enabled_plugins, collect_directory_manifests, gate_manifest
133
+ except Exception as exc: # noqa: BLE001
134
+ raise RpcError(f"plugin providers unavailable: {exc}", condition="plugins_unavailable") from exc
135
+ disabled, enabled = _get_disabled_plugins(), _get_enabled_plugins()
136
+ roots = {
137
+ Path(m.path).resolve(): str(m.name)
138
+ for m in collect_directory_manifests()
139
+ if m.kind == "model-provider" and m.source == "user" and m.path and gate_manifest(m, disabled, enabled).enabled
140
+ }
141
+ if not roots:
142
+ return []
143
+
144
+ def owned() -> dict[Path, list]:
145
+ found: dict[Path, list] = {root: [] for root in roots}
146
+ for profile, directory in _user_plugin_profiles().values():
147
+ root = next((r for r in roots if directory == r or r in directory.parents), None)
148
+ if root is not None:
149
+ found[root].append(profile)
150
+ return found
151
+
152
+ found = owned()
153
+ missing = [root for root, profiles in found.items() if not profiles]
154
+ if missing:
155
+ for root in missing:
156
+ _load_plugin(root)
157
+ found = owned()
158
+ return [(name, p) for root, name in roots.items() for p in (found[root] or [None])]
159
+
160
+
161
+ def _other_profile_plugin(provider_id: str) -> bool:
162
+ """Whether ``provider_id`` is a plugin provider that another profile installed (the registry is process-wide)."""
163
+ from providers import get_provider_profile
164
+
165
+ profile = get_provider_profile(provider_id)
166
+ if profile is None or id(profile) not in _user_plugin_profiles():
167
+ return False
168
+ return not any(p is profile for _name, p in installed_plugin_profiles())
169
+
170
+
171
+ def _setup_state(profile: Any) -> str:
172
+ """The Agent's own setup verdict for a plugin provider, without spawning its CLI or reading its credentials."""
173
+ try:
174
+ status = auth_status(profile.name)
175
+ except RpcError:
176
+ return "unavailable"
177
+ if status.get("error"):
178
+ return "unavailable"
179
+ if profile.auth_type == "external_process":
180
+ return "ready" if status.get("configured") else "missing_cli"
181
+ return "ready" if status.get("logged_in") or status.get("configured") else "needs_setup"
182
+
183
+
184
+ def plugin_providers() -> list[dict]:
185
+ """Sanitized rows for this profile's enabled model-provider plugins: identity and setup state, no paths or secrets."""
186
+ rows = []
187
+ for manifest_name, profile in installed_plugin_profiles():
188
+ if profile is None:
189
+ rows.append({"name": manifest_name, "display_name": manifest_name, "auth_type": "", "setup": "not_loaded"})
190
+ continue
191
+ name = str(profile.name).strip().lower()
192
+ rows.append({"name": name, "display_name": str(profile.display_name or name).strip(), "auth_type": str(profile.auth_type), "setup": _setup_state(profile)})
193
+ return rows
194
+
195
+
196
+ def _plugin_catalog(provider_id: str) -> list[str]:
197
+ """Discovery for an installed plugin the Agent's catalog has no fetcher for: the profile's own listing, then its fallback.
198
+
199
+ API-key plugins already get both from ``provider_model_ids`` with their key, so only keyless auth types list here.
200
+ """
201
+ for _name, profile in installed_plugin_profiles():
202
+ if profile is None or str(profile.name).strip().lower() != provider_id.strip().lower():
203
+ continue
204
+ try:
205
+ live = profile.fetch_models(timeout=8.0) if profile.supports_model_listing and profile.auth_type != "api_key" else None
206
+ except Exception: # noqa: BLE001 - a broken plugin answers with its fallback catalog
207
+ log.debug("fetch_models(%r) failed", provider_id, exc_info=True)
208
+ live = None
209
+ return [str(m) for m in (live or profile.fallback_models or ()) if m]
210
+ return []
211
+
212
+
64
213
  def model_ids(provider_id: str, *, force_refresh: bool) -> list[str]:
65
214
  try:
66
215
  from hermes_cli.models import provider_model_ids
67
216
  except Exception as exc: # noqa: BLE001
68
217
  raise RpcError(f"model catalog unavailable: {exc}", condition="providers_unavailable") from exc
218
+ try:
219
+ if _other_profile_plugin(provider_id):
220
+ return []
221
+ except Exception: # noqa: BLE001 - no readable provider registry means no plugin to keep apart
222
+ log.debug("plugin ownership check for %r failed", provider_id, exc_info=True)
69
223
  try:
70
224
  ids = provider_model_ids(provider_id, force_refresh=force_refresh)
71
225
  except TypeError:
72
226
  ids = provider_model_ids(provider_id)
73
227
  except Exception: # noqa: BLE001
74
228
  log.debug("provider_model_ids(%r) failed", provider_id, exc_info=True)
229
+ ids = []
230
+ ids = [str(m) for m in (ids or []) if m]
231
+ if ids:
232
+ return ids
233
+ try:
234
+ return _plugin_catalog(provider_id)
235
+ except RpcError:
75
236
  return []
76
- return [str(m) for m in (ids or []) if m]
77
237
 
78
238
 
79
239
  def resolve_runtime(*, requested: str | None, api_key: str | None, base_url: str | None, target_model: str | None) -> dict:
@@ -7,6 +7,7 @@ import re
7
7
 
8
8
  from .. import SIDECAR_RPC_VERSION
9
9
  from ..errors import InvalidParams, RpcError
10
+ from ..home import edit_launch_env
10
11
  from ..rpc import CallContext
11
12
 
12
13
 
@@ -53,6 +54,7 @@ def register(registry) -> None:
53
54
  os.environ.pop(name, None)
54
55
  for name, value in to_set.items():
55
56
  os.environ[name] = value
57
+ edit_launch_env(to_set, to_unset)
56
58
  # Cached agents bound the credentials they were built with; none of them may outlive a credential change.
57
59
  from .chat import evict_all_agents
58
60
 
@@ -1,28 +1,16 @@
1
1
  """``text.*``: pure helpers from the Agent that the server must match exactly
2
- (redaction patterns, image input routing, portal tags)."""
2
+ (image input routing, portal tags)."""
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
6
  import logging
7
7
 
8
- from ..errors import InvalidParams
9
8
  from ..home import profile_home_param, scoped_home
10
9
  from ..rpc import CallContext
11
10
 
12
11
  log = logging.getLogger("talaria_sidecar.text")
13
12
 
14
13
 
15
- def redact(text: str, *, force: bool = True) -> str:
16
- try:
17
- from agent.redact import redact_sensitive_text
18
- except Exception: # noqa: BLE001
19
- return text
20
- try:
21
- return redact_sensitive_text(text, force=force)
22
- except TypeError:
23
- return redact_sensitive_text(text)
24
-
25
-
26
14
  def image_mode(*, provider: str, model: str, cfg: dict | None, requested_provider: str) -> dict:
27
15
  """``native`` or ``text`` for current-turn image uploads, as the Agent decides it."""
28
16
  try:
@@ -66,13 +54,6 @@ def portal_tags() -> dict:
66
54
 
67
55
 
68
56
  def register(registry) -> None:
69
- @registry.method("text.redact")
70
- def redact_(ctx: CallContext, params: dict) -> dict:
71
- text = params.get("text")
72
- if not isinstance(text, str):
73
- raise InvalidParams("text must be a string")
74
- return {"text": redact(text, force=bool(params.get("force", True)))}
75
-
76
57
  @registry.method("text.image_mode")
77
58
  def image_mode_(ctx: CallContext, params: dict) -> dict:
78
59
  cfg = params.get("cfg") if isinstance(params.get("cfg"), dict) else None
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import dataclasses
6
6
  import logging
7
+ from datetime import date, datetime, timezone
7
8
  from typing import Any
8
9
 
9
10
  from ..errors import InvalidParams, RpcError
@@ -22,6 +23,10 @@ def _plain(value: Any) -> Any:
22
23
  return [_plain(v) for v in value]
23
24
  if isinstance(value, (str, int, float, bool)) or value is None:
24
25
  return value
26
+ if isinstance(value, datetime) and value.tzinfo is not None:
27
+ return value.astimezone(timezone.utc).isoformat().replace("+00:00", "Z")
28
+ if isinstance(value, date):
29
+ return value.isoformat()
25
30
  if hasattr(value, "__dict__"):
26
31
  return {k: _plain(v) for k, v in vars(value).items() if not k.startswith("_")}
27
32
  return str(value)
@@ -45,7 +50,8 @@ def account(provider: str, *, base_url: str | None, api_key: str | None) -> dict
45
50
  row.setdefault("provider", provider)
46
51
  row["windows"] = [w for w in (row.get("windows") or []) if isinstance(w, dict)]
47
52
  row["details"] = list(row.get("details") or [])
48
- row["available"] = bool(row.get("available"))
53
+ # The Agent's `available` is a property, which `_plain` (asdict/vars) never sees; read it off the snapshot.
54
+ row["available"] = bool(getattr(snapshot, "available", row.get("available")))
49
55
  return row
50
56
 
51
57