@maudecode/talaria-web 1.15.0 → 1.17.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 (386) hide show
  1. package/_release.json +4 -4
  2. package/dist/api/automation-raw.js +6 -3
  3. package/dist/api/automation-raw.js.map +1 -1
  4. package/dist/api/automation-router.d.ts +158 -16
  5. package/dist/api/automation-router.js +12 -6
  6. package/dist/api/automation-router.js.map +1 -1
  7. package/dist/api/chat-router.d.ts +193 -6
  8. package/dist/api/chat-router.js +39 -16
  9. package/dist/api/chat-router.js.map +1 -1
  10. package/dist/api/git-router.d.ts +12 -1
  11. package/dist/api/git-router.js +60 -38
  12. package/dist/api/git-router.js.map +1 -1
  13. package/dist/api/raw-routes.d.ts +6 -0
  14. package/dist/api/raw-routes.js +35 -58
  15. package/dist/api/raw-routes.js.map +1 -1
  16. package/dist/api/router.d.ts +4 -2
  17. package/dist/api/router.js +8 -8
  18. package/dist/api/router.js.map +1 -1
  19. package/dist/api/sessions-router.d.ts +974 -42
  20. package/dist/api/sessions-router.js +87 -25
  21. package/dist/api/sessions-router.js.map +1 -1
  22. package/dist/api/settings-router.d.ts +254 -11
  23. package/dist/api/settings-router.js +68 -18
  24. package/dist/api/settings-router.js.map +1 -1
  25. package/dist/api/sse-routes.d.ts +11 -3
  26. package/dist/api/sse-routes.js +145 -65
  27. package/dist/api/sse-routes.js.map +1 -1
  28. package/dist/api/tools-raw.js +6 -2
  29. package/dist/api/tools-raw.js.map +1 -1
  30. package/dist/api/tools-router.d.ts +86 -9
  31. package/dist/api/tools-router.js +19 -15
  32. package/dist/api/tools-router.js.map +1 -1
  33. package/dist/app.d.ts +0 -8397
  34. package/dist/app.js +7 -2
  35. package/dist/app.js.map +1 -1
  36. package/dist/auth/gate.js +11 -4
  37. package/dist/auth/gate.js.map +1 -1
  38. package/dist/auth/oidc.d.ts +6 -2
  39. package/dist/auth/oidc.js +16 -11
  40. package/dist/auth/oidc.js.map +1 -1
  41. package/dist/auth/store.d.ts +14 -2
  42. package/dist/auth/store.js +26 -1
  43. package/dist/auth/store.js.map +1 -1
  44. package/dist/cli/dotenv.d.ts +1 -1
  45. package/dist/cli/dotenv.js +36 -3
  46. package/dist/cli/dotenv.js.map +1 -1
  47. package/dist/config/agent-config.d.ts +2 -0
  48. package/dist/config/agent-config.js +9 -0
  49. package/dist/config/agent-config.js.map +1 -1
  50. package/dist/http/addresses.d.ts +6 -0
  51. package/dist/http/addresses.js +26 -0
  52. package/dist/http/addresses.js.map +1 -1
  53. package/dist/http/context.d.ts +6 -3
  54. package/dist/http/context.js +10 -4
  55. package/dist/http/context.js.map +1 -1
  56. package/dist/links.d.ts +26 -0
  57. package/dist/links.js +60 -0
  58. package/dist/links.js.map +1 -0
  59. package/dist/onboarding.js +30 -7
  60. package/dist/onboarding.js.map +1 -1
  61. package/dist/profiles/profiles.js +1 -1
  62. package/dist/profiles/profiles.js.map +1 -1
  63. package/dist/providers/catalog.d.ts +167 -5
  64. package/dist/providers/catalog.js +662 -135
  65. package/dist/providers/catalog.js.map +1 -1
  66. package/dist/providers/tables.d.ts +3 -1
  67. package/dist/providers/tables.js +9 -4
  68. package/dist/providers/tables.js.map +1 -1
  69. package/dist/redact.d.ts +1 -0
  70. package/dist/redact.js +518 -15
  71. package/dist/redact.js.map +1 -1
  72. package/dist/runtime.js +79 -37
  73. package/dist/runtime.js.map +1 -1
  74. package/dist/sessions/anchor.d.ts +24 -3
  75. package/dist/sessions/anchor.js +94 -44
  76. package/dist/sessions/anchor.js.map +1 -1
  77. package/dist/sessions/background-tasks.d.ts +4 -1
  78. package/dist/sessions/background-tasks.js +7 -4
  79. package/dist/sessions/background-tasks.js.map +1 -1
  80. package/dist/sessions/claude-code.d.ts +19 -0
  81. package/dist/sessions/claude-code.js +250 -0
  82. package/dist/sessions/claude-code.js.map +1 -0
  83. package/dist/sessions/completions.d.ts +13 -1
  84. package/dist/sessions/completions.js +64 -12
  85. package/dist/sessions/completions.js.map +1 -1
  86. package/dist/sessions/compress.d.ts +14 -1
  87. package/dist/sessions/compress.js +52 -11
  88. package/dist/sessions/compress.js.map +1 -1
  89. package/dist/sessions/journal.d.ts +8 -0
  90. package/dist/sessions/journal.js +44 -10
  91. package/dist/sessions/journal.js.map +1 -1
  92. package/dist/sessions/list.d.ts +13 -5
  93. package/dist/sessions/list.js +56 -26
  94. package/dist/sessions/list.js.map +1 -1
  95. package/dist/sessions/merge.d.ts +122 -9
  96. package/dist/sessions/merge.js +544 -33
  97. package/dist/sessions/merge.js.map +1 -1
  98. package/dist/sessions/pending.d.ts +5 -0
  99. package/dist/sessions/pending.js +30 -10
  100. package/dist/sessions/pending.js.map +1 -1
  101. package/dist/sessions/service.d.ts +100 -4
  102. package/dist/sessions/service.js +567 -102
  103. package/dist/sessions/service.js.map +1 -1
  104. package/dist/sessions/session.d.ts +16 -1
  105. package/dist/sessions/session.js +29 -2
  106. package/dist/sessions/session.js.map +1 -1
  107. package/dist/sessions/shares.js +11 -2
  108. package/dist/sessions/shares.js.map +1 -1
  109. package/dist/sessions/state-db.d.ts +26 -4
  110. package/dist/sessions/state-db.js +126 -8
  111. package/dist/sessions/state-db.js.map +1 -1
  112. package/dist/sessions/store.js +6 -1
  113. package/dist/sessions/store.js.map +1 -1
  114. package/dist/sessions/streams.d.ts +10 -0
  115. package/dist/sessions/streams.js +14 -3
  116. package/dist/sessions/streams.js.map +1 -1
  117. package/dist/sessions/todo.d.ts +6 -1
  118. package/dist/sessions/todo.js +11 -4
  119. package/dist/sessions/todo.js.map +1 -1
  120. package/dist/sessions/tool-display.d.ts +12 -1
  121. package/dist/sessions/tool-display.js +76 -1
  122. package/dist/sessions/tool-display.js.map +1 -1
  123. package/dist/sessions/turn.d.ts +49 -10
  124. package/dist/sessions/turn.js +459 -185
  125. package/dist/sessions/turn.js.map +1 -1
  126. package/dist/settings.d.ts +20 -2
  127. package/dist/settings.js +49 -2
  128. package/dist/settings.js.map +1 -1
  129. package/dist/sidecar/fake.d.ts +2 -2
  130. package/dist/sidecar/fake.js +7 -2
  131. package/dist/sidecar/fake.js.map +1 -1
  132. package/dist/spa.js +2 -1
  133. package/dist/spa.js.map +1 -1
  134. package/dist/test/harness.js +2 -0
  135. package/dist/test/harness.js.map +1 -1
  136. package/dist/tools/commands.d.ts +15 -0
  137. package/dist/tools/commands.js +52 -0
  138. package/dist/tools/commands.js.map +1 -0
  139. package/dist/tools/crons.d.ts +4 -1
  140. package/dist/tools/crons.js +58 -11
  141. package/dist/tools/crons.js.map +1 -1
  142. package/dist/tools/insights.d.ts +8 -2
  143. package/dist/tools/insights.js +18 -2
  144. package/dist/tools/insights.js.map +1 -1
  145. package/dist/tools/kanban.d.ts +41 -1
  146. package/dist/tools/kanban.js +113 -2
  147. package/dist/tools/kanban.js.map +1 -1
  148. package/dist/tools/mcp.d.ts +2 -1
  149. package/dist/tools/mcp.js +16 -6
  150. package/dist/tools/mcp.js.map +1 -1
  151. package/dist/tools/terminal.d.ts +1 -0
  152. package/dist/tools/terminal.js +1 -1
  153. package/dist/tools/terminal.js.map +1 -1
  154. package/dist/tools/update-notifications.d.ts +1 -1
  155. package/dist/tools/update-notifications.js +1 -1
  156. package/dist/tools/update-notifications.js.map +1 -1
  157. package/dist/tools/updates.d.ts +15 -3
  158. package/dist/tools/updates.js +207 -41
  159. package/dist/tools/updates.js.map +1 -1
  160. package/dist/workspace/fs.d.ts +7 -3
  161. package/dist/workspace/fs.js +5 -1
  162. package/dist/workspace/fs.js.map +1 -1
  163. package/dist/workspace/git.d.ts +57 -10
  164. package/dist/workspace/git.js +302 -195
  165. package/dist/workspace/git.js.map +1 -1
  166. package/dist/workspace/media-refs.d.ts +13 -0
  167. package/dist/workspace/media-refs.js +165 -0
  168. package/dist/workspace/media-refs.js.map +1 -0
  169. package/dist/workspace/media.d.ts +19 -0
  170. package/dist/workspace/media.js +63 -2
  171. package/dist/workspace/media.js.map +1 -1
  172. package/dist/workspace/preview.d.ts +28 -0
  173. package/dist/workspace/preview.js +110 -0
  174. package/dist/workspace/preview.js.map +1 -0
  175. package/dist/workspace/rollback.d.ts +5 -4
  176. package/dist/workspace/rollback.js +12 -9
  177. package/dist/workspace/rollback.js.map +1 -1
  178. package/dist/workspace/upload.d.ts +2 -0
  179. package/dist/workspace/upload.js +4 -1
  180. package/dist/workspace/upload.js.map +1 -1
  181. package/dist/workspace/workspaces.d.ts +7 -1
  182. package/dist/workspace/workspaces.js +30 -21
  183. package/dist/workspace/workspaces.js.map +1 -1
  184. package/dist/workspace/worktrees.d.ts +2 -4
  185. package/dist/workspace/worktrees.js +28 -33
  186. package/dist/workspace/worktrees.js.map +1 -1
  187. package/package.json +4 -2
  188. package/sidecar/scripts/replay_sidecar.py +4 -2
  189. package/sidecar/talaria_sidecar/__init__.py +1 -1
  190. package/sidecar/talaria_sidecar/__main__.py +7 -1
  191. package/sidecar/talaria_sidecar/home.py +98 -2
  192. package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
  193. package/sidecar/talaria_sidecar/methods/aux.py +19 -3
  194. package/sidecar/talaria_sidecar/methods/chat.py +551 -61
  195. package/sidecar/talaria_sidecar/methods/commands.py +61 -33
  196. package/sidecar/talaria_sidecar/methods/config.py +14 -10
  197. package/sidecar/talaria_sidecar/methods/cron.py +10 -6
  198. package/sidecar/talaria_sidecar/methods/kanban.py +115 -15
  199. package/sidecar/talaria_sidecar/methods/oauth.py +439 -0
  200. package/sidecar/talaria_sidecar/methods/process.py +60 -1
  201. package/sidecar/talaria_sidecar/methods/profiles.py +12 -5
  202. package/sidecar/talaria_sidecar/methods/providers.py +161 -1
  203. package/sidecar/talaria_sidecar/methods/runtime.py +5 -0
  204. package/sidecar/talaria_sidecar/methods/text.py +18 -21
  205. package/sidecar/talaria_sidecar/methods/usage.py +264 -5
  206. package/static/dist/FILES.txt +126 -121
  207. package/static/dist/assets/AppShell-fb2td9zi.js +1 -0
  208. package/static/dist/assets/ChatPage-cj96kiy9.js +25 -0
  209. package/static/dist/assets/Dialog-kxcg7ard.js +1 -0
  210. package/static/dist/assets/ExtensionRoute-jf7h0754.js +1 -0
  211. package/static/dist/assets/HubRoute-gmanpd5y.js +2 -0
  212. package/static/dist/assets/{Markdown-dy8t4dy7.js → Markdown-mdwlvj3w.js} +125 -125
  213. package/static/dist/assets/RightPanel-ftvrjlcp.js +1 -0
  214. package/static/dist/assets/Select-bilaypej.js +1 -0
  215. package/static/dist/assets/SessionListPanel-og8n36qs.js +1 -0
  216. package/static/dist/assets/SettingsLayout-jonjefmj.js +1 -0
  217. package/static/dist/assets/States-nmejrwrm.js +1 -0
  218. package/static/dist/assets/Toaster-nnl8zkt9.js +1 -0
  219. package/static/dist/assets/_app-gdidzd94.js +1 -0
  220. package/static/dist/assets/_app.ext._extensionId-opyh9y0v.js +1 -0
  221. package/static/dist/assets/_app.index-hbarzbjx.js +1 -0
  222. package/static/dist/assets/_app.insights-luywlarz.js +1 -0
  223. package/static/dist/assets/_app.kanban-kincnbr8.js +1 -0
  224. package/static/dist/assets/_app.logs-oi2gr3gs.js +1 -0
  225. package/static/dist/assets/_app.memory-b6jfivt6.js +1 -0
  226. package/static/dist/assets/_app.profiles-lpvixmqp.js +1 -0
  227. package/static/dist/assets/{_app.session._sessionId-gyoh5ylx.js → _app.session._sessionId-bfd4h3id.js} +1 -1
  228. package/static/dist/assets/_app.settings-vowx3iq0.js +1 -0
  229. package/static/dist/assets/_app.settings._section-cnntg1tu.js +3 -0
  230. package/static/dist/assets/_app.skills-fb57lpau.js +1 -0
  231. package/static/dist/assets/_app.tasks-eszpx1q7.js +2 -0
  232. package/static/dist/assets/_app.todos-nr05ac6w.js +1 -0
  233. package/static/dist/assets/_app.workspaces-d2luomu6.js +1 -0
  234. package/static/dist/assets/{abnfDiagram-VCTEODGH-cjqletra.js → abnfDiagram-VCTEODGH-b2x1lqot.js} +1 -1
  235. package/static/dist/assets/architecture-7GRP2DOG-hd0o7bde.js +1 -0
  236. package/static/dist/assets/architectureDiagram-5GKGNRK7-h38o5l7b.js +36 -0
  237. package/static/dist/assets/{back-e4m6nsnm.js → back-bemaer09.js} +1 -1
  238. package/static/dist/assets/{blockDiagram-I7D4REHJ-b195z021.js → blockDiagram-I7D4REHJ-bqquxv2u.js} +1 -1
  239. package/static/dist/assets/{c4Diagram-7LVT6UL2-olu212k1.js → c4Diagram-7LVT6UL2-f3fb8w6z.js} +1 -1
  240. package/static/dist/assets/cancel-drd0g6d9.js +1 -0
  241. package/static/dist/assets/channel-cpdgf867.js +1 -0
  242. package/static/dist/assets/{chunk-4HAMMTFA-b8o1nufs.js → chunk-4HAMMTFA-bzfyyzp3.js} +1 -1
  243. package/static/dist/assets/{chunk-75Z2AOVW-l1seddto.js → chunk-75Z2AOVW-ly99rfx5.js} +1 -1
  244. package/static/dist/assets/{chunk-DU6HZSFF-lo5zg15v.js → chunk-DU6HZSFF-bg6ecfqf.js} +3 -3
  245. package/static/dist/assets/{chunk-F27PBJKO-dsdzrp16.js → chunk-F27PBJKO-e8k7mmqw.js} +1 -1
  246. package/static/dist/assets/{chunk-GMAD6QVW-f3fxqq8b.js → chunk-GMAD6QVW-cmalnoaj.js} +1 -1
  247. package/static/dist/assets/{chunk-GVQU2GXP-h8egxtf6.js → chunk-GVQU2GXP-gmif5o01.js} +1 -1
  248. package/static/dist/assets/{chunk-IMKFNOWR-iolwd8s0.js → chunk-IMKFNOWR-f1nwwh6u.js} +1 -1
  249. package/static/dist/assets/{chunk-L3NEJ4N5-b88bbtwi.js → chunk-L3NEJ4N5-oktfh5v7.js} +1 -1
  250. package/static/dist/assets/{chunk-OSK3NFVY-e3e7nr6s.js → chunk-OSK3NFVY-b1sdx96z.js} +1 -1
  251. package/static/dist/assets/{chunk-P2QGCYS3-mdc46rt1.js → chunk-P2QGCYS3-fhu4nzxt.js} +1 -1
  252. package/static/dist/assets/{chunk-POPQ4Y6H-kqlllrrm.js → chunk-POPQ4Y6H-em31rnef.js} +1 -1
  253. package/static/dist/assets/{chunk-PWAF6VOD-he585k2x.js → chunk-PWAF6VOD-bxhhyftw.js} +1 -1
  254. package/static/dist/assets/{chunk-SHT3W25Y-gh94y9a4.js → chunk-SHT3W25Y-fmrvwcav.js} +3 -3
  255. package/static/dist/assets/{chunk-SVP7TREG-euxp9y7e.js → chunk-SVP7TREG-lymukl2j.js} +1 -1
  256. package/static/dist/assets/{chunk-TICWLB2K-c8cxes1q.js → chunk-TICWLB2K-lmza6k23.js} +1 -1
  257. package/static/dist/assets/classDiagram-ZZMXUADV-ko09sguk.js +1 -0
  258. package/static/dist/assets/classDiagram-v2-VYDZK3BY-ko09sguk.js +1 -0
  259. package/static/dist/assets/{copied-kq0u3ht7.js → copied-ima3pysb.js} +1 -1
  260. package/static/dist/assets/copy-fw5b1pc6.js +1 -0
  261. package/static/dist/assets/{cynefin-OW5HDTMX-lxd44ux4.js → cynefin-OW5HDTMX-czlhbyhs.js} +1 -1
  262. package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-njpiwat8.js → cynefinDiagram-5FMLGOSQ-gq7mvswb.js} +1 -1
  263. package/static/dist/assets/{dagre-GXQ25YYZ-nvgy6yju.js → dagre-GXQ25YYZ-cqdbf7dr.js} +4 -4
  264. package/static/dist/assets/{diagram-S7CK7UJ4-g98nyv72.js → diagram-S7CK7UJ4-o6arxa58.js} +3 -3
  265. package/static/dist/assets/{diagram-UQ7AKVKN-ovgm6lvv.js → diagram-UQ7AKVKN-nhnd68tz.js} +1 -1
  266. package/static/dist/assets/{diagram-VSXAHHWV-pb2bn2e4.js → diagram-VSXAHHWV-dk2sclzn.js} +1 -1
  267. package/static/dist/assets/{diagram-VX7I27RA-6j4mvfqo.js → diagram-VX7I27RA-n3k5nyx3.js} +1 -1
  268. package/static/dist/assets/{diagram-Z3DM3KII-dqmb982r.js → diagram-Z3DM3KII-m6q9cwdh.js} +1 -1
  269. package/static/dist/assets/{ebnfDiagram-PWID7BFC-ozbpzb6d.js → ebnfDiagram-PWID7BFC-lsa5641p.js} +1 -1
  270. package/static/dist/assets/endpoints-d33siokx.js +64 -0
  271. package/static/dist/assets/{erDiagram-RLTQ6QDP-hl7m8vua.js → erDiagram-RLTQ6QDP-fo2ckbe6.js} +1 -1
  272. package/static/dist/assets/eventmodeling-NTZA5JFV-emctlo64.js +1 -0
  273. package/static/dist/assets/flowDiagram-HODETNUW-bfi3moqa.js +1 -0
  274. package/static/dist/assets/{ganttDiagram-EL5Y4UJY-ompixqmh.js → ganttDiagram-EL5Y4UJY-nzqz6x9g.js} +1 -1
  275. package/static/dist/assets/{gitGraph-4MIJSDKK-b6vxwp9q.js → gitGraph-4MIJSDKK-gukvcxe6.js} +1 -1
  276. package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-k98f57on.js → gitGraphDiagram-WWUBYQGX-lqhdp8f0.js} +1 -1
  277. package/static/dist/assets/{highlighted-body-KPVGNVTW-cd3ff6ge.js → highlighted-body-KPVGNVTW-igw6cp39.js} +1 -1
  278. package/static/dist/assets/index-cxq58xb1.js +11 -0
  279. package/static/dist/assets/index-hgtx08n5.css +2 -0
  280. package/static/dist/assets/{info-A6RAGUB7-hjcwjami.js → info-A6RAGUB7-gvu7a93l.js} +1 -1
  281. package/static/dist/assets/infoDiagram-27XIBGKW-m8t8g6w1.js +2 -0
  282. package/static/dist/assets/{ishikawaDiagram-5VMMS53U-bxak4mwu.js → ishikawaDiagram-5VMMS53U-l07vkbgj.js} +1 -1
  283. package/static/dist/assets/{journeyDiagram-3NMN7TZE-khmihclr.js → journeyDiagram-3NMN7TZE-hybkgj8l.js} +1 -1
  284. package/static/dist/assets/{kanban-definition-UXKFOSKX-h1r32wdx.js → kanban-definition-UXKFOSKX-by13uvtn.js} +1 -1
  285. package/static/dist/assets/{lazyRouteComponent-b2eg0fws.js → lazyRouteComponent-af0lvgjx.js} +1 -1
  286. package/static/dist/assets/{line-friyjof3.js → line-osxq6fq7.js} +1 -1
  287. package/static/dist/assets/link-jyxicdhg.js +1 -0
  288. package/static/dist/assets/login-ec21t39x.js +1 -0
  289. package/static/dist/assets/mermaid-HWGCJPDP-gfl8z3ep.js +1 -0
  290. package/static/dist/assets/{mermaid-parser.core-f1gvpwrt.js → mermaid-parser.core-i91oxmou.js} +3 -3
  291. package/static/dist/assets/{mindmap-definition-YA3MSWOX-mt2oxwue.js → mindmap-definition-YA3MSWOX-m82mx8ww.js} +1 -1
  292. package/static/dist/assets/onboarding-btnltvyz.js +1 -0
  293. package/static/dist/assets/onboarding_model_label-dp7xffoh.js +1 -0
  294. package/static/dist/assets/{packet-AYTQ26CC-jik4ixlf.js → packet-AYTQ26CC-nbtgge2o.js} +1 -1
  295. package/static/dist/assets/{pegDiagram-XKGWAZYB-erxlv6cc.js → pegDiagram-XKGWAZYB-f1zxzga1.js} +1 -1
  296. package/static/dist/assets/{pie-WAS4IAKB-mnkoldfl.js → pie-WAS4IAKB-of8vuhxs.js} +1 -1
  297. package/static/dist/assets/{pieDiagram-E7YTZNPT-i7lwrada.js → pieDiagram-E7YTZNPT-o7y8ftio.js} +3 -3
  298. package/static/dist/assets/{quadrantDiagram-AXDQQJYC-fr8ejdoy.js → quadrantDiagram-AXDQQJYC-iqiwia1t.js} +1 -1
  299. package/static/dist/assets/{radar-RG4KPBEZ-otarjte2.js → radar-RG4KPBEZ-delnsy2v.js} +1 -1
  300. package/static/dist/assets/{railroad-74A4TZTK-lw362oto.js → railroad-74A4TZTK-nspnyq9u.js} +1 -1
  301. package/static/dist/assets/railroad-abnf-HS5TGJTU-nst1qxyn.js +1 -0
  302. package/static/dist/assets/railroad-ebnf-LZEXJU2U-ci37x8qy.js +1 -0
  303. package/static/dist/assets/railroad-peg-WCYAUIDC-aimkk07z.js +1 -0
  304. package/static/dist/assets/{railroadDiagram-O6MQD6OU-esyn75pw.js → railroadDiagram-O6MQD6OU-z41tqx5z.js} +1 -1
  305. package/static/dist/assets/refresh-b4uams1i.js +1 -0
  306. package/static/dist/assets/refresh-cw-nt1dtsx4.js +1 -0
  307. package/static/dist/assets/{requirementDiagram-BXWQKSXE-cyhi5u29.js → requirementDiagram-BXWQKSXE-bdvif194.js} +1 -1
  308. package/static/dist/assets/{sankeyDiagram-P5KCCOFB-jljpbo0h.js → sankeyDiagram-P5KCCOFB-h73e0rol.js} +1 -1
  309. package/static/dist/assets/{saved-do49si3v.js → saved-kg52csy7.js} +1 -1
  310. package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-fz0dhlu7.js → sequenceDiagram-WJ2MYXX4-iv95mb1z.js} +1 -1
  311. package/static/dist/assets/{settings_section_extensions_title-b7lojn0o.js → settings_section_extensions_title-ej0iapxe.js} +1 -1
  312. package/static/dist/assets/share._token-f97e94r0.js +2 -0
  313. package/static/dist/assets/sliders-horizontal-fj18it47.js +1 -0
  314. package/static/dist/assets/sse-fwpqtzbg.js +1 -0
  315. package/static/dist/assets/{stateDiagram-D77RDMKH-kdnithdx.js → stateDiagram-D77RDMKH-k417ixmf.js} +1 -1
  316. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-hwn4apct.js +1 -0
  317. package/static/dist/assets/{swimlanes-42K2YHIH-esd6gmz3.js → swimlanes-42K2YHIH-esola70w.js} +1 -1
  318. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-d1h97oyg.js +8 -0
  319. package/static/dist/assets/{timeline-definition-24CTP7MA-g0qygccv.js → timeline-definition-24CTP7MA-getrz2y8.js} +1 -1
  320. package/static/dist/assets/todoStore-eginf9h4.js +1 -0
  321. package/static/dist/assets/{treeView-Q6P3EWNA-ndchzkuu.js → treeView-Q6P3EWNA-hc7ap1q7.js} +1 -1
  322. package/static/dist/assets/{treemap-WGGIJYW6-hwjxj0zz.js → treemap-WGGIJYW6-b7k1syy5.js} +1 -1
  323. package/static/dist/assets/tts-lxdxnmjd.js +1 -0
  324. package/static/dist/assets/{untitled-mbr6tuhu.js → untitled-ovpfthg4.js} +1 -1
  325. package/static/dist/assets/useForm-bcblv1rx.js +1 -0
  326. package/static/dist/assets/useLocale-fi5jb8ma.js +1 -0
  327. package/static/dist/assets/{useParams-lo9u33d1.js → useParams-lhg566ew.js} +1 -1
  328. package/static/dist/assets/{useSearch-lq1mr090.js → useSearch-lbrh6ejn.js} +1 -1
  329. package/static/dist/assets/{vennDiagram-4TSXK5OY-gubovd1r.js → vennDiagram-4TSXK5OY-n6pcy5qb.js} +1 -1
  330. package/static/dist/assets/{wardley-WFR3VGLG-ir3om65j.js → wardley-WFR3VGLG-lpa18vz1.js} +1 -1
  331. package/static/dist/assets/{wardleyDiagram-VM6X3IG4-ce0or1ie.js → wardleyDiagram-VM6X3IG4-kpclwwg7.js} +2 -2
  332. package/static/dist/assets/{xychartDiagram-S5SC5T6Z-dexylcjg.js → xychartDiagram-S5SC5T6Z-paa2n8y0.js} +1 -1
  333. package/static/dist/index.html +1 -1
  334. package/static/dist/sw.js +2 -2
  335. package/static/dist/assets/AppShell-orat1s43.js +0 -1
  336. package/static/dist/assets/ChatPage-ojhka3hy.js +0 -22
  337. package/static/dist/assets/HubRoute-gxgw1t33.js +0 -2
  338. package/static/dist/assets/RightPanel-c66ani1s.js +0 -1
  339. package/static/dist/assets/Select-fswf66up.js +0 -1
  340. package/static/dist/assets/SessionListPanel-goh5mtt8.js +0 -1
  341. package/static/dist/assets/SettingsLayout-n5d4wq4m.js +0 -1
  342. package/static/dist/assets/States-kxyu50at.js +0 -1
  343. package/static/dist/assets/Toaster-6zmbcqgg.js +0 -1
  344. package/static/dist/assets/_app-fowyruiu.js +0 -1
  345. package/static/dist/assets/_app.ext._extensionId-nvjyrwhy.js +0 -1
  346. package/static/dist/assets/_app.index-c9zv4y7w.js +0 -1
  347. package/static/dist/assets/_app.insights-jf12nofo.js +0 -1
  348. package/static/dist/assets/_app.kanban-lri5a4yu.js +0 -1
  349. package/static/dist/assets/_app.logs-md1p0zqo.js +0 -1
  350. package/static/dist/assets/_app.memory-bzvf2e28.js +0 -1
  351. package/static/dist/assets/_app.profiles-jpg7zmbn.js +0 -1
  352. package/static/dist/assets/_app.settings-mpuwlx4j.js +0 -1
  353. package/static/dist/assets/_app.settings._section-knz8rwoj.js +0 -1
  354. package/static/dist/assets/_app.skills-fwu1dkir.js +0 -1
  355. package/static/dist/assets/_app.tasks-h9gphtl3.js +0 -2
  356. package/static/dist/assets/_app.todos-hd1dnvrh.js +0 -1
  357. package/static/dist/assets/_app.workspaces-l8toyn5f.js +0 -1
  358. package/static/dist/assets/architecture-7GRP2DOG-cavnusaw.js +0 -1
  359. package/static/dist/assets/architectureDiagram-5GKGNRK7-k5a7v36n.js +0 -36
  360. package/static/dist/assets/channel-gozy3dp3.js +0 -1
  361. package/static/dist/assets/classDiagram-ZZMXUADV-n0vs2f2q.js +0 -1
  362. package/static/dist/assets/classDiagram-v2-VYDZK3BY-n0vs2f2q.js +0 -1
  363. package/static/dist/assets/endpoints-k8agnro8.js +0 -9
  364. package/static/dist/assets/eventmodeling-NTZA5JFV-j1y2ekec.js +0 -1
  365. package/static/dist/assets/flowDiagram-HODETNUW-g2cvcbm5.js +0 -1
  366. package/static/dist/assets/index-nfy0ezs6.css +0 -2
  367. package/static/dist/assets/index-nqed3oum.js +0 -11
  368. package/static/dist/assets/infoDiagram-27XIBGKW-fm06rj36.js +0 -2
  369. package/static/dist/assets/link-m90ahx3n.js +0 -1
  370. package/static/dist/assets/login-bw7m2fzo.js +0 -1
  371. package/static/dist/assets/matchContext-f8oy3lo7.js +0 -1
  372. package/static/dist/assets/mermaid-HWGCJPDP-njwcl62n.js +0 -1
  373. package/static/dist/assets/onboarding-ret37j1e.js +0 -1
  374. package/static/dist/assets/railroad-abnf-HS5TGJTU-m741owar.js +0 -1
  375. package/static/dist/assets/railroad-ebnf-LZEXJU2U-noa4fbia.js +0 -1
  376. package/static/dist/assets/railroad-peg-WCYAUIDC-o4bpw4nb.js +0 -1
  377. package/static/dist/assets/refresh-cw-k66keho6.js +0 -1
  378. package/static/dist/assets/search-j5ki6a9o.js +0 -1
  379. package/static/dist/assets/share._token-kg8w647b.js +0 -2
  380. package/static/dist/assets/sliders-horizontal-c4wya4dy.js +0 -1
  381. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-k2wg2645.js +0 -1
  382. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-n5kvjtgr.js +0 -8
  383. package/static/dist/assets/tabBuild-bjmyvmn5.js +0 -56
  384. package/static/dist/assets/todoStore-kjk5l0sy.js +0 -1
  385. package/static/dist/assets/useForm-ocxu460f.js +0 -1
  386. package/static/dist/assets/useLocale-fzy5jhg6.js +0 -1
