@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
@@ -17,8 +17,8 @@ import { atomicWriteText } from '../fs/atomic.js';
17
17
  import { str } from '../util.js';
18
18
  import { loadEnvFile } from './env-file.js';
19
19
  import { ACCOUNT_USAGE_PROVIDERS, FALLBACK_MODELS, OAUTH_PROVIDERS, PORTAL_PROVIDERS, PROVIDER_DISPLAY, PROVIDER_ENV_VAR, PROVIDER_ENV_VAR_ALIASES, PROVIDER_MODELS, SELF_HOSTED_PROVIDER_IDS, } from './tables.js';
20
- import { activeProviderFromConfig, canonicaliseProviderId, configuredModelIds, configuredModelOptions, customProviderEntries, customProviderSlug, dict, effectiveDefaultModel, isDict, isOpenAiFamilyProvider, mainModelSupportsServiceTier, modelSection, providerIdentity, resolveProviderAlias, } from '../config/agent-config.js';
21
- /** Python `_model_matches_picker_selection`: same bare id, and the routing hints agree when both name one. */
20
+ import { activeProviderFromConfig, canonicaliseProviderId, configuredModelIds, configuredModelOptions, customProviderEntries, customProviderSlug, dict, effectiveDefaultModel, isDict, isOpenAiFamilyProvider, mainModelSupportsServiceTier, modelSection, parseProviderQualifiedModel, providerIdentity, resolveProviderAlias, } from '../config/agent-config.js';
21
+ /** Python `_model_matches_picker_selection`: same bare id, and the routing hints agree when the selection names one. */
22
22
  function modelMatchesPickerSelection(modelId, selected, providerId) {
23
23
  const candidate = modelId.trim();
24
24
  const sel = selected.trim();
@@ -26,12 +26,28 @@ function modelMatchesPickerSelection(modelId, selected, providerId) {
26
26
  return false;
27
27
  if (candidate === sel)
28
28
  return true;
29
- const bare = (v) => (v.startsWith('@') && v.includes(':') ? v.slice(v.indexOf(':') + 1) : v);
30
- if (bare(sel) !== bare(candidate))
31
- return false;
32
- const selProvider = sel.startsWith('@') && sel.includes(':') ? sel.slice(1, sel.indexOf(':')).toLowerCase() : '';
33
- const candProvider = candidate.startsWith('@') && candidate.includes(':') ? candidate.slice(1, candidate.indexOf(':')).toLowerCase() : providerId.trim().toLowerCase();
34
- return !selProvider || !candProvider || selProvider === candProvider;
29
+ const [candBare, candProvider] = parseProviderQualifiedModel(candidate) ?? [candidate, providerId.trim()];
30
+ const [selBare, selProvider] = parseProviderQualifiedModel(sel) ?? [sel, ''];
31
+ return selBare === candBare && (!selProvider || selProvider.toLowerCase() === candProvider.toLowerCase());
32
+ }
33
+ /** TAL-301: every entry names its routing provider and bare id, split once by `parseProviderQualifiedModel`. */
34
+ function stampModelEntries(models, providerId) {
35
+ return models.map((m) => {
36
+ const [bare, provider] = parseProviderQualifiedModel(m.id) ?? [m.id, providerId];
37
+ return { ...m, bare_id: bare, provider_id: provider };
38
+ });
39
+ }
40
+ /** TAL-301: the catalog entry a stored `(model, provider)` pair selects, so clients render the pairing instead of computing it. */
41
+ export function catalogOptionId(catalog, model, provider) {
42
+ const bare = str(model).trim();
43
+ const pid = str(provider).trim().toLowerCase();
44
+ if (!bare || !pid)
45
+ return null;
46
+ for (const g of catalog.groups)
47
+ for (const e of [...g.models, ...(g.extra_models ?? [])])
48
+ if (e.bare_id === bare && str(e.provider_id).toLowerCase() === pid)
49
+ return e.id;
50
+ return null;
35
51
  }
36
52
  /** Python `_split_picker_overflow_models`: past 25 rows the picker shows 15, keeping the selected model visible. */
37
53
  export function splitPickerOverflow(models, selected, providerId) {
@@ -49,13 +65,41 @@ export function splitPickerOverflow(models, selected, providerId) {
49
65
  }
50
66
  return [visible, extras];
51
67
  }
68
+ /**
69
+ * TAL-388: stamp each auxiliary slot with its display value and the catalog entry whose provider/model pair equals the
70
+ * saved one, so clients tick exactly one option (a bare id listed under two providers matches only its own provider).
71
+ */
72
+ export function stampAuxiliarySelections(aux, catalog) {
73
+ const options = catalog.groups.flatMap((g) => [...g.models, ...(g.extra_models ?? [])].map((m) => {
74
+ const parsed = parseProviderQualifiedModel(m.id);
75
+ return { id: m.id, label: m.label, group: g.provider, provider: canonicaliseProviderId(parsed?.[1] ?? g.provider_id), bare: parsed?.[0] ?? m.id };
76
+ }));
77
+ const describe = (providerRaw, modelRaw) => {
78
+ const provider = str(providerRaw).trim() || 'auto';
79
+ const model = str(modelRaw).trim();
80
+ const key = canonicaliseProviderId(provider);
81
+ const match = provider === 'auto' ? undefined : options.find((o) => o.provider === key && o.bare === model);
82
+ if (match)
83
+ return { value_label: match.label || match.bare, provider_label: match.group, option_id: match.id };
84
+ const group = catalog.groups.find((g) => canonicaliseProviderId(g.provider_id) === key);
85
+ return { value_label: model || null, provider_label: provider === 'auto' ? null : group?.provider ?? displayName(provider), option_id: null };
86
+ };
87
+ // Auto falls back to the main chat model; name the effective one the catalog resolved (a legacy string `model` or a
88
+ // `HERMES_MODEL`-style override never reaches `aux.main`).
89
+ const mainParsed = parseProviderQualifiedModel(catalog.default_model);
90
+ const main = describe(mainParsed?.[1] ?? catalog.active_provider ?? aux.main.provider, mainParsed?.[0] ?? (catalog.default_model || aux.main.model));
91
+ const tasks = aux.tasks.map((t) => {
92
+ if ((str(t.provider).trim() || 'auto') === 'auto' && !str(t.model).trim())
93
+ return { ...t, is_auto: true, value_label: main.value_label, provider_label: main.provider_label, selected_option_id: null, in_catalog: true };
94
+ const pinned = describe(t.provider, t.model);
95
+ return { ...t, is_auto: false, value_label: pinned.value_label, provider_label: pinned.provider_label, selected_option_id: pinned.option_id, in_catalog: pinned.option_id !== null };
96
+ });
97
+ return { ...aux, tasks };
98
+ }
52
99
  /** Python `_BESPOKE_CATALOG_PROVIDERS`: cards whose catalog is resolved by their own rule, never the generic live probe. */
53
100
  const BESPOKE_CATALOG_PROVIDERS = new Set(['openai-codex', 'nous', 'xai-oauth', 'lmstudio', 'opencode-go']);
54
101
  /** Python `_unqualified_model_id`: strip a picker routing hint (`@provider:`). */
55
- function unqualifiedModelId(id) {
56
- const raw = id.trim();
57
- return raw.startsWith('@') && raw.includes(':') ? raw.slice(raw.indexOf(':') + 1) : raw;
58
- }
102
+ const unqualifiedModelId = (id) => parseProviderQualifiedModel(id)?.[0] ?? id.trim();
59
103
  /** Python `_MODEL_PICKER_OVERFLOW_THRESHOLD` / `_MODEL_PICKER_VISIBLE_TARGET`. */
60
104
  const MODEL_PICKER_OVERFLOW_THRESHOLD = 25;
61
105
  const MODEL_PICKER_VISIBLE_TARGET = 15;
@@ -64,7 +108,110 @@ const LIVE_TTL_S = 86_400;
64
108
  const PROVIDERS_TTL_S = 30;
65
109
  const QUOTA_TIMEOUT_MS = 15_000;
66
110
  const OPENROUTER_KEY_URL = 'https://openrouter.ai/api/v1/key';
111
+ const PLUGIN_LIST_TIMEOUT_MS = 15_000;
112
+ /** TAL-288: the card text for a plugin provider the Agent does not report ready. */
113
+ const PLUGIN_SETUP_ERRORS = {
114
+ missing_cli: "This provider's CLI was not found on the server. Install it and sign in, then refresh.",
115
+ needs_setup: "This provider is not set up yet. Finish its setup in Hermes, then refresh.",
116
+ not_loaded: 'This provider is installed but could not be loaded. Check it with hermes plugins doctor, then restart Talaria Web.',
117
+ unavailable: "This provider's setup status is unavailable.",
118
+ };
119
+ const PLUGIN_NO_MODELS = 'This provider listed no models.';
67
120
  const COST_SNAPSHOT_MAX_DAYS = 365;
121
+ const FIVE_HOUR_WINDOW_S = 18_000;
122
+ const WEEK_WINDOW_S = 604_800;
123
+ /** Swift `rounded()`: half away from zero. */
124
+ function roundTo(value, digits) {
125
+ const scale = 10 ** digits;
126
+ return (Math.sign(value) * Math.round(Math.abs(value) * scale)) / scale;
127
+ }
128
+ const finite = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : null);
129
+ /** ISO-8601 UTC for epoch seconds, without a `.000` fraction. */
130
+ export const isoAt = (seconds) => new Date(seconds * 1000).toISOString().replace('.000Z', 'Z');
131
+ /** ISO-8601 UTC (`…Z`) or null. Accepts the pre-TAL-409 sidecar's `str(datetime)` space form; a zone-less stamp is UTC. */
132
+ export function isoUtc(value) {
133
+ if (typeof value !== 'string' || !value.trim())
134
+ return null;
135
+ let text = value.trim().replace(' ', 'T');
136
+ if (text.includes('T') && !/(Z|[+-]\d{2}:?\d{2})$/i.test(text))
137
+ text += 'Z';
138
+ const ms = Date.parse(text);
139
+ return Number.isFinite(ms) ? isoAt(ms / 1000) : null;
140
+ }
141
+ /** The forecast a pace implies: per-hour or per-day budget until reset, and whether the projection empties the window first. */
142
+ export function quotaForecast(pace, remaining) {
143
+ const daily = pace.minutes_to_reset >= 24 * 60;
144
+ const margin = pace.projected_minutes_to_empty === null ? null : pace.projected_minutes_to_empty - pace.minutes_to_reset;
145
+ return {
146
+ outcome: margin === null || margin >= 0 ? 'safe' : 'warning',
147
+ budget_unit: daily ? 'day' : 'hour',
148
+ budget_percent: remaining !== null && pace.minutes_to_reset > 0 ? remaining / (pace.minutes_to_reset / (daily ? 24 * 60 : 60)) : null,
149
+ depletion_margin_minutes: margin,
150
+ };
151
+ }
152
+ /**
153
+ * TAL-409: the one normaliser for account-usage windows (Python `_snapshot_windows_payload` plus the pace math the
154
+ * App used to run). Blank labels drop; `reset_at` becomes ISO `Z`; each window gets its duration, pace and forecast
155
+ * as of `nowSeconds`; the indexes name the windows the App shows for pace, session and weekly selections.
156
+ */
157
+ export function normalizeQuotaWindows(raw, nowSeconds) {
158
+ const nowMs = nowSeconds * 1000;
159
+ const windows = [];
160
+ for (const entry of Array.isArray(raw) ? raw : []) {
161
+ if (!isDict(entry))
162
+ continue;
163
+ const label = str(entry.label).trim();
164
+ if (!label)
165
+ continue;
166
+ const usedRaw = finite(entry.used_percent);
167
+ const remainingRaw = finite(entry.remaining_percent);
168
+ const clamp = (v) => Math.min(Math.max(v, 0), 100);
169
+ const used = usedRaw !== null ? clamp(usedRaw) : remainingRaw !== null ? clamp(100 - remainingRaw) : null;
170
+ const remaining = used === null ? null : 100 - used;
171
+ const resetAt = isoUtc(entry.reset_at);
172
+ const resetMs = resetAt === null ? null : Date.parse(resetAt);
173
+ const minutesToReset = resetMs === null ? null : Math.max(0, roundTo((resetMs - nowMs) / 60_000, 0));
174
+ const lower = label.toLowerCase();
175
+ const providedSeconds = finite(entry.window_seconds);
176
+ const windowSeconds = providedSeconds
177
+ ?? (lower.includes('week') ? WEEK_WINDOW_S
178
+ : lower.includes('5h') ? FIVE_HOUR_WINDOW_S
179
+ // A "Session" window is 5h, or the weekly window a provider collapsed into one session bucket.
180
+ : lower.includes('session') && minutesToReset !== null ? (minutesToReset > 5 * 60 ? WEEK_WINDOW_S : FIVE_HOUR_WINDOW_S)
181
+ : null);
182
+ let pace = null;
183
+ if (resetAt !== null && resetMs !== null && minutesToReset !== null && resetMs > nowMs && used !== null && remaining !== null
184
+ && (windowSeconds === FIVE_HOUR_WINDOW_S || windowSeconds === WEEK_WINDOW_S)) {
185
+ const windowMinutes = windowSeconds / 60;
186
+ const elapsed = Math.max(0, windowMinutes - minutesToReset);
187
+ const expectedRemaining = roundTo(Math.min(Math.max((minutesToReset / windowMinutes) * 100, 0), 100), 1);
188
+ const expectedUsed = roundTo(100 - expectedRemaining, 1);
189
+ const usagePerMinute = elapsed > 0 ? used / elapsed : 0;
190
+ pace = {
191
+ expected_remaining_percent: expectedRemaining,
192
+ pace_delta_percent: roundTo(remaining - expectedRemaining, 1),
193
+ burn_rate: expectedUsed > 0 ? roundTo(used / expectedUsed, 2) : 0,
194
+ minutes_to_reset: minutesToReset,
195
+ projected_minutes_to_empty: usagePerMinute > 0 ? roundTo(remaining / usagePerMinute, 0) : null,
196
+ elapsed_minutes: elapsed,
197
+ valid_until: resetAt,
198
+ };
199
+ }
200
+ windows.push({
201
+ label, used_percent: usedRaw, remaining_percent: remaining, reset_at: resetAt, detail: typeof entry.detail === 'string' ? entry.detail : null,
202
+ window_seconds: windowSeconds, pace, forecast: pace ? quotaForecast(pace, remaining) : null,
203
+ });
204
+ }
205
+ const index = (match) => { const i = windows.findIndex(match); return i < 0 ? null : i; };
206
+ const labelHas = (needle) => (w) => w.label.toLowerCase().includes(needle);
207
+ const knownPace = (w) => w.window_seconds === FIVE_HOUR_WINDOW_S || w.window_seconds === WEEK_WINDOW_S || ['week', 'session', '5h'].some((n) => labelHas(n)(w));
208
+ return {
209
+ windows,
210
+ pace_window_index: index(labelHas('week')) ?? index(knownPace),
211
+ session_window_index: index(labelHas('session')) ?? index(labelHas('5h')),
212
+ weekly_window_index: index(labelHas('week')),
213
+ };
214
+ }
68
215
  export function displayName(pid) {
69
216
  return PROVIDER_DISPLAY[pid] ?? pid.split('-').map((w) => (w ? w[0].toUpperCase() + w.slice(1) : w)).join(' ');
70
217
  }
@@ -84,9 +231,7 @@ export function formatOllamaLabel(mid) {
84
231
  }
85
232
  /** Python `_get_label_for_model`: reuse a known label, else prettify the bare id. */
86
233
  export function labelForModel(modelId, groups) {
87
- let lookup = modelId;
88
- if (lookup.startsWith('@') && lookup.includes(':'))
89
- lookup = lookup.slice(lookup.indexOf(':') + 1);
234
+ const lookup = unqualifiedModelId(modelId);
90
235
  const hasScheme = (s) => s.includes('://');
91
236
  const norm = (s) => (s.includes('/') && !hasScheme(s) ? s.slice(s.indexOf('/') + 1) : s).replaceAll('-', '.').toLowerCase();
92
237
  const target = norm(lookup);
@@ -155,12 +300,20 @@ export function deduplicateModelIds(groups) {
155
300
  m.id = `@${groups[gi].provider_id}:${m.id}`;
156
301
  }
157
302
  }
303
+ /** Quota sources keep the first row per source id, ordered by provider id, account label, then source id (TAL-272). */
304
+ export function uniqueQuotaSources(sources) {
305
+ const cmp = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
306
+ return sources.filter((q, i) => sources.findIndex((o) => o.source_id === q.source_id) === i)
307
+ .sort((a, b) => cmp(a.provider_id, b.provider_id) || cmp(a.account_label, b.account_label) || cmp(a.source_id, b.source_id));
308
+ }
158
309
  export class ProviderCatalog {
159
310
  deps;
160
311
  liveFailed = new Set();
161
312
  liveIds = new Map();
162
313
  liveInflight = new Map();
163
314
  providersCache = new Map();
315
+ /** TAL-301: the last catalog served per profile home, for the sync session payloads' `model_option_id`. */
316
+ lastModels = new Map();
164
317
  constructor(deps) {
165
318
  this.deps = deps;
166
319
  }
@@ -183,6 +336,29 @@ export class ProviderCatalog {
183
336
  this.liveIds.delete(key);
184
337
  }
185
338
  this.providersCache.clear();
339
+ // The picker catalog changed: option ids rebuild on the next read rather than pairing against the old one.
340
+ if (profileHome)
341
+ this.lastModels.delete(profileHome);
342
+ else
343
+ this.lastModels.clear();
344
+ }
345
+ /** TAL-301: the entry id a stored `(model, provider)` pair selects in the last catalog built for this home; a cold home starts building one. */
346
+ modelOptionFor(profileHome, model, provider) {
347
+ const catalog = this.lastModels.get(profileHome);
348
+ if (!catalog) {
349
+ void this.warmModelOptions(profileHome);
350
+ return null;
351
+ }
352
+ return catalogOptionId(catalog, model, provider);
353
+ }
354
+ /** Builds the catalog `modelOptionFor` reads once per home; an unavailable catalog leaves every option id null. */
355
+ async warmModelOptions(profileHome) {
356
+ if (this.lastModels.has(profileHome))
357
+ return;
358
+ try {
359
+ await this.models(profileHome);
360
+ }
361
+ catch { /* fail closed: no option ids */ }
186
362
  }
