@maudecode/talaria-web 1.21.0 → 1.23.2

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 (390) hide show
  1. package/_release.json +4 -4
  2. package/dist/api/automation-router.d.ts +14 -14
  3. package/dist/api/chat-router.d.ts +225 -68
  4. package/dist/api/groups-router.d.ts +764 -0
  5. package/dist/api/groups-router.js +79 -0
  6. package/dist/api/groups-router.js.map +1 -0
  7. package/dist/api/raw-routes.d.ts +1 -1
  8. package/dist/api/raw-routes.js +15 -3
  9. package/dist/api/raw-routes.js.map +1 -1
  10. package/dist/api/router.d.ts +41 -0
  11. package/dist/api/router.js +2 -0
  12. package/dist/api/router.js.map +1 -1
  13. package/dist/api/sessions-router.d.ts +590 -160
  14. package/dist/api/sessions-router.js +14 -14
  15. package/dist/api/sessions-router.js.map +1 -1
  16. package/dist/api/settings-router.d.ts +948 -1
  17. package/dist/api/settings-router.js +54 -2
  18. package/dist/api/settings-router.js.map +1 -1
  19. package/dist/api/sse-routes.d.ts +1 -1
  20. package/dist/api/sse-routes.js +11 -3
  21. package/dist/api/sse-routes.js.map +1 -1
  22. package/dist/api/tools-raw.js +27 -136
  23. package/dist/api/tools-raw.js.map +1 -1
  24. package/dist/api/tools-router.d.ts +109 -24
  25. package/dist/api/tools-router.js +69 -9
  26. package/dist/api/tools-router.js.map +1 -1
  27. package/dist/app.js +5 -1
  28. package/dist/app.js.map +1 -1
  29. package/dist/auth/gate.js +5 -1
  30. package/dist/auth/gate.js.map +1 -1
  31. package/dist/cli/ctl.d.ts +5 -2
  32. package/dist/cli/ctl.js +90 -42
  33. package/dist/cli/ctl.js.map +1 -1
  34. package/dist/config/agent-config.d.ts +6 -1
  35. package/dist/config/agent-config.js +75 -3
  36. package/dist/config/agent-config.js.map +1 -1
  37. package/dist/feedback.d.ts +21 -0
  38. package/dist/feedback.js +328 -0
  39. package/dist/feedback.js.map +1 -0
  40. package/dist/fs/stat.d.ts +2 -0
  41. package/dist/fs/stat.js +4 -0
  42. package/dist/fs/stat.js.map +1 -1
  43. package/dist/http/context.d.ts +10 -1
  44. package/dist/http/context.js +0 -6
  45. package/dist/http/context.js.map +1 -1
  46. package/dist/http/pinned.js +19 -8
  47. package/dist/http/pinned.js.map +1 -1
  48. package/dist/providers/catalog.d.ts +10 -2
  49. package/dist/providers/catalog.js +113 -15
  50. package/dist/providers/catalog.js.map +1 -1
  51. package/dist/redact.js +9 -3
  52. package/dist/redact.js.map +1 -1
  53. package/dist/release.d.ts +14 -1
  54. package/dist/release.js +40 -2
  55. package/dist/release.js.map +1 -1
  56. package/dist/runtime.js +29 -5
  57. package/dist/runtime.js.map +1 -1
  58. package/dist/server.js +1 -0
  59. package/dist/server.js.map +1 -1
  60. package/dist/sessions/anchor.d.ts +3 -2
  61. package/dist/sessions/anchor.js +11 -10
  62. package/dist/sessions/anchor.js.map +1 -1
  63. package/dist/sessions/cli-sessions.js +11 -6
  64. package/dist/sessions/cli-sessions.js.map +1 -1
  65. package/dist/sessions/compress.d.ts +10 -9
  66. package/dist/sessions/compress.js +31 -20
  67. package/dist/sessions/compress.js.map +1 -1
  68. package/dist/sessions/gateway-sessions.d.ts +11 -0
  69. package/dist/sessions/gateway-sessions.js +79 -24
  70. package/dist/sessions/gateway-sessions.js.map +1 -1
  71. package/dist/sessions/gateway-watcher.js +8 -4
  72. package/dist/sessions/gateway-watcher.js.map +1 -1
  73. package/dist/sessions/groups.d.ts +60 -0
  74. package/dist/sessions/groups.js +339 -0
  75. package/dist/sessions/groups.js.map +1 -0
  76. package/dist/sessions/list.d.ts +8 -3
  77. package/dist/sessions/list.js +17 -6
  78. package/dist/sessions/list.js.map +1 -1
  79. package/dist/sessions/merge.d.ts +14 -5
  80. package/dist/sessions/merge.js +42 -13
  81. package/dist/sessions/merge.js.map +1 -1
  82. package/dist/sessions/pending.d.ts +1 -1
  83. package/dist/sessions/pending.js +15 -6
  84. package/dist/sessions/pending.js.map +1 -1
  85. package/dist/sessions/prefix-index.d.ts +1 -14
  86. package/dist/sessions/prefix-index.js +1 -50
  87. package/dist/sessions/prefix-index.js.map +1 -1
  88. package/dist/sessions/reasoning-display.d.ts +2 -0
  89. package/dist/sessions/reasoning-display.js +31 -0
  90. package/dist/sessions/reasoning-display.js.map +1 -0
  91. package/dist/sessions/relay.d.ts +34 -2
  92. package/dist/sessions/relay.js +84 -7
  93. package/dist/sessions/relay.js.map +1 -1
  94. package/dist/sessions/service.d.ts +7 -2
  95. package/dist/sessions/service.js +106 -64
  96. package/dist/sessions/service.js.map +1 -1
  97. package/dist/sessions/session.d.ts +6 -0
  98. package/dist/sessions/session.js +13 -0
  99. package/dist/sessions/session.js.map +1 -1
  100. package/dist/sessions/shares.d.ts +3 -1
  101. package/dist/sessions/shares.js +33 -11
  102. package/dist/sessions/shares.js.map +1 -1
  103. package/dist/sessions/source-kind.js +2 -0
  104. package/dist/sessions/source-kind.js.map +1 -1
  105. package/dist/sessions/state-db.d.ts +2 -1
  106. package/dist/sessions/state-db.js +12 -5
  107. package/dist/sessions/state-db.js.map +1 -1
  108. package/dist/sessions/storage/json-documents.js +2 -0
  109. package/dist/sessions/storage/json-documents.js.map +1 -1
  110. package/dist/sessions/store.d.ts +1 -0
  111. package/dist/sessions/store.js +10 -3
  112. package/dist/sessions/store.js.map +1 -1
  113. package/dist/sessions/streams.d.ts +7 -2
  114. package/dist/sessions/streams.js +16 -6
  115. package/dist/sessions/streams.js.map +1 -1
  116. package/dist/sessions/titles.d.ts +2 -0
  117. package/dist/sessions/titles.js +11 -0
  118. package/dist/sessions/titles.js.map +1 -1
  119. package/dist/sessions/todo.js +10 -1
  120. package/dist/sessions/todo.js.map +1 -1
  121. package/dist/sessions/turn.js +50 -21
  122. package/dist/sessions/turn.js.map +1 -1
  123. package/dist/settings.js +6 -1
  124. package/dist/settings.js.map +1 -1
  125. package/dist/sidecar/client.d.ts +3 -0
  126. package/dist/sidecar/client.js +2 -1
  127. package/dist/sidecar/client.js.map +1 -1
  128. package/dist/sidecar/discover.js +3 -2
  129. package/dist/sidecar/discover.js.map +1 -1
  130. package/dist/spa.js +1 -1
  131. package/dist/spa.js.map +1 -1
  132. package/dist/test/group-fixture.d.ts +66 -0
  133. package/dist/test/group-fixture.js +89 -0
  134. package/dist/test/group-fixture.js.map +1 -0
  135. package/dist/test/harness.d.ts +3 -0
  136. package/dist/test/harness.js +3 -0
  137. package/dist/test/harness.js.map +1 -1
  138. package/dist/tools/connectors.d.ts +9 -1
  139. package/dist/tools/connectors.js +48 -0
  140. package/dist/tools/connectors.js.map +1 -1
  141. package/dist/tools/gateway-restart.d.ts +1 -1
  142. package/dist/tools/insights.d.ts +3 -0
  143. package/dist/tools/insights.js +6 -7
  144. package/dist/tools/insights.js.map +1 -1
  145. package/dist/tools/skills.d.ts +6 -0
  146. package/dist/tools/skills.js +17 -0
  147. package/dist/tools/skills.js.map +1 -1
  148. package/dist/tools/update-notifications.d.ts +2 -0
  149. package/dist/tools/update-notifications.js +7 -3
  150. package/dist/tools/update-notifications.js.map +1 -1
  151. package/dist/tools/updates.d.ts +26 -9
  152. package/dist/tools/updates.js +144 -47
  153. package/dist/tools/updates.js.map +1 -1
  154. package/dist/workspace/fs.d.ts +2 -0
  155. package/dist/workspace/fs.js +63 -1
  156. package/dist/workspace/fs.js.map +1 -1
  157. package/dist/workspace/media-refs.js +10 -3
  158. package/dist/workspace/media-refs.js.map +1 -1
  159. package/dist/workspace/upload.d.ts +1 -1
  160. package/dist/workspace/upload.js +52 -37
  161. package/dist/workspace/upload.js.map +1 -1
  162. package/dist/workspace/workspaces.d.ts +5 -1
  163. package/dist/workspace/workspaces.js +32 -6
  164. package/dist/workspace/workspaces.js.map +1 -1
  165. package/package.json +4 -3
  166. package/sidecar/agent_importer.json +8 -0
  167. package/sidecar/scripts/agent_importer_contract.py +19 -0
  168. package/sidecar/scripts/suite_runner.py +55 -8
  169. package/sidecar/scripts/test.sh +3 -1
  170. package/sidecar/talaria_sidecar/__init__.py +1 -1
  171. package/sidecar/talaria_sidecar/home.py +12 -5
  172. package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
  173. package/sidecar/talaria_sidecar/methods/agent_imports.py +346 -0
  174. package/sidecar/talaria_sidecar/methods/chat.py +141 -58
  175. package/sidecar/talaria_sidecar/methods/commands.py +6 -4
  176. package/sidecar/talaria_sidecar/methods/config.py +8 -3
  177. package/sidecar/talaria_sidecar/methods/connectors.py +138 -4
  178. package/sidecar/talaria_sidecar/methods/dashboard_plugins.py +21 -0
  179. package/sidecar/talaria_sidecar/methods/delegation.py +126 -0
  180. package/sidecar/talaria_sidecar/methods/gateway.py +5 -0
  181. package/sidecar/talaria_sidecar/methods/gateway_sessions.py +35 -6
  182. package/sidecar/talaria_sidecar/methods/groups.py +120 -0
  183. package/sidecar/talaria_sidecar/methods/memory.py +27 -3
  184. package/sidecar/talaria_sidecar/methods/nous_account.py +130 -0
  185. package/sidecar/talaria_sidecar/methods/oauth.py +25 -4
  186. package/sidecar/talaria_sidecar/methods/outbound_drafts.py +1 -19
  187. package/sidecar/talaria_sidecar/methods/plugin_catalog.py +73 -2
  188. package/sidecar/talaria_sidecar/methods/providers.py +51 -1
  189. package/sidecar/talaria_sidecar/methods/skill_revisions.py +177 -0
  190. package/sidecar/talaria_sidecar/methods/skills.py +10 -0
  191. package/sidecar/talaria_sidecar/methods/tts.py +67 -0
  192. package/sidecar/talaria_sidecar/rpc.py +11 -2
  193. package/static/dist/FILES.txt +154 -143
  194. package/static/dist/THIRD_PARTY_LICENSES.md +1 -1
  195. package/static/dist/assets/AppShell-jwd95ofg.js +8 -0
  196. package/static/dist/assets/{Button-nuaikybd.js → Button-nrcj7shj.js} +1 -1
  197. package/static/dist/assets/ChatPage-fmgk8xpk.js +2 -0
  198. package/static/dist/assets/Composer-oya4ggu7.js +5 -0
  199. package/static/dist/assets/ConnectTargets-rg90uiui.js +1 -0
  200. package/static/dist/assets/Dialog-gk0uatl8.js +1 -0
  201. package/static/dist/assets/ExtensionRoute-ji4xce9l.js +1 -0
  202. package/static/dist/assets/HubRoute-2pgy4aa2.js +2 -0
  203. package/static/dist/assets/Markdown-d6br42ts.js +2 -0
  204. package/static/dist/assets/{MarkdownRenderer-ccgqrza3.js → MarkdownRenderer-k8rmb83g.js} +6 -6
  205. package/static/dist/assets/OAuthSignIn-pbls9ljk.js +1 -0
  206. package/static/dist/assets/QueueCard-liib58ru.js +1 -0
  207. package/static/dist/assets/ResourceSwitch-gtdv2jgs.js +1 -0
  208. package/static/dist/assets/RightPanel-b89e4kx3.js +1 -0
  209. package/static/dist/assets/Select-l6qybenp.js +1 -0
  210. package/static/dist/assets/SessionListPanel-l4vy95j8.js +1 -0
  211. package/static/dist/assets/SettingsLayout-chw7pl2m.js +1 -0
  212. package/static/dist/assets/TerminalPanel-qrd0gkx7.js +16 -0
  213. package/static/dist/assets/Toaster-ihzgaq9z.js +1 -0
  214. package/static/dist/assets/_app-cn1au4zq.js +1 -0
  215. package/static/dist/assets/{_app.ext._extensionId-n797jp72.js → _app.ext._extensionId-m4npkrr0.js} +1 -1
  216. package/static/dist/assets/_app.groups-c8ycbp5n.js +1 -0
  217. package/static/dist/assets/{_app.index-ne43mdw2.js → _app.index-f1fckz3h.js} +1 -1
  218. package/static/dist/assets/{_app.insights-fqonovb4.js → _app.insights-ezivk7ad.js} +1 -1
  219. package/static/dist/assets/{_app.kanban-kysliv19.js → _app.kanban-zbkeov0f.js} +1 -1
  220. package/static/dist/assets/{_app.logs-febut82r.js → _app.logs-dqam5zyz.js} +1 -1
  221. package/static/dist/assets/{_app.memory-ha86oj1c.js → _app.memory-gzxkm3a0.js} +1 -1
  222. package/static/dist/assets/{_app.profiles-cnu3gxxq.js → _app.profiles-j28l31hl.js} +1 -1
  223. package/static/dist/assets/{_app.session._sessionId-ifzjpcgn.js → _app.session._sessionId-55egsufs.js} +1 -1
  224. package/static/dist/assets/{_app.settings-nmakdiaf.js → _app.settings-f5ltpieb.js} +1 -1
  225. package/static/dist/assets/_app.settings._section-dda0vfot.js +3 -0
  226. package/static/dist/assets/{_app.skills-f3wad8yo.js → _app.skills-kuc81hq0.js} +1 -1
  227. package/static/dist/assets/_app.tasks-gyy6vj5l.js +1 -0
  228. package/static/dist/assets/{_app.todos-bml1m3r1.js → _app.todos-d0xoy68t.js} +1 -1
  229. package/static/dist/assets/{_app.workspaces-f3d066lx.js → _app.workspaces-pawp2gxk.js} +1 -1
  230. package/static/dist/assets/_delete-qnxuo87s.js +1 -0
  231. package/static/dist/assets/{abnfDiagram-VCTEODGH-i53jc4yp.js → abnfDiagram-VCTEODGH-mhav7n5a.js} +1 -1
  232. package/static/dist/assets/{architecture-7GRP2DOG-e3bv5m6f.js → architecture-7GRP2DOG-ozbanukt.js} +1 -1
  233. package/static/dist/assets/{architectureDiagram-5GKGNRK7-pakekxu2.js → architectureDiagram-5GKGNRK7-febtor5m.js} +1 -1
  234. package/static/dist/assets/archive-gyk9c34a.js +1 -0
  235. package/static/dist/assets/autoReadClaim-nlyv8tu9.js +1 -0
  236. package/static/dist/assets/{blockDiagram-I7D4REHJ-bc6bo1n6.js → blockDiagram-I7D4REHJ-ocais2jf.js} +1 -1
  237. package/static/dist/assets/{c4Diagram-7LVT6UL2-btvwk2qx.js → c4Diagram-7LVT6UL2-cgpfjx5f.js} +1 -1
  238. package/static/dist/assets/channel-els5ri1f.js +1 -0
  239. package/static/dist/assets/{chunk-4HAMMTFA-dlwab11c.js → chunk-4HAMMTFA-hvqdiiow.js} +1 -1
  240. package/static/dist/assets/{chunk-75Z2AOVW-dp5gh7i8.js → chunk-75Z2AOVW-hxbulvtb.js} +1 -1
  241. package/static/dist/assets/{chunk-DU6HZSFF-bi06zl1c.js → chunk-DU6HZSFF-kkp6a7gp.js} +3 -3
  242. package/static/dist/assets/{chunk-F27PBJKO-kvv1rnlh.js → chunk-F27PBJKO-ljz0gssc.js} +1 -1
  243. package/static/dist/assets/{chunk-GMAD6QVW-ji0rx9by.js → chunk-GMAD6QVW-bo0enyxn.js} +1 -1
  244. package/static/dist/assets/{chunk-GVQU2GXP-nmz0j82p.js → chunk-GVQU2GXP-cnq7l2v6.js} +1 -1
  245. package/static/dist/assets/{chunk-IMKFNOWR-kmi1qx88.js → chunk-IMKFNOWR-gjnkrfu6.js} +1 -1
  246. package/static/dist/assets/{chunk-L3NEJ4N5-xhb7mebj.js → chunk-L3NEJ4N5-gjfp71qi.js} +1 -1
  247. package/static/dist/assets/{chunk-OSK3NFVY-5a9sgscq.js → chunk-OSK3NFVY-ieq5r94b.js} +1 -1
  248. package/static/dist/assets/{chunk-P2QGCYS3-dfezp4vu.js → chunk-P2QGCYS3-hdefjk56.js} +1 -1
  249. package/static/dist/assets/{chunk-POPQ4Y6H-gd2vq3c0.js → chunk-POPQ4Y6H-mcbt8wpr.js} +1 -1
  250. package/static/dist/assets/{chunk-PWAF6VOD-wvw2nksf.js → chunk-PWAF6VOD-mx2drja9.js} +1 -1
  251. package/static/dist/assets/{chunk-SHT3W25Y-f58q97px.js → chunk-SHT3W25Y-j7dd9i66.js} +1 -1
  252. package/static/dist/assets/{chunk-SVP7TREG-abgwpyz9.js → chunk-SVP7TREG-jcnt3lxt.js} +1 -1
  253. package/static/dist/assets/{chunk-TICWLB2K-h4sbs8ds.js → chunk-TICWLB2K-lrwv5hx9.js} +1 -1
  254. package/static/dist/assets/classDiagram-ZZMXUADV-krpt9pdd.js +1 -0
  255. package/static/dist/assets/classDiagram-v2-VYDZK3BY-krpt9pdd.js +1 -0
  256. package/static/dist/assets/completionAudio-fhxf0h9j.js +1 -0
  257. package/static/dist/assets/{copy-m9h56qa8.js → copy-gyzrqmyr.js} +1 -1
  258. package/static/dist/assets/{cynefin-OW5HDTMX-g081vffs.js → cynefin-OW5HDTMX-heq36srt.js} +1 -1
  259. package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-odxvjesk.js → cynefinDiagram-5FMLGOSQ-crrmq338.js} +1 -1
  260. package/static/dist/assets/{dagre-GXQ25YYZ-cpdwzjs4.js → dagre-GXQ25YYZ-m7fpnt6k.js} +1 -1
  261. package/static/dist/assets/{diagram-S7CK7UJ4-clv5mxvu.js → diagram-S7CK7UJ4-ebm1ebv6.js} +1 -1
  262. package/static/dist/assets/{diagram-UQ7AKVKN-m2ia1mbf.js → diagram-UQ7AKVKN-hc0ypsp1.js} +1 -1
  263. package/static/dist/assets/{diagram-VSXAHHWV-ea6zv636.js → diagram-VSXAHHWV-mu8q5n4b.js} +1 -1
  264. package/static/dist/assets/{diagram-VX7I27RA-oqlef685.js → diagram-VX7I27RA-i488wdlb.js} +1 -1
  265. package/static/dist/assets/{diagram-Z3DM3KII-kjubcbwu.js → diagram-Z3DM3KII-gz3nwvq9.js} +1 -1
  266. package/static/dist/assets/{dist-carq1azj.js → dist-ge7hom9n.js} +4 -4
  267. package/static/dist/assets/{dist-crc46di9.js → dist-kpg5ccm9.js} +1 -1
  268. package/static/dist/assets/{ebnfDiagram-PWID7BFC-fg0zri3e.js → ebnfDiagram-PWID7BFC-lwskecx4.js} +1 -1
  269. package/static/dist/assets/{erDiagram-RLTQ6QDP-eb7pmk3x.js → erDiagram-RLTQ6QDP-c0bysigr.js} +1 -1
  270. package/static/dist/assets/{eventmodeling-NTZA5JFV-gvi1fxhn.js → eventmodeling-NTZA5JFV-k19e509j.js} +1 -1
  271. package/static/dist/assets/feedbackVideo.worker-JBVT3sDD.js +480 -0
  272. package/static/dist/assets/flowDiagram-HODETNUW-n4m8e6kq.js +1 -0
  273. package/static/dist/assets/folder-plus-m1ywe0cv.js +1 -0
  274. package/static/dist/assets/{ganttDiagram-EL5Y4UJY-jmisowrc.js → ganttDiagram-EL5Y4UJY-mz1p8yzt.js} +1 -1
  275. package/static/dist/assets/getPseudoElementBounds-ci0x9gki.js +1 -0
  276. package/static/dist/assets/{gitGraph-4MIJSDKK-m7kt3u6e.js → gitGraph-4MIJSDKK-h0uskiog.js} +1 -1
  277. package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-nq7vfzh2.js → gitGraphDiagram-WWUBYQGX-e4d9um1j.js} +1 -1
  278. package/static/dist/assets/group_discussions-fk5hry4l.js +1 -0
  279. package/static/dist/assets/{highlighted-body-KPVGNVTW-gcu017ay.js → highlighted-body-KPVGNVTW-d47n6oqu.js} +1 -1
  280. package/static/dist/assets/{host-ipp4e84c.js → host-f7eudvft.js} +1 -1
  281. package/static/dist/assets/index-lab8g9gs.css +2 -0
  282. package/static/dist/assets/index-n5rg2q2r.js +13 -0
  283. package/static/dist/assets/{info-A6RAGUB7-idazwfqe.js → info-A6RAGUB7-k04of9c6.js} +1 -1
  284. package/static/dist/assets/{infoDiagram-27XIBGKW-bb9jh8gl.js → infoDiagram-27XIBGKW-l98n1tgz.js} +1 -1
  285. package/static/dist/assets/{ishikawaDiagram-5VMMS53U-nqc132rv.js → ishikawaDiagram-5VMMS53U-gkznu2d6.js} +1 -1
  286. package/static/dist/assets/{journeyDiagram-3NMN7TZE-fpvnhy44.js → journeyDiagram-3NMN7TZE-mt23qvx1.js} +1 -1
  287. package/static/dist/assets/{kanban-definition-UXKFOSKX-ndzqm6fu.js → kanban-definition-UXKFOSKX-ab93l7y5.js} +1 -1
  288. package/static/dist/assets/katex-ic33v820.js +257 -0
  289. package/static/dist/assets/katex-qcv7xo0t.css +1 -0
  290. package/static/dist/assets/{lazyRouteComponent-javud235.js → lazyRouteComponent-dt9efiqk.js} +1 -1
  291. package/static/dist/assets/{line-g625e7cj.js → line-dkm9r3as.js} +1 -1
  292. package/static/dist/assets/link-2-ozyjqwhh.js +1 -0
  293. package/static/dist/assets/{link-gfdaho08.js → link-dip3g6zs.js} +1 -1
  294. package/static/dist/assets/{locale-cs-n41hptse.js → locale-cs-brltdkcp.js} +1 -1
  295. package/static/dist/assets/{locale-de-k7ht47ew.js → locale-de-h6e9oq6v.js} +1 -1
  296. package/static/dist/assets/{locale-es-ih078lco.js → locale-es-oklmh80f.js} +1 -1
  297. package/static/dist/assets/{locale-fr-ojabnf41.js → locale-fr-b17ozoi2.js} +1 -1
  298. package/static/dist/assets/{locale-it-kb96jufy.js → locale-it-whe01eef.js} +1 -1
  299. package/static/dist/assets/{locale-ja-jpsp3xs1.js → locale-ja-g1s0rtpc.js} +1 -1
  300. package/static/dist/assets/{locale-pl-dbap4leu.js → locale-pl-d9p3lqcb.js} +1 -1
  301. package/static/dist/assets/{locale-pt-nci7w72x.js → locale-pt-l75on74f.js} +1 -1
  302. package/static/dist/assets/{locale-ru-soghe4cs.js → locale-ru-wl3mbsaw.js} +1 -1
  303. package/static/dist/assets/{locale-tr-ks00mu68.js → locale-tr-cz87itrq.js} +1 -1
  304. package/static/dist/assets/{locale-vi-ij7twfm6.js → locale-vi-elqj1y4k.js} +1 -1
  305. package/static/dist/assets/{locale-zh-Hant-feakwr7a.js → locale-zh-Hant-hf8gqm7d.js} +1 -1
  306. package/static/dist/assets/{locale-zh-ren0vjyv.js → locale-zh-cdckwb0u.js} +1 -1
  307. package/static/dist/assets/login-go5xfzkx.js +1 -0
  308. package/static/dist/assets/mermaid-HWGCJPDP-o7wh7c74.js +1 -0
  309. package/static/dist/assets/{mermaid-parser.core-plzkuzov.js → mermaid-parser.core-c4ywstu7.js} +3 -3
  310. package/static/dist/assets/{mindmap-definition-YA3MSWOX-ojrgs569.js → mindmap-definition-YA3MSWOX-mnvkgz5u.js} +1 -1
  311. package/static/dist/assets/onboarding-hbbpvp6w.js +1 -0
  312. package/static/dist/assets/{packet-AYTQ26CC-gyaf9s8h.js → packet-AYTQ26CC-dfm2ygox.js} +1 -1
  313. package/static/dist/assets/{pegDiagram-XKGWAZYB-bf6xj8mq.js → pegDiagram-XKGWAZYB-kfbyls4v.js} +1 -1
  314. package/static/dist/assets/{pie-WAS4IAKB-f5ymzelo.js → pie-WAS4IAKB-jniw7r2b.js} +1 -1
  315. package/static/dist/assets/{pieDiagram-E7YTZNPT-d0qjl3zi.js → pieDiagram-E7YTZNPT-cir9nlg4.js} +1 -1
  316. package/static/dist/assets/presence-ivo54s19.js +1 -0
  317. package/static/dist/assets/prompt_queue_cancelled-cebcuagu.js +1 -0
  318. package/static/dist/assets/{quadrantDiagram-AXDQQJYC-oqy1mras.js → quadrantDiagram-AXDQQJYC-2nnb2u4m.js} +1 -1
  319. package/static/dist/assets/queryKeys-mbi6o5vh.js +9 -0
  320. package/static/dist/assets/{radar-RG4KPBEZ-b6jcjvda.js → radar-RG4KPBEZ-jfvtqhq2.js} +1 -1
  321. package/static/dist/assets/{railroad-74A4TZTK-e3a6lk5k.js → railroad-74A4TZTK-hnm0ikx7.js} +1 -1
  322. package/static/dist/assets/{railroad-abnf-HS5TGJTU-ff5hocrx.js → railroad-abnf-HS5TGJTU-hcfz5gyc.js} +1 -1
  323. package/static/dist/assets/{railroad-ebnf-LZEXJU2U-exkhyw4n.js → railroad-ebnf-LZEXJU2U-ocbvmcwo.js} +1 -1
  324. package/static/dist/assets/{railroad-peg-WCYAUIDC-jefk9b7c.js → railroad-peg-WCYAUIDC-kkbzgo8d.js} +1 -1
  325. package/static/dist/assets/{railroadDiagram-O6MQD6OU-e4amks1i.js → railroadDiagram-O6MQD6OU-ox5v7853.js} +1 -1
  326. package/static/dist/assets/rename-ccd52yws.js +1 -0
  327. package/static/dist/assets/{requirementDiagram-BXWQKSXE-c77k1e2y.js → requirementDiagram-BXWQKSXE-e1iq54h4.js} +1 -1
  328. package/static/dist/assets/{sankeyDiagram-P5KCCOFB-oa1cc52s.js → sankeyDiagram-P5KCCOFB-mmza9r2u.js} +1 -1
  329. package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-j20gqt22.js → sequenceDiagram-WJ2MYXX4-dioic69h.js} +1 -1
  330. package/static/dist/assets/{share._token-kmfoflw6.js → share._token-i51g4ccy.js} +1 -1
  331. package/static/dist/assets/sliders-horizontal-nj1dipq4.js +1 -0
  332. package/static/dist/assets/{stateDiagram-D77RDMKH-co2ghkdp.js → stateDiagram-D77RDMKH-do5wvqjw.js} +1 -1
  333. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-f0sedx52.js +1 -0
  334. package/static/dist/assets/{swimlanes-42K2YHIH-m30cktqe.js → swimlanes-42K2YHIH-mhjzgtgl.js} +1 -1
  335. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-n0x444kc.js +8 -0
  336. package/static/dist/assets/{timeline-definition-24CTP7MA-liuep6fl.js → timeline-definition-24CTP7MA-i5820ppg.js} +1 -1
  337. package/static/dist/assets/toast-esv4ma70.js +56 -0
  338. package/static/dist/assets/{treeView-Q6P3EWNA-np5d2jv2.js → treeView-Q6P3EWNA-btp8feob.js} +1 -1
  339. package/static/dist/assets/{treemap-WGGIJYW6-cmpltxh1.js → treemap-WGGIJYW6-lv71dt7p.js} +1 -1
  340. package/static/dist/assets/tts-bq7ax6xp.js +1 -0
  341. package/static/dist/assets/{useForm-f1udzsjf.js → useForm-d5wxx0l0.js} +1 -1
  342. package/static/dist/assets/{useParams-ierevvts.js → useParams-gk1u0y6m.js} +1 -1
  343. package/static/dist/assets/{useSearch-i7nr9ypl.js → useSearch-nsrme1pf.js} +1 -1
  344. package/static/dist/assets/useTranscript-m9ve9mml.js +1 -0
  345. package/static/dist/assets/{vennDiagram-4TSXK5OY-b2g42qrg.js → vennDiagram-4TSXK5OY-bffv7e11.js} +1 -1
  346. package/static/dist/assets/{wardley-WFR3VGLG-lb0eqbff.js → wardley-WFR3VGLG-f1m16ivx.js} +1 -1
  347. package/static/dist/assets/{wardleyDiagram-VM6X3IG4-ermwaqha.js → wardleyDiagram-VM6X3IG4-ous4pq48.js} +1 -1
  348. package/static/dist/assets/{xychartDiagram-S5SC5T6Z-dll0jldk.js → xychartDiagram-S5SC5T6Z-jxgbp4as.js} +1 -1
  349. package/static/dist/index.html +1 -1
  350. package/static/dist/sw.js +1 -1
  351. package/static/dist/assets/AppShell-c5989995.js +0 -8
  352. package/static/dist/assets/ChatPage-bfg4khph.js +0 -6
  353. package/static/dist/assets/ConnectTargets-k5wo9yo8.js +0 -1
  354. package/static/dist/assets/Dialog-o8vo2aws.js +0 -1
  355. package/static/dist/assets/ExtensionRoute-l1yjrso4.js +0 -1
  356. package/static/dist/assets/Field-j7o8cfps.js +0 -1
  357. package/static/dist/assets/HubRoute-efnwe4xz.js +0 -2
  358. package/static/dist/assets/Markdown-clqf5m22.js +0 -2
  359. package/static/dist/assets/OAuthSignIn-h7bjaqp6.js +0 -1
  360. package/static/dist/assets/ResourceSwitch-oai2zqaj.js +0 -1
  361. package/static/dist/assets/RightPanel-ho161a45.js +0 -1
  362. package/static/dist/assets/Select-d1g5a8bm.js +0 -1
  363. package/static/dist/assets/SessionListPanel-i7kaokhi.js +0 -1
  364. package/static/dist/assets/SettingsLayout-lnecm87w.js +0 -1
  365. package/static/dist/assets/States-easntmry.js +0 -1
  366. package/static/dist/assets/TerminalPanel-lxhwd4jl.js +0 -16
  367. package/static/dist/assets/Toaster-l4sciy8t.js +0 -1
  368. package/static/dist/assets/_app-mqexqbg8.js +0 -1
  369. package/static/dist/assets/_app.settings._section-djglnjmi.js +0 -3
  370. package/static/dist/assets/_app.tasks-l10py1c0.js +0 -1
  371. package/static/dist/assets/archive-ihqi7z2x.js +0 -1
  372. package/static/dist/assets/channel-jn4f8443.js +0 -1
  373. package/static/dist/assets/classDiagram-ZZMXUADV-dzrw66se.js +0 -1
  374. package/static/dist/assets/classDiagram-v2-VYDZK3BY-dzrw66se.js +0 -1
  375. package/static/dist/assets/completionAudio-ce86msbu.js +0 -1
  376. package/static/dist/assets/flowDiagram-HODETNUW-abekab15.js +0 -1
  377. package/static/dist/assets/folder-plus-ksaons88.js +0 -1
  378. package/static/dist/assets/index-d6vbxlra.js +0 -13
  379. package/static/dist/assets/index-ey6qo0zx.css +0 -2
  380. package/static/dist/assets/katex-cbdd1ohw.css +0 -1
  381. package/static/dist/assets/katex-pbij2oo6.js +0 -257
  382. package/static/dist/assets/login-iv6yq4xf.js +0 -1
  383. package/static/dist/assets/mermaid-HWGCJPDP-cwj5cl6k.js +0 -1
  384. package/static/dist/assets/onboarding-ff4emacu.js +0 -1
  385. package/static/dist/assets/queryKeys-x8fjxhut.js +0 -9
  386. package/static/dist/assets/refresh-cw-lfp4u1oh.js +0 -1
  387. package/static/dist/assets/sliders-horizontal-g5jkzd9k.js +0 -1
  388. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-faw1opgh.js +0 -1
  389. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-hfil433g.js +0 -8
  390. package/static/dist/assets/toast-zmb35u3r.js +0 -56
