@maudecode/talaria-web 1.16.1 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (445) hide show
  1. package/_release.json +8 -8
  2. package/dist/api/auth-raw.d.ts +1 -1
  3. package/dist/api/auth-raw.js +4 -4
  4. package/dist/api/auth-raw.js.map +1 -1
  5. package/dist/api/automation-raw.d.ts +6 -0
  6. package/dist/api/automation-raw.js +59 -9
  7. package/dist/api/automation-raw.js.map +1 -1
  8. package/dist/api/automation-router.d.ts +163 -10
  9. package/dist/api/automation-router.js +25 -8
  10. package/dist/api/automation-router.js.map +1 -1
  11. package/dist/api/chat-router.d.ts +700 -15
  12. package/dist/api/chat-router.js +74 -55
  13. package/dist/api/chat-router.js.map +1 -1
  14. package/dist/api/git-router.d.ts +47 -2
  15. package/dist/api/git-router.js +31 -22
  16. package/dist/api/git-router.js.map +1 -1
  17. package/dist/api/raw-routes.d.ts +14 -0
  18. package/dist/api/raw-routes.js +277 -36
  19. package/dist/api/raw-routes.js.map +1 -1
  20. package/dist/api/router.d.ts +10 -0
  21. package/dist/api/router.js +10 -8
  22. package/dist/api/router.js.map +1 -1
  23. package/dist/api/sessions-router.d.ts +1710 -73
  24. package/dist/api/sessions-router.js +164 -10
  25. package/dist/api/sessions-router.js.map +1 -1
  26. package/dist/api/settings-router.d.ts +132 -2
  27. package/dist/api/settings-router.js +15 -5
  28. package/dist/api/settings-router.js.map +1 -1
  29. package/dist/api/sse-routes.d.ts +16 -3
  30. package/dist/api/sse-routes.js +194 -67
  31. package/dist/api/sse-routes.js.map +1 -1
  32. package/dist/api/tools-raw.js +12 -25
  33. package/dist/api/tools-raw.js.map +1 -1
  34. package/dist/api/tools-router.d.ts +425 -19
  35. package/dist/api/tools-router.js +157 -26
  36. package/dist/api/tools-router.js.map +1 -1
  37. package/dist/app.d.ts +0 -9377
  38. package/dist/app.js +42 -17
  39. package/dist/app.js.map +1 -1
  40. package/dist/auth/gate.js +8 -2
  41. package/dist/auth/gate.js.map +1 -1
  42. package/dist/auth/oidc.d.ts +5 -2
  43. package/dist/auth/oidc.js +8 -7
  44. package/dist/auth/oidc.js.map +1 -1
  45. package/dist/bin/talaria-web.js +1 -1
  46. package/dist/bin/talaria-web.js.map +1 -1
  47. package/dist/cli/dotenv.js +1 -0
  48. package/dist/cli/dotenv.js.map +1 -1
  49. package/dist/config/agent-config.d.ts +2 -0
  50. package/dist/config/agent-config.js +12 -2
  51. package/dist/config/agent-config.js.map +1 -1
  52. package/dist/config.d.ts +2 -0
  53. package/dist/config.js +5 -1
  54. package/dist/config.js.map +1 -1
  55. package/dist/http/context.d.ts +20 -3
  56. package/dist/http/context.js +41 -8
  57. package/dist/http/context.js.map +1 -1
  58. package/dist/http/csp.d.ts +2 -1
  59. package/dist/http/csp.js +3 -2
  60. package/dist/http/csp.js.map +1 -1
  61. package/dist/onboarding.js +4 -0
  62. package/dist/onboarding.js.map +1 -1
  63. package/dist/profiles/profiles.d.ts +10 -0
  64. package/dist/profiles/profiles.js +40 -2
  65. package/dist/profiles/profiles.js.map +1 -1
  66. package/dist/providers/catalog.d.ts +101 -8
  67. package/dist/providers/catalog.js +463 -123
  68. package/dist/providers/catalog.js.map +1 -1
  69. package/dist/raster-image.d.ts +7 -0
  70. package/dist/raster-image.js +267 -0
  71. package/dist/raster-image.js.map +1 -0
  72. package/dist/redact.d.ts +1 -0
  73. package/dist/redact.js +354 -44
  74. package/dist/redact.js.map +1 -1
  75. package/dist/runtime.js +111 -27
  76. package/dist/runtime.js.map +1 -1
  77. package/dist/server.js +5 -0
  78. package/dist/server.js.map +1 -1
  79. package/dist/sessions/anchor.d.ts +2 -0
  80. package/dist/sessions/anchor.js +8 -3
  81. package/dist/sessions/anchor.js.map +1 -1
  82. package/dist/sessions/background-tasks.d.ts +2 -9
  83. package/dist/sessions/background-tasks.js +2 -12
  84. package/dist/sessions/background-tasks.js.map +1 -1
  85. package/dist/sessions/claude-code.d.ts +19 -0
  86. package/dist/sessions/claude-code.js +250 -0
  87. package/dist/sessions/claude-code.js.map +1 -0
  88. package/dist/sessions/cli-sessions.d.ts +15 -4
  89. package/dist/sessions/cli-sessions.js +120 -29
  90. package/dist/sessions/cli-sessions.js.map +1 -1
  91. package/dist/sessions/completions.d.ts +61 -9
  92. package/dist/sessions/completions.js +282 -34
  93. package/dist/sessions/completions.js.map +1 -1
  94. package/dist/sessions/compress.d.ts +14 -1
  95. package/dist/sessions/compress.js +52 -11
  96. package/dist/sessions/compress.js.map +1 -1
  97. package/dist/sessions/export.d.ts +5 -0
  98. package/dist/sessions/export.js +30 -0
  99. package/dist/sessions/export.js.map +1 -1
  100. package/dist/sessions/gateway-watcher.d.ts +7 -3
  101. package/dist/sessions/gateway-watcher.js +31 -19
  102. package/dist/sessions/gateway-watcher.js.map +1 -1
  103. package/dist/sessions/handoff.d.ts +32 -0
  104. package/dist/sessions/handoff.js +114 -0
  105. package/dist/sessions/handoff.js.map +1 -0
  106. package/dist/sessions/journal.d.ts +8 -0
  107. package/dist/sessions/journal.js +44 -10
  108. package/dist/sessions/journal.js.map +1 -1
  109. package/dist/sessions/list.d.ts +13 -3
  110. package/dist/sessions/list.js +46 -15
  111. package/dist/sessions/list.js.map +1 -1
  112. package/dist/sessions/merge.d.ts +53 -5
  113. package/dist/sessions/merge.js +219 -23
  114. package/dist/sessions/merge.js.map +1 -1
  115. package/dist/sessions/recovery.d.ts +22 -0
  116. package/dist/sessions/recovery.js +506 -0
  117. package/dist/sessions/recovery.js.map +1 -0
  118. package/dist/sessions/relay.d.ts +21 -2
  119. package/dist/sessions/relay.js +156 -11
  120. package/dist/sessions/relay.js.map +1 -1
  121. package/dist/sessions/service.d.ts +152 -8
  122. package/dist/sessions/service.js +640 -72
  123. package/dist/sessions/service.js.map +1 -1
  124. package/dist/sessions/session.d.ts +17 -1
  125. package/dist/sessions/session.js +63 -4
  126. package/dist/sessions/session.js.map +1 -1
  127. package/dist/sessions/state-db-worker.d.ts +1 -0
  128. package/dist/sessions/state-db-worker.js +33 -0
  129. package/dist/sessions/state-db-worker.js.map +1 -0
  130. package/dist/sessions/state-db.d.ts +87 -4
  131. package/dist/sessions/state-db.js +417 -8
  132. package/dist/sessions/state-db.js.map +1 -1
  133. package/dist/sessions/store.d.ts +7 -0
  134. package/dist/sessions/store.js +36 -22
  135. package/dist/sessions/store.js.map +1 -1
  136. package/dist/sessions/streams.d.ts +10 -0
  137. package/dist/sessions/streams.js +14 -3
  138. package/dist/sessions/streams.js.map +1 -1
  139. package/dist/sessions/titles.d.ts +2 -0
  140. package/dist/sessions/titles.js +4 -0
  141. package/dist/sessions/titles.js.map +1 -1
  142. package/dist/sessions/todo.d.ts +6 -1
  143. package/dist/sessions/todo.js +11 -4
  144. package/dist/sessions/todo.js.map +1 -1
  145. package/dist/sessions/tool-display.d.ts +12 -1
  146. package/dist/sessions/tool-display.js +50 -1
  147. package/dist/sessions/tool-display.js.map +1 -1
  148. package/dist/sessions/turn-context.d.ts +1 -1
  149. package/dist/sessions/turn-context.js +11 -7
  150. package/dist/sessions/turn-context.js.map +1 -1
  151. package/dist/sessions/turn.d.ts +67 -11
  152. package/dist/sessions/turn.js +413 -111
  153. package/dist/sessions/turn.js.map +1 -1
  154. package/dist/sessions/wakeup-pause.d.ts +59 -0
  155. package/dist/sessions/wakeup-pause.js +181 -0
  156. package/dist/sessions/wakeup-pause.js.map +1 -0
  157. package/dist/settings.d.ts +20 -2
  158. package/dist/settings.js +45 -9
  159. package/dist/settings.js.map +1 -1
  160. package/dist/sidecar/fake.d.ts +4 -4
  161. package/dist/sidecar/fake.js +5 -2
  162. package/dist/sidecar/fake.js.map +1 -1
  163. package/dist/spa.js +2 -1
  164. package/dist/spa.js.map +1 -1
  165. package/dist/startup.d.ts +13 -5
  166. package/dist/startup.js +48 -0
  167. package/dist/startup.js.map +1 -1
  168. package/dist/test/harness.js +2 -0
  169. package/dist/test/harness.js.map +1 -1
  170. package/dist/test/ts-worker-hooks.d.ts +1 -0
  171. package/dist/test/ts-worker-hooks.js +24 -0
  172. package/dist/test/ts-worker-hooks.js.map +1 -0
  173. package/dist/text/diff.d.ts +9 -14
  174. package/dist/text/diff.js +74 -108
  175. package/dist/text/diff.js.map +1 -1
  176. package/dist/tools/crons.js +45 -8
  177. package/dist/tools/crons.js.map +1 -1
  178. package/dist/tools/dashboard-plugins.d.ts +53 -0
  179. package/dist/tools/dashboard-plugins.js +188 -0
  180. package/dist/tools/dashboard-plugins.js.map +1 -0
  181. package/dist/tools/extensions.d.ts +13 -1
  182. package/dist/tools/extensions.js +132 -60
  183. package/dist/tools/extensions.js.map +1 -1
  184. package/dist/tools/health.d.ts +32 -0
  185. package/dist/tools/health.js +111 -19
  186. package/dist/tools/health.js.map +1 -1
  187. package/dist/tools/insights.d.ts +8 -2
  188. package/dist/tools/insights.js +18 -2
  189. package/dist/tools/insights.js.map +1 -1
  190. package/dist/tools/kanban.d.ts +41 -1
  191. package/dist/tools/kanban.js +113 -2
  192. package/dist/tools/kanban.js.map +1 -1
  193. package/dist/tools/mcp.d.ts +15 -1
  194. package/dist/tools/mcp.js +99 -5
  195. package/dist/tools/mcp.js.map +1 -1
  196. package/dist/tools/project-os.d.ts +11 -0
  197. package/dist/tools/project-os.js +267 -0
  198. package/dist/tools/project-os.js.map +1 -0
  199. package/dist/tools/skills.d.ts +5 -1
  200. package/dist/tools/skills.js +5 -3
  201. package/dist/tools/skills.js.map +1 -1
  202. package/dist/tools/terminal.d.ts +1 -0
  203. package/dist/tools/terminal.js +1 -1
  204. package/dist/tools/terminal.js.map +1 -1
  205. package/dist/tools/updates.d.ts +33 -8
  206. package/dist/tools/updates.js +236 -57
  207. package/dist/tools/updates.js.map +1 -1
  208. package/dist/tools/wiki.d.ts +68 -0
  209. package/dist/tools/wiki.js +413 -0
  210. package/dist/tools/wiki.js.map +1 -0
  211. package/dist/workspace/escape.d.ts +61 -0
  212. package/dist/workspace/escape.js +149 -0
  213. package/dist/workspace/escape.js.map +1 -0
  214. package/dist/workspace/extract.d.ts +22 -0
  215. package/dist/workspace/extract.js +308 -0
  216. package/dist/workspace/extract.js.map +1 -0
  217. package/dist/workspace/fs.d.ts +19 -3
  218. package/dist/workspace/fs.js +96 -23
  219. package/dist/workspace/fs.js.map +1 -1
  220. package/dist/workspace/git.d.ts +18 -0
  221. package/dist/workspace/git.js +38 -25
  222. package/dist/workspace/git.js.map +1 -1
  223. package/dist/workspace/media.d.ts +4 -4
  224. package/dist/workspace/media.js +18 -45
  225. package/dist/workspace/media.js.map +1 -1
  226. package/dist/workspace/preview.d.ts +28 -0
  227. package/dist/workspace/preview.js +110 -0
  228. package/dist/workspace/preview.js.map +1 -0
  229. package/dist/workspace/rollback.d.ts +36 -21
  230. package/dist/workspace/rollback.js +179 -270
  231. package/dist/workspace/rollback.js.map +1 -1
  232. package/dist/workspace/unzip.js +4 -1
  233. package/dist/workspace/unzip.js.map +1 -1
  234. package/dist/workspace/upload.d.ts +9 -4
  235. package/dist/workspace/upload.js +9 -3
  236. package/dist/workspace/upload.js.map +1 -1
  237. package/dist/workspace/worktrees.d.ts +2 -1
  238. package/dist/workspace/worktrees.js +14 -15
  239. package/dist/workspace/worktrees.js.map +1 -1
  240. package/package.json +4 -2
  241. package/sidecar/agent_dependency.json +4 -4
  242. package/sidecar/scripts/replay_sidecar.py +25 -1
  243. package/sidecar/talaria_sidecar/__init__.py +1 -1
  244. package/sidecar/talaria_sidecar/__main__.py +7 -1
  245. package/sidecar/talaria_sidecar/home.py +52 -2
  246. package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
  247. package/sidecar/talaria_sidecar/methods/aux.py +39 -5
  248. package/sidecar/talaria_sidecar/methods/chat.py +431 -43
  249. package/sidecar/talaria_sidecar/methods/checkpoints.py +89 -0
  250. package/sidecar/talaria_sidecar/methods/commands.py +96 -2
  251. package/sidecar/talaria_sidecar/methods/cron.py +39 -8
  252. package/sidecar/talaria_sidecar/methods/gateway.py +32 -15
  253. package/sidecar/talaria_sidecar/methods/kanban.py +140 -19
  254. package/sidecar/talaria_sidecar/methods/mcp.py +1 -1
  255. package/sidecar/talaria_sidecar/methods/process.py +115 -2
  256. package/sidecar/talaria_sidecar/methods/profiles.py +84 -8
  257. package/sidecar/talaria_sidecar/methods/providers.py +27 -8
  258. package/sidecar/talaria_sidecar/methods/runtime.py +3 -0
  259. package/sidecar/talaria_sidecar/methods/state_db.py +39 -5
  260. package/sidecar/talaria_sidecar/methods/text.py +17 -1
  261. package/sidecar/talaria_sidecar/methods/usage.py +257 -4
  262. package/sidecar/talaria_sidecar/runtime.py +17 -1
  263. package/static/dist/FILES.txt +127 -125
  264. package/static/dist/assets/AppShell-g3bkgnvt.js +1 -0
  265. package/static/dist/assets/ChatPage-oo7bdan6.js +25 -0
  266. package/static/dist/assets/Dialog-g355qxhp.js +1 -0
  267. package/static/dist/assets/ExtensionRoute-i2y9fvjm.js +1 -0
  268. package/static/dist/assets/HubRoute-lmkrbw47.js +2 -0
  269. package/static/dist/assets/{Markdown-jqrx5es0.js → Markdown-j4kut4j2.js} +125 -125
  270. package/static/dist/assets/RightPanel-ohlsdzfl.js +1 -0
  271. package/static/dist/assets/Select-kddq75qc.js +1 -0
  272. package/static/dist/assets/SessionListPanel-lu3oy0hw.js +1 -0
  273. package/static/dist/assets/SettingsLayout-o9bv4mgb.js +1 -0
  274. package/static/dist/assets/States-eq6fe29m.js +1 -0
  275. package/static/dist/assets/Toaster-pc247u9f.js +1 -0
  276. package/static/dist/assets/_app-f8n1i0m1.js +1 -0
  277. package/static/dist/assets/_app.ext._extensionId-gfbzpoou.js +1 -0
  278. package/static/dist/assets/_app.index-bzzv80hk.js +1 -0
  279. package/static/dist/assets/_app.insights-dzixbryi.js +1 -0
  280. package/static/dist/assets/_app.kanban-c6hep2us.js +1 -0
  281. package/static/dist/assets/_app.logs-i69qds98.js +1 -0
  282. package/static/dist/assets/_app.memory-kl4vf0gd.js +1 -0
  283. package/static/dist/assets/_app.profiles-b8zinmyp.js +1 -0
  284. package/static/dist/assets/{_app.session._sessionId-hh27wwu2.js → _app.session._sessionId-fn1bhde6.js} +1 -1
  285. package/static/dist/assets/_app.settings-cg8plwmf.js +1 -0
  286. package/static/dist/assets/_app.settings._section-bkwbi9zn.js +3 -0
  287. package/static/dist/assets/_app.skills-cs47sbvs.js +1 -0
  288. package/static/dist/assets/_app.tasks-dwvk64tc.js +1 -0
  289. package/static/dist/assets/_app.todos-c8xjm8p3.js +1 -0
  290. package/static/dist/assets/_app.workspaces-ehvd1gds.js +1 -0
  291. package/static/dist/assets/{abnfDiagram-VCTEODGH-oity9y4o.js → abnfDiagram-VCTEODGH-c8kdixke.js} +1 -1
  292. package/static/dist/assets/architecture-7GRP2DOG-6crvnylg.js +1 -0
  293. package/static/dist/assets/{architectureDiagram-5GKGNRK7-ceaepnyr.js → architectureDiagram-5GKGNRK7-cku06x1p.js} +1 -1
  294. package/static/dist/assets/{back-eu64w89w.js → back-h7aun7ew.js} +1 -1
  295. package/static/dist/assets/{blockDiagram-I7D4REHJ-n79st4pj.js → blockDiagram-I7D4REHJ-jzthp1wa.js} +1 -1
  296. package/static/dist/assets/{c4Diagram-7LVT6UL2-mi73akzk.js → c4Diagram-7LVT6UL2-mwr67mt8.js} +1 -1
  297. package/static/dist/assets/{cancel-mzvkdjg8.js → cancel-fhi0vcnu.js} +1 -1
  298. package/static/dist/assets/channel-b2zo73z1.js +1 -0
  299. package/static/dist/assets/{chunk-4HAMMTFA-laat0g1q.js → chunk-4HAMMTFA-gpq1y228.js} +1 -1
  300. package/static/dist/assets/{chunk-75Z2AOVW-0psy1tr2.js → chunk-75Z2AOVW-ktje58c4.js} +1 -1
  301. package/static/dist/assets/{chunk-DU6HZSFF-lw8uox8x.js → chunk-DU6HZSFF-ofajdixh.js} +3 -3
  302. package/static/dist/assets/{chunk-F27PBJKO-erassib3.js → chunk-F27PBJKO-cmev85ti.js} +1 -1
  303. package/static/dist/assets/{chunk-GMAD6QVW-fz7rx619.js → chunk-GMAD6QVW-htjgt22y.js} +1 -1
  304. package/static/dist/assets/{chunk-GVQU2GXP-gdb0epb2.js → chunk-GVQU2GXP-qndvoxl7.js} +1 -1
  305. package/static/dist/assets/{chunk-IMKFNOWR-ea8t26qw.js → chunk-IMKFNOWR-e5bkd5q6.js} +1 -1
  306. package/static/dist/assets/{chunk-L3NEJ4N5-mb1gu9kz.js → chunk-L3NEJ4N5-mj1155r6.js} +1 -1
  307. package/static/dist/assets/{chunk-OSK3NFVY-eovmccvy.js → chunk-OSK3NFVY-e7gkikcw.js} +1 -1
  308. package/static/dist/assets/{chunk-P2QGCYS3-bo55zjo5.js → chunk-P2QGCYS3-f7t54xfm.js} +1 -1
  309. package/static/dist/assets/{chunk-POPQ4Y6H-ja7ohe4h.js → chunk-POPQ4Y6H-i927v9oa.js} +1 -1
  310. package/static/dist/assets/{chunk-PWAF6VOD-e30g9nus.js → chunk-PWAF6VOD-m84lmnyt.js} +1 -1
  311. package/static/dist/assets/{chunk-SHT3W25Y-gbb798f7.js → chunk-SHT3W25Y-mi0tlcu9.js} +1 -1
  312. package/static/dist/assets/{chunk-SVP7TREG-h2iw8urm.js → chunk-SVP7TREG-dnau702j.js} +1 -1
  313. package/static/dist/assets/{chunk-TICWLB2K-fd2cottz.js → chunk-TICWLB2K-edem1gkg.js} +1 -1
  314. package/static/dist/assets/classDiagram-ZZMXUADV-oe99st8x.js +1 -0
  315. package/static/dist/assets/classDiagram-v2-VYDZK3BY-oe99st8x.js +1 -0
  316. package/static/dist/assets/{copied-h65fc3cx.js → copied-hcplv46p.js} +1 -1
  317. package/static/dist/assets/copy-magtofyp.js +1 -0
  318. package/static/dist/assets/{cynefin-OW5HDTMX-lbltyl5j.js → cynefin-OW5HDTMX-siqlulkl.js} +1 -1
  319. package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-lxhsqq4a.js → cynefinDiagram-5FMLGOSQ-i648tah9.js} +1 -1
  320. package/static/dist/assets/{dagre-GXQ25YYZ-gn4nnaln.js → dagre-GXQ25YYZ-mwn6uc17.js} +1 -1
  321. package/static/dist/assets/{diagram-S7CK7UJ4-pa3ij6q7.js → diagram-S7CK7UJ4-l8abxaes.js} +1 -1
  322. package/static/dist/assets/{diagram-UQ7AKVKN-zbipyz57.js → diagram-UQ7AKVKN-mekq7zgq.js} +1 -1
  323. package/static/dist/assets/{diagram-VSXAHHWV-d1kbpzgf.js → diagram-VSXAHHWV-fnky6lvc.js} +1 -1
  324. package/static/dist/assets/{diagram-VX7I27RA-i3ty14xl.js → diagram-VX7I27RA-xe6r5qfw.js} +1 -1
  325. package/static/dist/assets/{diagram-Z3DM3KII-m8xefqa7.js → diagram-Z3DM3KII-nq07dg7k.js} +1 -1
  326. package/static/dist/assets/{ebnfDiagram-PWID7BFC-nz7wj05l.js → ebnfDiagram-PWID7BFC-c62ipm3g.js} +1 -1
  327. package/static/dist/assets/endpoints-klbxod4t.js +64 -0
  328. package/static/dist/assets/{erDiagram-RLTQ6QDP-fa41ib83.js → erDiagram-RLTQ6QDP-h39kmnzy.js} +1 -1
  329. package/static/dist/assets/eventmodeling-NTZA5JFV-obfede4u.js +1 -0
  330. package/static/dist/assets/flowDiagram-HODETNUW-kdpsp1je.js +1 -0
  331. package/static/dist/assets/{ganttDiagram-EL5Y4UJY-n7zq01qu.js → ganttDiagram-EL5Y4UJY-ngbsyp37.js} +1 -1
  332. package/static/dist/assets/{gitGraph-4MIJSDKK-c89emb07.js → gitGraph-4MIJSDKK-fr2i7caa.js} +1 -1
  333. package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-cz7cgr6w.js → gitGraphDiagram-WWUBYQGX-eravdti4.js} +1 -1
  334. package/static/dist/assets/{highlighted-body-KPVGNVTW-d9bapmni.js → highlighted-body-KPVGNVTW-jwpop9p8.js} +1 -1
  335. package/static/dist/assets/host-i828xuo7.js +1 -0
  336. package/static/dist/assets/index-eh8zilnv.css +2 -0
  337. package/static/dist/assets/index-r648f4o1.js +11 -0
  338. package/static/dist/assets/{info-A6RAGUB7-ndddhhhh.js → info-A6RAGUB7-h11x0dxo.js} +1 -1
  339. package/static/dist/assets/{infoDiagram-27XIBGKW-ccxigcpf.js → infoDiagram-27XIBGKW-dfh47dw9.js} +1 -1
  340. package/static/dist/assets/{ishikawaDiagram-5VMMS53U-le0z9vtc.js → ishikawaDiagram-5VMMS53U-i6ypq351.js} +1 -1
  341. package/static/dist/assets/{journeyDiagram-3NMN7TZE-ivsmb26o.js → journeyDiagram-3NMN7TZE-lduf1z95.js} +1 -1
  342. package/static/dist/assets/{kanban-definition-UXKFOSKX-h6n2awt0.js → kanban-definition-UXKFOSKX-e18bu4pg.js} +1 -1
  343. package/static/dist/assets/lazyRouteComponent-e1y4t2i9.js +1 -0
  344. package/static/dist/assets/{line-dmzlfe0u.js → line-meizqsvn.js} +1 -1
  345. package/static/dist/assets/link-oot2yn73.js +1 -0
  346. package/static/dist/assets/login-gu28l9ou.js +1 -0
  347. package/static/dist/assets/mermaid-HWGCJPDP-mtwrj8fn.js +1 -0
  348. package/static/dist/assets/{mermaid-parser.core-gwyd5bnq.js → mermaid-parser.core-ntne45i9.js} +3 -3
  349. package/static/dist/assets/{mindmap-definition-YA3MSWOX-l68gg5h7.js → mindmap-definition-YA3MSWOX-27b26eok.js} +1 -1
  350. package/static/dist/assets/onboarding-lkphzu1s.js +1 -0
  351. package/static/dist/assets/onboarding_model_label-ftpjdwqk.js +1 -0
  352. package/static/dist/assets/{packet-AYTQ26CC-gpv06065.js → packet-AYTQ26CC-l6jdka8p.js} +1 -1
  353. package/static/dist/assets/{pegDiagram-XKGWAZYB-e52vs23u.js → pegDiagram-XKGWAZYB-g1er3bxj.js} +1 -1
  354. package/static/dist/assets/{pie-WAS4IAKB-l6m7nz5s.js → pie-WAS4IAKB-i0ylcrt9.js} +1 -1
  355. package/static/dist/assets/{pieDiagram-E7YTZNPT-b4is36bq.js → pieDiagram-E7YTZNPT-fbz1e55z.js} +1 -1
  356. package/static/dist/assets/{quadrantDiagram-AXDQQJYC-m8vt2788.js → quadrantDiagram-AXDQQJYC-hhue7d8o.js} +1 -1
  357. package/static/dist/assets/{radar-RG4KPBEZ-nxbrhiov.js → radar-RG4KPBEZ-f9b70aa4.js} +1 -1
  358. package/static/dist/assets/{railroad-74A4TZTK-222mu9hi.js → railroad-74A4TZTK-lgsuoeda.js} +1 -1
  359. package/static/dist/assets/railroad-abnf-HS5TGJTU-nf9d6pdx.js +1 -0
  360. package/static/dist/assets/railroad-ebnf-LZEXJU2U-f7ummkt5.js +1 -0
  361. package/static/dist/assets/railroad-peg-WCYAUIDC-oczq5tqs.js +1 -0
  362. package/static/dist/assets/{railroadDiagram-O6MQD6OU-o8lldfzv.js → railroadDiagram-O6MQD6OU-eqpy9fnz.js} +1 -1
  363. package/static/dist/assets/refresh-abtu93ke.js +1 -0
  364. package/static/dist/assets/refresh-cw-hlwb02qv.js +1 -0
  365. package/static/dist/assets/{requirementDiagram-BXWQKSXE-ovyq38kv.js → requirementDiagram-BXWQKSXE-ogf6puhi.js} +1 -1
  366. package/static/dist/assets/{sankeyDiagram-P5KCCOFB-ou7lznor.js → sankeyDiagram-P5KCCOFB-fqb8xcjf.js} +1 -1
  367. package/static/dist/assets/{saved-bazmcup9.js → saved-girnabkf.js} +1 -1
  368. package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-ovckdzf2.js → sequenceDiagram-WJ2MYXX4-dlcz6qjz.js} +1 -1
  369. package/static/dist/assets/{settings_section_extensions_title-ertwwhlx.js → settings_section_extensions_title-h14cwk6f.js} +1 -1
  370. package/static/dist/assets/share._token-kl0r7ido.js +2 -0
  371. package/static/dist/assets/sliders-horizontal-n1zm34qq.js +1 -0
  372. package/static/dist/assets/sse-iwku0sjo.js +1 -0
  373. package/static/dist/assets/{stateDiagram-D77RDMKH-fcrexkkv.js → stateDiagram-D77RDMKH-fyoznpcs.js} +1 -1
  374. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-ixhx8r46.js +1 -0
  375. package/static/dist/assets/{swimlanes-42K2YHIH-f7usu8fw.js → swimlanes-42K2YHIH-oefldhfy.js} +1 -1
  376. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-h9ersk2e.js +8 -0
  377. package/static/dist/assets/{timeline-definition-24CTP7MA-gp1hvz9f.js → timeline-definition-24CTP7MA-b1vl2zil.js} +1 -1
  378. package/static/dist/assets/todoStore-jjr5yr78.js +1 -0
  379. package/static/dist/assets/{treeView-Q6P3EWNA-gb9gf11u.js → treeView-Q6P3EWNA-f7hcci5o.js} +1 -1
  380. package/static/dist/assets/{treemap-WGGIJYW6-nuvfnvvi.js → treemap-WGGIJYW6-nl85071a.js} +1 -1
  381. package/static/dist/assets/units-kgxzxfhi.js +1 -0
  382. package/static/dist/assets/{untitled-htelhc68.js → untitled-cikfjntn.js} +1 -1
  383. package/static/dist/assets/{useForm-h2t0u14e.js → useForm-kqoqg0k9.js} +1 -1
  384. package/static/dist/assets/useLocale-o59tbiea.js +1 -0
  385. package/static/dist/assets/{useParams-dqd64jv7.js → useParams-b4dq6n4q.js} +1 -1
  386. package/static/dist/assets/{useSearch-j30pvsy7.js → useSearch-ipfmv3he.js} +1 -1
  387. package/static/dist/assets/{vennDiagram-4TSXK5OY-fzchbzaj.js → vennDiagram-4TSXK5OY-h5hzbk0n.js} +1 -1
  388. package/static/dist/assets/{wardley-WFR3VGLG-m47mhfbh.js → wardley-WFR3VGLG-jftvpm3e.js} +1 -1
  389. package/static/dist/assets/{wardleyDiagram-VM6X3IG4-odg6ct1z.js → wardleyDiagram-VM6X3IG4-hedh85ge.js} +1 -1
  390. package/static/dist/assets/{xychartDiagram-S5SC5T6Z-mrhms5um.js → xychartDiagram-S5SC5T6Z-e49523ab.js} +1 -1
  391. package/static/dist/index.html +1 -1
  392. package/static/dist/sw.js +2 -2
  393. package/static/dist/assets/AppShell-ef3bhu32.js +0 -1
  394. package/static/dist/assets/Button-nx37f4u1.js +0 -1
  395. package/static/dist/assets/ChatPage-cdcpykcg.js +0 -25
  396. package/static/dist/assets/Dialog-mec42hvb.js +0 -1
  397. package/static/dist/assets/HubRoute-ecb65zpr.js +0 -2
  398. package/static/dist/assets/RightPanel-d1lgvk4s.js +0 -1
  399. package/static/dist/assets/Select-h4ivl9qf.js +0 -1
  400. package/static/dist/assets/SessionListPanel-ewib046b.js +0 -1
  401. package/static/dist/assets/SettingsLayout-lx654cup.js +0 -1
  402. package/static/dist/assets/States-ehsb6lzo.js +0 -1
  403. package/static/dist/assets/Toaster-mch1pa3t.js +0 -1
  404. package/static/dist/assets/_app-m8d961ef.js +0 -1
  405. package/static/dist/assets/_app.ext._extensionId-mp33hbzi.js +0 -1
  406. package/static/dist/assets/_app.index-dk6zp7e0.js +0 -1
  407. package/static/dist/assets/_app.insights-jt2fk4i1.js +0 -1
  408. package/static/dist/assets/_app.kanban-if4pubeu.js +0 -1
  409. package/static/dist/assets/_app.logs-mfaw9m6g.js +0 -1
  410. package/static/dist/assets/_app.memory-h3eaarb6.js +0 -1
  411. package/static/dist/assets/_app.profiles-ienktovs.js +0 -1
  412. package/static/dist/assets/_app.settings-dpoe0bzf.js +0 -1
  413. package/static/dist/assets/_app.settings._section-l468ynag.js +0 -3
  414. package/static/dist/assets/_app.skills-kcbncc8i.js +0 -1
  415. package/static/dist/assets/_app.tasks-occdrqhw.js +0 -2
  416. package/static/dist/assets/_app.todos-nnke2rcc.js +0 -1
  417. package/static/dist/assets/_app.workspaces-cdlatvwj.js +0 -1
  418. package/static/dist/assets/architecture-7GRP2DOG-hj11333z.js +0 -1
  419. package/static/dist/assets/channel-mbvmf2vv.js +0 -1
  420. package/static/dist/assets/classDiagram-ZZMXUADV-o8r05k0n.js +0 -1
  421. package/static/dist/assets/classDiagram-v2-VYDZK3BY-o8r05k0n.js +0 -1
  422. package/static/dist/assets/copy-e6977uwq.js +0 -1
  423. package/static/dist/assets/endpoints-k7yf25m0.js +0 -64
  424. package/static/dist/assets/eventmodeling-NTZA5JFV-kpbq09bx.js +0 -1
  425. package/static/dist/assets/flowDiagram-HODETNUW-lcz0kx14.js +0 -1
  426. package/static/dist/assets/index-h00usru2.js +0 -11
  427. package/static/dist/assets/index-n73ijkv2.css +0 -2
  428. package/static/dist/assets/lazyRouteComponent-m1rtyk4t.js +0 -1
  429. package/static/dist/assets/link-budeg5t9.js +0 -1
  430. package/static/dist/assets/login-kjhasvp8.js +0 -1
  431. package/static/dist/assets/matchContext-f8oy3lo7.js +0 -1
  432. package/static/dist/assets/mermaid-HWGCJPDP-jb5d211j.js +0 -1
  433. package/static/dist/assets/onboarding-cdfrk0pj.js +0 -1
  434. package/static/dist/assets/railroad-abnf-HS5TGJTU-kdvgl6x8.js +0 -1
  435. package/static/dist/assets/railroad-ebnf-LZEXJU2U-itiqslby.js +0 -1
  436. package/static/dist/assets/railroad-peg-WCYAUIDC-go7kov78.js +0 -1
  437. package/static/dist/assets/refresh-cw-kbcje1uy.js +0 -1
  438. package/static/dist/assets/search-jen74z4u.js +0 -1
  439. package/static/dist/assets/share._token-dan9hgvj.js +0 -2
  440. package/static/dist/assets/sliders-horizontal-f9cb3lh3.js +0 -1
  441. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-lcfraua2.js +0 -1
  442. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-ogmanni0.js +0 -8
  443. package/static/dist/assets/todoStore-eqtid4r4.js +0 -1
  444. package/static/dist/assets/useLocale-hcsb84pj.js +0 -1
  445. package/static/dist/assets/useNavigate-nwy70ztj.js +0 -1