@@ -7,14 +7,22 @@ import threading
7
7
  from typing import Any
8
8
 
9
9
  from ..errors import InvalidParams, RpcError
10
- from ..home import profile_home_param, scoped_home
10
+ from ..home import _is_named_profile, profile_home_param, scoped_home
11
11
  from ..rpc import CallContext
12
+ from .providers import plugin_providers
12
13
 
13
14
  log = logging.getLogger("talaria_sidecar.commands")
14
15
 
15
16
  _NEVER_EXPOSE = frozenset({"sethome", "restart", "update", "commands"})
16
17
  _ALIASES = {"reload_mcp": "reload-mcp", "reload_skills": "reload-skills", "codex_runtime": "codex-runtime"}
17
- _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)
18
26
  _RELOAD_MCP_LOCK = threading.Lock()
19
27
  _RELOAD_SKILLS_LOCK = threading.Lock()
20
28
  _CODEX_RUNTIME_LOCK = threading.Lock()
@@ -45,7 +53,13 @@ def list_commands() -> list[dict[str, Any]]:
45
53
  out.append({
46
54
  "name": cmd.name, "description": cmd.description, "category": cmd.category, "aliases": list(cmd.aliases),
47
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,
48
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})
49
63
  try:
50
64
  from hermes_cli.plugins import get_plugin_commands