@@ -0,0 +1,346 @@
1
+ """Profile import adapter. Native planning runs on private snapshots; publication uses anchored descriptors."""
2
+ from __future__ import annotations
3
+
4
+ from contextlib import contextmanager, nullcontext
5
+ import hashlib
6
+ import json
7
+ from itertools import islice
8
+ import os
9
+ from pathlib import Path
10
+ import re
11
+ import secrets
12
+ import stat
13
+ import tempfile
14
+ import time
15
+
16
+ from .. import home as homes
17
+ from ..errors import InvalidParams, RpcError
18
+ from . import config, memory, plugin_catalog
19
+
20
+ _SOURCES = {'claude-code': ('.claude', 'Claude Code'), 'codex': ('.codex', 'Codex CLI'), 'cursor': ('.cursor', 'Cursor')}
21
+ _CREDENTIALS = {'.credentials.json', 'auth.json', 'credentials.json', 'cli-config.json', '.env'}
22
+ _TARGETS = ('config.yaml', 'memories', 'skills', 'import-sync.json')
23
+ _MEMORY_FILES = {'memories/MEMORY.md', 'memories/USER.md'}
24
+ _PREVIEWS = {}
25
+ _MAX_FILES = 2048
26
+ _MAX_BYTES = 16 * 1024 * 1024
27
+ _LEDGER = '.talaria-agent-imports'
28
+
29
+
30
+ def _result(status, *, items=None, stripped=None, receipt=None, sources=None, message=None, can_apply=False, catalog_required=False):
31
+ return dict(status=status, items=items or [], stripped_secrets=stripped or [], receipt=receipt,
32
+ sources=sources or [], message=message, can_apply=can_apply, catalog_required=catalog_required)
33
+
34
+
35
+ @contextmanager
36
+ def _directory(path):
37
+ """Open every ancestor without following links; the held descriptor owns all subsequent IO."""
38
+ path = Path(path)
39
+ if not path.is_absolute():
40
+ raise OSError('An absolute directory is required')
41
+ fd = os.open(path.anchor, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
42
+ try:
43
+ for part in path.parts[1:]:
44
+ child = os.open(part, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=fd)
45
+ os.close(fd)
46
+ fd = child
47
+ yield fd
48
+ finally:
49
+ os.close(fd)
50
+
51
+
52
+ def _read(fd, name):
53
+ child = os.open(name, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK, dir_fd=fd)
54
+ try:
55
+ info = os.fstat(child)
56
+ if not stat.S_ISREG(info.st_mode) or info.st_nlink != 1 or info.st_size > _MAX_BYTES:
57
+ raise OSError('Unsafe or oversized import file')
58
+ with os.fdopen(os.dup(child), 'rb') as stream:
59
+ body = stream.read(_MAX_BYTES + 1)
60
+ after = os.fstat(child)
61
+ if (info.st_size, info.st_mtime_ns, info.st_ctime_ns) != (after.st_size, after.st_mtime_ns, after.st_ctime_ns):
62
+ raise OSError('Import input changed during read')
63
+ return body, (info.st_dev, info.st_ino, info.st_mode, info.st_mtime_ns, info.st_ctime_ns)
64
+ finally:
65
+ os.close(child)
66
+
67
+
68
+ def _snapshot(path, names):
69
+ """Bounded snapshot of only importer inputs. Credential stores are never read."""
70
+ files, identities = {}, {}
71
+ def walk(fd, name, rel):
72
+ try:
73
+ info = os.stat(name, dir_fd=fd, follow_symlinks=False)
74
+ except FileNotFoundError:
75
+ return
76
+ if rel.endswith('.lock') and rel[:-5] in _MEMORY_FILES:
77
+ if not stat.S_ISREG(info.st_mode) or info.st_nlink != 1:
78
+ raise OSError('Unsafe memory lock')
79
+ return
80
+ if stat.S_ISDIR(info.st_mode):
81
+ child = os.open(name, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=fd)
82
+ try:
83
+ identities[rel] = (info.st_dev, info.st_ino, info.st_mode)
84
+ if len(identities) > _MAX_FILES:
85
+ raise OSError('Import tree too large')
86
+ with os.scandir(child) as entries:
87
+ names = [entry.name for entry in islice(entries, _MAX_FILES + 1)]
88
+ if len(names) > _MAX_FILES:
89
+ raise OSError('Import directory too large')
90
+ for entry in sorted(names):
91
+ if entry not in _CREDENTIALS and entry not in {'.git', '__pycache__'}:
92
+ walk(child, entry, rel + '/' + entry)
93
+ finally:
94
+ os.close(child)
95
+ else:
96
+ body, identity = _read(fd, name)
97
+ files[rel], identities[rel] = body, identity
98
+ if len(identities) > _MAX_FILES or sum(map(len, files.values())) > _MAX_BYTES:
99
+ raise OSError('Import tree too large')
100
+ with _directory(path) as fd:
101
+ info = os.fstat(fd)
102
+ identities['.'] = (info.st_dev, info.st_ino, info.st_mode)
103
+ for name in names:
104
+ walk(fd, name, name)
105
+ return files, identities
106
+
107
+
108
+ def _source(agent):
109
+ os_home = Path.home()
110
+ source = os_home / _SOURCES[agent][0]
111
+ names = {'claude-code': ['CLAUDE.md', 'settings.json', 'skills', 'commands'],
112
+ 'codex': ['AGENTS.md', 'config.toml', 'memories', 'skills'],
113
+ 'cursor': ['AGENTS.md', 'mcp.json', 'rules', 'skills']}[agent]
114
+ files, identity = _snapshot(source, names)
115
+ if agent == 'claude-code':
116
+ extra, extra_identity = _snapshot(os_home, ['.claude.json'])
117
+ files.update({'../' + k: v for k, v in extra.items()})
118
+ identity.update({'../' + k: v for k, v in extra_identity.items()})
119
+ return source, files, identity
120
+
121
+
122
+ def _stamp(snapshot):
123
+ files, identities = snapshot
124
+ return hashlib.sha256(json.dumps([identities, {k: hashlib.sha256(v).hexdigest() for k, v in files.items()}], sort_keys=True).encode()).hexdigest()
125
+
126
+
127
+ def _materialize(root, files, identities):
128
+ root.mkdir()
129
+ for name, info in identities.items():
130
+ if name != '.' and stat.S_ISDIR(info[2]):
131
+ (root / name).mkdir(parents=True, exist_ok=True)
132
+ for name, body in files.items():
133
+ path = root / name
134
+ path.parent.mkdir(parents=True, exist_ok=True)
135
+ path.write_bytes(body)
136
+ path.chmod(stat.S_IMODE(identities[name][2]))
137
+
138
+
139
+ def _project(report):
140
+ # No native reason, path, command, content or arbitrary detail crosses the boundary.
141
+ kinds = {'source': 'Source', 'settings': 'Settings', 'claude-md': 'Claude Code instructions',
142
+ 'agents-md': 'Instructions', 'command-allowlist': 'Allowed commands', 'command-denylist': 'Denied commands',
143
+ 'mcp-servers': 'MCP servers', 'skills': 'Skills', 'skill': 'Skill', 'memories': 'Memories',
144
+ 'rules': 'Cursor rules', 'config': 'Configuration', 'slash-commands': 'Slash commands'}
145
+ statuses = {'imported', 'skipped', 'conflict', 'error'}
146
+ items = report.get('items')
147
+ if not isinstance(items, list) or len(items) > _MAX_FILES:
148
+ raise ValueError('Invalid native report')
149
+ projected = []
150
+ for item in items:
151
+ if not isinstance(item, dict) or item.get('kind') not in kinds or item.get('status') not in statuses:
152
+ raise ValueError('Invalid native disposition')
153
+ projected.append(dict(kind=item['kind'], status=item['status'], label=kinds[item['kind']]))
154
+ stripped = report.get('stripped_secrets', [])
155
+ if not isinstance(stripped, list) or len(stripped) > _MAX_FILES:
156
+ raise ValueError('Invalid stripped-secret names')
157
+ return projected, [name for name in stripped if isinstance(name, str) and len(name) <= 200
158
+ and re.fullmatch(r'[A-Za-z0-9_.-]+', name)]
159
+
160
+
161
+ @contextmanager
162
+ def _parent(root_fd, rel, create=False):
163
+ parts = Path(rel).parts
164
+ if not parts or any(p in ('.', '..') for p in parts) or Path(rel).is_absolute():
165
+ raise OSError('Unsafe output path')
166
+ fd = os.dup(root_fd)
167
+ try:
168
+ for part in parts[:-1]:
169
+ if create:
170
+ try:
171
+ os.mkdir(part, 0o700, dir_fd=fd)
172
+ os.fsync(fd)
173
+ except FileExistsError:
174
+ pass
175
+ child = os.open(part, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=fd)
176
+ os.close(fd)
177
+ fd = child
178
+ yield fd, parts[-1]
179
+ finally:
180
+ os.close(fd)
181
+
182
+
183
+ def _write(root_fd, rel, body, *, exclusive=False, mode=0o600, expected=None):
184
+ with _parent(root_fd, rel, create=True) as (fd, name):
185
+ tmp = '.import-' + secrets.token_hex(16)
186
+ child = os.open(tmp, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600, dir_fd=fd)
187
+ try:
188
+ with os.fdopen(child, 'wb') as stream:
189
+ os.fchmod(stream.fileno(), stat.S_IMODE(mode) & 0o777)
190
+ stream.write(body)
191
+ stream.flush()
192
+ os.fsync(stream.fileno())
193
+ guard = memory.file_lock(Path(name), parent_fd=fd) if rel in _MEMORY_FILES else nullcontext()
194
+ with guard:
195
+ if expected is not None and _read(fd, name) != expected:
196
+ raise OSError('Import destination changed during publication')
197
+ if exclusive:
198
+ os.link(tmp, name, src_dir_fd=fd, dst_dir_fd=fd, follow_symlinks=False)
199
+ else:
200
+ # Replacing a link cannot follow it. Never mutate the old inode in place.
201
+ os.rename(tmp, name, src_dir_fd=fd, dst_dir_fd=fd)
202
+ os.fsync(fd)
203
+ finally:
204
+ try:
205
+ os.unlink(tmp, dir_fd=fd)
206
+ except FileNotFoundError:
207
+ pass
208
+
209
+
210
+ def _ledger(root_fd, token, result=None):
211
+ name = _LEDGER + '/' + hashlib.sha256(token.encode()).hexdigest() + '.json'
212
+ if result is not None:
213
+ _write(root_fd, name, json.dumps(result).encode())
214
+ return result
215
+ try:
216
+ with _parent(root_fd, name) as (fd, leaf):
217
+ return json.loads(_read(fd, leaf)[0])
218
+ except FileNotFoundError:
219
+ return None
220
+
221
+
222
+ def _refresh_runtime(home):
223
+ from .commands import _reload_mcp, _reload_skills
224
+ try:
225
+ with homes.scoped_home(home, load_plugins=False):
226
+ _reload_skills()
227
+ _reload_mcp(log_errors=False)
228
+ return True
229
+ except Exception:
230
+ return False
231
+
232
+
233
+ def run(ctx, params):
234
+ action, agent, token = params.get('action'), params.get('agent'), params.get('receipt')
235
+ if action not in ('discover', 'preview', 'apply', 'result') or action == 'preview' and agent not in _SOURCES or action in ('apply', 'result') and (not isinstance(token, str) or not 0 < len(token) <= 128):
236
+ raise InvalidParams('Invalid profile import request')
237
+ home = homes.profile_home_param(params)
238
+ with homes.scoped_home(home, load_plugins=False, read_only=True), plugin_catalog._lock:
239
+ try:
240
+ with _directory(home) as root_fd:
241
+ # Read the durable consumption record before plugin/source checks. A retry is readback only.
242
+ if action in ('apply', 'result'):
243
+ previous = _ledger(root_fd, token)
244
+ if previous is not None:
245
+ if 'expected' in previous:
246
+ files, _ = _snapshot(home, _TARGETS)
247
+ if all(name in files and hashlib.sha256(files[name]).hexdigest() == digest for name, digest in previous['expected'].items()):
248
+ return _ledger(root_fd, token, previous['confirmed'])
249
+ return previous['result']
250
+ return previous
251
+ if action == 'result':
252
+ # Fence a late apply that never reached the consumption record.
253
+ _PREVIEWS.pop(token, None)
254
+ return _result('changed', message='No import was confirmed. Preview again.')
255
+ try:
256
+ config.assert_config_writable(home)
257
+ except RpcError:
258
+ return _result('unavailable', message='Profile configuration is managed or its write policy cannot be verified. Import is unavailable.')
259
+ native = plugin_catalog.reviewed_importer(home)
260
+ if native is None:
261
+ return _result('unavailable', message='Install and enable Agent Importers from the plugin catalog.', catalog_required=True)
262
+ importer, sync, plugin = native
263
+ if action == 'discover':
264
+ sources = []
265
+ for name, (_, label) in _SOURCES.items():
266
+ try:
267
+ _source(name)
268
+ sources.append(dict(agent=name, label=label))
269
+ except OSError:
270
+ pass
271
+ return _result('available', sources=sources)
272
+ now = time.monotonic()
273
+ for old in list(_PREVIEWS):
274
+ if _PREVIEWS[old]['expires'] <= now:
275
+ del _PREVIEWS[old]
276
+ receipt = _PREVIEWS.get(token) if action == 'apply' else None
277
+ if action == 'apply' and (receipt is None or receipt['home'] != str(home)):
278
+ return _result('changed', message='Preview expired or belongs to another profile. Preview again.')
279
+ agent = receipt['agent'] if receipt else agent
280
+ source, source_files, source_identity = _source(agent)
281
+ if home == source or home in source.parents or source in home.parents:
282
+ raise OSError('Source overlaps the active profile')
283
+ target = _snapshot(home, _TARGETS)
284
+ identity = [str(home), str(source), plugin, _stamp((source_files, source_identity)), _stamp(target)]
285
+ if receipt and receipt['identity'] != identity:
286
+ return _result('changed', message='Source, profile or plugin changed. Preview again.')
287
+ if ctx is not None:
288
+ ctx.check_cancelled()
289
+ # Private temporary copies isolate the native path-based API from hostile path replacement.
290
+ with tempfile.TemporaryDirectory(prefix='talaria-import-') as temp:
291
+ stage_source, stage_target = Path(temp).resolve() / 'source', Path(temp).resolve() / 'target'
292
+ _materialize(stage_source, source_files, source_identity)
293
+ _materialize(stage_target, *target)
294
+ report = importer(agent, stage_source, stage_target, execute=False, overwrite=False).run()
295
+ items, stripped = _project(report)
296
+ plan = json.dumps([items, stripped], sort_keys=True)
297
+ if receipt and plan != receipt['plan']:
298
+ return _result('changed', message='Import plan changed. Preview again.')
299
+ if action == 'preview':
300
+ if not any(i['status'] == 'imported' for i in items):
301
+ return _result('preview', items=items, stripped=stripped)
302
+ token = secrets.token_urlsafe(32)
303
+ if len(_PREVIEWS) >= 64:
304
+ del _PREVIEWS[next(iter(_PREVIEWS))]
305
+ _PREVIEWS[token] = dict(home=str(home), agent=agent, identity=identity, plan=plan, expires=now + 300)
306
+ return _result('preview', items=items, stripped=stripped, receipt=token,
307
+ can_apply=any(i['status'] == 'imported' for i in items))
308
+ del _PREVIEWS[token]
309
+ unknown = _result('unknown', message='Import outcome is unknown. Check the profile before starting a new preview.')
310
+ # Fsync the consumed record before any native write. Restart or cancellation cannot replay it.
311
+ _ledger(root_fd, token, unknown)
312
+ try:
313
+ report = importer(agent, stage_source, stage_target, execute=True, overwrite=False).run()
314
+ items, stripped = _project(report)
315
+ sync.update_sync_manifest(agent, stage_source, stage_target, False, report,
316
+ refresh_digest=not any(i['status'] == 'error' for i in items))
317
+ manifest_path = stage_target / 'import-sync.json'
318
+ manifest = json.loads(manifest_path.read_text())
319
+ manifest['agents'][agent]['source'] = str(source)
320
+ manifest_path.write_text(json.dumps(manifest))
321
+ config.assert_config_writable(home)
322
+ output, output_identity = _snapshot(stage_target, _TARGETS)
323
+ # Check original objects again before publication; no write resolves a client/native path.
324
+ fresh_source = _source(agent)
325
+ if (plugin_catalog.reviewed_importer(home)[2] != plugin
326
+ or _stamp(fresh_source[1:]) != identity[3] or _stamp(_snapshot(home, _TARGETS)) != identity[4]):
327
+ return _ledger(root_fd, token, _result('changed', message='Import inputs changed before publication. Preview again.'))
328
+ result = _result('partial' if any(i['status'] == 'error' for i in items) else 'applied', items=items, stripped=stripped)
329
+ result['message'] = 'Files imported. Reload Skills and MCP before using the imported setup.'
330
+ _ledger(root_fd, token, dict(result=unknown, expected={k: hashlib.sha256(v).hexdigest() for k, v in output.items()}, confirmed=result))
331
+ for rel, body in output.items():
332
+ if target[0].get(rel) != body:
333
+ _write(root_fd, rel, body, exclusive=rel not in target[0], mode=output_identity[rel][2], expected=(target[0][rel], target[1][rel]) if rel in target[0] else None)
334
+ result = _result('partial' if any(i['status'] == 'error' for i in items) else 'applied', items=items, stripped=stripped)
335
+ if not _refresh_runtime(home):
336
+ result['message'] = 'Files imported. Reload Skills and MCP before using the imported setup.'
337
+ return _ledger(root_fd, token, result)
338
+ except Exception:
339
+ # Exceptions may include secret values. The durable unknown record is the only public result.
340
+ return unknown
341
+ except (OSError, ValueError, TypeError, KeyError):
342
+ return _result('unknown' if action == 'result' else 'changed' if action == 'apply' else 'unavailable', message='Import paths or state are unsafe, unavailable or changed. Check the profile before continuing.')
343
+
344
+
345
+ def register(registry):
346
+ registry.method('profiles.import')(run)
@@ -18,6 +18,7 @@ import importlib.util
18
18
  import inspect
19
19
  import json
20
20
  import logging
21
+ import math
21
22
  import os
22
23
  import queue
23
24
  import re
@@ -34,7 +35,7 @@ from ..errors import InvalidParams, RpcError
34
35
  from ..home import profile_home_param, scoped_home, session_workspace
35
36
  from ..rpc import CallContext
36
37
  from .checkpoints import checkpoint_kwargs
37
- from .providers import runtime_failure
38
+ from .providers import require_runtime_ownership, runtime_failure
38
39
  from .state_db import release_all, release_home, release_session_db
39
40
  from .toolsets import disabled_toolsets
40
41
 
@@ -69,6 +70,12 @@ class _Run:
69
70
  self.session_ended = False
70
71
  self.home = None # the profile home its agent runs under, set by ``start``
71
72
  self.mcp_discovery_outcome = "ok"
73
+ self.deadline: float | None = None
74
+
75
+ def prompt_cancelled(self, data: dict) -> bool:
76
+ # Vault answers racing a clear retain their existing unlock-then-release cleanup at turn end.
77
+ ended = self.session_ended and not str(data.get("kind") or "").startswith("vault_")
78
+ return self.ctx.cancelled or self.cancel.is_set() or self.finished.is_set() or ended
72
79
 
73
80
 
74
81
  class _ClarifyEntry:
@@ -80,7 +87,77 @@ class _ClarifyEntry:
80
87
  self.deadline: float | None = None
81
88
 
82
89
 
90
+ def _ask_clarify(run: _Run, data: dict, timeout: int, emit=None) -> str | None:
91
+ """Park on a prompt the server relays until it is answered, the turn is cancelled or ends, or ``timeout``
92
+ passes (``<= 0``: no limit). The answer, or None, goes back to the Agent and is never emitted."""
93
+ emit = emit or run.ctx.emit
94
+ entry = _ClarifyEntry(data)
95
+ entry.data["clarify_id"] = entry.clarify_id
96
+ entry.deadline = None if timeout <= 0 else time.monotonic() + float(timeout)
97
+ if run.deadline is not None:
98
+ entry.deadline = min(entry.deadline, run.deadline) if entry.deadline is not None else run.deadline
99
+ with run.lock:
100
+ if run.prompt_cancelled(data) or (entry.deadline is not None and entry.deadline <= time.monotonic()):
101
+ return None
102
+ run.clarify_entries[entry.clarify_id] = entry
103
+ try:
104
+ emit("clarify", dict(entry.data))
105
+ while not entry.event.is_set():
106
+ if run.prompt_cancelled(data):
107
+ break
108
+ if entry.deadline is None:
109
+ entry.event.wait(0.1)
110
+ continue
111
+ remaining = entry.deadline - time.monotonic()
112
+ if remaining <= 0:
113
+ break
114
+ entry.event.wait(min(0.1, remaining))
115
+ finally:
116
+ with run.lock:
117
+ run.clarify_entries.pop(entry.clarify_id, None)
118
+ emit("clarify_resolved", {"clarify_id": entry.clarify_id, "session_id": run.session_id})
119
+ return None if run.prompt_cancelled(data) else entry.result
120
+
121
+
122
+ def _clarify_callback(run: _Run, params: dict, emit=None):
123
+ def clarify_callback(question, choices=None, multi_select=False, questions=None):
124
+ # TAL-351: remove the legacy signature path when v0.21.6 is the oldest supported Agent.
125
+ structured = isinstance(question, list)
126
+ if structured:
127
+ questions, question = question, ""
128
+ choices_list = [str(c) for c in (choices or [])]
129
+ # Predecessor `_clarify_timeout_seconds`: the Agent's own resolver over this profile's config
130
+ # (``clarify.timeout`` else ``agent.clarify_timeout`` else 3600); ``<= 0`` waits until answered or cancelled.
131
+ # The advertised ``timeout_seconds`` is what the clients count down, so it is the same number.
132
+ timeout = _clarify_timeout(params)
133
+ if run.deadline is not None:
134
+ remaining = max(1, math.ceil(run.deadline - time.monotonic()))
135
+ timeout = min(timeout, remaining) if timeout > 0 else remaining
136
+ data = {"question": str(question or ""), "choices_offered": choices_list, "session_id": run.session_id, "kind": "clarify", "requested_at": time.time(), "timeout_seconds": timeout}
137
+ if multi_select:
138
+ data["multi_select"] = True
139
+ # Batch prompts (the Agent's ``_run_batch``) carry no top-level question and expect the ``{"answers": {...}}`` reply.
140
+ if isinstance(questions, list) and questions:
141
+ data["questions"] = questions
142
+ answer = _ask_clarify(run, data, timeout, emit)
143
+ if answer is not None:
144
+ if structured:
145
+ return {"answers": json.loads(answer)["answers"], "outcome": "submitted"}
146
+ return answer
147
+ cancelled = run.prompt_cancelled(data)
148
+ if structured:
149
+ return {"answers": {}, "outcome": "cancelled" if cancelled else "timed_out",
150
+ "notice": "The user cancelled the clarification." if cancelled else "The clarification timed out."}
151
+ # Older tools have no outcome field. A batch understands timed_out; a single needs an empty answer.
152
+ if questions:
153
+ return json.dumps({"answers": {}, "timed_out": not cancelled})
154
+ return ""
155
+
156
+ return clarify_callback
157
+
158
+
83
159
  _RUNS: dict[str, _Run] = {}
160
+ _COMMAND_RUNS: dict[str, _Run] = {}
84
161
  _RUNS_BY_SESSION: dict[str, str] = {}
85
162
  # Which run owns the Agent's gateway approval callback per session; register/unregister happen under one lock so a
86
163
  # stale run can never remove a successor's callback, and a successor can never leak a predecessor's.
@@ -108,6 +185,43 @@ _MCP_REDISCOVERY_WAIT = 2.0
108
185
  _MCP_PENDING_KEY = "mcp_discovery_pending"
109
186
 
110
187
 
188
+ def cancel_command_choices(session_id: str | None = None, home=None) -> None:
189
+ with _RUNS_LOCK:
190
+ for run in _COMMAND_RUNS.values():
191
+ if (session_id is None or run.session_id == session_id) and (home is None or run.home == os.path.realpath(home)):
192
+ run.session_ended = True
193
+ run.cancel.set()
194
+
195
+
196
+ @contextlib.contextmanager
197
+ def command_choices(ctx: CallContext, params: dict):
198
+ session_id = str(params.get("session_id") or "").strip()
199
+ try:
200
+ from hermes_cli.plugin_choices import bind_asker, clarify_asker
201
+ except ImportError:
202
+ yield
203
+ return
204
+ if not session_id:
205
+ yield
206
+ return
207
+ run = _Run(uuid.uuid4().hex, session_id, ctx)
208
+ # The server's commands.exec budget includes every question and the handler's final output.
209
+ run.deadline = time.monotonic() + 120
210
+ run.home = os.path.realpath(profile_home_param(params))
211
+ with _MCP_DISCOVERY_LOCK, _RUNS_LOCK:
212
+ ctx.check_cancelled()
213
+ if _DRAINING.is_set() or run.home in _MCP_CLOSING_HOMES:
214
+ raise RpcError("Command profile is unavailable", condition="profile_unavailable")
215
+ _COMMAND_RUNS[run.stream_id] = run
216
+ try:
217
+ with bind_asker(clarify_asker(_clarify_callback(run, params))):
218
+ yield
219
+ finally:
220
+ run.finished.set()
221
+ with _RUNS_LOCK:
222
+ _COMMAND_RUNS.pop(run.stream_id, None)
223
+
224
+
111
225
  def _snippet(raw: Any, limit: int = _TOOL_RESULT_SNIPPET_MAX) -> str:
112
226
  """Predecessor ``_tool_result_snippet``: the ``output``/``result``/``error`` of a dict (or JSON) result, hard-cut."""
113
227
  if limit <= 0:
@@ -313,8 +427,14 @@ def _usage(agent: Any) -> dict:
313
427
  value = getattr(agent, name, None)
314
428
  return value if isinstance(value, (int, float)) else 0
315
429
 
430
+ try:
431
+ from gateway.session_usage import plan_gauge
432
+ gauge = plan_gauge(agent)
433
+ except Exception: # noqa: BLE001 - older Agent capability omission clears a complete snapshot
434
+ gauge = None
316
435
  cost = getattr(agent, "session_estimated_cost_usd", None)
317
436
  return {
437
+ **({"plan_gauge": gauge} if gauge is not None else {}),
318
438
  "prompt_tokens": int(_num("session_prompt_tokens")),
319
439
  "completion_tokens": int(_num("session_completion_tokens")),
320
440
  "cache_read_tokens": int(_num("session_cache_read_tokens")),
@@ -695,6 +815,7 @@ def _cancel_checkpoint(run: _Run) -> list | None:
695
815
 
696
816
 
697
817
  def _resolve_runtime(provider: str | None, model: str) -> dict:
818
+ require_runtime_ownership(provider)
698
819
  try:
699
820
  from hermes_cli.runtime_provider import resolve_runtime_provider
700
821
  except Exception as exc: # noqa: BLE001
@@ -1363,6 +1484,7 @@ def _cancel_background_reviews(entries, message: str) -> list[threading.Thread]:
1363
1484
 
1364
1485
  def evict_all_agents() -> int:
1365
1486
  """Drop every cached agent (credentials or environment changed underneath them)."""
1487
+ cancel_command_choices()
1366
1488
  with _AGENT_CACHE_LOCK:
1367
1489
  entries = list(_AGENT_CACHE.values())
1368
1490
  _AGENT_CACHE.clear()
@@ -1387,7 +1509,7 @@ def drain_agents(timeout: float = _AGENT_RELEASE_TIMEOUT) -> None:
1387
1509
  run.cancel.set()
1388
1510
  while time.monotonic() < deadline:
1389
1511
  with _AGENT_CACHE_LOCK, _RUNS_LOCK:
1390
- if not _COMMITTING and not any(_RUNS.get(run.stream_id) is run for run in runs):
1512
+ if not _COMMITTING and not _COMMAND_RUNS and not any(_RUNS.get(run.stream_id) is run for run in runs):
1391
1513
  break
1392
1514
  time.sleep(0.05)
1393
1515
  # A turn may have cached its agent after the first eviction.
@@ -1440,6 +1562,7 @@ def release_profile_agents(home):
1440
1562
  raise RpcError("The profile is already being deleted.", condition="profile_unavailable")
1441
1563
  _MCP_CLOSING_HOMES.add(target)
1442
1564
  try:
1565
+ cancel_command_choices(home=home)
1443
1566
  with _AGENT_CACHE_LOCK:
1444
1567
  entries = [_AGENT_CACHE.pop(sid) for sid, entry in list(_AGENT_CACHE.items()) if os.path.realpath(entry[2]) == target]
1445
1568
  started = _release_agents_locked(entries)
@@ -1470,8 +1593,8 @@ def release_profile_agents(home):
1470
1593
 
1471
1594
 
1472
1595
  def _holding_home(target: str) -> bool:
1473
- with _AGENT_CACHE_LOCK:
1474
- return any(os.path.realpath(home) == target for _agent, home in _COMMITTING)
1596
+ with _AGENT_CACHE_LOCK, _RUNS_LOCK:
1597
+ return any(os.path.realpath(home) == target for _agent, home in _COMMITTING) or any(run.home == target for run in _COMMAND_RUNS.values())
1475
1598
 
1476
1599
 
1477
1600
  def _acquire_session_db(home):
@@ -1889,28 +2012,7 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
1889
2012
  emit("notice_clear", {"key": str(key)})
1890
2013
 
1891
2014
  def ask(data: dict, timeout: int) -> str | None:
1892
- """Park on a prompt the server relays until it is answered, the turn is cancelled or ends, or ``timeout``
1893
- passes (``<= 0``: no limit). The answer, or None, goes back to the Agent and is never emitted."""
1894
- entry = _ClarifyEntry(data)
1895
- entry.data["clarify_id"] = entry.clarify_id
1896
- entry.deadline = None if timeout <= 0 else time.monotonic() + float(timeout)
1897
- with run.lock:
1898
- run.clarify_entries[entry.clarify_id] = entry
1899
- emit("clarify", dict(entry.data))
1900
- while not entry.event.is_set():
1901
- if run.cancel.is_set() or run.finished.is_set() or (data.get("kind") == "skill_secret" and (ctx.cancelled or run.session_ended)):
1902
- break
1903
- if entry.deadline is None:
1904
- entry.event.wait(1.0)
1905
- continue
1906
- remaining = entry.deadline - time.monotonic()
1907
- if remaining <= 0:
1908
- break
1909
- entry.event.wait(min(1.0, remaining))
1910
- with run.lock:
1911
- run.clarify_entries.pop(entry.clarify_id, None)
1912
- emit("clarify_resolved", {"clarify_id": entry.clarify_id, "session_id": session_id})
1913
- return entry.result
2015
+ return _ask_clarify(run, data, timeout, emit)
1914
2016
 
1915
2017
  def ask_vault(kind: str, fields: dict) -> str:
1916
2018
  """TAL-887: a password-manager prompt; an empty answer is the Agent's decline, as are expiry and cancel."""
@@ -1966,35 +2068,7 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
1966
2068
  except Exception: # noqa: BLE001 - never expose an exception that could include the submitted value
1967
2069
  return {**skipped, "success": False}
1968
2070
 
1969
- def clarify_callback(question, choices=None, multi_select=False, questions=None):
1970
- # TAL-351: remove the legacy signature path when v0.21.6 is the oldest supported Agent.
1971
- structured = isinstance(question, list)
1972
- if structured:
1973
- questions, question = question, ""
1974
- choices_list = [str(c) for c in (choices or [])]
1975
- # Predecessor `_clarify_timeout_seconds`: the Agent's own resolver over this profile's config
1976
- # (``clarify.timeout`` else ``agent.clarify_timeout`` else 3600); ``<= 0`` waits until answered or cancelled.
1977
- # The advertised ``timeout_seconds`` is what the clients count down, so it is the same number.
1978
- timeout = _clarify_timeout(params)
1979
- data = {"question": str(question or ""), "choices_offered": choices_list, "session_id": session_id, "kind": "clarify", "requested_at": time.time(), "timeout_seconds": timeout}
1980
- if multi_select:
1981
- data["multi_select"] = True
1982
- # Batch prompts (the Agent's ``_run_batch``) carry no top-level question and expect the ``{"answers": {...}}`` reply.
1983
- if isinstance(questions, list) and questions:
1984
- data["questions"] = questions
1985
- answer = ask(data, timeout)
1986
- if answer is not None:
1987
- if structured:
1988
- return {"answers": json.loads(answer)["answers"], "outcome": "submitted"}
1989
- return answer
1990
- cancelled = run.cancel.is_set() or run.finished.is_set()
1991
- if structured:
1992
- return {"answers": {}, "outcome": "cancelled" if cancelled else "timed_out",
1993
- "notice": "The user cancelled the clarification." if cancelled else "The clarification timed out."}
1994
- # Older tools have no outcome field. A batch understands timed_out; a single needs an empty answer.
1995
- if questions:
1996
- return json.dumps({"answers": {}, "timed_out": not cancelled})
1997
- return ""
2071
+ clarify_callback = _clarify_callback(run, params, emit)
1998
2072
 
1999
2073
  # TAL-818: a ``manage_connections`` card (a hosted connect, an MCP install) opened; the server turns the frame into
2000
2074
  # the chat's connect card, which reads and answers the operation through ``connectors.operation`` / ``respond``.
@@ -2465,10 +2539,18 @@ def refine(params: dict) -> str:
2465
2539
  return f"⚗ Reviewing this conversation in the background{tail} — any memory/skill updates will be reported when done."
2466
2540
 
2467
2541
 
2468
- def _run_for(params: dict) -> _Run | None:
2542
+ def _run_for(params: dict, *, include_commands: bool = False) -> _Run | None:
2469
2543
  stream_id = str(params.get("stream_id") or "").strip()
2470
2544
  session_id = str(params.get("session_id") or "").strip()
2471
2545
  with _RUNS_LOCK:
2546
+ clarify_id = str(params.get("clarify_id") or "")
2547
+ home = params.get("profile_home")
2548
+ if include_commands and clarify_id and session_id and home:
2549
+ for run in _COMMAND_RUNS.values():
2550
+ if run.session_id == session_id and run.home == os.path.realpath(home):
2551
+ with run.lock:
2552
+ if clarify_id in run.clarify_entries:
2553
+ return run
2472
2554
  if stream_id:
2473
2555
  return _RUNS.get(stream_id)
2474
2556
  if session_id:
@@ -2619,6 +2701,7 @@ def register(registry) -> None:
2619
2701
  in place."""
2620
2702
  session_id = str(params.get("session_id") or "").strip()
2621
2703
  end_session = params.get("clear_session") is True
2704
+ cancel_command_choices(session_id)
2622
2705
  with _AGENT_CACHE_LOCK:
2623
2706
  with _RUNS_LOCK:
2624
2707
  run = _RUNS.get(_RUNS_BY_SESSION.get(session_id) or "")
@@ -2775,7 +2858,7 @@ def register(registry) -> None:
2775
2858
  def clarify_respond_(ctx: CallContext, params: dict) -> dict:
2776
2859
  response = str(params.get("response") or "")
2777
2860
  clarify_id = str(params.get("clarify_id") or "").strip()
2778
- run = _run_for(params)
2861
+ run = _run_for(params, include_commands=True)
2779
2862
  if run is None:
2780
2863
  return {"ok": False}
2781
2864
  if params.get("session_id") and str(params["session_id"]) != run.session_id:
@@ -2786,7 +2869,7 @@ def register(registry) -> None:
2786
2869
  if target is None or target.event.is_set() or (target.deadline is not None and time.monotonic() >= target.deadline):
2787
2870
  return {"ok": False}
2788
2871
  skill_secret = target.data.get("kind") == "skill_secret"
2789
- if skill_secret and (not clarify_id or ctx.cancelled or run.ctx.cancelled or run.cancel.is_set() or run.finished.is_set() or run.session_ended):
2872
+ if ctx.cancelled or run.prompt_cancelled(target.data) or (skill_secret and not clarify_id):
2790
2873
  return {"ok": False}
2791
2874
  # Secure answers are as typed; an empty reply is their decline. Only the first answer releases a waiter.
2792
2875
  secure = str(target.data.get("kind") or "").startswith("vault_") or skill_secret