@@ -0,0 +1,89 @@
1
+ """``checkpoints.*``: a workspace's filesystem checkpoints through the Agent's own ``CheckpointManager`` (TAL-913).
2
+
3
+ The Agent keeps every project's checkpoints as refs in one shared git store under the profile home; listing, diffing and
4
+ restoring go through its manager so the store layout, the nested-repository refusal and the pre-rollback snapshot stay
5
+ the Agent's.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from ..errors import InvalidParams, RpcError
11
+ from ..home import profile_home_param, scoped_home
12
+ from ..rpc import CallContext
13
+
14
+
15
+ def checkpoint_kwargs() -> dict:
16
+ """The profile's ``checkpoints`` config as ``AIAgent`` kwargs, resolved as the Agent's gateway does
17
+ (``gateway/run.py::_checkpoint_agent_kwargs``): the legacy ``checkpoints: true`` form, else the section over
18
+ ``DEFAULT_CONFIG``. Only a real ``true`` turns them on, so a quoted ``"false"`` stays off."""
19
+ from hermes_cli.config import DEFAULT_CONFIG, load_config
20
+
21
+ section = (load_config() or {}).get("checkpoints", {})
22
+ if isinstance(section, bool):
23
+ section = {"enabled": section}
24
+ elif not isinstance(section, dict):
25
+ section = {}
26
+ defaults = DEFAULT_CONFIG["checkpoints"]
27
+ return {
28
+ "checkpoints_enabled": section.get("enabled", defaults["enabled"]) is True,
29
+ "checkpoint_max_snapshots": section.get("max_snapshots", defaults["max_snapshots"]),
30
+ "checkpoint_max_total_size_mb": section.get("max_total_size_mb", defaults["max_total_size_mb"]),
31
+ "checkpoint_max_file_size_mb": section.get("max_file_size_mb", defaults["max_file_size_mb"]),
32
+ }
33
+
34
+
35
+ def _manager():
36
+ from tools.checkpoint_manager import CheckpointManager
37
+
38
+ kwargs = checkpoint_kwargs()
39
+ return CheckpointManager(enabled=kwargs["checkpoints_enabled"], max_snapshots=kwargs["checkpoint_max_snapshots"],
40
+ max_total_size_mb=kwargs["checkpoint_max_total_size_mb"], max_file_size_mb=kwargs["checkpoint_max_file_size_mb"])
41
+
42
+
43
+ def _param(params: dict, name: str) -> str:
44
+ value = params.get(name)
45
+ if not isinstance(value, str) or not value.strip():
46
+ raise InvalidParams(f"{name} is required")
47
+ return value
48
+
49
+
50
+ def _agent_result(result: dict) -> dict:
51
+ """The Agent answers a refusal (unknown checkpoint, uncaptured nested repository, failed checkout) as ``success: False``."""
52
+ if not result.get("success"):
53
+ raise RpcError(str(result.get("error") or "checkpoint operation failed"), condition="checkpoint_failed")
54
+ return result
55
+
56
+
57
+ def _owned(manager, workspace: str, checkpoint: str) -> str:
58
+ """The full hash of ``checkpoint`` among ``workspace``'s own checkpoints. The store is shared by every project and the
59
+ Agent's diff and restore accept any commit in it, so another workspace's hash would check that tree out here."""
60
+ matches = [c["hash"] for c in manager.list_checkpoints(workspace) if c.get("hash") and str(c["hash"]).startswith(checkpoint.lower())]
61
+ if len(matches) != 1:
62
+ raise RpcError(f"Checkpoint '{checkpoint}' is not a checkpoint of this workspace", condition="checkpoint_failed")
63
+ return matches[0]
64
+
65
+
66
+ def register(registry) -> None:
67
+ @registry.method("checkpoints.list")
68
+ def list_(ctx: CallContext, params: dict) -> dict:
69
+ workspace = _param(params, "workspace")
70
+ with scoped_home(profile_home_param(params)):
71
+ from tools.checkpoint_manager import _store_path
72
+
73
+ return {"checkpoints": _manager().list_checkpoints(workspace), "store": str(_store_path())}
74
+
75
+ @registry.method("checkpoints.diff")
76
+ def diff(ctx: CallContext, params: dict) -> dict:
77
+ workspace, checkpoint = _param(params, "workspace"), _param(params, "checkpoint")
78
+ with scoped_home(profile_home_param(params)):
79
+ manager = _manager()
80
+ result = _agent_result(manager.diff(workspace, _owned(manager, workspace, checkpoint)))
81
+ return {"stat": str(result.get("stat") or ""), "diff": str(result.get("diff") or "")}
82
+
83
+ @registry.method("checkpoints.restore")
84
+ def restore(ctx: CallContext, params: dict) -> dict:
85
+ workspace, checkpoint = _param(params, "workspace"), _param(params, "checkpoint")
86
+ with scoped_home(profile_home_param(params)):
87
+ manager = _manager()
88
+ result = _agent_result(manager.restore(workspace, _owned(manager, workspace, checkpoint)))
89
+ return {"restored_to": str(result.get("restored_to") or ""), "reason": str(result.get("reason") or "")}
@@ -15,7 +15,14 @@ log = logging.getLogger("talaria_sidecar.commands")
15
15
 
16
16
  _NEVER_EXPOSE = frozenset({"sethome", "restart", "update", "commands"})
17
17
  _ALIASES = {"reload_mcp": "reload-mcp", "reload_skills": "reload-skills", "codex_runtime": "codex-runtime"}
18
- _ALLOWED = frozenset({"reload-mcp", "reload-skills", "codex-runtime", "credits"})
18
+ # The commands ``commands.exec`` runs, with the catalog row the sidecar lists when the Agent registry lacks one.
19
+ _RUNTIME_COMMANDS = {
20
+ "reload-mcp": ("Reload MCP servers from config", "Tools & Skills", ""),
21
+ "reload-skills": ("Re-scan skills from disk", "Tools & Skills", ""),
22
+ "codex-runtime": ("Toggle codex app-server runtime for OpenAI/Codex models", "Configuration", "[auto|codex_app_server]"),
23
+ "credits": ("Show your Nous credits", "Info", ""),
24
+ }
25
+ _ALLOWED = frozenset(_RUNTIME_COMMANDS)
19
26
  _RELOAD_MCP_LOCK = threading.Lock()
20
27
  _RELOAD_SKILLS_LOCK = threading.Lock()
21
28
  _CODEX_RUNTIME_LOCK = threading.Lock()
@@ -46,7 +53,13 @@ def list_commands() -> list[dict[str, Any]]:
46
53
  out.append({
47
54
  "name": cmd.name, "description": cmd.description, "category": cmd.category, "aliases": list(cmd.aliases),
48
55
  "args_hint": cmd.args_hint, "subcommands": list(cmd.subcommands), "cli_only": bool(cmd.cli_only), "gateway_only": bool(cmd.gateway_only),
56
+ "exec": cmd.name in _ALLOWED,
49
57
  })
58
+ listed = {c["name"] for c in out}
59
+ for name, (description, category, args_hint) in _RUNTIME_COMMANDS.items():
60
+ if name not in listed:
61
+ out.append({"name": name, "description": description, "category": category, "aliases": [a for a, n in _ALIASES.items() if n == name],
62
+ "args_hint": args_hint, "subcommands": [], "cli_only": False, "gateway_only": False, "exec": True})
50
63
  try:
51
64
  from hermes_cli.plugins import get_plugin_commands
52
65
 
@@ -55,7 +68,7 @@ def list_commands() -> list[dict[str, Any]]:
55
68
  if name in existing or name in _NEVER_EXPOSE:
56
69
  continue
57
70
  out.append({"name": name, "description": str(info.get("description", "Plugin command")), "category": "Plugin", "aliases": [],
58
- "args_hint": str(info.get("args_hint", "")), "subcommands": [], "cli_only": False, "gateway_only": False})
71
+ "args_hint": str(info.get("args_hint", "")), "subcommands": [], "cli_only": False, "gateway_only": False, "exec": True})
59
72
  except Exception: # noqa: BLE001 - plugin registry is optional
60
73
  log.debug("plugin command listing failed", exc_info=True)
61
74
  return out
@@ -231,6 +244,77 @@ def execute_plugin_command(command: str) -> str | None:
231
244
  return f"Plugin command error: {type(exc).__name__}"
232
245
 
233
246
 
247
+ def _profile_bundles(skill_bundles) -> dict[str, dict[str, Any]]:
248
+ """The scoped profile's bundles keyed ``/slug``, read from disk the way ``scan_bundles`` reads them.
249
+
250
+ ``scan_bundles``/``get_skill_bundles`` publish one profile's bundles in a process-wide cache keyed only by mtime,
251
+ which cron jobs in other profiles read concurrently, so the sidecar never fills it."""
252
+ out: dict[str, dict[str, Any]] = {}
253
+ for path in skill_bundles._iter_bundle_files():
254
+ info = skill_bundles._load_bundle_file(path)
255
+ if info:
256
+ out.setdefault(f"/{info['slug']}", info)
257
+ return out
258
+
259
+
260
+ def list_command_bundles() -> list[dict[str, Any]]:
261
+ """The profile's skill bundles as slash-command rows; [] when the bundle runtime is missing or fails."""
262
+ try:
263
+ import agent.skill_bundles as skill_bundles
264
+ except ImportError:
265
+ return []
266
+ try:
267
+ bundles = sorted(_profile_bundles(skill_bundles).values(), key=lambda b: b["slug"])
268
+ except Exception: # noqa: BLE001
269
+ log.warning("Failed to list skill bundles", exc_info=True)
270
+ return []
271
+ return [{"name": str(b["slug"]).strip().lower(), "description": str(b.get("description") or "").strip() or "Skill bundle",
272
+ "skill_count": len(b.get("skills") or []), "source": "bundle"} for b in bundles if str(b.get("slug") or "").strip()]
273
+
274
+
275
+ def _bundle_invocation(info: dict[str, Any], instruction: str) -> tuple[str, list[str], list[str]] | None:
276
+ """``build_bundle_invocation_message`` for a bundle read by ``_profile_bundles`` (that one looks it up in the shared cache),
277
+ skipping the skills disabled for Web chat (``skills.platform_disabled.webui``)."""
278
+ from agent.skill_commands import _disabled_skill_names, _load_skill_blocks, _load_skill_payload, _scaffold_header
279
+
280
+ name = info["name"]
281
+ loaded, missing, disabled, blocks = _load_skill_blocks(
282
+ [(skill or "").strip() for skill in info["skills"]], _load_skill_payload, lambda _skill: f'[Loaded as part of the "{name}" skill bundle.]',
283
+ None, disabled_names=_disabled_skill_names("webui"))
284
+ if not blocks:
285
+ return None
286
+ header = _scaffold_header(f'"{name}" skill bundle', loaded, lead_lines=[f"Bundle: {name}"], missing=missing, disabled=disabled,
287
+ extra_instruction=info.get("instruction") or "", user_instruction=instruction)
288
+ return "\n\n".join([header, *blocks]), loaded, missing
289
+
290
+
291
+ def resolve_bundle_command(command: str) -> dict[str, Any]:
292
+ """Expand ``/<bundle> [instruction]`` into the user message that loads the bundle's skills."""
293
+ name, instruction = parse_slash_command(command)
294
+ try:
295
+ import agent.skill_bundles as skill_bundles
296
+ from agent.skill_commands import resolve_slash_key
297
+ except ImportError as exc:
298
+ raise RpcError("Skill bundle runtime unavailable", condition="bundle_unavailable") from exc
299
+ try:
300
+ bundles = _profile_bundles(skill_bundles)
301
+ key = resolve_slash_key(name, bundles)
302
+ result = _bundle_invocation(bundles[key], instruction) if key else None
303
+ except ValueError as exc:
304
+ raise InvalidParams(str(exc)) from exc
305
+ except Exception as exc: # noqa: BLE001
306
+ log.warning("Failed to resolve skill bundle command", exc_info=True)
307
+ raise RpcError("Skill bundle command unavailable", condition="bundle_unavailable") from exc
308
+ if key is None:
309
+ raise RpcError("Bundle command not found", condition="bundle_not_found")
310
+ message, loaded, missing = result or ("", [], [])
311
+ message = str(message or "").strip()
312
+ if not message:
313
+ raise RpcError("Bundle command returned no invocation text", condition="bundle_unavailable")
314
+ return {"name": key.lstrip("/"), "source": "bundle", "message": message, "loaded_skills": [str(s) for s in loaded or []],
315
+ "missing_skills": [str(s) for s in missing or []]}
316
+
317
+
234
318
  def resolve_moa_config(preset: str | None) -> dict:
235
319
  try:
236
320
  from hermes_cli.moa_config import moa_usage, normalize_moa_config
@@ -339,6 +423,16 @@ def register(registry) -> None:
339
423
  raise RpcError(f"unknown command {parse_slash_command(command)[0]!r}", condition="command_not_found")
340
424
  return {"output": output, "source": source}
341
425
 
426
+ @registry.method("commands.bundles")
427
+ def bundles(ctx: CallContext, params: dict) -> dict:
428
+ with scoped_home(profile_home_param(params)):
429
+ return {"bundles": list_command_bundles()}
430
+
431
+ @registry.method("commands.bundle_resolve")
432
+ def bundle_resolve(ctx: CallContext, params: dict) -> dict:
433
+ with scoped_home(profile_home_param(params)):
434
+ return resolve_bundle_command(str(params.get("command") or ""))
435
+
342
436
  @registry.method("commands.moa_preset")
343
437
  def moa(ctx: CallContext, params: dict) -> dict:
344
438
  with scoped_home(profile_home_param(params)):
@@ -33,6 +33,35 @@ _RUNNING_LOCK = threading.Lock()
33
33
  _SNAPSHOT_LOCK = threading.Lock()
34
34
 
35
35
 
36
+ def _main_model_pin() -> tuple[str | None, str | None]:
37
+ """``(provider, model)`` the scoped profile's main agent runs on: the Agent's own ``cron.jobs._main_model_pin``.
38
+
39
+ Agent 0.21.3 predates that helper, so it is rebuilt from the readers 0.21.4 composes: the home's effective
40
+ ``config.yaml`` model and the provider the runtime resolves for it. ``(None, None)`` when no model is configured.
41
+ """
42
+ from cron import jobs
43
+
44
+ if hasattr(jobs, "_main_model_pin"):
45
+ return jobs._main_model_pin()
46
+ from hermes_cli.config_effective import load_user_config_effective
47
+ from hermes_constants import get_hermes_home
48
+
49
+ cfg_path = get_hermes_home() / "config.yaml"
50
+ cfg = load_user_config_effective(cfg_path) if cfg_path.exists() else {}
51
+ model_cfg = cfg.get("model") or {}
52
+ model = model_cfg.get("default") or model_cfg.get("model") if isinstance(model_cfg, dict) else model_cfg
53
+ model = model.strip() or None if isinstance(model, str) else None
54
+ if not model:
55
+ return None, None
56
+ provider = None
57
+ with contextlib.suppress(Exception):
58
+ from hermes_cli.runtime_provider import resolve_runtime_provider
59
+
60
+ resolved = resolve_runtime_provider(requested=None).get("provider")
61
+ provider = resolved.strip().lower() or None if isinstance(resolved, str) else None
62
+ return provider, model
63
+
64
+
36
65
  class CronUnavailable(RpcError):