51
65
 
@@ -54,25 +68,60 @@ def list_commands() -> list[dict[str, Any]]:
54
68
  if name in existing or name in _NEVER_EXPOSE:
55
69
  continue
56
70
  out.append({"name": name, "description": str(info.get("description", "Plugin command")), "category": "Plugin", "aliases": [],
57
- "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})
58
72
  except Exception: # noqa: BLE001 - plugin registry is optional
59
73
  log.debug("plugin command listing failed", exc_info=True)
60
74
  return out
61
75
 
62
76
 
77
+ def retire_unscoped_launch_mcp_servers() -> set[str]:
78
+ """Close the launch profile's connections opened before multiplexing started; returns their server names.
79
+
80
+ Until the first named-profile call, the launch profile's connections and tools are unscoped (process-wide). Once
81
+ multiplexing is active, a scoped shutdown never selects them and scoped discovery opens duplicates beside them, so
82
+ the launch profile's own calls close them first and its scope reconnects them from the current config."""
83
+ from agent.secret_scope import is_multiplex_active
84
+ from hermes_constants import get_hermes_home
85
+ from tools.mcp_tool import _lock, _servers
86
+ from tools.mcp_tool_lifecycle import shutdown_mcp_servers
87
+ from tools.mcp_tool_scope import _key_name, _key_scope
88
+
89
+ if not is_multiplex_active() or _is_named_profile(get_hermes_home()):
90
+ return set()
91
+ with _lock:
92
+ names = {_key_name(key) for key in _servers if _key_scope(key) is None}
93
+ if names:
94
+ shutdown_mcp_servers(scope=None, names=names)
95
+ return names
96
+
97
+
63
98
  def _reload_mcp() -> str:
64
99
  with _RELOAD_MCP_LOCK:
65
100
  try:
66
- from tools.mcp_tool import _lock, _servers, discover_mcp_tools, shutdown_mcp_servers
101
+ from agent.secret_scope import is_multiplex_active
102
+ from tools.mcp_tool import _lock, _mcp_tool_server_names, _server_visible_in_scope, _servers
103
+ from tools.mcp_tool_discovery import discover_mcp_tools
104
+ from tools.mcp_tool_lifecycle import shutdown_mcp_servers
105
+ from tools.mcp_tool_scope import _key_name
106
+ from tools.registry import registry
67
107
  except Exception as exc: # noqa: BLE001
68
108
  raise RpcError("MCP runtime unavailable", condition="mcp_unavailable") from exc
69
- try:
70
- with _lock:
71
- old = set(_servers.keys())
72
- shutdown_mcp_servers()
73
- tools = discover_mcp_tools()
109
+ # Like the gateway's ``_execute_mcp_reload``: under multiplexing only this profile's connections go down and
110
+ # come back. The bare shutdown is the process-wide wildcard, which would drop every other profile's servers.
111
+ scope = registry.current_scope_key() if is_multiplex_active() else None
112
+
113
+ def server_names() -> set[str]:
74
114
  with _lock:
75
- connected = set(_servers.keys())
115
+ return {_key_name(key) for key in _servers if _server_visible_in_scope(key, scope)}
116
+
117
+ try:
118
+ old = server_names() | retire_unscoped_launch_mcp_servers()
119
+ shutdown_mcp_servers(scope=scope)
120
+ tools = discover_mcp_tools() or []
121
+ connected = server_names()
122
+ if scope is not None:
123
+ with _lock:
124
+ tools = [name for name in tools if _mcp_tool_server_names.get(name) in connected]
76
125
  except Exception as exc: # noqa: BLE001
77
126
  log.warning("Failed to reload MCP servers", exc_info=True)
78
127
  raise RpcError("Failed to reload MCP servers", condition="mcp_reload_failed") from exc
@@ -84,7 +133,7 @@ def _reload_mcp() -> str:
84
133
  lines.append(f"Added: {', '.join(sorted(added))}")
85
134
  if removed:
86
135
  lines.append(f"Removed: {', '.join(sorted(removed))}")
87
- lines.append(f"{len(tools or [])} tool(s) available across {len(connected)} server(s)" if connected else "No MCP servers connected")
136
+ lines.append(f"{len(tools)} tool(s) available across {len(connected)} server(s)" if connected else "No MCP servers connected")
88
137
  if not reconnected and not added and not removed:
89
138
  lines.append("Tooling state was already current")
90
139
  return "\n".join(lines)
@@ -230,27 +279,6 @@ def resolve_moa_config(preset: str | None) -> dict:
230
279
  return resolved
231
280
 
232
281
 
233
- def plugin_provider_profiles() -> list[dict]:
234
- """Model-provider plugins registered with the Agent's provider registry."""
235
- try:
236
- from providers import list_providers
237
- except Exception: # noqa: BLE001
238
- return []
239
- out = []
240
- try:
241
- for profile in list_providers():
242
- name = str(getattr(profile, "name", "") or "").strip().lower()
243
- if not name:
244
- continue
245
- env_vars = [str(v) for v in (getattr(profile, "env_vars", ()) or ())]
246
- api_key_env = next((v for v in env_vars if not v.upper().endswith(("_BASE_URL", "_URL", "_FOLDER_ID"))), None)
247
- out.append({"name": name, "display_name": str(getattr(profile, "display_name", "") or name).strip(), "env_vars": env_vars, "api_key_env": api_key_env})
248
- except Exception: # noqa: BLE001
249
- log.debug("Failed to enumerate model-provider plugins", exc_info=True)
250
- return []
251
- return out
252
-
253
-
254
282
  PLUGIN_VISIBILITY_HOOKS = ("pre_tool_call", "post_tool_call", "pre_llm_call", "post_llm_call")
255
283
 
256
284
 
@@ -332,7 +360,7 @@ def register(registry) -> None:
332
360
  @registry.method("plugins.providers")
333
361
  def providers(ctx: CallContext, params: dict) -> dict:
334
362
  with scoped_home(profile_home_param(params)):
335
- return {"providers": plugin_provider_profiles()}
363
+ return {"providers": plugin_providers()}
336
364
 
337
365
  @registry.method("plugins.list")
338
366
  def list_(ctx: CallContext, params: dict) -> dict:
@@ -2,12 +2,13 @@
2
2
  Agent's per-model reasoning capability.
3
3
 
4
4
  The TypeScript server owns every config.yaml *policy* (which keys mean what);
5
- the sidecar only parses and serialises YAML with the Agent's PyYAML so the Node
6
- side needs no YAML dependency. Writes are atomic and keep the file's mode.
5
+ the sidecar only parses and serialises YAML with the Agent's own parser so the
6
+ Node side needs no YAML dependency. Writes are atomic and keep the file's mode.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
+ import importlib
11
12
  import logging
12
13
  import os
13
14
  import tempfile
@@ -20,12 +21,15 @@ from ..rpc import CallContext
20
21
  log = logging.getLogger("talaria_sidecar.config")
21
22
 
22
23
 
23
- def _yaml():
24
- try:
25
- import yaml
26
- except Exception as exc: # noqa: BLE001
27
- raise RpcError(f"PyYAML unavailable: {exc}", condition="yaml_unavailable") from exc
28
- return yaml
24
+ def yaml_parser():
25
+ """The Agent's YAML module: ``hermes_yaml`` on Agents that ship it, PyYAML on released Agents."""
26
+ failures = []
27
+ for name in ("hermes_yaml", "yaml"):
28
+ try:
29
+ return importlib.import_module(name)
30
+ except Exception as exc: # noqa: BLE001
31
+ failures.append(f"{name}: {exc}")
32
+ raise RpcError(f"No YAML parser available ({'; '.join(failures)})", condition="yaml_unavailable")
29
33
 
30
34
 
31
35
  def config_path_param(params: dict) -> Path:
@@ -45,7 +49,7 @@ def read_config(path: Path) -> dict:
45
49
  if not path.exists():
46
50
  return {"path": str(path), "exists": False, "config": {}}
47
51
  try:
48
- data = _yaml().safe_load(path.read_text(encoding="utf-8"))
52
+ data = yaml_parser().safe_load(path.read_text(encoding="utf-8"))
49
53
  except RpcError:
50
54
  raise
51
55
  except Exception as exc: # noqa: BLE001
@@ -70,7 +74,7 @@ def write_config(path: Path, config: dict) -> dict:
70
74
  raise InvalidParams("config must be an object")
71
75
  target = _write_target(path)
72
76
  target.parent.mkdir(parents=True, exist_ok=True)
73
- text = _yaml().safe_dump(config, sort_keys=False, allow_unicode=True)
77
+ text = yaml_parser().safe_dump(config, sort_keys=False, allow_unicode=True)
74
78
  mode = None
75
79
  try:
76
80
  mode = target.stat().st_mode & 0o777
@@ -252,14 +252,18 @@ def _run_in_child(job, execution_home, ctx: CallContext):
252
252
  process.join(timeout=5)
253
253
  status, payload = "cancelled", ["cancelled", ""]
254
254
  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:
255
+ if not process.is_alive():
256
+ # The child may have put its result just before exiting; otherwise it died (OOM, signal) without one.
257
+ try:
258
+ status, *payload = result_queue.get_nowait()
259
+ except queue.Empty:
261
260
  payload = [f"cron run subprocess exited with code {process.exitcode} without producing a result", ""]
262
261
  break
262
+ if time.monotonic() >= deadline:
263
+ process.terminate()
264
+ process.join(timeout=5)
265
+ payload = [f"cron run subprocess produced no result within {_result_timeout(job):g}s and was terminated", ""]
266
+ break
263
267
  process.join(timeout=5)
264
268
  if process.is_alive():
265
269
  process.terminate()
@@ -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,102 @@ 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
+ try:
100
+ return bool(_kb()._claim_is_live(claim))
101
+ except Exception: # noqa: BLE001 - an older Agent without the helper
102
+ return bool(claim["claim_lock"] and claim["worker_pid"])
103
+
104
+
105
+ def _claim_live(conn, task_id) -> bool:
106
+ return _row_claim_live(conn.execute("SELECT * FROM tasks WHERE id = ?", (task_id,)).fetchone())
107
+
108
+
109
+ RUNNING_EXIT_MESSAGE = "task is running; confirm leaving Running"
110
+
111
+
112
+ def _guard_running_exit(row, confirm_running_exit: bool) -> None:
113
+ """A write that takes a task out of Running needs the client's explicit confirmation, judged on the row the
114
+ write sees: a client's card policy can predate a dispatcher claim (TAL-557)."""
115
+ if row["status"] == "running" and not confirm_running_exit:
116
+ raise Conflict(RUNNING_EXIT_MESSAGE)
117
+
118
+
119
+ FENCE_PREFIX = "talaria-fence:"
120
+ FENCE_SECONDS = 60
121
+
122
+
123
+ def _clear_stale_fences(conn) -> None:
124
+ """Release fences whose write never finished (the sidecar died mid-write), so the dispatcher can claim again."""
125
+ now = int(time.time())
126
+ stale = []
127
+ for row in conn.execute("SELECT id, claim_lock FROM tasks WHERE claim_lock LIKE ?", (FENCE_PREFIX + "%",)).fetchall():
128
+ try:
129
+ expires = int(str(row["claim_lock"])[len(FENCE_PREFIX):].split(":", 1)[0])
130
+ except ValueError:
131
+ expires = 0
132
+ if expires < now:
133
+ stale.append((row["id"], row["claim_lock"]))
134
+ if not stale:
135
+ return
136
+ kb = _kb()
137
+ with kb.write_txn(conn):
138
+ for task_id, fence in stale:
139
+ conn.execute("UPDATE tasks SET claim_lock = NULL WHERE id = ? AND claim_lock = ?", (task_id, fence))
140
+
141
+
142
+ def _fenced(conn, task_id: str, write, *, confirm_running_exit: bool, releases_claim: bool = True):
143
+ """Run an Agent transition (block, complete, archive) with the dispatcher fenced out.
144
+
145
+ The Agent's helpers open their own write transaction, which cannot nest, so the checks and a fence lock share
146
+ one transaction first: ``claim_task`` claims only a ``ready`` task with ``claim_lock IS NULL``, so the card cannot
147
+ become a live run before the helper runs. ``releases_claim`` writes (block, complete) refuse a live worker's claim;
148
+ archive keeps it, because ``archive_task`` terminates that worker. The fence expires, so a write the sidecar never
149
+ finished stops holding the card once ``_clear_stale_fences`` next runs.
150
+ """
151
+ kb = _kb()
152
+ _clear_stale_fences(conn)
153
+ fence = f"{FENCE_PREFIX}{int(time.time()) + FENCE_SECONDS}:{secrets.token_hex(8)}"
154
+ with kb.write_txn(conn):
155
+ row = conn.execute("SELECT * FROM tasks WHERE id = ?", (task_id,)).fetchone()
156
+ if row is None:
157
+ raise NotFound("task not found")
158
+ _guard_running_exit(row, confirm_running_exit)
159
+ live = _row_claim_live(row)
160
+ if live and releases_claim:
161
+ raise Conflict(LIVE_CLAIM_MESSAGE)
162
+ # Only an unclaimed ready task is claimable; anything else needs no fence.
163
+ fenced = row["status"] == "ready" and row["claim_lock"] is None
164
+ if fenced:
165
+ conn.execute("UPDATE tasks SET claim_lock = ? WHERE id = ?", (fence, task_id))
166
+ try:
167
+ return write()
168
+ finally:
169
+ if fenced:
170
+ with kb.write_txn(conn):
171
+ conn.execute("UPDATE tasks SET claim_lock = NULL WHERE id = ? AND claim_lock = ?", (task_id, fence))
172
+
173
+
174
+ def _task_dict(task, conn):
86
175
  data = _obj_dict(task)
87
176
  if not data:
88
177
  return data
178
+ # Facts the server's card policy needs (TAL-557): the Agent completes a non-review task only with
179
+ # stored or supplied evidence, and refuses to release a live worker's claim.
180
+ data["claim_live"] = data.get("status") == "running" and _claim_live(conn, data.get("id"))
181
+ data["has_completion_evidence"] = bool(str(data.get("result") or "").strip())
89
182
  try:
90
183
  age = _kb().task_age(task)
91
184
  except Exception: # noqa: BLE001
@@ -131,12 +224,16 @@ def _validate_status(status: str) -> str:
131
224
  return value
132
225
 
133
226
 
134
- def _set_status_direct(conn, task_id: str, new_status: str) -> bool:
227
+ def _set_status_direct(conn, task_id: str, new_status: str, *, confirm_running_exit: bool = False) -> bool:
135
228
  kb = _kb()
136
229
  with kb.write_txn(conn):
137
- prev = conn.execute("SELECT status, current_run_id FROM tasks WHERE id = ?", (task_id,)).fetchone()
230
+ prev = conn.execute("SELECT * FROM tasks WHERE id = ?", (task_id,)).fetchone()
138
231
  if prev is None:
139
232
  return False
233
+ # Checked inside the write transaction, so a claim taken since the client's read is never released.
234
+ _guard_running_exit(prev, confirm_running_exit)
235
+ if _row_claim_live(prev):
236
+ raise Conflict(LIVE_CLAIM_MESSAGE)
140
237
  was_running = prev["status"] == "running"
141
238
  cur = conn.execute(
142
239
  "UPDATE tasks SET status = ?, "
@@ -202,14 +299,15 @@ def _patch_task(conn, task_id: str, body: dict) -> None:
202
299
  if "status" not in body or body.get("status") in (None, ""):
203
300
  return
204
301
  status = _validate_status(body.get("status"))
302
+ confirm = bool(body.get("confirm_running_exit"))
205
303
  if status == "done":
206
- if not kb.complete_task(conn, task_id, result=body.get("result"), summary=body.get("summary")):
304
+ 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
305
  raise NotFound("task not found")
208
306
  elif status == "blocked":
209
- if not kb.block_task(conn, task_id, reason=body.get("block_reason") or body.get("reason")):
307
+ 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
308
  raise NotFound("task not found")
211
309
  elif status == "archived":
212
- if not kb.archive_task(conn, task_id):
310
+ if not _fenced(conn, task_id, lambda: kb.archive_task(conn, task_id), confirm_running_exit=confirm, releases_claim=False):
213
311
  raise NotFound("task not found")
214
312
  elif status == "running":
215
313
  raise InvalidParams("Cannot set status to 'running' directly; use the dispatcher/claim path")
@@ -220,10 +318,10 @@ def _patch_task(conn, task_id: str, body: dict) -> None:
220
318
  if current.status == "blocked":
221
319
  if not kb.unblock_task(conn, task_id):
222
320
  raise NotFound("task not found")
223
- elif not _set_status_direct(conn, task_id, "ready"):
321
+ elif not _set_status_direct(conn, task_id, "ready", confirm_running_exit=confirm):
224
322
  raise NotFound("task not found")
225
323
  elif status in ("triage", "todo"):
226
- if not _set_status_direct(conn, task_id, status):
324
+ if not _set_status_direct(conn, task_id, status, confirm_running_exit=confirm):
227
325
  raise NotFound("task not found")
228
326
 
229
327
 
@@ -320,6 +418,7 @@ def board_payload(params: dict) -> dict:
320
418
  profile = _str(params, "profile") or "default"
321
419
  assignee = profile
322
420
  with _conn(board=board) as conn:
421
+ _clear_stale_fences(conn)
323
422
  latest = _latest_event_id(conn)
324
423
  if since is not None and since >= latest:
325
424
  return {"changed": False, "latest_event_id": latest, "read_only": False}
@@ -327,7 +426,7 @@ def board_payload(params: dict) -> dict:
327
426
  link_counts, comment_counts = _link_counts(conn, tasks), _comment_counts(conn)
328
427
 
329
428
  def row(task):
330
- data = _task_dict(task)
429
+ data = _task_dict(task, conn)
331
430
  data["link_counts"] = link_counts.get(task.id, {"parents": 0, "children": 0})
332
431
  data["comment_count"] = comment_counts.get(task.id, 0)
333
432
  return data
@@ -365,7 +464,7 @@ def create_task_payload(params: dict) -> dict:
365
464
  )
366
465
  if body.get("status"):
367
466
  _patch_task(conn, task_id, {"status": body.get("status")})
368
- return {"task": _task_dict(kb.get_task(conn, task_id)), "read_only": False}
467
+ return {"task": _task_dict(kb.get_task(conn, task_id), conn), "read_only": False}
369
468
 
370
469
 
371
470
  def patch_task_payload(params: dict) -> dict:
@@ -375,7 +474,7 @@ def patch_task_payload(params: dict) -> dict:
375
474
  kb = _kb()
376
475
  with _conn(board=_board(params)) as conn:
377
476
  _patch_task(conn, task_id, params.get("patch") or {})
378
- return {"task": _task_dict(kb.get_task(conn, task_id)), "read_only": False}
477
+ return {"task": _task_dict(kb.get_task(conn, task_id), conn), "read_only": False}
379
478
 
380
479
 
381
480
  def comment_payload(params: dict) -> dict:
@@ -418,7 +517,7 @@ def task_detail_payload(params: dict) -> dict:
418
517
  if not task:
419
518
  raise NotFound("task not found")
420
519
  return {
421
- "task": _task_dict(task),
520
+ "task": _task_dict(task, conn),
422
521
  "comments": [_obj_dict(c) for c in kb.list_comments(conn, task_id)],
423
522
  "events": [_obj_dict(e) for e in kb.list_events(conn, task_id)],
424
523
  "links": {"parents": kb.parent_ids(conn, task_id), "children": kb.child_ids(conn, task_id)},
@@ -533,7 +632,8 @@ def bulk_payload(params: dict) -> dict:
533
632
  results.append(entry)
534
633
  continue
535
634
  if body.get("archive"):
536
- if not kb.archive_task(conn, task_id):
635
+ # Bulk carries no running-exit confirmation, so a running row is refused like a card write.
636
+ if not _fenced(conn, task_id, lambda: kb.archive_task(conn, task_id), confirm_running_exit=False, releases_claim=False):
537
637
  entry.update(ok=False, error="archive refused")
538
638
  elif body.get("status") is not None:
539
639
  _patch_task(conn, task_id, {"status": body.get("status")})
@@ -578,7 +678,7 @@ def task_action_payload(params: dict) -> dict:
578
678
  if not kb.get_task(conn, task_id):
579
679
  raise NotFound("task not found")
580
680
  if action == "block":
581
- ok = kb.block_task(conn, task_id, reason=params.get("reason"))
681
+ 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
682
  elif action == "unblock":
583
683
  if hasattr(kb, "unblock_task"):
584
684
  ok = kb.unblock_task(conn, task_id)
@@ -589,7 +689,7 @@ def task_action_payload(params: dict) -> dict:
589
689
  raise InvalidParams(f"invalid action: {action}")
590
690
  if not ok:
591
691
  raise Conflict(f"{action} refused")
592
- return {"task": _task_dict(kb.get_task(conn, task_id)), "read_only": False}
692
+ return {"task": _task_dict(kb.get_task(conn, task_id), conn), "read_only": False}
593
693
 
594
694
 
595
695
  def list_boards_payload(params: dict) -> dict: