@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
@@ -2,12 +2,13 @@
2
2
  Agent's per-model reasoning capability.
3
3
 
4
4
  The TypeScript server owns every config.yaml *policy* (which keys mean what);
5
- the sidecar only parses and serialises YAML with the Agent's PyYAML so the Node
6
- side needs no YAML dependency. Writes are atomic and keep the file's mode.
5
+ the sidecar only parses and serialises YAML with the Agent's own parser so the
6
+ Node side needs no YAML dependency. Writes are atomic and keep the file's mode.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
+ import importlib
11
12
  import logging
12
13
  import os
13
14
  import tempfile
@@ -20,12 +21,15 @@ from ..rpc import CallContext
20
21
  log = logging.getLogger("talaria_sidecar.config")
21
22
 
22
23
 
23
- def _yaml():
24
- try:
25
- import yaml
26
- except Exception as exc: # noqa: BLE001
27
- raise RpcError(f"PyYAML unavailable: {exc}", condition="yaml_unavailable") from exc
28
- return yaml
24
+ def yaml_parser():
25
+ """The Agent's YAML module: ``hermes_yaml`` on Agents that ship it, PyYAML on released Agents."""
26
+ failures = []
27
+ for name in ("hermes_yaml", "yaml"):
28
+ try:
29
+ return importlib.import_module(name)
30
+ except Exception as exc: # noqa: BLE001
31
+ failures.append(f"{name}: {exc}")
32
+ raise RpcError(f"No YAML parser available ({'; '.join(failures)})", condition="yaml_unavailable")
29
33
 
30
34
 
31
35
  def config_path_param(params: dict) -> Path:
@@ -45,7 +49,7 @@ def read_config(path: Path) -> dict:
45
49
  if not path.exists():
46
50
  return {"path": str(path), "exists": False, "config": {}}
47
51
  try:
48
- data = _yaml().safe_load(path.read_text(encoding="utf-8"))
52
+ data = yaml_parser().safe_load(path.read_text(encoding="utf-8"))
49
53
  except RpcError:
50
54
  raise
51
55
  except Exception as exc: # noqa: BLE001
@@ -70,7 +74,7 @@ def write_config(path: Path, config: dict) -> dict:
70
74
  raise InvalidParams("config must be an object")
71
75
  target = _write_target(path)
72
76
  target.parent.mkdir(parents=True, exist_ok=True)
73
- text = _yaml().safe_dump(config, sort_keys=False, allow_unicode=True)
77
+ text = yaml_parser().safe_dump(config, sort_keys=False, allow_unicode=True)
74
78
  mode = None
75
79
  try:
76
80
  mode = target.stat().st_mode & 0o777
@@ -379,25 +379,33 @@ def register(registry) -> None:
379
379
  post["profile"] = str(body["profile"]).strip()
380
380
  if body.get("toast_notifications") is False:
381
381
  post["toast_notifications"] = False
382
- snapshot_home = params.get("execution_home")
383
- # The selected profile's provider/model snapshot is resolved before the job exists (predecessor
384
- # ``_selected_profile_snapshot_updates`` ran before ``create_job``), so a failure leaves nothing behind.
385
- if post.get("profile") and snapshot_home and not (body.get("model") and body.get("provider")) and not body.get("no_agent"):
382
+ if body.get("owner_profile"):
383
+ post["owner_profile"] = str(body["owner_profile"]).strip()
384
+ execution_home = params.get("execution_home")
385
+ if post.get("profile"):
386
+ if not execution_home:
387
+ raise InvalidParams("execution_home is required for a profile job")
388
+ home = Path(execution_home)
389
+ model, provider = (value.strip() or None if isinstance(value, str) else None for value in (body.get("model"), body.get("provider")))
390
+ # The scheduler ignores Talaria's profile field. Pin the execution profile's main model
391
+ # before creating it in that profile's own store. Scheduled workers bind credentials/config
392
+ # to the store home; owner_profile only preserves management from the creating Web profile.
393
+ if post.get("profile") and execution_home and not (model and provider) and not body.get("no_agent"):
386
394
  try:
387
- from cron.jobs import _compute_provider_model_snapshots
388
-
389
- with _SNAPSHOT_LOCK, scoped_home(Path(snapshot_home)):
390
- provider_snapshot, model_snapshot = _compute_provider_model_snapshots(provider=body.get("provider") or None, model=body.get("model") or None, base_url=None, no_agent=False)
391
- if body.get("provider") is None:
392
- post["provider_snapshot"] = provider_snapshot
393
- if body.get("model") is None:
394
- post["model_snapshot"] = model_snapshot
395
+ from cron.jobs import _main_model_pin
396
+
397
+ with _SNAPSHOT_LOCK, scoped_home(Path(execution_home)):
398
+ profile_provider, profile_model = _main_model_pin()
399
+ if not profile_model:
400
+ raise ValueError("Profile has no main model configured")
401
+ provider = provider or profile_provider
402
+ model = model or profile_model
395
403
  except Exception as exc: # noqa: BLE001
396
- raise RpcError(f"Cannot safely resolve cron snapshots for profile {post['profile']!r}", condition="cron_snapshot_failed") from exc
404
+ raise RpcError(f"Cannot safely resolve cron model for profile {post['profile']!r}", condition="cron_snapshot_failed") from exc
397
405
  with _store(home), scoped_home(home):
398
406
  try:
399
407
  job = jobs.create_job(prompt=body.get("prompt") or "", schedule=body["schedule"], name=body.get("name") or None, deliver=body.get("deliver") or "local",
400
- skills=body.get("skills") or [], model=body.get("model") or None, provider=body.get("provider") or None, **kwargs)
408
+ skills=body.get("skills") or [], model=model, provider=provider, **kwargs)
401
409
  except Exception as exc: # noqa: BLE001
402
410
  raise InvalidParams(str(exc)) from exc
403
411
  if post:
@@ -0,0 +1,439 @@
1
+ """``oauth.*``: the Agent's device-code sign-ins for Nous Portal, OpenAI Codex, xAI and MiniMax (TAL-398).
2
+
3
+ ``oauth.start`` asks the provider for a code and answers what the user has to see; a daemon thread then waits for the
4
+ approval and saves the credential with the Agent's own auth-store helpers under the flow's profile home, the steps its
5
+ dashboard and ``hermes auth add`` run. Only the lifecycle is ours: a flow belongs to the home that started it, a cancel
6
+ or an expiry means nothing is saved afterwards, and a new start for the same home and provider supersedes the old one.
7
+ Flows live in this process only.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import logging
13
+ import os
14
+ import secrets
15
+ import threading
16
+ import time
17
+ from dataclasses import dataclass, field
18
+ from datetime import datetime, timezone
19
+ from pathlib import Path
20
+ from typing import Any, Callable
21
+
22
+ from ..errors import InvalidParams, RpcError
23
+ from ..home import profile_home_param, scoped_home
24
+ from ..rpc import CallContext
25
+
26
+ log = logging.getLogger("talaria_sidecar.oauth")
27
+
28
+ _CODEX_ISSUER = "https://auth.openai.com"
29
+ _CODEX_EXPIRES_IN = 15 * 60 # OpenAI's device-code lifetime, as the Agent dashboard uses it
30
+ # An ended flow stays readable this long so the last poll still sees its ending.
31
+ _ENDED_TTL = 10 * 60
32
+ # A pending flow past its code's expiry by this much ends expired even if its provider call has not returned yet.
33
+ _EXPIRY_GRACE = 60
34
+
35
+ _DENIED = "Sign-in was declined."
36
+ _EXPIRED = "The sign-in code expired before it was approved."
37
+
38
+
39
+ class _Stop(Exception):
40
+ """Raised inside a cancelled flow's provider call so the worker unwinds without saving."""
41
+
42
+
43
+ @dataclass
44
+ class Flow:
45
+ flow_id: str
46
+ provider: str
47
+ home: Path
48
+ expires_at: float
49
+ interval: int
50
+ status: str = "pending"
51
+ error: str | None = None
52
+ ended_at: float | None = None
53
+ # The OAuth ``error`` code of the provider's last non-200 answer (``access_denied``, ``expired_token``).
54
+ oauth_error: str = ""
55
+ cancelled: threading.Event = field(default_factory=threading.Event)
56
+ # Serialises the cancel check with the credential save: a cancel cannot land between them.
57
+ lock: threading.Lock = field(default_factory=threading.Lock)
58
+
59
+ def end(self, status: str, error: str | None = None) -> None:
60
+ self.status, self.error, self.ended_at = status, error, time.time()
61
+
62
+ def view(self) -> dict:
63
+ return {"flow_id": self.flow_id, "provider": self.provider, "status": self.status, "error": self.error}
64
+
65
+
66
+ class _Watched:
67
+ """The HTTP client handed to an Agent poll helper: stops a cancelled flow and records the OAuth error code."""
68
+
69
+ def __init__(self, client: Any, flow: Flow):
70
+ self._client, self._flow = client, flow
71
+
72
+ def _guard(self) -> None:
73
+ if self._flow.cancelled.is_set():
74
+ raise _Stop()
75
+
76
+ def post(self, *args: Any, **kwargs: Any) -> Any:
77
+ self._guard()
78
+ response = self._client.post(*args, **kwargs)
79
+ if response.status_code != 200:
80
+ try:
81
+ payload = response.json()
82
+ except Exception: # noqa: BLE001 - a non-JSON error carries no OAuth code
83
+ payload = None
84
+ if isinstance(payload, dict) and isinstance(payload.get("error"), str):
85
+ self._flow.oauth_error = payload["error"]
86
+ return response
87
+
88
+ def send(self, *args: Any, **kwargs: Any) -> Any:
89
+ self._guard()
90
+ return self._client.send(*args, **kwargs)
91
+
92
+ def __getattr__(self, name: str) -> Any:
93
+ return getattr(self._client, name)
94
+
95
+
96
+ def _first_line(exc: BaseException) -> str:
97
+ text = (str(exc).strip().splitlines() or [""])[0].strip() or type(exc).__name__
98
+ return text[:300]
99
+
100
+
101
+ def _remaining(flow: Flow) -> int:
102
+ return max(1, int(flow.expires_at - time.time()))
103
+
104
+
105
+ def _now_z() -> str:
106
+ return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
107
+
108
+
109
+ def _client(timeout: float, **kwargs: Any) -> Any:
110
+ import httpx
111
+
112
+ return httpx.Client(timeout=httpx.Timeout(timeout), headers={"Accept": "application/json"}, **kwargs)
113
+
114
+
115
+ # ── providers: (begin, wait, save) over the Agent's own steps ──────────
116
+ # begin() -> (display, state) runs under the flow's home; wait(flow, state) -> tokens polls; save(flow, state, tokens)
117
+ # runs under the home with the flow's lock held.
118
+
119
+
120
+ def _nous_begin() -> tuple[dict, dict]:
121
+ from hermes_cli import anon_auth
122
+ from hermes_cli.auth import PROVIDER_REGISTRY, _request_device_code
123
+
124
+ if anon_auth.guest_enabled() and anon_auth.has_guest():
125
+ # The free tier's sign-in moves its connectors to the account (``anon_auth.run_sign_in``); a plain device-code
126
+ # sign-in here would orphan them.
127
+ raise RpcError("This profile uses the Nous free tier. Sign in from Hermes with `hermes portal`.", condition="oauth_unsupported")
128
+ pconfig = PROVIDER_REGISTRY["nous"]
129
+ portal = (os.getenv("HERMES_PORTAL_BASE_URL") or os.getenv("NOUS_PORTAL_BASE_URL") or pconfig.portal_base_url).rstrip("/")
130
+ with _client(15.0) as client:
131
+ data = _request_device_code(client, portal, pconfig.client_id, pconfig.scope)
132
+ display = {"user_code": str(data["user_code"]), "verification_url": str(data["verification_uri_complete"]), "expires_in": int(data["expires_in"]), "interval": int(data["interval"])}
133
+ return display, {"portal": portal, "client_id": pconfig.client_id, "scope": pconfig.scope, "device_code": str(data["device_code"])}
134
+
135
+
136
+ def _nous_wait(flow: Flow, state: dict) -> dict:
137
+ from hermes_cli.auth import _poll_for_token
138
+
139
+ with _client(15.0) as client:
140
+ return _poll_for_token(client=_Watched(client, flow), portal_base_url=state["portal"], client_id=state["client_id"], device_code=state["device_code"],
141
+ expires_in=_remaining(flow), poll_interval=flow.interval)
142
+
143
+
144
+ def _nous_save(flow: Flow, state: dict, token_data: dict) -> None:
145
+ from hermes_cli import anon_auth
146
+ from hermes_cli.auth import persist_nous_credentials, refresh_nous_oauth_from_state
147
+
148
+ now = datetime.now(timezone.utc)
149
+ ttl = int(token_data.get("expires_in") or 0)
150
+ auth_state = {
151
+ "portal_base_url": state["portal"], "inference_base_url": token_data.get("inference_base_url"), "client_id": state["client_id"],
152
+ "scope": token_data.get("scope") or state["scope"], "token_type": token_data.get("token_type", "Bearer"),
153
+ "access_token": token_data["access_token"], "refresh_token": token_data.get("refresh_token"), "obtained_at": now.isoformat(),
154
+ "expires_at": datetime.fromtimestamp(now.timestamp() + ttl, tz=timezone.utc).isoformat() if ttl else None, "expires_in": ttl,
155
+ }
156
+ full_state = refresh_nous_oauth_from_state(auth_state, timeout_seconds=15.0, force_refresh=False)
157
+ persist_nous_credentials(full_state)
158
+ anon_auth.settle_after_upgrade(full_state)
159
+
160
+
161
+ def _codex_begin() -> tuple[dict, dict]:
162
+ from hermes_cli.auth import CODEX_OAUTH_CLIENT_ID
163
+ from hermes_cli.auth_codex import _codex_request_device_code
164
+
165
+ data = _codex_request_device_code(_CODEX_ISSUER, CODEX_OAUTH_CLIENT_ID)
166
+ display = {"user_code": str(data["user_code"]), "verification_url": f"{_CODEX_ISSUER}/codex/device", "expires_in": _CODEX_EXPIRES_IN, "interval": int(data["interval"])}
167
+ return display, {"device_auth_id": str(data["device_auth_id"]), "user_code": str(data["user_code"])}
168
+
169
+
170
+ def _codex_wait(flow: Flow, state: dict) -> dict:
171
+ """The Agent's Codex poll (``auth_codex._codex_poll_authorization_code``) cannot be stopped, so its loop runs here:
172
+ 403/404 is still pending, a transport error is retried a few times in a row, and the exchange is the Agent's."""
173
+ import httpx
174
+ from hermes_cli.auth import CODEX_OAUTH_CLIENT_ID
175
+ from hermes_cli.auth_codex import _codex_exchange_authorization_code, _codex_http_client
176
+
177
+ payload = {"device_auth_id": state["device_auth_id"], "user_code": state["user_code"]}
178
+ blips = 0
179
+ code_resp = None
180
+ with _codex_http_client(timeout=httpx.Timeout(15.0)) as client:
181
+ while code_resp is None:
182
+ if flow.cancelled.wait(flow.interval):
183
+ raise _Stop()
184
+ if time.time() >= flow.expires_at:
185
+ raise TimeoutError(_EXPIRED)
186
+ try:
187
+ response = client.post(f"{_CODEX_ISSUER}/api/accounts/deviceauth/token", json=payload, headers={"Content-Type": "application/json"})
188
+ except httpx.TransportError:
189
+ blips += 1
190
+ if blips >= 6:
191
+ raise
192
+ continue
193
+ blips = 0
194
+ if response.status_code == 200:
195
+ code_resp = response.json()
196
+ elif response.status_code not in (403, 404):
197
+ raise RuntimeError(f"OpenAI device sign-in returned HTTP {response.status_code}.")
198
+ if flow.cancelled.is_set():
199
+ raise _Stop()
200
+ tokens = _codex_exchange_authorization_code(_CODEX_ISSUER, CODEX_OAUTH_CLIENT_ID, code_resp)
201
+ return {"access_token": tokens.get("access_token", ""), "refresh_token": tokens.get("refresh_token", "")}
202
+
203
+
204
+ def _codex_save(flow: Flow, state: dict, tokens: dict) -> None:
205
+ from hermes_cli.auth import _save_codex_tokens
206
+
207
+ _save_codex_tokens(tokens, _now_z())
208
+
209
+
210
+ def _xai_begin() -> tuple[dict, dict]:
211
+ from hermes_cli.auth import _xai_oauth_discovery, _xai_oauth_request_device_code
212
+
213
+ discovery = _xai_oauth_discovery(20.0)
214
+ with _client(20.0) as client:
215
+ data = _xai_oauth_request_device_code(client)
216
+ display = {"user_code": str(data["user_code"]), "verification_url": str(data.get("verification_uri_complete") or data["verification_uri"]),
217
+ "expires_in": int(data["expires_in"]), "interval": int(data["interval"])}
218
+ return display, {"discovery": discovery, "device_code": str(data["device_code"])}
219
+
220
+
221
+ def _xai_wait(flow: Flow, state: dict) -> dict:
222
+ from hermes_cli.auth import _xai_oauth_poll_device_token
223
+
224
+ with _client(20.0) as client:
225
+ data = _xai_oauth_poll_device_token(_Watched(client, flow), token_endpoint=state["discovery"]["token_endpoint"], device_code=state["device_code"],
226
+ expires_in=_remaining(flow), poll_interval=flow.interval)
227
+ return {
228
+ "access_token": str(data.get("access_token", "") or "").strip(), "refresh_token": str(data.get("refresh_token", "") or "").strip(),
229
+ "id_token": str(data.get("id_token", "") or "").strip(), "expires_in": data.get("expires_in"),
230
+ "token_type": str(data.get("token_type") or "Bearer").strip() or "Bearer",
231
+ }
232
+
233
+
234
+ def _xai_save(flow: Flow, state: dict, tokens: dict) -> None:
235
+ from hermes_cli.auth import _save_xai_oauth_tokens, mark_provider_active_if_unset, unsuppress_credential_source
236
+
237
+ # As the Agent dashboard does: persist without taking over an active chat provider, become active only when none
238
+ # is, and re-enable the device-code source an earlier removal suppressed.
239
+ _save_xai_oauth_tokens(tokens, discovery=state["discovery"], auth_mode="oauth_device_code", set_active=False, last_refresh=_now_z())
240
+ mark_provider_active_if_unset("xai-oauth")
241
+ unsuppress_credential_source("xai-oauth", "device_code")
242
+
243
+
244
+ def _minimax_begin() -> tuple[dict, dict]:
245
+ from hermes_cli.auth import MINIMAX_OAUTH_CLIENT_ID, MINIMAX_OAUTH_GLOBAL_BASE, _minimax_pkce_pair, _minimax_request_user_code
246
+
247
+ verifier, challenge, csrf_state = _minimax_pkce_pair()
248
+ portal = (os.getenv("MINIMAX_PORTAL_BASE_URL") or MINIMAX_OAUTH_GLOBAL_BASE).rstrip("/")
249
+ with _client(15.0, follow_redirects=True) as client:
250
+ data = _minimax_request_user_code(client, portal_base_url=portal, client_id=MINIMAX_OAUTH_CLIENT_ID, code_challenge=challenge, state=csrf_state)
251
+ interval_ms = int(data["interval"]) if data.get("interval") is not None else None
252
+ # ``expired_in`` is a unix-ms deadline or a TTL in seconds (the Agent's own heuristic).
253
+ expired_in = int(data["expired_in"])
254
+ expires_in = max(0, int(expired_in / 1000.0 - time.time())) if expired_in > 1_000_000_000_000 else expired_in
255
+ display = {"user_code": str(data["user_code"]), "verification_url": str(data["verification_uri"]), "expires_in": expires_in, "interval": max(2, (interval_ms or 2000) // 1000)}
256
+ return display, {"portal": portal, "client_id": MINIMAX_OAUTH_CLIENT_ID, "verifier": verifier, "user_code": str(data["user_code"]), "expired_in": expired_in, "interval_ms": interval_ms}
257
+
258
+
259
+ def _minimax_wait(flow: Flow, state: dict) -> dict:
260
+ from hermes_cli.auth import _minimax_poll_token
261
+
262
+ with _client(15.0, follow_redirects=True) as client:
263
+ return _minimax_poll_token(_Watched(client, flow), portal_base_url=state["portal"], client_id=state["client_id"], user_code=state["user_code"],
264
+ code_verifier=state["verifier"], expired_in=state["expired_in"], interval_ms=state["interval_ms"])
265
+
266
+
267
+ def _minimax_save(flow: Flow, state: dict, token_data: dict) -> None:
268
+ from hermes_cli.auth import MINIMAX_OAUTH_GLOBAL_INFERENCE, MINIMAX_OAUTH_SCOPE, _minimax_resolve_token_expiry_unix, _minimax_save_auth_state
269
+
270
+ now = datetime.now(timezone.utc)
271
+ expires_at = _minimax_resolve_token_expiry_unix(int(token_data["expired_in"]), now=now)
272
+ _minimax_save_auth_state({
273
+ "provider": "minimax-oauth", "region": "global", "portal_base_url": state["portal"], "inference_base_url": MINIMAX_OAUTH_GLOBAL_INFERENCE,
274
+ "client_id": state["client_id"], "scope": MINIMAX_OAUTH_SCOPE, "token_type": token_data.get("token_type", "Bearer"),
275
+ "access_token": token_data["access_token"], "refresh_token": token_data["refresh_token"], "resource_url": token_data.get("resource_url"),
276
+ "obtained_at": now.isoformat(), "expires_at": datetime.fromtimestamp(expires_at, tz=timezone.utc).isoformat(), "expires_in": max(0, int(expires_at - now.timestamp())),
277
+ })
278
+
279
+
280
+ Provider = tuple[Callable[[], tuple[dict, dict]], Callable[[Flow, dict], dict], Callable[[Flow, dict, dict], None]]
281
+ PROVIDERS: dict[str, Provider] = {
282
+ "nous": (_nous_begin, _nous_wait, _nous_save),
283
+ "openai-codex": (_codex_begin, _codex_wait, _codex_save),
284
+ "xai-oauth": (_xai_begin, _xai_wait, _xai_save),
285
+ "minimax-oauth": (_minimax_begin, _minimax_wait, _minimax_save),
286
+ }
287
+
288
+ _FLOWS: dict[str, Flow] = {}
289
+ _FLOWS_LOCK = threading.Lock()
290
+ # The newest start per (home, provider): only it may publish a flow, whatever order the provider requests finish in.
291
+ _GENERATIONS: dict[tuple[str, str], int] = {}
292
+
293
+
294
+ def _ending(flow: Flow, exc: BaseException) -> tuple[str, str]:
295
+ """Classify why a provider call ended without tokens: declined, expired, or another failure with its reason."""
296
+ code = str(getattr(exc, "code", None) or getattr(exc, "oauth_error_code", None) or "")
297
+ if flow.oauth_error == "access_denied" or code in ("access_denied", "authorization_denied"):
298
+ return "denied", _DENIED
299
+ if flow.oauth_error == "expired_token" or code in ("expired_token", "device_code_timeout", "timeout") or isinstance(exc, TimeoutError) or time.time() >= flow.expires_at:
300
+ return "expired", _EXPIRED
301
+ return "error", _first_line(exc)
302
+
303
+
304
+ def _run(flow: Flow, wait: Callable[[Flow, dict], dict], save: Callable[[Flow, dict, dict], None], state: dict) -> None:
305
+ try:
306
+ tokens = wait(flow, state)
307
+ with flow.lock:
308
+ if flow.status != "pending": # cancelled or expired while the provider answered: nothing is saved
309
+ return
310
+ with scoped_home(flow.home):
311
+ save(flow, state, tokens)
312
+ flow.end("approved")
313
+ log.info("oauth: %s sign-in approved (flow=%s)", flow.provider, flow.flow_id[:6])
314
+ except _Stop:
315
+ pass
316
+ except Exception as exc: # noqa: BLE001 - the thread has no caller; the ending is the flow's status
317
+ with flow.lock:
318
+ if flow.status == "pending":
319
+ flow.end(*_ending(flow, exc))
320
+ log.info("oauth: %s sign-in ended %s (flow=%s)", flow.provider, flow.status, flow.flow_id[:6])
321
+
322
+
323
+ def _cancel(flow: Flow) -> None:
324
+ with flow.lock:
325
+ if flow.status == "pending":
326
+ flow.cancelled.set()
327
+ flow.end("cancelled")
328
+
329
+
330
+ def _home_key(home: Path) -> str:
331
+ try:
332
+ return str(home.resolve())
333
+ except OSError:
334
+ return str(home)
335
+
336
+
337
+ def _same_home(left: Path, right: Path) -> bool:
338
+ try:
339
+ return left.resolve() == right.resolve()
340
+ except OSError:
341
+ return False
342
+
343
+
344
+ def _gc(now: float) -> None:
345
+ with _FLOWS_LOCK:
346
+ for flow_id, flow in list(_FLOWS.items()):
347
+ if flow.ended_at is not None and now - flow.ended_at > _ENDED_TTL:
348
+ del _FLOWS[flow_id]
349
+
350
+
351
+ def _pending(home: Path, provider: str) -> list[Flow]:
352
+ with _FLOWS_LOCK:
353
+ return [f for f in _FLOWS.values() if f.provider == provider and f.status == "pending" and _same_home(f.home, home)]
354
+
355
+
356
+ def start(home: Path, provider: str, *, providers: dict[str, Provider] | None = None) -> dict:
357
+ steps = (providers or PROVIDERS).get(provider)
358
+ if steps is None:
359
+ raise InvalidParams(f"{provider} has no device-code sign-in")
360
+ begin, wait, save = steps
361
+ _gc(time.time())
362
+ key = (_home_key(home), provider)
363
+ with _FLOWS_LOCK:
364
+ generation = _GENERATIONS[key] = _GENERATIONS.get(key, 0) + 1
365
+ # A new start replaces the pending flow for this home and provider before the code request, so an approval of the
366
+ # old code that lands while the provider answers can no longer save.
367
+ for old in _pending(home, provider):
368
+ _cancel(old)
369
+ try:
370
+ with scoped_home(home):
371
+ display, state = begin()
372
+ except RpcError:
373
+ raise
374
+ except Exception as exc: # noqa: BLE001
375
+ log.debug("oauth: %s start failed", provider, exc_info=True)
376
+ raise RpcError(_first_line(exc), condition="oauth_failed") from exc
377
+ if not str(display["verification_url"]).lower().startswith(("https://", "http://")):
378
+ # The browser opens this link: anything but a web address is refused before a flow exists.
379
+ raise RpcError("The provider answered an invalid sign-in link.", condition="oauth_failed")
380
+ flow = Flow(secrets.token_urlsafe(16), provider, home, time.time() + int(display["expires_in"]), max(1, int(display["interval"])))
381
+ with _FLOWS_LOCK:
382
+ if _GENERATIONS.get(key) != generation:
383
+ # A newer start for this home and provider began while this one waited on the provider: it owns the sign-in.
384
+ raise RpcError("A newer sign-in for this provider replaced this one.", condition="oauth_superseded")
385
+ superseded = [f for f in _FLOWS.values() if f.provider == provider and f.status == "pending" and _same_home(f.home, home)]
386
+ _FLOWS[flow.flow_id] = flow
387
+ for old in superseded:
388
+ _cancel(old)
389
+ threading.Thread(target=_run, args=(flow, wait, save, state), daemon=True, name=f"oauth-{flow.flow_id[:6]}").start()
390
+ return {"flow_id": flow.flow_id, "provider": provider, "status": "pending", **display}
391
+
392
+
393
+ def _flow(home: Path, flow_id: str) -> Flow:
394
+ with _FLOWS_LOCK:
395
+ flow = _FLOWS.get(flow_id)
396
+ if flow is None or not _same_home(flow.home, home):
397
+ raise RpcError("Unknown or expired sign-in.", condition="oauth_flow_not_found")
398
+ return flow
399
+
400
+
401
+ def poll(home: Path, flow_id: str) -> dict:
402
+ flow = _flow(home, flow_id)
403
+ if flow.status == "pending" and time.time() > flow.expires_at + _EXPIRY_GRACE:
404
+ # The provider call outlived its code: end it now, and a late approval can no longer save.
405
+ with flow.lock:
406
+ if flow.status == "pending":
407
+ flow.cancelled.set()
408
+ flow.end("expired", _EXPIRED)
409
+ return flow.view()
410
+
411
+
412
+ def cancel(home: Path, flow_id: str) -> dict:
413
+ flow = _flow(home, flow_id)
414
+ _cancel(flow)
415
+ return flow.view()
416
+
417
+
418
+ def _flow_id(params: dict) -> str:
419
+ flow_id = str(params.get("flow_id") or "").strip()
420
+ if not flow_id:
421
+ raise InvalidParams("flow_id is required")
422
+ return flow_id
423
+
424
+
425
+ def register(reg) -> None:
426
+ @reg.method("oauth.start")
427
+ def start_(ctx: CallContext, params: dict) -> dict:
428
+ provider = str(params.get("provider") or "").strip().lower()
429
+ if not provider:
430
+ raise InvalidParams("provider is required")
431
+ return start(profile_home_param(params), provider)
432
+
433
+ @reg.method("oauth.poll", requires_agent=False)
434
+ def poll_(ctx: CallContext, params: dict) -> dict:
435
+ return poll(profile_home_param(params), _flow_id(params))
436
+
437
+ @reg.method("oauth.cancel", requires_agent=False)
438
+ def cancel_(ctx: CallContext, params: dict) -> dict:
439
+ return cancel(profile_home_param(params), _flow_id(params))