37
66
  def __init__(self, exc: Exception):
38
67
  super().__init__(f"cron unavailable: {exc}", condition="cron_unavailable")
@@ -252,14 +281,18 @@ def _run_in_child(job, execution_home, ctx: CallContext):
252
281
  process.join(timeout=5)
253
282
  status, payload = "cancelled", ["cancelled", ""]
254
283
  break
255
- if time.monotonic() >= deadline:
256
- if process.is_alive():
257
- process.terminate()
258
- process.join(timeout=5)
259
- payload = [f"cron run subprocess produced no result within {_result_timeout(job):g}s and was terminated", ""]
260
- else:
284
+ if not process.is_alive():
285
+ # The child may have put its result just before exiting; otherwise it died (OOM, signal) without one.
286
+ try:
287
+ status, *payload = result_queue.get_nowait()
288
+ except queue.Empty:
261
289
  payload = [f"cron run subprocess exited with code {process.exitcode} without producing a result", ""]
262
290
  break
291
+ if time.monotonic() >= deadline:
292
+ process.terminate()
293
+ process.join(timeout=5)
294
+ payload = [f"cron run subprocess produced no result within {_result_timeout(job):g}s and was terminated", ""]
295
+ break
263
296
  process.join(timeout=5)
264
297
  if process.is_alive():