187
363
  /**
188
364
  * The process-environment value of `name` as a named profile may see it: values `loadStartupEnv` copied from the
@@ -306,6 +482,23 @@ export class ProviderCatalog {
306
482
  this.liveInflight.set(key, run);
307
483
  return run;
308
484
  }
485
+ /**
486
+ * TAL-288: the model-provider plugins installed and enabled in this profile, as the sidecar reads them from the Agent.
487
+ * A failed lookup lists none, so every other provider stays usable; an id that cannot be a provider routing hint is dropped.
488
+ */
489
+ async pluginProviders(profileHome) {
490
+ const sidecar = this.deps.sidecar();
491
+ if (!sidecar)
492
+ return [];
493
+ try {
494
+ const { providers } = await sidecar.call('plugins.providers', { profile_home: profileHome }, { timeoutMs: PLUGIN_LIST_TIMEOUT_MS });
495
+ return providers.filter((p, i, all) => /^[a-z0-9][a-z0-9._-]{0,63}$/.test(p.name) && all.findIndex((q) => q.name === p.name) === i);
496
+ }
497
+ catch (error) {
498
+ this.deps.log(`[catalog] plugin providers failed: ${str(error.message)}`);
499
+ return [];
500
+ }
501
+ }
309
502
  /** Python `get_providers`. */
310
503
  async providers(profileHome) {
311
504
  const config = await this.deps.config.read(profileHome);
@@ -316,10 +509,16 @@ export class ProviderCatalog {
316
509
  return structuredClone(hit.payload);
317
510
  const active = activeProviderFromConfig(config);
318
511
  const known = new Set([...Object.keys(PROVIDER_DISPLAY), ...Object.keys(PROVIDER_MODELS), ...OAUTH_PROVIDERS]);
512
+ // A plugin never stands in for a built-in provider, by id or alias (that would route to the built-in's billing); it
513
+ // does own a `providers.<id>` config entry.
514
+ const plugins = (await this.pluginProviders(profileHome)).filter((p) => !known.has(p.name) && !known.has(providerIdentity(p.name)));
515
+ const pluginIds = new Set(plugins.map((p) => p.name));
319
516
  const providersCfg = dict(config.providers);
320
517
  for (const key of Object.keys(providersCfg)) {
321
518
  const identity = canonicaliseProviderId(key);
322
- known.add(known.has(identity) ? identity : key);
519
+ const id = known.has(identity) ? identity : key;
520
+ if (!pluginIds.has(id))
521
+ known.add(id);
323
522
  }
324
523
  const rows = [];
325
524
  for (const pid of [...known].sort()) {
@@ -364,6 +563,17 @@ export class ProviderCatalog {
364
563
  models_total: modelsTotal,
365
564
  });
366
565
  }
566
+ // TAL-288: a plugin is selectable only once the Agent reports its setup ready; it never borrows a built-in's key.
567
+ for (const plugin of plugins) {
568
+ const ready = plugin.setup === 'ready';
569
+ const live = ready ? await this.liveModelIds(profileHome, plugin.name) : [];
570
+ rows.push({
571
+ id: plugin.name, display_name: plugin.display_name || plugin.name, has_key: ready,
572
+ configurable: false, is_oauth: false, is_plugin_provider: true, is_self_hosted: false, is_custom: false, key_source: ready ? 'plugin' : 'none',
573
+ base_url: null, auth_error: plugin.setup === 'ready' ? (live.length ? null : PLUGIN_NO_MODELS) : PLUGIN_SETUP_ERRORS[plugin.setup], env_var: null,
574
+ models: live.map((id) => ({ id, label: labelForModel(id, []) })), models_total: live.length,
575
+ });
576
+ }
367
577
  for (const cp of customProviderEntries(config)) {
368
578
  const name = str(cp.name).trim();
369
579
  if (!name)
@@ -383,6 +593,8 @@ export class ProviderCatalog {
383
593
  // Python `_provider_sort_key`: active first, then `custom:*`, then keyed providers, then the rest (alphabetical within).
384
594
  const rank = (p) => (str(p.id) === active ? 0 : str(p.id).startsWith('custom:') ? 1 : p.has_key ? 2 : 3);
385
595
  rows.sort((a, b) => rank(a) - rank(b) || (str(a.id) < str(b.id) ? -1 : str(a.id) > str(b.id) ? 1 : 0));
596
+ for (const row of rows)
597
+ row.models = stampModelEntries(row.models, str(row.id));
386
598
  const payload = { providers: rows, active_provider: active };
387
599
  this.providersCache.set(profileHome, { at: this.deps.now(), key: cacheKey, payload });
388
600
  return structuredClone(payload);
@@ -431,12 +643,18 @@ export class ProviderCatalog {
431
643
  if (canonical && this.providerHasKey(canonical, config, envValues, profileHome))
432
644
  detected.add(canonical);
433
645
  }
434
- // Python: OAuth providers the Agent reports as logged in join the picker with their live catalog (#1567, #2545).
435
- const oauthLoggedIn = new Set();
436
- for (const row of (await this.providers(profileHome)).providers)
437
- if (row.is_oauth === true && row.has_key === true)
438
- oauthLoggedIn.add(str(row.id));
439
- for (const pid of oauthLoggedIn)
646
+ // Python: OAuth providers the Agent reports as logged in join the picker with their live catalog (#1567, #2545); so do
647
+ // ready plugin providers (TAL-288), under their own name.
648
+ const signedIn = new Set();
649
+ const pluginNames = new Map();
650
+ for (const row of (await this.providers(profileHome)).providers) {
651
+ if (row.has_key !== true || (row.is_oauth !== true && row.is_plugin_provider !== true))
652
+ continue;
653
+ signedIn.add(str(row.id));
654
+ if (row.is_plugin_provider === true)
655
+ pluginNames.set(str(row.id), str(row.display_name) || str(row.id));
656
+ }
657
+ for (const pid of signedIn)
440
658
  detected.add(pid);
441
659
  const fallbackCfg = Array.isArray(config.fallback_providers) ? config.fallback_providers.filter(isDict) : [];
442
660
  for (const entry of fallbackCfg) {
@@ -469,7 +687,7 @@ export class ProviderCatalog {
469
687
  detected.add(named?.[0] ?? active ?? 'custom');
470
688
  }
471
689
  const groups = [];
472
- for (const pid of [...detected].map((p) => (p.startsWith('custom') ? p : canonicaliseProviderId(p) || p)).filter((v, i, a) => v && a.indexOf(v) === i).sort()) {
690
+ for (const pid of [...detected].map((p) => (p.startsWith('custom') || pluginNames.has(p) ? p : canonicaliseProviderId(p) || p)).filter((v, i, a) => v && a.indexOf(v) === i).sort()) {
473
691
  if (pid.startsWith('custom:')) {
474
692
  const g = namedCustom.get(pid);
475
693
  const models = [...(g?.models ?? [])];
@@ -490,7 +708,7 @@ export class ProviderCatalog {
490
708
  let raw = [];
491
709
  if ('models' in providerCfg && providerCfg.models_discovered !== true)
492
710
  raw = configuredModelOptions(providerCfg.models);
493
- if (!raw.length && (this.providerHasKey(pid, config, envValues, profileHome) || oauthLoggedIn.has(pid))) {
711
+ if (!raw.length && (this.providerHasKey(pid, config, envValues, profileHome) || signedIn.has(pid))) {
494
712
  const live = await this.liveModelIds(profileHome, pid);
495
713
  if (live.length)
496
714
  raw = live.map((id) => ({ id, label: pid === 'nous' ? `${formatOllamaLabel(id.includes('/') ? id.slice(id.indexOf('/') + 1) : id)} (via Nous)` : labelForModel(id, []) }));
@@ -504,7 +722,7 @@ export class ProviderCatalog {
504
722
  if (!raw.some((m) => m.id === id))
505
723
  raw.push({ id, label: labelForModel(id, groups) });
506
724
  if (raw.length)
507
- groups.push({ provider: displayName(pid), provider_id: pid, models: applyProviderPrefix(raw, pid, active) });
725
+ groups.push({ provider: pluginNames.get(pid) ?? displayName(pid), provider_id: pid, models: applyProviderPrefix(raw, pid, active) });
508
726
  }
509
727
  if (defaultModel) {
510
728
  const all = new Set(groups.flatMap((g) => g.models.map((m) => m.id)));
@@ -550,10 +768,20 @@ export class ProviderCatalog {
550
768
  for (const [k, v] of Object.entries(rawAliases))
551
769
  if (k && v)
552
770
  aliases[k.trim()] = str(v).trim();
771
+ const [defaultBare, defaultProvider] = parseProviderQualifiedModel(defaultModel) ?? [defaultModel, active];
772
+ const defaults = { active_provider: active, default_model: defaultModel, default_provider_id: defaultProvider, default_bare_id: defaultBare };
553
773
  if (!kept.length && defaultModel) {
554
- return { active_provider: active, default_model: defaultModel, groups: [{ provider: 'Default', provider_id: active ?? 'default', models: [{ id: defaultModel, label: labelForModel(defaultModel, []) }] }], aliases: {}, configured_model_badges: {} };
774
+ const providerId = active ?? 'default';
775
+ return this.remember(profileHome, { ...defaults, groups: [{ provider: 'Default', provider_id: providerId, models: stampModelEntries([{ id: defaultModel, label: labelForModel(defaultModel, []) }], providerId) }], aliases: {}, configured_model_badges: {} });
555
776
  }
556
- return { active_provider: active, default_model: defaultModel, groups: kept, aliases, configured_model_badges: this.badges(kept, active, defaultModel, fallbackCfg) };
777
+ const stamped = kept.map((g) => ({ ...g, models: stampModelEntries(g.models, g.provider_id), ...(g.extra_models ? { extra_models: stampModelEntries(g.extra_models, g.provider_id) } : {}) }));
778
+ return this.remember(profileHome, { ...defaults, groups: stamped, aliases, configured_model_badges: this.badges(kept, active, defaultModel, fallbackCfg) });
779
+ }
780
+ /** Stamps the default's entry id and keeps the catalog for `modelOptionFor`. */
781
+ remember(profileHome, catalog) {
782
+ catalog.default_option_id = catalogOptionId(catalog, catalog.default_bare_id ?? null, catalog.default_provider_id ?? null);
783
+ this.lastModels.set(profileHome, catalog);
784
+ return structuredClone(catalog);
557
785
  }
558
786
  /** Python `_configured_model_badges_from_static_catalog`: which picker rows are the main/fallback selections. */
559
787
  badges(groups, active, defaultModel, fallback) {
@@ -609,7 +837,7 @@ export class ProviderCatalog {
609
837
  if (provider !== 'nous' && ids.length > MODEL_PICKER_OVERFLOW_THRESHOLD)
610
838
  ids = ids.slice(0, MODEL_PICKER_VISIBLE_TARGET);
611
839
  const annotateFastTier = isOpenAiFamilyProvider(provider);
612
- const models = ids.filter(Boolean).map((id) => ({ id, label: labelForModel(id, []), ...(annotateFastTier ? { supports_fast_tier: mainModelSupportsServiceTier(id, provider) } : {}) }));
840
+ const models = stampModelEntries(ids.filter(Boolean).map((id) => ({ id, label: labelForModel(id, []), ...(annotateFastTier ? { supports_fast_tier: mainModelSupportsServiceTier(id, provider) } : {}) })), provider);
613
841
  return { provider, source, models, count: models.length };
614
842
  }
615
843
  /**
@@ -621,7 +849,7 @@ export class ProviderCatalog {
621
849
  const config = await this.deps.config.read(profileHome);
622
850
  const section = modelSection(config);
623
851
  const pid = canonicaliseProviderId(provider ?? section.provider) || null;
624
- const bare = model.replace(/^@[^:]+:/, '');
852
+ const bare = unqualifiedModelId(model);
625
853
  const positive = (v) => { const n = Math.trunc(Number(v)); return Number.isFinite(n) && n > 0 ? n : null; };
626
854
  const fromModels = (models) => {
627
855
  if (isDict(models)) {
@@ -644,7 +872,7 @@ export class ProviderCatalog {
644
872
  let baseUrl = '';
645
873
  let configContextLength = null;
646
874
  const sectionModel = str(section.default || (typeof config.model === 'string' ? config.model : '')).trim();
647
- if (sectionModel && [model, bare].includes(sectionModel.replace(/^@[^:]+:/, '')))
875
+ if (sectionModel && [model, bare].includes(unqualifiedModelId(sectionModel)))
648
876
  configContextLength = positive(section.context_length);
649
877
  if (!pid || canonicaliseProviderId(section.provider) === pid)
650
878
  baseUrl = str(section.base_url).trim();
@@ -683,10 +911,12 @@ export class ProviderCatalog {
683
911
  /** Python `get_provider_quota`. */
684
912
  accountUsageCache = new Map();
685
913
  async quota(profileHome, providerRaw, opts = {}) {
914
+ const at = opts.at ?? this.deps.now();
915
+ const computed_at = isoAt(at);
686
916
  const config = await this.deps.config.read(profileHome);
687
917
  const provider = (providerRaw ?? activeProviderFromConfig(config) ?? '').trim().toLowerCase();
688
918
  if (!provider)
689
- return { ok: false, provider: null, display_name: null, supported: false, status: 'unavailable', quota: null, message: 'No active provider is configured.' };
919
+ return { computed_at, ok: false, provider: null, display_name: null, supported: false, status: 'unavailable', quota: null, message: 'No active provider is configured.' };
690
920
  const name = displayName(provider);
691
921
  if (ACCOUNT_USAGE_PROVIDERS.has(provider)) {
692
922
  const sidecar = this.deps.sidecar();
@@ -707,22 +937,25 @@ export class ProviderCatalog {
707
937
  }
708
938
  this.accountUsageCache.set(cacheKey, { at: this.deps.now(), limits });
709
939
  }
940
+ // The cache keeps the Agent's raw windows; pace is recomputed as of every response.
941
+ if (limits)
942
+ limits = { ...limits, ...normalizeQuotaWindows(limits.windows, at), fetched_at: isoUtc(limits.fetched_at) };
710
943
  if (limits?.available)
711
- return { ok: true, provider, display_name: name, supported: true, status: limits.stale ? 'stale' : 'available', label: limits.title, quota: null, account_limits: limits, message: limits.stale ? `${name} refresh failed; showing last-known account limits.` : `${name} account limits loaded.` };
944
+ return { computed_at, ok: true, provider, display_name: name, supported: true, status: limits.stale ? 'stale' : 'available', label: limits.title, quota: null, account_limits: limits, message: limits.stale ? `${name} refresh failed; showing last-known account limits.` : `${name} account limits loaded.` };
712
945
  const reason = str(limits?.unavailable_reason).trim();
713
- return { ok: false, provider, display_name: name, supported: true, status: 'unavailable', quota: null, account_limits: limits, message: reason ? `${name} account limits are unavailable. ${reason}` : `${name} account limits are unavailable. Confirm provider authentication and try again.` };
946
+ return { computed_at, ok: false, provider, display_name: name, supported: true, status: 'unavailable', quota: null, account_limits: limits, message: reason ? `${name} account limits are unavailable. ${reason}` : `${name} account limits are unavailable. Confirm provider authentication and try again.` };
714
947
  }
715
948
  if (provider === 'openrouter') {
716
949
  const apiKey = this.apiKeyFor('openrouter', profileHome, config);
717
950
  if (!apiKey)
718
- return { ok: false, provider, display_name: name, supported: true, status: 'no_key', quota: null, message: 'OpenRouter quota status needs an OPENROUTER_API_KEY configured on the server.' };
951
+ return { computed_at, ok: false, provider, display_name: name, supported: true, status: 'no_key', quota: null, message: 'OpenRouter quota status needs an OPENROUTER_API_KEY configured on the server.' };
719
952
  const info = await this.fetchOpenRouterKey(apiKey);
720
953
  if (info.kind === 'ok')
721
- return { ok: true, provider, display_name: name, supported: true, status: 'available', label: 'OpenRouter credits', quota: info.quota, message: 'OpenRouter quota status loaded.' };
954
+ return { computed_at, ok: true, provider, display_name: name, supported: true, status: 'available', label: 'OpenRouter credits', quota: info.quota, message: 'OpenRouter quota status loaded.' };
722
955
  const status = info.kind === 'invalid_key' ? 'invalid_key' : 'unavailable';
723
- return { ok: false, provider, display_name: name, supported: true, status, quota: null, message: status === 'invalid_key' ? 'OpenRouter rejected the configured API key.' : 'OpenRouter quota status is temporarily unavailable.' };
956
+ return { computed_at, ok: false, provider, display_name: name, supported: true, status, quota: null, message: status === 'invalid_key' ? 'OpenRouter rejected the configured API key.' : 'OpenRouter quota status is temporarily unavailable.' };
724
957
  }
725
- return { ok: false, provider, display_name: name, supported: false, status: 'unsupported', quota: null, message: `No verified server-side quota or balance endpoint is available for ${name}.` };
958
+ return { computed_at, ok: false, provider, display_name: name, supported: false, status: 'unsupported', quota: null, message: `No verified server-side quota or balance endpoint is available for ${name}.` };
726
959
  }
727
960
  async fetchOpenRouterKey(apiKey) {
728
961
  const f = this.deps.fetch ?? fetch;
@@ -776,28 +1009,32 @@ export class ProviderCatalog {
776
1009
  quotaProfileScopeId(profile) {
777
1010
  return `qscope_${createHash('sha256').update(`${this.quotaServerScopeId()}\0${profile}`).digest('hex').slice(0, 32)}`;
778
1011
  }
779
- /** Python `get_provider_quotas`: one source per keyed provider, in the stable-identity envelope the iOS widget persists. */
1012
+ /**
1013
+ * Python `get_provider_quotas`: one source per keyed provider, in the stable-identity envelope the iOS widget persists.
1014
+ * Each source id appears once (two custom providers can share a slug), ordered by provider, account label, then source id.
1015
+ */
780
1016
  async quotas(profileHome, profile, opts = {}) {
1017
+ const at = this.deps.now();
781
1018
  const status = await this.providers(profileHome);
782
1019
  const active = status.active_provider;
783
1020
  const scopeId = this.quotaProfileScopeId(profile);
784
1021
  // Python `_quota_source_id(profile, provider, "provider")`: the single-credential descriptor per provider.
785
1022
  const sourceId = (pid) => `qsrc_${createHash('sha256').update(`${scopeId}\0${pid}\0provider`).digest('hex').slice(0, 32)}`;
786
- let descriptors = status.providers.filter((p) => p.has_key || p.is_custom).map((p) => ({ source_id: sourceId(str(p.id)), provider_id: str(p.id), provider_label: str(p.display_name) || str(p.id) }));
1023
+ let descriptors = uniqueQuotaSources(status.providers.filter((p) => p.has_key || p.is_custom).map((p) => ({ source_id: sourceId(str(p.id)), provider_id: str(p.id), provider_label: str(p.display_name) || str(p.id), account_label: str(p.display_name) || str(p.id) })));
787
1024
  const requested = str(opts.sourceId).trim() || null;
788
1025
  if (requested)
789
1026
  descriptors = descriptors.filter((d) => d.source_id === requested);
790
1027
  const sources = await Promise.all(descriptors.map(async (d) => {
791
- const q = await this.quota(profileHome, d.provider_id, { refresh: opts.refresh ?? false });
1028
+ const q = await this.quota(profileHome, d.provider_id, { refresh: opts.refresh ?? false, at });
792
1029
  const limits = dict(q.account_limits);
793
1030
  return {
794
- source_id: d.source_id, provider_id: d.provider_id, provider_label: d.provider_label, account_label: d.provider_label,
1031
+ source_id: d.source_id, provider_id: d.provider_id, provider_label: d.provider_label, account_label: d.account_label,
795
1032
  is_active_provider: d.provider_id === active, supported: q.supported === true, status: str(limits.status) || str(q.status) || 'unavailable',
796
- plan: limits.plan ?? null, windows: limits.windows ?? [], quota: q.quota ?? null, balances: q.balances ?? [], details: limits.details ?? [],
1033
+ plan: limits.plan ?? null, windows: limits.windows ?? [], pace_window_index: limits.pace_window_index ?? null, session_window_index: limits.session_window_index ?? null, weekly_window_index: limits.weekly_window_index ?? null, quota: q.quota ?? null, balances: q.balances ?? [], details: limits.details ?? [],
797
1034
  unavailable_reason: limits.unavailable_reason ?? null, retry_after: limits.retry_after ?? null, fetched_at: limits.fetched_at ?? null, message: q.message ?? null,
798
1035
  };
799
1036
  }));
800
- return { version: 1, scope_id: scopeId, profile_id: profile, active_provider: active, requested_source_id: requested, missing_source: Boolean(requested && !descriptors.length), sources };
1037
+ return { version: 1, computed_at: isoAt(at), scope_id: scopeId, profile_id: profile, active_provider: active, requested_source_id: requested, missing_source: Boolean(requested && !descriptors.length), sources };
801
1038
  }
802
1039
  /** Python `get_provider_cost_history` (OpenRouter only; daily snapshots under `<home>/cost-snapshots`). */
803
1040
  async costHistory(profileHome, providerRaw, days) {