265
298
  process.terminate()
@@ -392,8 +425,6 @@ def register(registry) -> None:
392
425
  # to the store home; owner_profile only preserves management from the creating Web profile.
393
426
  if post.get("profile") and execution_home and not (model and provider) and not body.get("no_agent"):
394
427
  try:
395
- from cron.jobs import _main_model_pin
396
-
397
428
  with _SNAPSHOT_LOCK, scoped_home(Path(execution_home)):
398
429
  profile_provider, profile_model = _main_model_pin()
399
430
  if not profile_model:
@@ -1,4 +1,4 @@
1
- """``gateway.*``: Hermes gateway lifecycle through the ``hermes`` CLI (ported from api/gateway_restart.py)."""
1
+ """``gateway.*``: Hermes gateway lifecycle (start, stop, restart) through the ``hermes`` CLI."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -16,7 +16,10 @@ from ..home import profile_home_param
16
16
  from ..rpc import CallContext
17
17
 
18
18
  log = logging.getLogger("talaria_sidecar.gateway")
19
- _RESTART_LOCK = threading.Lock()
19
+ # One lifecycle action at a time across every action: overlapping `hermes gateway` subprocesses race each other.
20
+ _ACTION_LOCK = threading.Lock()
21
+ ACTIONS = ("start", "stop", "restart")
22
+ _DONE = {"start": "started", "stop": "stopped", "restart": "restarted"}
20
23
 
21
24
 
22
25
  def _hermes_command() -> str:
@@ -39,9 +42,11 @@ def _drain(stream, sink: list[str]) -> None:
39
42
  pass
40
43
 
41
44
 
42
- def restart(home: Path, cli_profile: str | None, ctx: CallContext, *, quick_timeout: float, background_wait: float) -> dict:
43
- if not _RESTART_LOCK.acquire(blocking=False):
44
- return {"status": "busy", "message": "Restart already in progress. Please wait a moment and try again."}
45
+ def control(action: str, home: Path, cli_profile: str | None, ctx: CallContext, *, quick_timeout: float, background_wait: float) -> dict:
46
+ if action not in ACTIONS:
47
+ raise InvalidParams("unsupported gateway action")
48
+ if not _ACTION_LOCK.acquire(blocking=False):
49
+ return {"status": "busy", "message": "Another gateway action is already in progress. Please wait a moment and try again."}
45
50
  try:
46
51
  env = dict(os.environ)
47
52
  env["HERMES_HOME"] = str(home)
@@ -49,9 +54,12 @@ def restart(home: Path, cli_profile: str | None, ctx: CallContext, *, quick_time
49
54
  cmd = [_hermes_command()]
50
55
  if cli_profile:
51
56
  cmd.extend(["--profile", cli_profile])
52
- cmd.extend(["gateway", "restart"])
53
- log.info("Restarting gateway via %s (HERMES_HOME=%s)", " ".join(cmd), home)
54
- proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, env=env)
57
+ cmd.extend(["gateway", action])
58
+ log.info("Running gateway %s via %s (HERMES_HOME=%s)", action, " ".join(cmd), home)
59
+ try:
60
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, env=env)
61
+ except FileNotFoundError:
62
+ return {"status": "failed", "message": "Hermes CLI not found"}
55
63
  # Drain both pipes from the start: a chatty restart must never block on a full pipe and be mistaken for a hang.
56
64
  out: list[str] = []
57
65
  err: list[str] = []
@@ -67,8 +75,8 @@ def restart(home: Path, cli_profile: str | None, ctx: CallContext, *, quick_time
67
75
  def outcome() -> dict:
68
76
  stdout, stderr = collected()
69
77
  if proc.returncode == 0:
70
- return {"status": "completed", "message": "Gateway service restarted successfully", "detail": stdout or stderr}
71
- return {"status": "failed", "message": f"Restart failed: {stderr or stdout}", "detail": stdout or stderr, "returncode": proc.returncode}
78
+ return {"status": "completed", "message": f"Gateway service {_DONE[action]} successfully", "detail": stdout or stderr}
79
+ return {"status": "failed", "message": f"{action.capitalize()} failed: {stderr or stdout}", "detail": stdout or stderr, "returncode": proc.returncode}
72
80
 
73
81
  ctx.emit("progress", {"phase": "started"})
74
82
  deadline = time.monotonic() + quick_timeout
@@ -90,17 +98,26 @@ def restart(home: Path, cli_profile: str | None, ctx: CallContext, *, quick_time
90
98
  proc.kill()
91
99
  proc.wait(timeout=5)
92
100
  collected()
93
- return {"status": "failed", "message": f"Gateway restart process timed out after {background_wait:.0f}s and was terminated"}
101
+ return {"status": "failed", "message": f"Gateway {action} process timed out after {background_wait:.0f}s and was terminated", "timed_out": True}
94
102
  return outcome()
95
103
  finally:
96
- _RESTART_LOCK.release()
104
+ _ACTION_LOCK.release()
97
105
 
98
106
 
99
107
  def register(registry) -> None:
100
- @registry.method("gateway.restart", requires_agent=False)
101
- def restart_(ctx: CallContext, params: dict) -> dict:
108
+ def run(ctx: CallContext, params: dict, action: str) -> dict:
102
109
  home = profile_home_param(params)
103
110
  cli_profile = params.get("cli_profile")
104
111
  if cli_profile is not None and not isinstance(cli_profile, str):
105
112
  raise InvalidParams("cli_profile must be a string")
106
- return restart(home, cli_profile or None, ctx, quick_timeout=float(params.get("quick_timeout_seconds") or 2.0), background_wait=float(params.get("background_wait_seconds") or 240.0))
113
+ # A restart drains the old gateway first, so it gets longer than start or stop.
114
+ default_wait = 240.0 if action == "restart" else 60.0
115
+ return control(action, home, cli_profile or None, ctx, quick_timeout=float(params.get("quick_timeout_seconds") or 2.0), background_wait=float(params.get("background_wait_seconds") or default_wait))
116
+
117
+ @registry.method("gateway.control", requires_agent=False)
118
+ def control_(ctx: CallContext, params: dict) -> dict:
119
+ return run(ctx, params, str(params.get("action") or ""))
120
+
121
+ @registry.method("gateway.restart", requires_agent=False)
122
+ def restart_(ctx: CallContext, params: dict) -> dict:
123
+ return run(ctx, params, "restart")
@@ -8,6 +8,7 @@ server; the sidecar receives typed params.
8
8
  from __future__ import annotations
9
9
 
10
10
  import json
11
+ import secrets
11
12
  import time
12
13
  from dataclasses import asdict, is_dataclass
13
14
  from typing import Any
@@ -82,10 +83,120 @@ def _obj_dict(value):
82
83
  return dict(getattr(value, "__dict__", {}))
83
84
 
84
85
 
85
- def _task_dict(task):
86
+ LIVE_CLAIM_MESSAGE = "task has a live worker; archive it to stop the worker"
87
+
88
+
89
+ def _row_claim_live(row) -> bool:
90
+ """The Agent's ``_claim_is_live`` for a ``tasks`` row: a running task's worker process still holds its claim.
91
+
92
+ Releasing that claim (block, a direct status change) leaves the worker running, so the card
93
+ policy withholds those writes and the write paths refuse them (TAL-557). Unknown liveness counts as live.
94
+ """
95
+ if row is None or row["status"] != "running":
96
+ return False
97
+ columns = row.keys()
98
+ claim = {k: row[k] if k in columns else None for k in ("status", "claim_lock", "worker_pid", "worker_started_at")}
99
+ claimed = bool(claim["claim_lock"] and claim["worker_pid"])
100
+ try:
101
+ helper = getattr(_kb(), "_claim_is_live", None)
102
+ except RpcError:
103
+ return claimed
104
+ if helper is not None:
105
+ try:
106
+ return bool(helper(claim))
107
+ except Exception: # noqa: BLE001 - the Agent could not decide: unknown liveness counts as live
108
+ return claimed
109
+ if not claimed:
110
+ return False
111
+ try:
112
+ # Agent 0.21.3 has no _claim_is_live, but its dispatcher's own worker PID check. Like its crash sweep, probe
113
+ # only a claim this host issued: another host's PID means nothing here, so a remote claim stays live.
114
+ from hermes_cli.kanban_db_dispatch import _pid_alive
115
+
116
+ if not str(claim["claim_lock"]).startswith(_kb()._host_prefix()):
117
+ return True
118
+ return bool(_pid_alive(int(claim["worker_pid"])))
119
+ except Exception: # noqa: BLE001
120
+ return True
121
+
122
+
123
+ def _claim_live(conn, task_id) -> bool:
124
+ return _row_claim_live(conn.execute("SELECT * FROM tasks WHERE id = ?", (task_id,)).fetchone())
125
+
126
+
127
+ RUNNING_EXIT_MESSAGE = "task is running; confirm leaving Running"
128
+
129
+
130
+ def _guard_running_exit(row, confirm_running_exit: bool) -> None:
131
+ """A write that takes a task out of Running needs the client's explicit confirmation, judged on the row the
132
+ write sees: a client's card policy can predate a dispatcher claim (TAL-557)."""
133
+ if row["status"] == "running" and not confirm_running_exit:
134
+ raise Conflict(RUNNING_EXIT_MESSAGE)
135
+
136
+
137
+ FENCE_PREFIX = "talaria-fence:"
138
+ FENCE_SECONDS = 60
139
+
140
+
141
+ def _clear_stale_fences(conn) -> None:
142
+ """Release fences whose write never finished (the sidecar died mid-write), so the dispatcher can claim again."""
143
+ now = int(time.time())
144
+ stale = []
145
+ for row in conn.execute("SELECT id, claim_lock FROM tasks WHERE claim_lock LIKE ?", (FENCE_PREFIX + "%",)).fetchall():
146
+ try:
147
+ expires = int(str(row["claim_lock"])[len(FENCE_PREFIX):].split(":", 1)[0])
148
+ except ValueError:
149
+ expires = 0
150
+ if expires < now:
151
+ stale.append((row["id"], row["claim_lock"]))
152
+ if not stale:
153
+ return
154
+ kb = _kb()
155
+ with kb.write_txn(conn):
156
+ for task_id, fence in stale:
157
+ conn.execute("UPDATE tasks SET claim_lock = NULL WHERE id = ? AND claim_lock = ?", (task_id, fence))
158
+
159
+
160
+ def _fenced(conn, task_id: str, write, *, confirm_running_exit: bool, releases_claim: bool = True):
161
+ """Run an Agent transition (block, complete, archive) with the dispatcher fenced out.
162
+
163
+ The Agent's helpers open their own write transaction, which cannot nest, so the checks and a fence lock share
164
+ one transaction first: ``claim_task`` claims only a ``ready`` task with ``claim_lock IS NULL``, so the card cannot
165
+ become a live run before the helper runs. ``releases_claim`` writes (block, complete) refuse a live worker's claim;
166
+ archive keeps it, because ``archive_task`` terminates that worker. The fence expires, so a write the sidecar never
167
+ finished stops holding the card once ``_clear_stale_fences`` next runs.
168
+ """
169
+ kb = _kb()
170
+ _clear_stale_fences(conn)
171
+ fence = f"{FENCE_PREFIX}{int(time.time()) + FENCE_SECONDS}:{secrets.token_hex(8)}"
172
+ with kb.write_txn(conn):
173
+ row = conn.execute("SELECT * FROM tasks WHERE id = ?", (task_id,)).fetchone()
174
+ if row is None:
175
+ raise NotFound("task not found")
176
+ _guard_running_exit(row, confirm_running_exit)
177
+ live = _row_claim_live(row)
178
+ if live and releases_claim:
179
+ raise Conflict(LIVE_CLAIM_MESSAGE)
180
+ # Only an unclaimed ready task is claimable; anything else needs no fence.
181
+ fenced = row["status"] == "ready" and row["claim_lock"] is None
182
+ if fenced:
183
+ conn.execute("UPDATE tasks SET claim_lock = ? WHERE id = ?", (fence, task_id))
184
+ try:
185
+ return write()
186
+ finally:
187
+ if fenced:
188
+ with kb.write_txn(conn):
189
+ conn.execute("UPDATE tasks SET claim_lock = NULL WHERE id = ? AND claim_lock = ?", (task_id, fence))
190
+
191
+
192
+ def _task_dict(task, conn):
86
193
  data = _obj_dict(task)
87
194
  if not data:
88
195
  return data
196
+ # Facts the server's card policy needs (TAL-557): the Agent completes a non-review task only with
197
+ # stored or supplied evidence, and refuses to release a live worker's claim.
198
+ data["claim_live"] = data.get("status") == "running" and _claim_live(conn, data.get("id"))
199
+ data["has_completion_evidence"] = bool(str(data.get("result") or "").strip())
89
200
  try:
90
201
  age = _kb().task_age(task)
91
202
  except Exception: # noqa: BLE001
@@ -131,12 +242,16 @@ def _validate_status(status: str) -> str:
131
242
  return value
132
243
 
133
244
 
134
- def _set_status_direct(conn, task_id: str, new_status: str) -> bool:
245
+ def _set_status_direct(conn, task_id: str, new_status: str, *, confirm_running_exit: bool = False) -> bool:
135
246
  kb = _kb()
136
247
  with kb.write_txn(conn):
137
- prev = conn.execute("SELECT status, current_run_id FROM tasks WHERE id = ?", (task_id,)).fetchone()
248
+ prev = conn.execute("SELECT * FROM tasks WHERE id = ?", (task_id,)).fetchone()
138
249
  if prev is None:
139
250
  return False
251
+ # Checked inside the write transaction, so a claim taken since the client's read is never released.
252
+ _guard_running_exit(prev, confirm_running_exit)
253
+ if _row_claim_live(prev):
254
+ raise Conflict(LIVE_CLAIM_MESSAGE)
140
255
  was_running = prev["status"] == "running"
141
256
  cur = conn.execute(
142
257
  "UPDATE tasks SET status = ?, "
@@ -202,14 +317,15 @@ def _patch_task(conn, task_id: str, body: dict) -> None:
202
317
  if "status" not in body or body.get("status") in (None, ""):
203
318
  return
204
319
  status = _validate_status(body.get("status"))
320
+ confirm = bool(body.get("confirm_running_exit"))
205
321
  if status == "done":
206
- if not kb.complete_task(conn, task_id, result=body.get("result"), summary=body.get("summary")):
322
+ if not _fenced(conn, task_id, lambda: kb.complete_task(conn, task_id, result=body.get("result"), summary=body.get("summary")), confirm_running_exit=confirm):
207
323
  raise NotFound("task not found")
208
324
  elif status == "blocked":
209
- if not kb.block_task(conn, task_id, reason=body.get("block_reason") or body.get("reason")):
325
+ if not _fenced(conn, task_id, lambda: kb.block_task(conn, task_id, reason=body.get("block_reason") or body.get("reason")), confirm_running_exit=confirm):
210
326
  raise NotFound("task not found")
211
327
  elif status == "archived":
212
- if not kb.archive_task(conn, task_id):
328
+ if not _fenced(conn, task_id, lambda: kb.archive_task(conn, task_id), confirm_running_exit=confirm, releases_claim=False):
213
329
  raise NotFound("task not found")
214
330
  elif status == "running":
215
331
  raise InvalidParams("Cannot set status to 'running' directly; use the dispatcher/claim path")
@@ -220,10 +336,10 @@ def _patch_task(conn, task_id: str, body: dict) -> None:
220
336
  if current.status == "blocked":
221
337
  if not kb.unblock_task(conn, task_id):
222
338
  raise NotFound("task not found")
223
- elif not _set_status_direct(conn, task_id, "ready"):
339
+ elif not _set_status_direct(conn, task_id, "ready", confirm_running_exit=confirm):
224
340
  raise NotFound("task not found")
225
341
  elif status in ("triage", "todo"):
226
- if not _set_status_direct(conn, task_id, status):
342
+ if not _set_status_direct(conn, task_id, status, confirm_running_exit=confirm):
227
343
  raise NotFound("task not found")
228
344
 
229
345
 
@@ -320,6 +436,7 @@ def board_payload(params: dict) -> dict:
320
436
  profile = _str(params, "profile") or "default"
321
437
  assignee = profile
322
438
  with _conn(board=board) as conn:
439
+ _clear_stale_fences(conn)
323
440
  latest = _latest_event_id(conn)
324
441
  if since is not None and since >= latest:
325
442
  return {"changed": False, "latest_event_id": latest, "read_only": False}
@@ -327,7 +444,7 @@ def board_payload(params: dict) -> dict:
327
444
  link_counts, comment_counts = _link_counts(conn, tasks), _comment_counts(conn)
328
445
 
329
446
  def row(task):
330
- data = _task_dict(task)
447
+ data = _task_dict(task, conn)
331
448
  data["link_counts"] = link_counts.get(task.id, {"parents": 0, "children": 0})
332
449
  data["comment_count"] = comment_counts.get(task.id, 0)
333
450
  return data
@@ -365,7 +482,7 @@ def create_task_payload(params: dict) -> dict:
365
482
  )
366
483
  if body.get("status"):
367
484
  _patch_task(conn, task_id, {"status": body.get("status")})
368
- return {"task": _task_dict(kb.get_task(conn, task_id)), "read_only": False}
485
+ return {"task": _task_dict(kb.get_task(conn, task_id), conn), "read_only": False}
369
486
 
370
487
 
371
488
  def patch_task_payload(params: dict) -> dict:
@@ -375,7 +492,7 @@ def patch_task_payload(params: dict) -> dict:
375
492
  kb = _kb()
376
493
  with _conn(board=_board(params)) as conn:
377
494
  _patch_task(conn, task_id, params.get("patch") or {})
378
- return {"task": _task_dict(kb.get_task(conn, task_id)), "read_only": False}
495
+ return {"task": _task_dict(kb.get_task(conn, task_id), conn), "read_only": False}
379
496
 
380
497
 
381
498
  def comment_payload(params: dict) -> dict:
@@ -418,7 +535,7 @@ def task_detail_payload(params: dict) -> dict:
418
535
  if not task:
419
536
  raise NotFound("task not found")
420
537
  return {
421
- "task": _task_dict(task),
538
+ "task": _task_dict(task, conn),
422
539
  "comments": [_obj_dict(c) for c in kb.list_comments(conn, task_id)],
423
540
  "events": [_obj_dict(e) for e in kb.list_events(conn, task_id)],
424
541
  "links": {"parents": kb.parent_ids(conn, task_id), "children": kb.child_ids(conn, task_id)},
@@ -533,7 +650,8 @@ def bulk_payload(params: dict) -> dict:
533
650
  results.append(entry)
534
651
  continue
535
652
  if body.get("archive"):
536
- if not kb.archive_task(conn, task_id):
653
+ # Bulk carries no running-exit confirmation, so a running row is refused like a card write.
654
+ if not _fenced(conn, task_id, lambda: kb.archive_task(conn, task_id), confirm_running_exit=False, releases_claim=False):
537
655
  entry.update(ok=False, error="archive refused")
538
656
  elif body.get("status") is not None:
539
657
  _patch_task(conn, task_id, {"status": body.get("status")})
@@ -555,11 +673,14 @@ def bulk_payload(params: dict) -> dict:
555
673
 
556
674
 
557
675
  def dispatch_payload(params: dict) -> dict:
558
- kb = _kb()
559
- if not hasattr(kb, "dispatch_once"):
560
- raise InvalidParams("dispatcher is unavailable")
676
+ _kb()
677
+ try:
678
+ # Agent 0.21.6 dropped the kanban_db re-export; every supported release defines it here.
679
+ from hermes_cli.kanban_db_dispatch import dispatch_once
680
+ except Exception as exc: # noqa: BLE001
681
+ raise InvalidParams("dispatcher is unavailable") from exc
561
682
  with _conn(board=_board(params)) as conn:
562
- result = kb.dispatch_once(conn, dry_run=_bool(params, "dry_run"), max_spawn=_int(params, "max", 8, minimum=1, maximum=100))
683
+ result = dispatch_once(conn, dry_run=_bool(params, "dry_run"), max_spawn=_int(params, "max", 8, minimum=1, maximum=100))
563
684
  if isinstance(result, dict):
564
685
  return result
565
686
  try:
@@ -578,7 +699,7 @@ def task_action_payload(params: dict) -> dict:
578
699
  if not kb.get_task(conn, task_id):
579
700
  raise NotFound("task not found")
580
701
  if action == "block":
581
- ok = kb.block_task(conn, task_id, reason=params.get("reason"))
702
+ ok = _fenced(conn, task_id, lambda: kb.block_task(conn, task_id, reason=params.get("reason")), confirm_running_exit=bool(params.get("confirm_running_exit")))
582
703
  elif action == "unblock":
583
704
  if hasattr(kb, "unblock_task"):
584
705
  ok = kb.unblock_task(conn, task_id)
@@ -589,7 +710,7 @@ def task_action_payload(params: dict) -> dict:
589
710
  raise InvalidParams(f"invalid action: {action}")
590
711
  if not ok:
591
712
  raise Conflict(f"{action} refused")
592
- return {"task": _task_dict(kb.get_task(conn, task_id)), "read_only": False}
713
+ return {"task": _task_dict(kb.get_task(conn, task_id), conn), "read_only": False}
593
714
 
594
715
 
595
716
  def list_boards_payload(params: dict) -> dict: