@maudecode/talaria-web 0.0.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 (876) hide show
  1. package/contract_versions.json +18 -0
  2. package/dist/api/auth-raw.d.ts +9 -0
  3. package/dist/api/auth-raw.js +134 -0
  4. package/dist/api/auth-raw.js.map +1 -0
  5. package/dist/api/automation-raw.d.ts +16 -0
  6. package/dist/api/automation-raw.js +340 -0
  7. package/dist/api/automation-raw.js.map +1 -0
  8. package/dist/api/automation-router.d.ts +788 -0
  9. package/dist/api/automation-router.js +248 -0
  10. package/dist/api/automation-router.js.map +1 -0
  11. package/dist/api/chat-router.d.ts +375 -0
  12. package/dist/api/chat-router.js +446 -0
  13. package/dist/api/chat-router.js.map +1 -0
  14. package/dist/api/git-router.d.ts +681 -0
  15. package/dist/api/git-router.js +275 -0
  16. package/dist/api/git-router.js.map +1 -0
  17. package/dist/api/raw-routes.d.ts +10 -0
  18. package/dist/api/raw-routes.js +466 -0
  19. package/dist/api/raw-routes.js.map +1 -0
  20. package/dist/api/router.d.ts +226 -0
  21. package/dist/api/router.js +331 -0
  22. package/dist/api/router.js.map +1 -0
  23. package/dist/api/session-visibility.d.ts +12 -0
  24. package/dist/api/session-visibility.js +51 -0
  25. package/dist/api/session-visibility.js.map +1 -0
  26. package/dist/api/sessions-router.d.ts +1728 -0
  27. package/dist/api/sessions-router.js +815 -0
  28. package/dist/api/sessions-router.js.map +1 -0
  29. package/dist/api/settings-router.d.ts +592 -0
  30. package/dist/api/settings-router.js +474 -0
  31. package/dist/api/settings-router.js.map +1 -0
  32. package/dist/api/sse-routes.d.ts +51 -0
  33. package/dist/api/sse-routes.js +602 -0
  34. package/dist/api/sse-routes.js.map +1 -0
  35. package/dist/api/tools-raw.d.ts +5 -0
  36. package/dist/api/tools-raw.js +291 -0
  37. package/dist/api/tools-raw.js.map +1 -0
  38. package/dist/api/tools-router.d.ts +494 -0
  39. package/dist/api/tools-router.js +349 -0
  40. package/dist/api/tools-router.js.map +1 -0
  41. package/dist/app.d.ts +4787 -0
  42. package/dist/app.js +453 -0
  43. package/dist/app.js.map +1 -0
  44. package/dist/auth/cookies.d.ts +17 -0
  45. package/dist/auth/cookies.js +42 -0
  46. package/dist/auth/cookies.js.map +1 -0
  47. package/dist/auth/crypto.d.ts +9 -0
  48. package/dist/auth/crypto.js +38 -0
  49. package/dist/auth/crypto.js.map +1 -0
  50. package/dist/auth/gate.d.ts +39 -0
  51. package/dist/auth/gate.js +406 -0
  52. package/dist/auth/gate.js.map +1 -0
  53. package/dist/auth/oidc.d.ts +138 -0
  54. package/dist/auth/oidc.js +836 -0
  55. package/dist/auth/oidc.js.map +1 -0
  56. package/dist/auth/passkeys.d.ts +49 -0
  57. package/dist/auth/passkeys.js +310 -0
  58. package/dist/auth/passkeys.js.map +1 -0
  59. package/dist/auth/store.d.ts +116 -0
  60. package/dist/auth/store.js +485 -0
  61. package/dist/auth/store.js.map +1 -0
  62. package/dist/bin/talaria-web-mcp.d.ts +2 -0
  63. package/dist/bin/talaria-web-mcp.js +12 -0
  64. package/dist/bin/talaria-web-mcp.js.map +1 -0
  65. package/dist/bin/talaria-web.d.ts +2 -0
  66. package/dist/bin/talaria-web.js +87 -0
  67. package/dist/bin/talaria-web.js.map +1 -0
  68. package/dist/cli/ctl.d.ts +58 -0
  69. package/dist/cli/ctl.js +643 -0
  70. package/dist/cli/ctl.js.map +1 -0
  71. package/dist/cli/dotenv.d.ts +30 -0
  72. package/dist/cli/dotenv.js +115 -0
  73. package/dist/cli/dotenv.js.map +1 -0
  74. package/dist/cli/launcher.d.ts +49 -0
  75. package/dist/cli/launcher.js +213 -0
  76. package/dist/cli/launcher.js.map +1 -0
  77. package/dist/cli/supervise.d.ts +10 -0
  78. package/dist/cli/supervise.js +33 -0
  79. package/dist/cli/supervise.js.map +1 -0
  80. package/dist/cli/web-root.d.ts +2 -0
  81. package/dist/cli/web-root.js +23 -0
  82. package/dist/cli/web-root.js.map +1 -0
  83. package/dist/config/agent-config.d.ts +109 -0
  84. package/dist/config/agent-config.js +632 -0
  85. package/dist/config/agent-config.js.map +1 -0
  86. package/dist/config.d.ts +36 -0
  87. package/dist/config.js +105 -0
  88. package/dist/config.js.map +1 -0
  89. package/dist/fs/atomic.d.ts +7 -0
  90. package/dist/fs/atomic.js +68 -0
  91. package/dist/fs/atomic.js.map +1 -0
  92. package/dist/http/addresses.d.ts +5 -0
  93. package/dist/http/addresses.js +68 -0
  94. package/dist/http/addresses.js.map +1 -0
  95. package/dist/http/capped.d.ts +2 -0
  96. package/dist/http/capped.js +22 -0
  97. package/dist/http/capped.js.map +1 -0
  98. package/dist/http/context.d.ts +281 -0
  99. package/dist/http/context.js +327 -0
  100. package/dist/http/context.js.map +1 -0
  101. package/dist/http/csp.d.ts +11 -0
  102. package/dist/http/csp.js +51 -0
  103. package/dist/http/csp.js.map +1 -0
  104. package/dist/http/origin.d.ts +39 -0
  105. package/dist/http/origin.js +183 -0
  106. package/dist/http/origin.js.map +1 -0
  107. package/dist/http/pinned.d.ts +18 -0
  108. package/dist/http/pinned.js +62 -0
  109. package/dist/http/pinned.js.map +1 -0
  110. package/dist/index.d.ts +10 -0
  111. package/dist/index.js +12 -0
  112. package/dist/index.js.map +1 -0
  113. package/dist/mcp/server.d.ts +29 -0
  114. package/dist/mcp/server.js +221 -0
  115. package/dist/mcp/server.js.map +1 -0
  116. package/dist/onboarding.d.ts +57 -0
  117. package/dist/onboarding.js +381 -0
  118. package/dist/onboarding.js.map +1 -0
  119. package/dist/profiles/profiles.d.ts +96 -0
  120. package/dist/profiles/profiles.js +476 -0
  121. package/dist/profiles/profiles.js.map +1 -0
  122. package/dist/projects.d.ts +36 -0
  123. package/dist/projects.js +102 -0
  124. package/dist/projects.js.map +1 -0
  125. package/dist/providers/catalog.d.ts +118 -0
  126. package/dist/providers/catalog.js +868 -0
  127. package/dist/providers/catalog.js.map +1 -0
  128. package/dist/providers/env-file.d.ts +6 -0
  129. package/dist/providers/env-file.js +106 -0
  130. package/dist/providers/env-file.js.map +1 -0
  131. package/dist/providers/runtime-env.d.ts +20 -0
  132. package/dist/providers/runtime-env.js +85 -0
  133. package/dist/providers/runtime-env.js.map +1 -0
  134. package/dist/providers/tables.d.ts +54 -0
  135. package/dist/providers/tables.js +54 -0
  136. package/dist/providers/tables.js.map +1 -0
  137. package/dist/redact.d.ts +23 -0
  138. package/dist/redact.js +337 -0
  139. package/dist/redact.js.map +1 -0
  140. package/dist/release.d.ts +16 -0
  141. package/dist/release.js +114 -0
  142. package/dist/release.js.map +1 -0
  143. package/dist/runtime.d.ts +21 -0
  144. package/dist/runtime.js +680 -0
  145. package/dist/runtime.js.map +1 -0
  146. package/dist/server.d.ts +20 -0
  147. package/dist/server.js +90 -0
  148. package/dist/server.js.map +1 -0
  149. package/dist/sessions/anchor.d.ts +43 -0
  150. package/dist/sessions/anchor.js +311 -0
  151. package/dist/sessions/anchor.js.map +1 -0
  152. package/dist/sessions/cli-sessions.d.ts +27 -0
  153. package/dist/sessions/cli-sessions.js +158 -0
  154. package/dist/sessions/cli-sessions.js.map +1 -0
  155. package/dist/sessions/completions.d.ts +62 -0
  156. package/dist/sessions/completions.js +332 -0
  157. package/dist/sessions/completions.js.map +1 -0
  158. package/dist/sessions/drafts.d.ts +26 -0
  159. package/dist/sessions/drafts.js +117 -0
  160. package/dist/sessions/drafts.js.map +1 -0
  161. package/dist/sessions/events.d.ts +32 -0
  162. package/dist/sessions/events.js +80 -0
  163. package/dist/sessions/events.js.map +1 -0
  164. package/dist/sessions/export.d.ts +4 -0
  165. package/dist/sessions/export.js +186 -0
  166. package/dist/sessions/export.js.map +1 -0
  167. package/dist/sessions/gateway-watcher.d.ts +58 -0
  168. package/dist/sessions/gateway-watcher.js +199 -0
  169. package/dist/sessions/gateway-watcher.js.map +1 -0
  170. package/dist/sessions/journal.d.ts +103 -0
  171. package/dist/sessions/journal.js +543 -0
  172. package/dist/sessions/journal.js.map +1 -0
  173. package/dist/sessions/list.d.ts +130 -0
  174. package/dist/sessions/list.js +846 -0
  175. package/dist/sessions/list.js.map +1 -0
  176. package/dist/sessions/merge.d.ts +57 -0
  177. package/dist/sessions/merge.js +517 -0
  178. package/dist/sessions/merge.js.map +1 -0
  179. package/dist/sessions/pending.d.ts +69 -0
  180. package/dist/sessions/pending.js +271 -0
  181. package/dist/sessions/pending.js.map +1 -0
  182. package/dist/sessions/reasoning-titles.d.ts +31 -0
  183. package/dist/sessions/reasoning-titles.js +143 -0
  184. package/dist/sessions/reasoning-titles.js.map +1 -0
  185. package/dist/sessions/relay.d.ts +131 -0
  186. package/dist/sessions/relay.js +577 -0
  187. package/dist/sessions/relay.js.map +1 -0
  188. package/dist/sessions/service.d.ts +189 -0
  189. package/dist/sessions/service.js +1344 -0
  190. package/dist/sessions/service.js.map +1 -0
  191. package/dist/sessions/session.d.ts +131 -0
  192. package/dist/sessions/session.js +505 -0
  193. package/dist/sessions/session.js.map +1 -0
  194. package/dist/sessions/shares.d.ts +31 -0
  195. package/dist/sessions/shares.js +267 -0
  196. package/dist/sessions/shares.js.map +1 -0
  197. package/dist/sessions/state-db.d.ts +52 -0
  198. package/dist/sessions/state-db.js +641 -0
  199. package/dist/sessions/state-db.js.map +1 -0
  200. package/dist/sessions/state-saved.d.ts +13 -0
  201. package/dist/sessions/state-saved.js +47 -0
  202. package/dist/sessions/state-saved.js.map +1 -0
  203. package/dist/sessions/store.d.ts +124 -0
  204. package/dist/sessions/store.js +790 -0
  205. package/dist/sessions/store.js.map +1 -0
  206. package/dist/sessions/streams.d.ts +90 -0
  207. package/dist/sessions/streams.js +200 -0
  208. package/dist/sessions/streams.js.map +1 -0
  209. package/dist/sessions/titles.d.ts +17 -0
  210. package/dist/sessions/titles.js +231 -0
  211. package/dist/sessions/titles.js.map +1 -0
  212. package/dist/sessions/todo.d.ts +8 -0
  213. package/dist/sessions/todo.js +72 -0
  214. package/dist/sessions/todo.js.map +1 -0
  215. package/dist/sessions/turn-context.d.ts +27 -0
  216. package/dist/sessions/turn-context.js +136 -0
  217. package/dist/sessions/turn-context.js.map +1 -0
  218. package/dist/sessions/turn.d.ts +178 -0
  219. package/dist/sessions/turn.js +1357 -0
  220. package/dist/sessions/turn.js.map +1 -0
  221. package/dist/sessions/window.d.ts +10 -0
  222. package/dist/sessions/window.js +151 -0
  223. package/dist/sessions/window.js.map +1 -0
  224. package/dist/settings.d.ts +57 -0
  225. package/dist/settings.js +481 -0
  226. package/dist/settings.js.map +1 -0
  227. package/dist/sidecar/client.d.ts +75 -0
  228. package/dist/sidecar/client.js +252 -0
  229. package/dist/sidecar/client.js.map +1 -0
  230. package/dist/sidecar/discover.d.ts +22 -0
  231. package/dist/sidecar/discover.js +146 -0
  232. package/dist/sidecar/discover.js.map +1 -0
  233. package/dist/sidecar/fake.d.ts +37 -0
  234. package/dist/sidecar/fake.js +79 -0
  235. package/dist/sidecar/fake.js.map +1 -0
  236. package/dist/spa.d.ts +42 -0
  237. package/dist/spa.js +145 -0
  238. package/dist/spa.js.map +1 -0
  239. package/dist/startup.d.ts +19 -0
  240. package/dist/startup.js +44 -0
  241. package/dist/startup.js.map +1 -0
  242. package/dist/test/harness.d.ts +41 -0
  243. package/dist/test/harness.js +123 -0
  244. package/dist/test/harness.js.map +1 -0
  245. package/dist/text/diff.d.ts +16 -0
  246. package/dist/text/diff.js +122 -0
  247. package/dist/text/diff.js.map +1 -0
  248. package/dist/text/markdown.d.ts +8 -0
  249. package/dist/text/markdown.js +185 -0
  250. package/dist/text/markdown.js.map +1 -0
  251. package/dist/tools/crons.d.ts +51 -0
  252. package/dist/tools/crons.js +317 -0
  253. package/dist/tools/crons.js.map +1 -0
  254. package/dist/tools/extensions.d.ts +92 -0
  255. package/dist/tools/extensions.js +1096 -0
  256. package/dist/tools/extensions.js.map +1 -0
  257. package/dist/tools/health.d.ts +21 -0
  258. package/dist/tools/health.js +334 -0
  259. package/dist/tools/health.js.map +1 -0
  260. package/dist/tools/hygiene.d.ts +29 -0
  261. package/dist/tools/hygiene.js +112 -0
  262. package/dist/tools/hygiene.js.map +1 -0
  263. package/dist/tools/insights.d.ts +3 -0
  264. package/dist/tools/insights.js +92 -0
  265. package/dist/tools/insights.js.map +1 -0
  266. package/dist/tools/kanban.d.ts +25 -0
  267. package/dist/tools/kanban.js +79 -0
  268. package/dist/tools/kanban.js.map +1 -0
  269. package/dist/tools/mcp-health.d.ts +28 -0
  270. package/dist/tools/mcp-health.js +207 -0
  271. package/dist/tools/mcp-health.js.map +1 -0
  272. package/dist/tools/mcp.d.ts +34 -0
  273. package/dist/tools/mcp.js +269 -0
  274. package/dist/tools/mcp.js.map +1 -0
  275. package/dist/tools/memory.d.ts +20 -0
  276. package/dist/tools/memory.js +216 -0
  277. package/dist/tools/memory.js.map +1 -0
  278. package/dist/tools/skills.d.ts +43 -0
  279. package/dist/tools/skills.js +193 -0
  280. package/dist/tools/skills.js.map +1 -0
  281. package/dist/tools/terminal.d.ts +96 -0
  282. package/dist/tools/terminal.js +324 -0
  283. package/dist/tools/terminal.js.map +1 -0
  284. package/dist/tools/updates.d.ts +151 -0
  285. package/dist/tools/updates.js +1151 -0
  286. package/dist/tools/updates.js.map +1 -0
  287. package/dist/util.d.ts +6 -0
  288. package/dist/util.js +40 -0
  289. package/dist/util.js.map +1 -0
  290. package/dist/workspace/fs.d.ts +61 -0
  291. package/dist/workspace/fs.js +555 -0
  292. package/dist/workspace/fs.js.map +1 -0
  293. package/dist/workspace/git.d.ts +204 -0
  294. package/dist/workspace/git.js +1236 -0
  295. package/dist/workspace/git.js.map +1 -0
  296. package/dist/workspace/media.d.ts +47 -0
  297. package/dist/workspace/media.js +448 -0
  298. package/dist/workspace/media.js.map +1 -0
  299. package/dist/workspace/paths.d.ts +6 -0
  300. package/dist/workspace/paths.js +41 -0
  301. package/dist/workspace/paths.js.map +1 -0
  302. package/dist/workspace/rollback.d.ts +29 -0
  303. package/dist/workspace/rollback.js +337 -0
  304. package/dist/workspace/rollback.js.map +1 -0
  305. package/dist/workspace/unzip.d.ts +9 -0
  306. package/dist/workspace/unzip.js +61 -0
  307. package/dist/workspace/unzip.js.map +1 -0
  308. package/dist/workspace/upload.d.ts +40 -0
  309. package/dist/workspace/upload.js +215 -0
  310. package/dist/workspace/upload.js.map +1 -0
  311. package/dist/workspace/workspaces.d.ts +84 -0
  312. package/dist/workspace/workspaces.js +663 -0
  313. package/dist/workspace/workspaces.js.map +1 -0
  314. package/dist/workspace/worktrees.d.ts +14 -0
  315. package/dist/workspace/worktrees.js +171 -0
  316. package/dist/workspace/worktrees.js.map +1 -0
  317. package/dist/workspace/zip.d.ts +16 -0
  318. package/dist/workspace/zip.js +205 -0
  319. package/dist/workspace/zip.js.map +1 -0
  320. package/package.json +54 -0
  321. package/sidecar/agent_dependency.json +11 -0
  322. package/sidecar/scripts/record-fixtures.py +72 -0
  323. package/sidecar/scripts/replay_sidecar.py +113 -0
  324. package/sidecar/scripts/test.sh +22 -0
  325. package/sidecar/talaria_sidecar/__init__.py +10 -0
  326. package/sidecar/talaria_sidecar/__main__.py +46 -0
  327. package/sidecar/talaria_sidecar/errors.py +41 -0
  328. package/sidecar/talaria_sidecar/home.py +103 -0
  329. package/sidecar/talaria_sidecar/methods/__init__.py +37 -0
  330. package/sidecar/talaria_sidecar/methods/aux.py +160 -0
  331. package/sidecar/talaria_sidecar/methods/chat.py +791 -0
  332. package/sidecar/talaria_sidecar/methods/commands.py +340 -0
  333. package/sidecar/talaria_sidecar/methods/config.py +131 -0
  334. package/sidecar/talaria_sidecar/methods/cron.py +558 -0
  335. package/sidecar/talaria_sidecar/methods/gateway.py +106 -0
  336. package/sidecar/talaria_sidecar/methods/goals.py +225 -0
  337. package/sidecar/talaria_sidecar/methods/kanban.py +723 -0
  338. package/sidecar/talaria_sidecar/methods/mcp.py +65 -0
  339. package/sidecar/talaria_sidecar/methods/process.py +155 -0
  340. package/sidecar/talaria_sidecar/methods/profiles.py +254 -0
  341. package/sidecar/talaria_sidecar/methods/providers.py +241 -0
  342. package/sidecar/talaria_sidecar/methods/runtime.py +68 -0
  343. package/sidecar/talaria_sidecar/methods/skills.py +262 -0
  344. package/sidecar/talaria_sidecar/methods/state_db.py +418 -0
  345. package/sidecar/talaria_sidecar/methods/stt.py +131 -0
  346. package/sidecar/talaria_sidecar/methods/text.py +85 -0
  347. package/sidecar/talaria_sidecar/methods/usage.py +59 -0
  348. package/sidecar/talaria_sidecar/methods/worktree.py +71 -0
  349. package/sidecar/talaria_sidecar/rpc.py +218 -0
  350. package/sidecar/talaria_sidecar/runtime.py +330 -0
  351. package/static/brand/apple-touch-icon.png +0 -0
  352. package/static/brand/brandmark.svg +12 -0
  353. package/static/brand/favicon-192.png +0 -0
  354. package/static/brand/favicon-32.png +0 -0
  355. package/static/brand/favicon-512.png +0 -0
  356. package/static/brand/favicon-512.svg +14 -0
  357. package/static/brand/favicon-dark.svg +14 -0
  358. package/static/brand/favicon-light.svg +14 -0
  359. package/static/brand/favicon.ico +0 -0
  360. package/static/brand/favicon.svg +14 -0
  361. package/static/dist/FILES.txt +515 -0
  362. package/static/dist/assets/AppShell-gs0grlbs.js +1 -0
  363. package/static/dist/assets/ChatPage-gi1xlz52.js +18 -0
  364. package/static/dist/assets/ChatPage-gmu5c7lf.css +1 -0
  365. package/static/dist/assets/HubRoute-qs9vzuh0.js +2 -0
  366. package/static/dist/assets/InterVariable-nonokl6i.woff2 +0 -0
  367. package/static/dist/assets/KaTeX_AMS-Regular-e1why8ff.woff2 +0 -0
  368. package/static/dist/assets/KaTeX_AMS-Regular-mec5w1mt.woff +0 -0
  369. package/static/dist/assets/KaTeX_AMS-Regular-mos1wgtv.ttf +0 -0
  370. package/static/dist/assets/KaTeX_Caligraphic-Bold-axibvb5b.ttf +0 -0
  371. package/static/dist/assets/KaTeX_Caligraphic-Bold-eccvbn5c.woff +0 -0
  372. package/static/dist/assets/KaTeX_Caligraphic-Bold-n63xiolk.woff2 +0 -0
  373. package/static/dist/assets/KaTeX_Caligraphic-Regular-c5dvswgu.ttf +0 -0
  374. package/static/dist/assets/KaTeX_Caligraphic-Regular-iz5gexw5.woff +0 -0
  375. package/static/dist/assets/KaTeX_Caligraphic-Regular-npwmqylf.woff2 +0 -0
  376. package/static/dist/assets/KaTeX_Fraktur-Bold-ftsn4fxp.ttf +0 -0
  377. package/static/dist/assets/KaTeX_Fraktur-Bold-gokmhdpe.woff +0 -0
  378. package/static/dist/assets/KaTeX_Fraktur-Bold-ikhebgtj.woff2 +0 -0
  379. package/static/dist/assets/KaTeX_Fraktur-Regular-hzce8q9o.ttf +0 -0
  380. package/static/dist/assets/KaTeX_Fraktur-Regular-i0egury6.woff2 +0 -0
  381. package/static/dist/assets/KaTeX_Fraktur-Regular-okwm110b.woff +0 -0
  382. package/static/dist/assets/KaTeX_Main-Bold-c5g2m5kh.ttf +0 -0
  383. package/static/dist/assets/KaTeX_Main-Bold-ktk38ybk.woff2 +0 -0
  384. package/static/dist/assets/KaTeX_Main-Bold-ureqgmzo.woff +0 -0
  385. package/static/dist/assets/KaTeX_Main-BoldItalic-bd0upj71.woff +0 -0
  386. package/static/dist/assets/KaTeX_Main-BoldItalic-oj033t4i.woff2 +0 -0
  387. package/static/dist/assets/KaTeX_Main-BoldItalic-opttanob.ttf +0 -0
  388. package/static/dist/assets/KaTeX_Main-Italic-2p4bq1jf.woff2 +0 -0
  389. package/static/dist/assets/KaTeX_Main-Italic-dj86j9xo.ttf +0 -0
  390. package/static/dist/assets/KaTeX_Main-Italic-esmxqs8d.woff +0 -0
  391. package/static/dist/assets/KaTeX_Main-Regular-c978jrkj.ttf +0 -0
  392. package/static/dist/assets/KaTeX_Main-Regular-hbkzldb8.woff2 +0 -0
  393. package/static/dist/assets/KaTeX_Main-Regular-n865mmt0.woff +0 -0
  394. package/static/dist/assets/KaTeX_Math-BoldItalic-cbkxiib1.woff +0 -0
  395. package/static/dist/assets/KaTeX_Math-BoldItalic-hcom2j4y.ttf +0 -0
  396. package/static/dist/assets/KaTeX_Math-BoldItalic-jdo1yxu8.woff2 +0 -0
  397. package/static/dist/assets/KaTeX_Math-Italic-b5lfnctb.ttf +0 -0
  398. package/static/dist/assets/KaTeX_Math-Italic-cz4b2ure.woff2 +0 -0
  399. package/static/dist/assets/KaTeX_Math-Italic-lo894bvj.woff +0 -0
  400. package/static/dist/assets/KaTeX_SansSerif-Bold-h55uj19s.ttf +0 -0
  401. package/static/dist/assets/KaTeX_SansSerif-Bold-m9bkcp3c.woff +0 -0
  402. package/static/dist/assets/KaTeX_SansSerif-Bold-otxc8itm.woff2 +0 -0
  403. package/static/dist/assets/KaTeX_SansSerif-Italic-bp85cocg.ttf +0 -0
  404. package/static/dist/assets/KaTeX_SansSerif-Italic-g42k16nn.woff +0 -0
  405. package/static/dist/assets/KaTeX_SansSerif-Italic-k4kksncm.woff2 +0 -0
  406. package/static/dist/assets/KaTeX_SansSerif-Regular-evq9548m.ttf +0 -0
  407. package/static/dist/assets/KaTeX_SansSerif-Regular-izeggh93.woff +0 -0
  408. package/static/dist/assets/KaTeX_SansSerif-Regular-ltw53ck4.woff2 +0 -0
  409. package/static/dist/assets/KaTeX_Script-Regular-k8v4ghws.ttf +0 -0
  410. package/static/dist/assets/KaTeX_Script-Regular-o2nd302f.woff +0 -0
  411. package/static/dist/assets/KaTeX_Script-Regular-oybd33cp.woff2 +0 -0
  412. package/static/dist/assets/KaTeX_Size1-Regular-cjccv44r.woff2 +0 -0
  413. package/static/dist/assets/KaTeX_Size1-Regular-k13yopgz.woff +0 -0
  414. package/static/dist/assets/KaTeX_Size1-Regular-naiuhmx7.ttf +0 -0
  415. package/static/dist/assets/KaTeX_Size2-Regular-cnnxwlt7.woff +0 -0
  416. package/static/dist/assets/KaTeX_Size2-Regular-hlh6jazj.ttf +0 -0
  417. package/static/dist/assets/KaTeX_Size2-Regular-onxq3bzc.woff2 +0 -0
  418. package/static/dist/assets/KaTeX_Size3-Regular-i00haf83.woff +0 -0
  419. package/static/dist/assets/KaTeX_Size3-Regular-nk2kx36d.ttf +0 -0
  420. package/static/dist/assets/KaTeX_Size4-Regular-effsfy3y.woff +0 -0
  421. package/static/dist/assets/KaTeX_Size4-Regular-myjqyche.ttf +0 -0
  422. package/static/dist/assets/KaTeX_Size4-Regular-nv9nppzf.woff2 +0 -0
  423. package/static/dist/assets/KaTeX_Typewriter-Regular-iqvr3vwu.woff2 +0 -0
  424. package/static/dist/assets/KaTeX_Typewriter-Regular-kzj4h457.woff +0 -0
  425. package/static/dist/assets/KaTeX_Typewriter-Regular-owzsy8p5.ttf +0 -0
  426. package/static/dist/assets/Markdown-bt9bqd6a.js +598 -0
  427. package/static/dist/assets/Markdown-lwviulmg.css +1 -0
  428. package/static/dist/assets/RightPanel-dn19xu42.js +1 -0
  429. package/static/dist/assets/Select-y7wtf85g.js +1 -0
  430. package/static/dist/assets/SessionListPanel-igwdj9xr.js +1 -0
  431. package/static/dist/assets/SettingsLayout-n6mu0ghg.js +1 -0
  432. package/static/dist/assets/States-oil2issf.js +1 -0
  433. package/static/dist/assets/Toaster-jtn59pjm.js +1 -0
  434. package/static/dist/assets/_app-k4jrkgfa.js +1 -0
  435. package/static/dist/assets/_app.ext._extensionId-mmf1mepm.js +1 -0
  436. package/static/dist/assets/_app.index-mb41nf00.js +1 -0
  437. package/static/dist/assets/_app.insights-mz5cep8m.js +1 -0
  438. package/static/dist/assets/_app.kanban-ocgw506i.js +1 -0
  439. package/static/dist/assets/_app.logs-lstmoju6.js +1 -0
  440. package/static/dist/assets/_app.memory-dishj9ra.js +1 -0
  441. package/static/dist/assets/_app.profiles-dynpvsio.js +1 -0
  442. package/static/dist/assets/_app.session._sessionId-kevi4qd2.js +1 -0
  443. package/static/dist/assets/_app.settings-c2czm9v4.js +1 -0
  444. package/static/dist/assets/_app.settings._section-exwflld7.js +1 -0
  445. package/static/dist/assets/_app.skills-j5jh2nug.js +1 -0
  446. package/static/dist/assets/_app.tasks-b99d98fo.js +2 -0
  447. package/static/dist/assets/_app.todos-ma8urvn3.js +1 -0
  448. package/static/dist/assets/_app.workspaces-l86cxeod.js +1 -0
  449. package/static/dist/assets/abap-ij37pki6.js +1 -0
  450. package/static/dist/assets/abnfDiagram-VCTEODGH-hvrr25lb.js +1 -0
  451. package/static/dist/assets/actionscript-3-dz7y3j1x.js +1 -0
  452. package/static/dist/assets/ada-k9zgv987.js +1 -0
  453. package/static/dist/assets/andromeeda-c2n0n86o.js +1 -0
  454. package/static/dist/assets/angular-html-nu6l31w3.js +1 -0
  455. package/static/dist/assets/angular-ts-fbx4f8mm.js +1 -0
  456. package/static/dist/assets/apache-binppq6z.js +1 -0
  457. package/static/dist/assets/apex-h8illjly.js +1 -0
  458. package/static/dist/assets/apl-bx2xr7dh.js +1 -0
  459. package/static/dist/assets/applescript-h0ok5sww.js +1 -0
  460. package/static/dist/assets/ara-dlpjioua.js +1 -0
  461. package/static/dist/assets/arc-7popfhbk.js +1 -0
  462. package/static/dist/assets/architecture-7GRP2DOG-mqft9w2p.js +1 -0
  463. package/static/dist/assets/architectureDiagram-5GKGNRK7-b3r0z8sq.js +36 -0
  464. package/static/dist/assets/array-f36850hm.js +1 -0
  465. package/static/dist/assets/asciidoc-lx0cuujr.js +1 -0
  466. package/static/dist/assets/asm-j5diz3sz.js +1 -0
  467. package/static/dist/assets/astro-g77i97cb.js +1 -0
  468. package/static/dist/assets/aurora-x-h2hngdsl.js +1 -0
  469. package/static/dist/assets/awk-fecghegf.js +1 -0
  470. package/static/dist/assets/ayu-dark-nvvuggz9.js +1 -0
  471. package/static/dist/assets/ayu-light-k5fxgplt.js +1 -0
  472. package/static/dist/assets/ayu-mirage-gltrvhcx.js +1 -0
  473. package/static/dist/assets/ballerina-hk9oga18.js +1 -0
  474. package/static/dist/assets/bat-ghs7lag5.js +1 -0
  475. package/static/dist/assets/beancount-pc6q2dmq.js +1 -0
  476. package/static/dist/assets/berry-l96jp8n4.js +1 -0
  477. package/static/dist/assets/bibtex-jxn7maz2.js +1 -0
  478. package/static/dist/assets/bicep-i1yv3ngd.js +1 -0
  479. package/static/dist/assets/bird2-k3aagqn0.js +1 -0
  480. package/static/dist/assets/blade-boo0u92y.js +1 -0
  481. package/static/dist/assets/blockDiagram-I7D4REHJ-hd3gw0cj.js +129 -0
  482. package/static/dist/assets/bsl-f8m1ja7w.js +1 -0
  483. package/static/dist/assets/c-cat0w3sc.js +1 -0
  484. package/static/dist/assets/c3-j6ile59o.js +1 -0
  485. package/static/dist/assets/c4Diagram-7LVT6UL2-jlmbjiji.js +38 -0
  486. package/static/dist/assets/cadence-e3x2pgsu.js +1 -0
  487. package/static/dist/assets/cairo-mblac9dt.js +1 -0
  488. package/static/dist/assets/catppuccin-frappe-dj7qsd4s.js +1 -0
  489. package/static/dist/assets/catppuccin-latte-oh2bx7qn.js +1 -0
  490. package/static/dist/assets/catppuccin-macchiato-m3xyazph.js +1 -0
  491. package/static/dist/assets/catppuccin-mocha-m3rjj89j.js +1 -0
  492. package/static/dist/assets/channel-ix8cxdld.js +1 -0
  493. package/static/dist/assets/chunk-2Q5K7J3B-k08c29qw.js +1 -0
  494. package/static/dist/assets/chunk-4HAMMTFA-oydbpsin.js +62 -0
  495. package/static/dist/assets/chunk-5VM5RSS4-br0zxsux.js +15 -0
  496. package/static/dist/assets/chunk-75Z2AOVW-j633otj1.js +2 -0
  497. package/static/dist/assets/chunk-DU6HZSFF-ods2l027.js +128 -0
  498. package/static/dist/assets/chunk-F27PBJKO-ln4n2cwt.js +1 -0
  499. package/static/dist/assets/chunk-FOHPRMQF-opuflp73.js +161 -0
  500. package/static/dist/assets/chunk-GMAD6QVW-fpz133eh.js +72 -0
  501. package/static/dist/assets/chunk-GVQU2GXP-abs9ixiy.js +1 -0
  502. package/static/dist/assets/chunk-IMKFNOWR-j9vukdy1.js +231 -0
  503. package/static/dist/assets/chunk-JWPE2WC7-mw03m5s9.js +1 -0
  504. package/static/dist/assets/chunk-L3NEJ4N5-cfyw87r3.js +1 -0
  505. package/static/dist/assets/chunk-OSK3NFVY-baja78wr.js +10 -0
  506. package/static/dist/assets/chunk-P2QGCYS3-g66xby8h.js +1 -0
  507. package/static/dist/assets/chunk-POPQ4Y6H-osqu864t.js +1 -0
  508. package/static/dist/assets/chunk-PWAF6VOD-egejasv1.js +1 -0
  509. package/static/dist/assets/chunk-SHT3W25Y-l014h8zz.js +168 -0
  510. package/static/dist/assets/chunk-SVP7TREG-n3z10bp3.js +88 -0
  511. package/static/dist/assets/chunk-TICWLB2K-bzozkjc0.js +206 -0
  512. package/static/dist/assets/chunk-XXDRQBXY-n40pf9vy.js +1 -0
  513. package/static/dist/assets/chunk-Y2CYZVJY-n9gs7k4d.js +1 -0
  514. package/static/dist/assets/clarity-bdn1d8fx.js +1 -0
  515. package/static/dist/assets/classDiagram-ZZMXUADV-c210y3il.js +1 -0
  516. package/static/dist/assets/classDiagram-v2-VYDZK3BY-c210y3il.js +1 -0
  517. package/static/dist/assets/clojure-n5b9o4lq.js +1 -0
  518. package/static/dist/assets/cmake-f68p68h5.js +1 -0
  519. package/static/dist/assets/cobol-go622aep.js +1 -0
  520. package/static/dist/assets/codeowners-lgfjmw2s.js +1 -0
  521. package/static/dist/assets/codeql-cojlysyb.js +1 -0
  522. package/static/dist/assets/coffee-jwo1xjhq.js +1 -0
  523. package/static/dist/assets/common-lisp-ko58h8pe.js +1 -0
  524. package/static/dist/assets/copied-cjsttec1.js +1 -0
  525. package/static/dist/assets/coq-gns70ukp.js +1 -0
  526. package/static/dist/assets/cose-bilkent-JH36ORCC-eclcy6ja.js +1 -0
  527. package/static/dist/assets/cpp-nre7j0kq.js +1 -0
  528. package/static/dist/assets/crystal-lviso2je.js +1 -0
  529. package/static/dist/assets/csharp-kkz9hcxp.js +1 -0
  530. package/static/dist/assets/css-ywz9j4hb.js +1 -0
  531. package/static/dist/assets/csv-ol0sxk26.js +1 -0
  532. package/static/dist/assets/cue-h5naoluy.js +1 -0
  533. package/static/dist/assets/cynefin-OW5HDTMX-elpt7h1j.js +1 -0
  534. package/static/dist/assets/cynefinDiagram-5FMLGOSQ-5mzl5pi2.js +62 -0
  535. package/static/dist/assets/cypher-j2an42no.js +1 -0
  536. package/static/dist/assets/cytoscape.esm-bqu53u12.js +321 -0
  537. package/static/dist/assets/d-crxkca3p.js +1 -0
  538. package/static/dist/assets/dagre-GXQ25YYZ-ihwlhmfl.js +4 -0
  539. package/static/dist/assets/dagre-lkoknbjn.js +1 -0
  540. package/static/dist/assets/dark-plus-kin19s9x.js +1 -0
  541. package/static/dist/assets/dart-nsg0xvqh.js +1 -0
  542. package/static/dist/assets/dax-f82777dv.js +1 -0
  543. package/static/dist/assets/defaultLocale-eeoev87l.js +1 -0
  544. package/static/dist/assets/desktop-nvg88bea.js +1 -0
  545. package/static/dist/assets/diagram-S7CK7UJ4-4z90agaz.js +30 -0
  546. package/static/dist/assets/diagram-UQ7AKVKN-eifw0lg7.js +41 -0
  547. package/static/dist/assets/diagram-VSXAHHWV-ksh3sjqv.js +3 -0
  548. package/static/dist/assets/diagram-VX7I27RA-ett72f71.js +24 -0
  549. package/static/dist/assets/diagram-Z3DM3KII-l8scp8nu.js +24 -0
  550. package/static/dist/assets/diff-c5xjoml2.js +1 -0
  551. package/static/dist/assets/dist-c4c2m3kv.js +1 -0
  552. package/static/dist/assets/docker-s0pf2u92.js +1 -0
  553. package/static/dist/assets/dotenv-d2gvqxc1.js +1 -0
  554. package/static/dist/assets/dracula-eicfho73.js +1 -0
  555. package/static/dist/assets/dracula-soft-doslejd2.js +1 -0
  556. package/static/dist/assets/dream-maker-mz8d3pqb.js +1 -0
  557. package/static/dist/assets/ebnfDiagram-PWID7BFC-f0pxcmit.js +1 -0
  558. package/static/dist/assets/edge-dvv35byi.js +1 -0
  559. package/static/dist/assets/elixir-i2f1ki1g.js +1 -0
  560. package/static/dist/assets/elm-cpepyjm4.js +1 -0
  561. package/static/dist/assets/emacs-lisp-lhl63kmy.js +1 -0
  562. package/static/dist/assets/endpoints-ipnv1hd4.js +64 -0
  563. package/static/dist/assets/erDiagram-RLTQ6QDP-f47m5xh0.js +99 -0
  564. package/static/dist/assets/erb-idlyfheg.js +1 -0
  565. package/static/dist/assets/erlang-kbk8e1kh.js +1 -0
  566. package/static/dist/assets/eventmodeling-NTZA5JFV-d9b5a0ia.js +1 -0
  567. package/static/dist/assets/everforest-dark-cv4maom7.js +1 -0
  568. package/static/dist/assets/everforest-light-njdw7fx6.js +1 -0
  569. package/static/dist/assets/fennel-mm8r79co.js +1 -0
  570. package/static/dist/assets/fish-em0xdaat.js +1 -0
  571. package/static/dist/assets/flowDiagram-HODETNUW-kfxxsyrk.js +1 -0
  572. package/static/dist/assets/fluent-kzq1mil2.js +1 -0
  573. package/static/dist/assets/fortran-fixed-form-lwddxd99.js +1 -0
  574. package/static/dist/assets/fortran-free-form-jao3bzvo.js +1 -0
  575. package/static/dist/assets/fsharp-ouam7w4k.js +1 -0
  576. package/static/dist/assets/ganttDiagram-EL5Y4UJY-bmktjdi0.js +292 -0
  577. package/static/dist/assets/gdresource-gawivel9.js +1 -0
  578. package/static/dist/assets/gdscript-kcamxf4c.js +1 -0
  579. package/static/dist/assets/gdshader-hx54new5.js +1 -0
  580. package/static/dist/assets/genie-i5n3m91v.js +1 -0
  581. package/static/dist/assets/gherkin-lxn181dy.js +1 -0
  582. package/static/dist/assets/git-commit-e6qgtuh5.js +1 -0
  583. package/static/dist/assets/git-rebase-d9lpfpbn.js +1 -0
  584. package/static/dist/assets/gitGraph-4MIJSDKK-j2ueg258.js +1 -0
  585. package/static/dist/assets/gitGraphDiagram-WWUBYQGX-ms6586v9.js +106 -0
  586. package/static/dist/assets/github-dark-default-m0qs6qki.js +1 -0
  587. package/static/dist/assets/github-dark-dimmed-g0vz4qee.js +1 -0
  588. package/static/dist/assets/github-dark-high-contrast-hugubo52.js +1 -0
  589. package/static/dist/assets/github-dark-ljlyim8s.js +1 -0
  590. package/static/dist/assets/github-light-default-fgfak5mv.js +1 -0
  591. package/static/dist/assets/github-light-high-contrast-hka6wb3r.js +1 -0
  592. package/static/dist/assets/github-light-jhyi45t4.js +1 -0
  593. package/static/dist/assets/gleam-ix1dym3g.js +1 -0
  594. package/static/dist/assets/glimmer-js-39i0urfb.js +1 -0
  595. package/static/dist/assets/glimmer-ts-gdtd6n73.js +1 -0
  596. package/static/dist/assets/glsl-o1djm5pp.js +1 -0
  597. package/static/dist/assets/gn-cbzhsgl9.js +1 -0
  598. package/static/dist/assets/gnuplot-dr8f9gvm.js +1 -0
  599. package/static/dist/assets/go-enhtrrw8.js +1 -0
  600. package/static/dist/assets/graphlib-mrng1xrk.js +1 -0
  601. package/static/dist/assets/graphql-kj6rz55t.js +1 -0
  602. package/static/dist/assets/groovy-jff59t9e.js +1 -0
  603. package/static/dist/assets/gruvbox-dark-hard-lgsj7y5r.js +1 -0
  604. package/static/dist/assets/gruvbox-dark-medium-ezt86wrc.js +1 -0
  605. package/static/dist/assets/gruvbox-dark-soft-1a3jfdlm.js +1 -0
  606. package/static/dist/assets/gruvbox-light-hard-d82lddjc.js +1 -0
  607. package/static/dist/assets/gruvbox-light-medium-d3ffitv3.js +1 -0
  608. package/static/dist/assets/gruvbox-light-soft-e5gf7i2q.js +1 -0
  609. package/static/dist/assets/hack-pe1qqr2i.js +1 -0
  610. package/static/dist/assets/haml-cknnbcqs.js +1 -0
  611. package/static/dist/assets/handlebars-hwq64orh.js +1 -0
  612. package/static/dist/assets/haskell-o7nor2z2.js +1 -0
  613. package/static/dist/assets/haxe-4rwdh42h.js +1 -0
  614. package/static/dist/assets/hcl-nnng763h.js +1 -0
  615. package/static/dist/assets/highlighted-body-KPVGNVTW-ev52d2si.js +1 -0
  616. package/static/dist/assets/hjson-kscwmuzk.js +1 -0
  617. package/static/dist/assets/hlsl-kopkvwox.js +1 -0
  618. package/static/dist/assets/horizon-bright-gn4aqinx.js +1 -0
  619. package/static/dist/assets/horizon-h5nzrwlb.js +1 -0
  620. package/static/dist/assets/houston-kifsbmbx.js +1 -0
  621. package/static/dist/assets/html-derivative-h2i2pyjc.js +1 -0
  622. package/static/dist/assets/html-ha05xtvt.js +1 -0
  623. package/static/dist/assets/http-cf94zu27.js +1 -0
  624. package/static/dist/assets/hurl-lslax568.js +1 -0
  625. package/static/dist/assets/hxml-g52gxet6.js +1 -0
  626. package/static/dist/assets/hy-jc9www1l.js +1 -0
  627. package/static/dist/assets/imba-n9x1ej2f.js +1 -0
  628. package/static/dist/assets/index-bz7g7rhx.css +2 -0
  629. package/static/dist/assets/index-hywfjw88.js +11 -0
  630. package/static/dist/assets/info-A6RAGUB7-j717vyt7.js +1 -0
  631. package/static/dist/assets/infoDiagram-27XIBGKW-dsbpib36.js +2 -0
  632. package/static/dist/assets/ini-hg6eosim.js +1 -0
  633. package/static/dist/assets/init-ljpdkjra.js +1 -0
  634. package/static/dist/assets/ishikawaDiagram-5VMMS53U-n0sz3i2d.js +70 -0
  635. package/static/dist/assets/java-ndjf2o89.js +1 -0
  636. package/static/dist/assets/javascript-coftlwy3.js +1 -0
  637. package/static/dist/assets/jinja-etei9wy7.js +1 -0
  638. package/static/dist/assets/jison-hyekgft1.js +1 -0
  639. package/static/dist/assets/journeyDiagram-3NMN7TZE-mcxz1mme.js +139 -0
  640. package/static/dist/assets/json-lok0yaho.js +1 -0
  641. package/static/dist/assets/json5-e4tr07tg.js +1 -0
  642. package/static/dist/assets/jsonc-jbmkhoep.js +1 -0
  643. package/static/dist/assets/jsonl-j35ke6at.js +1 -0
  644. package/static/dist/assets/jsonnet-iewuy86o.js +1 -0
  645. package/static/dist/assets/jssm-m1449zlc.js +1 -0
  646. package/static/dist/assets/jsx-b64w0nbx.js +1 -0
  647. package/static/dist/assets/jsx-runtime-eu92a5pu.js +1 -0
  648. package/static/dist/assets/julia-jxxzz2yn.js +1 -0
  649. package/static/dist/assets/just-mc29iw9t.js +1 -0
  650. package/static/dist/assets/kanagawa-dragon-i9mmmgud.js +1 -0
  651. package/static/dist/assets/kanagawa-lotus-ev5oro17.js +1 -0
  652. package/static/dist/assets/kanagawa-wave-i06560uw.js +1 -0
  653. package/static/dist/assets/kanban-definition-UXKFOSKX-n6hq2lyc.js +89 -0
  654. package/static/dist/assets/kdl-kgzv1x9e.js +1 -0
  655. package/static/dist/assets/kotlin-nmx05b3f.js +1 -0
  656. package/static/dist/assets/kusto-fawmdms4.js +1 -0
  657. package/static/dist/assets/laserwave-goebpayr.js +1 -0
  658. package/static/dist/assets/latex-pafa03ft.js +1 -0
  659. package/static/dist/assets/lazyRouteComponent-faquioun.js +1 -0
  660. package/static/dist/assets/lean-jom31nm3.js +1 -0
  661. package/static/dist/assets/less-mwvscr3d.js +1 -0
  662. package/static/dist/assets/light-plus-mws1g85f.js +1 -0
  663. package/static/dist/assets/line-h5xq20s4.js +1 -0
  664. package/static/dist/assets/linear-becwlnhk.js +1 -0
  665. package/static/dist/assets/link-ib4prsn5.js +1 -0
  666. package/static/dist/assets/liquid-hz5c2pov.js +1 -0
  667. package/static/dist/assets/llvm-j5wmr3g9.js +1 -0
  668. package/static/dist/assets/log-eur4gurm.js +1 -0
  669. package/static/dist/assets/login-mvjys1ol.js +1 -0
  670. package/static/dist/assets/logo-j3h8r0pu.js +1 -0
  671. package/static/dist/assets/lua-leyf8mlb.js +1 -0
  672. package/static/dist/assets/luau-im5d6q8k.js +1 -0
  673. package/static/dist/assets/make-npl27xqy.js +1 -0
  674. package/static/dist/assets/markdown-fibv6lrm.js +1 -0
  675. package/static/dist/assets/marko-cn1fzpdv.js +1 -0
  676. package/static/dist/assets/matchContext-f8oy3lo7.js +1 -0
  677. package/static/dist/assets/material-theme-darker-dhm8zmok.js +1 -0
  678. package/static/dist/assets/material-theme-gdimc61s.js +1 -0
  679. package/static/dist/assets/material-theme-lighter-c1fszbb2.js +1 -0
  680. package/static/dist/assets/material-theme-ocean-iakojpmy.js +1 -0
  681. package/static/dist/assets/material-theme-palenight-hgxmykmi.js +1 -0
  682. package/static/dist/assets/matlab-o6nvvbf2.js +1 -0
  683. package/static/dist/assets/mdc-huhy88df.js +1 -0
  684. package/static/dist/assets/mdx-mmgeh26h.js +1 -0
  685. package/static/dist/assets/mermaid-HWGCJPDP-gmnkgf1n.js +1 -0
  686. package/static/dist/assets/mermaid-f9ad87lb.js +1 -0
  687. package/static/dist/assets/mermaid-parser.core-n9cuu5ue.js +7 -0
  688. package/static/dist/assets/min-dark-e5sicuw2.js +1 -0
  689. package/static/dist/assets/min-light-gst87fvf.js +1 -0
  690. package/static/dist/assets/mindmap-definition-YA3MSWOX-gta3u7z4.js +96 -0
  691. package/static/dist/assets/mipsasm-etoqqos6.js +1 -0
  692. package/static/dist/assets/mojo-fyyk0b1z.js +1 -0
  693. package/static/dist/assets/monokai-jl389a9o.js +1 -0
  694. package/static/dist/assets/moonbit-je86hbgx.js +1 -0
  695. package/static/dist/assets/move-g7w7k042.js +1 -0
  696. package/static/dist/assets/narrat-d1cszt6i.js +1 -0
  697. package/static/dist/assets/nextflow-fpd3eqgm.js +1 -0
  698. package/static/dist/assets/nextflow-groovy-nc95p6pm.js +1 -0
  699. package/static/dist/assets/nginx-e7lvhsmn.js +1 -0
  700. package/static/dist/assets/night-owl-light-b2j1bc8t.js +1 -0
  701. package/static/dist/assets/night-owl-nm3cgqfv.js +1 -0
  702. package/static/dist/assets/nim-dun5d8mk.js +1 -0
  703. package/static/dist/assets/nix-sxazvl0w.js +1 -0
  704. package/static/dist/assets/nord-jiiendyo.js +1 -0
  705. package/static/dist/assets/not-found-ccnnac39.js +1 -0
  706. package/static/dist/assets/nushell-nbi9xmdg.js +1 -0
  707. package/static/dist/assets/objective-c-oshe4ycx.js +1 -0
  708. package/static/dist/assets/objective-cpp-go29ofq1.js +1 -0
  709. package/static/dist/assets/ocaml-56kivtnr.js +1 -0
  710. package/static/dist/assets/odin-g772bhi5.js +1 -0
  711. package/static/dist/assets/onboarding-l6quy6gz.js +1 -0
  712. package/static/dist/assets/one-dark-pro-ext74l87.js +1 -0
  713. package/static/dist/assets/one-light-o5mlsn8i.js +1 -0
  714. package/static/dist/assets/openscad-e9e9mfyg.js +1 -0
  715. package/static/dist/assets/ordinal-d88pfdla.js +1 -0
  716. package/static/dist/assets/packet-AYTQ26CC-bpzuq5n6.js +1 -0
  717. package/static/dist/assets/pascal-dmg2rs6d.js +1 -0
  718. package/static/dist/assets/passkeys-ktie3sr0.js +1 -0
  719. package/static/dist/assets/path-b509aej0.js +1 -0
  720. package/static/dist/assets/pegDiagram-XKGWAZYB-juye13e5.js +1 -0
  721. package/static/dist/assets/perl-bj3oqtet.js +1 -0
  722. package/static/dist/assets/php-buzp12r0.js +1 -0
  723. package/static/dist/assets/pie-WAS4IAKB-odhb8buq.js +1 -0
  724. package/static/dist/assets/pieDiagram-E7YTZNPT-mgb14ksh.js +39 -0
  725. package/static/dist/assets/pkl-co2grztk.js +1 -0
  726. package/static/dist/assets/plastic-mm7c7uq7.js +1 -0
  727. package/static/dist/assets/plsql-l0i9f33n.js +1 -0
  728. package/static/dist/assets/po-f3ga9osn.js +1 -0
  729. package/static/dist/assets/poimandres-mnwq5v4d.js +1 -0
  730. package/static/dist/assets/polar-ldicpe6g.js +1 -0
  731. package/static/dist/assets/postcss-fgpvnt75.js +1 -0
  732. package/static/dist/assets/powerquery-mflxo46x.js +1 -0
  733. package/static/dist/assets/powershell-oado4mxi.js +1 -0
  734. package/static/dist/assets/prepaint-f14kbd49.js +21 -0
  735. package/static/dist/assets/prisma-go1e7wq8.js +1 -0
  736. package/static/dist/assets/prolog-cbjal8aj.js +1 -0
  737. package/static/dist/assets/proto-lrhov7xc.js +1 -0
  738. package/static/dist/assets/pug-mjad7he9.js +1 -0
  739. package/static/dist/assets/puppet-h22szrmn.js +1 -0
  740. package/static/dist/assets/purescript-dwpj7hdb.js +1 -0
  741. package/static/dist/assets/python-b767fgue.js +1 -0
  742. package/static/dist/assets/qml-d1gdp10s.js +1 -0
  743. package/static/dist/assets/qmldir-lsawched.js +1 -0
  744. package/static/dist/assets/qss-lyvmg4f4.js +1 -0
  745. package/static/dist/assets/quadrantDiagram-AXDQQJYC-cg36naog.js +7 -0
  746. package/static/dist/assets/r-o3r9c7ao.js +1 -0
  747. package/static/dist/assets/racket-nbfqjw5h.js +1 -0
  748. package/static/dist/assets/radar-RG4KPBEZ-lgzfpnef.js +1 -0
  749. package/static/dist/assets/railroad-74A4TZTK-gmglc3ua.js +1 -0
  750. package/static/dist/assets/railroad-abnf-HS5TGJTU-5mjl5wpv.js +1 -0
  751. package/static/dist/assets/railroad-ebnf-LZEXJU2U-bfram8v8.js +1 -0
  752. package/static/dist/assets/railroad-peg-WCYAUIDC-nwfczn88.js +1 -0
  753. package/static/dist/assets/railroadDiagram-O6MQD6OU-cmxwjt73.js +1 -0
  754. package/static/dist/assets/raku-hcy6uwm6.js +1 -0
  755. package/static/dist/assets/razor-ilc585yr.js +1 -0
  756. package/static/dist/assets/red-if4jt303.js +1 -0
  757. package/static/dist/assets/refresh-cw-mf192q3f.js +1 -0
  758. package/static/dist/assets/reg-ivi9d1my.js +1 -0
  759. package/static/dist/assets/regexp-mpjtcvwm.js +1 -0
  760. package/static/dist/assets/rel-gqpkg3ta.js +1 -0
  761. package/static/dist/assets/requirementDiagram-BXWQKSXE-ir0s2mek.js +84 -0
  762. package/static/dist/assets/riscv-j1f2yjp2.js +1 -0
  763. package/static/dist/assets/rolldown-runtime-b9miacsj.js +1 -0
  764. package/static/dist/assets/ron-bjqasg78.js +1 -0
  765. package/static/dist/assets/root-mbklpuv4.js +1 -0
  766. package/static/dist/assets/rose-pine-dawn-nlpzqzob.js +1 -0
  767. package/static/dist/assets/rose-pine-grpwpg67.js +1 -0
  768. package/static/dist/assets/rose-pine-moon-b9yyevxr.js +1 -0
  769. package/static/dist/assets/rosmsg-hv3wcot6.js +1 -0
  770. package/static/dist/assets/rough.esm-on4k4je3.js +1 -0
  771. package/static/dist/assets/rst-jrkwjdyi.js +1 -0
  772. package/static/dist/assets/ruby-n4tjcbpz.js +1 -0
  773. package/static/dist/assets/rust-jqduclx1.js +1 -0
  774. package/static/dist/assets/sankeyDiagram-P5KCCOFB-jo7jcdq0.js +40 -0
  775. package/static/dist/assets/sas-hjo6y93k.js +1 -0
  776. package/static/dist/assets/sass-m1yncbwu.js +1 -0
  777. package/static/dist/assets/saved-g777hp1t.js +1 -0
  778. package/static/dist/assets/scala-l9ahg4e9.js +1 -0
  779. package/static/dist/assets/scheme-mlodf39l.js +1 -0
  780. package/static/dist/assets/scss-ncidjv48.js +1 -0
  781. package/static/dist/assets/sdbl-bwg2t5pt.js +1 -0
  782. package/static/dist/assets/search-i2kfogyx.js +1 -0
  783. package/static/dist/assets/sequenceDiagram-WJ2MYXX4-n0pf6xk5.js +162 -0
  784. package/static/dist/assets/settings_section_extensions_title-hgvh507l.js +1 -0
  785. package/static/dist/assets/shaderlab-be89ch6g.js +1 -0
  786. package/static/dist/assets/share._token-il3y6js2.js +2 -0
  787. package/static/dist/assets/shellscript-m8whjpqd.js +1 -0
  788. package/static/dist/assets/shellsession-i5bj6syz.js +1 -0
  789. package/static/dist/assets/sizeCapture-INFHLROL-g612l48j.js +1 -0
  790. package/static/dist/assets/slack-dark-ny9l0r7b.js +1 -0
  791. package/static/dist/assets/slack-ochin-g98102hs.js +1 -0
  792. package/static/dist/assets/sliders-horizontal-kd99ffvl.js +1 -0
  793. package/static/dist/assets/smalltalk-g9lpwisn.js +1 -0
  794. package/static/dist/assets/snazzy-light-dlu9q2m5.js +1 -0
  795. package/static/dist/assets/solarized-dark-mx1nd4sm.js +1 -0
  796. package/static/dist/assets/solarized-light-mqzd33fz.js +1 -0
  797. package/static/dist/assets/solidity-ih313ji4.js +1 -0
  798. package/static/dist/assets/soy-hhh36ysq.js +1 -0
  799. package/static/dist/assets/sparql-pewjbauq.js +1 -0
  800. package/static/dist/assets/splunk-d8q89juf.js +1 -0
  801. package/static/dist/assets/sql-j21bzcl6.js +1 -0
  802. package/static/dist/assets/src-hjyib4mu.js +1 -0
  803. package/static/dist/assets/ssh-config-fzxlymn8.js +1 -0
  804. package/static/dist/assets/stata-bbu2onh1.js +1 -0
  805. package/static/dist/assets/stateDiagram-D77RDMKH-olbrwyv9.js +1 -0
  806. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-dnzlmdpu.js +1 -0
  807. package/static/dist/assets/stylus-hifjg0ye.js +1 -0
  808. package/static/dist/assets/surrealql-l5vczuco.js +1 -0
  809. package/static/dist/assets/svelte-idn422g6.js +1 -0
  810. package/static/dist/assets/swift-n11rc3v3.js +1 -0
  811. package/static/dist/assets/swimlanes-42K2YHIH-od14zrw6.js +1 -0
  812. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-ck0eqqdp.js +8 -0
  813. package/static/dist/assets/synthwave-84-clkt7319.js +1 -0
  814. package/static/dist/assets/system-verilog-l8k1po2l.js +1 -0
  815. package/static/dist/assets/systemd-d1zoxedb.js +1 -0
  816. package/static/dist/assets/talonscript-j9gt5qb1.js +1 -0
  817. package/static/dist/assets/tasl-meer3lyk.js +1 -0
  818. package/static/dist/assets/tcl-jdc54rsg.js +1 -0
  819. package/static/dist/assets/templ-e22qou3c.js +1 -0
  820. package/static/dist/assets/terraform-oav3wzsy.js +1 -0
  821. package/static/dist/assets/tex-deqpc5ee.js +1 -0
  822. package/static/dist/assets/timeline-definition-24CTP7MA-l0qokmnb.js +120 -0
  823. package/static/dist/assets/todoStore-la6uyqzj.js +1 -0
  824. package/static/dist/assets/tokyo-night-cnyqlmc2.js +1 -0
  825. package/static/dist/assets/toml-jj1ee1k1.js +1 -0
  826. package/static/dist/assets/trash-bxz1k9cw.js +1 -0
  827. package/static/dist/assets/treeView-Q6P3EWNA-hma3ip8m.js +1 -0
  828. package/static/dist/assets/treemap-WGGIJYW6-bghjng5m.js +1 -0
  829. package/static/dist/assets/ts-tags-ctnibc27.js +1 -0
  830. package/static/dist/assets/tsv-cxbgj4wh.js +1 -0
  831. package/static/dist/assets/tsx-i163n32g.js +1 -0
  832. package/static/dist/assets/turtle-g1kfgpwv.js +1 -0
  833. package/static/dist/assets/twig-x0fi7wm8.js +1 -0
  834. package/static/dist/assets/typescript-g18c34xz.js +1 -0
  835. package/static/dist/assets/typespec-hokgy7ic.js +1 -0
  836. package/static/dist/assets/typst-l6lt2vta.js +1 -0
  837. package/static/dist/assets/untitled-nfaw0q7m.js +1 -0
  838. package/static/dist/assets/useForm-k7fj2zsr.js +1 -0
  839. package/static/dist/assets/useLocale-evhacmv2.js +1 -0
  840. package/static/dist/assets/useParams-gpbjxj8s.js +1 -0
  841. package/static/dist/assets/useSearch-bhdptos7.js +1 -0
  842. package/static/dist/assets/useSelector-f2aak1ol.js +1 -0
  843. package/static/dist/assets/v-lwnsi8rl.js +1 -0
  844. package/static/dist/assets/vala-db1h2kp7.js +1 -0
  845. package/static/dist/assets/vb-nrezymby.js +1 -0
  846. package/static/dist/assets/vennDiagram-4TSXK5OY-bo97zf3z.js +34 -0
  847. package/static/dist/assets/verilog-jworpiw4.js +1 -0
  848. package/static/dist/assets/vesper-o1vwtlg2.js +1 -0
  849. package/static/dist/assets/vhdl-gnn4vfnm.js +1 -0
  850. package/static/dist/assets/viml-ogfqutxv.js +1 -0
  851. package/static/dist/assets/vitesse-black-b5y62t71.js +1 -0
  852. package/static/dist/assets/vitesse-dark-fj1ju380.js +1 -0
  853. package/static/dist/assets/vitesse-light-bjycd3ha.js +1 -0
  854. package/static/dist/assets/vue-html-nskj3du0.js +1 -0
  855. package/static/dist/assets/vue-mwu5l1bb.js +1 -0
  856. package/static/dist/assets/vue-vine-cqe77smq.js +1 -0
  857. package/static/dist/assets/vyper-jsmo764l.js +1 -0
  858. package/static/dist/assets/wardley-WFR3VGLG-pbjl6jav.js +1 -0
  859. package/static/dist/assets/wardleyDiagram-VM6X3IG4-bmy9i09l.js +78 -0
  860. package/static/dist/assets/wasm-g1zrybde.js +1 -0
  861. package/static/dist/assets/wasm-gezzbmz0.js +1 -0
  862. package/static/dist/assets/wenyan-lgcd7t1n.js +1 -0
  863. package/static/dist/assets/wgsl-gotoh48n.js +1 -0
  864. package/static/dist/assets/wikitext-j1379ez7.js +1 -0
  865. package/static/dist/assets/wit-neu67rwu.js +1 -0
  866. package/static/dist/assets/wolfram-mbb1p6oi.js +1 -0
  867. package/static/dist/assets/xml-gau7gvrv.js +1 -0
  868. package/static/dist/assets/xsl-ew09zq8b.js +1 -0
  869. package/static/dist/assets/xychartDiagram-S5SC5T6Z-eercu0mc.js +7 -0
  870. package/static/dist/assets/yaml-ofnrxtvf.js +1 -0
  871. package/static/dist/assets/zenscript-ge1h0r83.js +1 -0
  872. package/static/dist/assets/zig-ik06evde.js +1 -0
  873. package/static/dist/extension-sdk.js +1 -0
  874. package/static/dist/index.html +1 -0
  875. package/static/dist/manifest.webmanifest +29 -0
  876. package/static/dist/sw.js +944 -0
@@ -0,0 +1,1357 @@
1
+ /**
2
+ * One agent turn: admission, pending-state persistence, the sidecar
3
+ * `chat.start` call, live frame relay into the journal and stream channel,
4
+ * transcript settlement, cancel, steer, approvals, and clarify answers
5
+ * (Python `api/routes.py::_start_chat_stream_for_session`,
6
+ * `api/streaming.py::_run_agent_streaming`, `cancel_stream`).
7
+ */
8
+ import { randomUUID } from 'node:crypto';
9
+ import { closeSync, fstatSync, readFileSync, rmSync } from 'node:fs';
10
+ import { openAnchoredFd } from '../workspace/fs.js';
11
+ import { isWithin, resolvePathLikePython } from '../workspace/paths.js';
12
+ import { SidecarError } from '../sidecar/client.js';
13
+ import { HttpFailure, markSessionTitleGenerated } from './service.js';
14
+ import { StreamRegistry, SessionChannels } from './streams.js';
15
+ import { PendingPrompts } from './pending.js';
16
+ import { RunJournal } from './journal.js';
17
+ import { Session, titleFrom } from './session.js';
18
+ import { buildActiveTurnToken, redactSessionData, redactString } from '../redact.js';
19
+ import { dict } from '../config/agent-config.js';
20
+ import { ReasoningTitleTracker, reasoningEventPayload } from './reasoning-titles.js';
21
+ import { messageWindowForDisplay, messagesForLimitedPayload, toolCallsForMessageWindow } from './window.js';
22
+ import { attachTodoState } from './todo.js';
23
+ import { persistentStateChanges, persistentStateSnapshot } from './state-saved.js';
24
+ import { maxIterationsFromConfig, maxTokensFromConfig, processWakeupMaxIterations, reasoningConfigFromConfig, webuiEphemeralSystemPrompt, workspaceSystemMessage } from './turn-context.js';
25
+ import { assistantReplyAddedAfterCurrentTurn, buildPartialMessage, extractToolCallsFromMessages, injectMaxIterationSummaryFallback, isContextCompressionMarker, isDict, mergeDisplayMessagesAfterAgentResult, messageIdentity, messageText, sanitizeMessagesForApi, sessionLacksFinalAssistantAnswer, splitThinkingFromContent, stripXmlToolCalls, workspaceContextPrefix } from './merge.js';
26
+ import { fallbackTitleFromExchange, firstExchangeSnippets, isGenericFallbackTitle, latestExchangeSnippets, looksInvalidGeneratedTitle, sanitizeGeneratedTitle, titleLanguageMismatch, titlePrompts } from './titles.js';
27
+ import { str } from '../util.js';
28
+ export const CHAT_LOCK_WAIT_SECONDS = 2;
29
+ const IMAGE_MODE_TIMEOUT_MS = 15_000;
30
+ const TERMINAL_SSE_VISIBLE_MESSAGE_LIMIT = 80;
31
+ export function classifyProviderError(errStr, opts = {}) {
32
+ const lower = errStr.toLowerCase();
33
+ if (opts.condition === 'credential_missing')
34
+ return { label: 'Authentication failed', type: 'auth_mismatch', hint: 'The selected model may not be supported by your configured provider or your API key is invalid. Run `hermes model` in your terminal to update credentials, then restart the WebUI.' };
35
+ const cancelled = ['cancelled by user', 'canceled by user', 'user cancelled', 'user canceled', 'task cancelled', 'task canceled', 'cancellederror'].some((k) => lower.includes(k));
36
+ if (cancelled)
37
+ return { label: 'Task cancelled', type: 'cancelled', hint: cancelledTurnHint() };
38
+ if (['interrupted by user', 'response interrupted', 'operation interrupted', 'operation was interrupted', 'operation aborted', 'request was aborted', 'aborterror'].some((k) => lower.includes(k))) {
39
+ return { label: 'Response interrupted', type: 'interrupted', hint: 'The run stopped before a provider response completed. If you did not cancel it, try again.' };
40
+ }
41
+ const quota = ['insufficient_quota', 'insufficient credits', 'out of credits', 'credits exhausted', 'quota exceeded', 'usage limit', 'plan limit', 'billing'].some((k) => lower.includes(k));
42
+ const poolEmpty = lower.includes('credential(s) exhausted') || lower.includes('credentials exhausted') || (lower.includes('credential') && lower.includes('exhausted'));
43
+ const auth = !quota && !poolEmpty && (errStr.includes('401') || lower.includes('authentication') || lower.includes('unauthorized') || lower.includes('invalid api key') || lower.includes('invalid_api_key') || lower.includes('no cookie auth credentials'));
44
+ const notFound = errStr.includes('404') || lower.includes('not found') || lower.includes('does not exist') || lower.includes('model_not_found') || lower.includes('invalid model') || lower.includes('does not match any known model') || lower.includes('unknown model');
45
+ const rateLimit = !quota && (lower.includes('rate limit') || errStr.includes('429'));
46
+ const compressionExhausted = lower.includes('compression_exhausted') || lower.includes('compression exhausted') || (lower.includes('context length exceeded') && lower.includes('cannot compress further'));
47
+ if (poolEmpty)
48
+ return { label: 'No usable credentials', type: 'credential_pool_empty', hint: 'The credential pool for this provider has no usable keys left (all entries exhausted or unconfigured). Add or refresh a key for this provider in your Hermes config / credential pool, or switch providers via `hermes model`.' };
49
+ if (quota)
50
+ return { label: 'Out of credits', type: 'quota_exhausted', hint: 'Your provider account is out of credits or usage. Top up, wait for the plan window to reset, or switch providers via `hermes model`.' };
51
+ if (rateLimit)
52
+ return { label: 'Rate limit reached', type: 'rate_limit', hint: 'Rate limit reached. The fallback model (if configured) was also exhausted. Try again in a moment.' };
53
+ if (auth)
54
+ return { label: 'Authentication failed', type: 'auth_mismatch', hint: 'The selected model may not be supported by your configured provider or your API key is invalid. Run `hermes model` in your terminal to update credentials, then restart the WebUI.' };
55
+ if (notFound)
56
+ return { label: 'Model not found', type: 'model_not_found', hint: 'The selected model was not found by the provider. Check the model ID in Settings or run `hermes model` to verify it exists for your provider.' };
57
+ if (compressionExhausted)
58
+ return { label: 'Context compression exhausted', type: 'compression_exhausted', hint: 'The conversation context is too large to compress safely. Start a new conversation or retry with a narrower task.' };
59
+ if (opts.silentFailure)
60
+ return { label: 'No response from provider', type: 'no_response', hint: 'The provider returned no content and no error. This often means a usage/rate limit was hit silently. Check provider status, switch providers via `hermes model`, or try again in a moment.' };
61
+ return { label: 'Error', type: 'error', hint: '' };
62
+ }
63
+ export function cancelledTurnHint(agentName = 'Hermes') {
64
+ return `${agentName} stopped this turn at your request. Send a new message to continue.`;
65
+ }
66
+ export function providerErrorPayload(message, errType, hint = '', redact = true) {
67
+ const safe = redact ? redactString(message).trim() : message;
68
+ const payload = { message: safe || message, type: errType };
69
+ if (hint)
70
+ payload.hint = hint;
71
+ if (safe)
72
+ payload.details = safe.length > 1200 ? `${safe.slice(0, 1197).trimEnd()}…` : safe;
73
+ return payload;
74
+ }
75
+ /** The image formats a provider accepts inline; the MIME comes from the bytes, never from the client. */
76
+ /**
77
+ * Python `_IMAGE_MAGIC` / `_is_valid_image`: the declared MIME must match the file's signature; BMP joins the binary
78
+ * set, and SVG (text, no signature) is accepted on its declared type when the bytes read as an SVG document.
79
+ */
80
+ export function sniffImageMime(bytes, declared = '') {
81
+ const mime = declared.split(';', 1)[0]?.trim().toLowerCase() ?? '';
82
+ if (mime === 'image/svg+xml')
83
+ return /^\s*(?:<\?xml[^>]*>\s*)?(?:<!--[\s\S]*?-->\s*)*(?:<!DOCTYPE[^>]*>\s*)?<svg[\s>]/i.test(bytes.subarray(0, 4096).toString('utf8')) ? 'image/svg+xml' : null;
84
+ if (bytes.length >= 8 && bytes.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])))
85
+ return 'image/png';
86
+ if (bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff)
87
+ return 'image/jpeg';
88
+ if (bytes.length >= 6 && ['GIF87a', 'GIF89a'].includes(bytes.subarray(0, 6).toString('latin1')))
89
+ return 'image/gif';
90
+ if (bytes.length >= 12 && bytes.subarray(0, 4).toString('latin1') === 'RIFF' && bytes.subarray(8, 12).toString('latin1') === 'WEBP')
91
+ return 'image/webp';
92
+ if (bytes.length >= 2 && bytes.subarray(0, 2).toString('latin1') === 'BM')
93
+ return 'image/bmp';
94
+ return null;
95
+ }
96
+ const COMPRESSION_RECOVERY_TERMINAL_STATE = 'compression_exhausted';
97
+ const COMPRESSION_RECOVERY_ACTION_START_FOCUSED = 'start_focused_continuation';
98
+ /** Python `build_compression_recovery_payload` + `stamp_compression_exhausted_recovery`. */
99
+ export function stampCompressionExhaustedRecovery(s, message, details) {
100
+ const positive = (v) => { const n = Math.trunc(Number(v) || 0); return n > 0 ? n : 0; };
101
+ const payload = {
102
+ terminal_state: COMPRESSION_RECOVERY_TERMINAL_STATE,
103
+ recommended_action: COMPRESSION_RECOVERY_ACTION_START_FOCUSED,
104
+ source_session_id: s.session_id,
105
+ created_at: Date.now() / 1000,
106
+ title: 'Context compression exhausted',
107
+ summary: 'This run could not safely shrink the conversation enough to continue in place. Start a focused continuation, then describe the next narrow task.',
108
+ action_label: 'Start focused continuation',
109
+ message: message.trim(),
110
+ details: details.trim().slice(0, 1200),
111
+ last_prompt_tokens: positive(s.last_prompt_tokens),
112
+ threshold_tokens: positive(s.threshold_tokens),
113
+ context_length: positive(s.context_length),
114
+ };
115
+ s.recommended_recovery_action = COMPRESSION_RECOVERY_ACTION_START_FOCUSED;
116
+ s.compression_recovery = payload;
117
+ return payload;
118
+ }
119
+ /** Python `is_generic_continuation_intent`: short, content-free continuation requests (incl. CJK), punctuation-normalised. */
120
+ const GENERIC_CONTINUATION_INTENTS = new Set(['continue', 'continue please', 'go on', 'keep going', 'resume', 'proceed', 'carry on', '继续', '继续吧', '接着', '接着做', '继续做', '继续执行']);
121
+ export function isGenericContinuationIntent(text) {
122
+ // Python `[\W_]+` under re.UNICODE keeps CJK letters; JS `\w` is ASCII-only, so the Unicode classes are spelled out.
123
+ const normalized = text.trim().toLowerCase().replaceAll(/[^\p{L}\p{N}]+/gu, ' ').trim();
124
+ if (!normalized)
125
+ return false;
126
+ if (GENERIC_CONTINUATION_INTENTS.has(normalized))
127
+ return true;
128
+ const parts = normalized.split(' ');
129
+ return parts.length > 0 && parts.length <= 2 && parts.every((p) => GENERIC_CONTINUATION_INTENTS.has(p));
130
+ }
131
+ /** Python `_explicit_text_signal`: `agent.image_input_mode: text` or a configured `auxiliary.vision` backend. */
132
+ export function explicitTextSignal(cfg) {
133
+ const agent = dict(cfg.agent);
134
+ if (str(agent.image_input_mode ?? 'auto').trim().toLowerCase() === 'text')
135
+ return true;
136
+ const vision = dict(dict(cfg.auxiliary).vision);
137
+ const provider = str(vision.provider).trim().toLowerCase();
138
+ return !['', 'auto'].includes(provider) || Boolean(str(vision.model).trim()) || Boolean(str(vision.base_url).trim());
139
+ }
140
+ export const GATEWAY_APPROVAL_RELAY_UNAVAILABLE = 'Gateway approval could not be relayed because the active run is unavailable. Reopen the session or retry after it reconnects.';
141
+ export class TurnRunner {
142
+ deps;
143
+ writers = new Map();
144
+ sessionPuts = new Map();
145
+ /** Per-session chain for sidecar YOLO mutation + local commit, so opposing toggles cannot interleave. */
146
+ yoloChains = new Map();
147
+ abortControllers = new Map();
148
+ steers = new Map();
149
+ /** Streams whose run completed (`done` emitted) and only await title work; a late cancel is a no-op for these. */
150
+ settledStreams = new Set();
151
+ constructor(deps) {
152
+ this.deps = deps;
153
+ }
154
+ get registry() { return this.deps.registry; }
155
+ // ── admission ────────────────────────────────────────────────────────────
156
+ /** Python `_start_chat_stream_for_session`: persist pending state, register the channel, launch the worker. */
157
+ start(session, opts) {
158
+ const s = session;
159
+ if (this.deps.profileDeleting?.(s.profile ?? null))
160
+ return { error: `Profile '${str(s.profile)}' is being deleted.`, _status: 409 };
161
+ const locked = s.active_stream_id;
162
+ if (locked) {
163
+ if (this.registry.liveIds.has(locked) || this.registry.activeRuns.has(locked))
164
+ return { error: 'session already has an active stream', active_stream_id: locked, _status: 409 };
165
+ this.deps.service().clearStaleStreamState(s);
166
+ if (s.active_stream_id)
167
+ return { error: 'session already has an active stream', active_stream_id: s.active_stream_id, _status: 409 };
168
+ }
169
+ else {
170
+ const blocking = this.registry.activeRunStreamForSession(s.session_id);
171
+ if (blocking)
172
+ return { error: 'session already has an active stream', active_stream_id: blocking, _status: 409 };
173
+ }
174
+ const streamId = randomUUID().replace(/-/g, '');
175
+ const wasHiddenEmpty = !s.messages.length && !s.context_messages.length && !s.pending_user_message;
176
+ const attachments = opts.attachments ?? [];
177
+ const source = str(s.source_tag).toLowerCase() === 'fork' ? 'fork' : (opts.source ?? 'webui');
178
+ s.workspace = opts.workspace;
179
+ s.model = opts.model;
180
+ s.model_provider = opts.modelProvider;
181
+ s.active_stream_id = streamId;
182
+ s.post_compression_context_tokens_estimate = null;
183
+ s.pending_user_message = opts.msg;
184
+ s.pending_attachments = attachments;
185
+ s.pending_started_at = this.deps.now();
186
+ s.pending_user_source = source;
187
+ if (['', 'Untitled', 'New Chat'].includes(str(s.title).trim())) {
188
+ const provisional = titleFrom([{ role: 'user', content: opts.msg }], s.title || 'Untitled');
189
+ if (provisional && !['', 'Untitled', 'New Chat'].includes(provisional.trim()))
190
+ s.title = provisional;
191
+ }
192
+ if (this.deps.saveMode() === 'eager')
193
+ this.checkpointUserMessage(s, opts.msg, attachments, s.pending_started_at, source);
194
+ this.deps.store.save(s);
195
+ this.registry.writebackOwners.set(s.session_id, streamId);
196
+ if (wasHiddenEmpty)
197
+ this.deps.events.publish('session_new', { profile: s.profile, sessionId: s.session_id });
198
+ try {
199
+ this.deps.workspaces.setLastWorkspace(opts.workspace, s.profile);
200
+ }
201
+ catch { /* best effort */ }
202
+ const channel = this.registry.create(streamId, s.session_id);
203
+ if (opts.goalRelated)
204
+ this.registry.goalRelated.add(streamId);
205
+ this.registry.registerActiveRun({ stream_id: streamId, session_id: s.session_id, started_at: this.deps.now(), phase: 'starting', workspace: opts.workspace, model: opts.model, provider: opts.modelProvider, ephemeral: Boolean(opts.ephemeral) });
206
+ void this.run(s.session_id, streamId, channel, opts).catch((error) => {
207
+ this.deps.log(`[webui] ERROR turn ${streamId} crashed\n${error instanceof Error ? (error.stack ?? error.message) : String(error)}`);
208
+ });
209
+ const response = { stream_id: streamId, session_id: s.session_id, pending_started_at: s.pending_started_at, turn_id: null, title: s.title };
210
+ if (opts.normalizedModel && opts.model)
211
+ response.effective_model = opts.model;
212
+ if (opts.modelProvider)
213
+ response.effective_model_provider = opts.modelProvider;
214
+ return response;
215
+ }
216
+ /** Python `_checkpoint_user_message_for_eager_session_save`. */
217
+ checkpointUserMessage(s, msg, attachments, startedAt, source) {
218
+ const latest = s.messages[s.messages.length - 1];
219
+ if (latest?.role === 'user' && messageText(latest.content).split(/\s+/).join(' ') === msg.split(/\s+/).join(' '))
220
+ return;
221
+ const user = { role: 'user', content: msg };
222
+ const token = buildActiveTurnToken(s.active_stream_id, startedAt);
223
+ if (token)
224
+ user._active_turn_token = token;
225
+ if (source !== 'webui')
226
+ user._source = source;
227
+ if (typeof startedAt === 'number' && startedAt > 0)
228
+ user.timestamp = startedAt;
229
+ if (attachments.length)
230
+ user.attachments = [...attachments];
231
+ s.messages.push(user);
232
+ }
233
+ // ── worker ───────────────────────────────────────────────────────────────
234
+ async run(sessionId, streamId, channel, opts) {
235
+ const { deps } = this;
236
+ let writer = null;
237
+ try {
238
+ writer = deps.journal.writer(sessionId, streamId);
239
+ this.writers.set(streamId, writer);
240
+ }
241
+ catch (error) {
242
+ deps.log(`[webui] WARNING: run journal degraded for stream ${streamId}: ${error.message}`);
243
+ }
244
+ const put = (event, data) => {
245
+ for (const steerEvent of this.takeSteerEventsBefore(streamId, event))
246
+ put(steerEvent[0], steerEvent[1]);
247
+ if (this.registry.cancelled.has(streamId) && !['cancel', 'apperror', 'steer_consumed', 'pending_steer_leftover'].includes(event))
248
+ return;
249
+ // `cancel()` already wrote the terminal row and closed the stream: the worker's unwind adds no second one.
250
+ if (event === 'cancel' && !this.registry.streams.has(streamId)) {
251
+ try {
252
+ deps.onTerminal?.(streamId, 'cancelled');
253
+ }
254
+ catch { /* best effort */ }
255
+ return;
256
+ }
257
+ let eventId = null;
258
+ if (writer) {
259
+ try {
260
+ eventId = writer.appendSseEvent(event, data).event_id;
261
+ }
262
+ catch (error) {
263
+ deps.log(`[webui] WARNING: run journal append failed for ${streamId}/${event}: ${error.message}`);
264
+ }
265
+ }
266
+ channel.put([event, data, eventId]);
267
+ if (event === 'done' || event === 'cancel' || event === 'apperror' || event === 'error') {
268
+ try {
269
+ deps.onTerminal?.(streamId, event === 'done' ? 'completed' : event === 'cancel' ? 'cancelled' : 'failed');
270
+ }
271
+ catch { /* best effort */ }
272
+ }
273
+ };
274
+ this.sessionPuts.set(sessionId, put);
275
+ const activeRun = this.registry.activeRuns.get(streamId);
276
+ let s;
277
+ try {
278
+ s = deps.store.get(sessionId);
279
+ }
280
+ catch {
281
+ put('apperror', { type: 'error', message: 'Session not found', session_id: sessionId });
282
+ this.teardown(sessionId, streamId);
283
+ return;
284
+ }
285
+ const msgText = opts.msg;
286
+ const previousMessages = structuredClone(s.messages);
287
+ // Python `reconciled_state_db_messages_for_session(prefer_context=True)`: the model history is the owner context
288
+ // extended append-only with the Agent's state.db rows (a CLI continuation of this session reaches the model), except
289
+ // for a compressed context whose anchor cannot be verified — that stays context-only.
290
+ const localContext = s.context_messages.length ? s.context_messages : s.messages.filter((m) => !m._error && !m._partial);
291
+ const previousContext = structuredClone(localContext.some((m) => isContextCompressionMarker(m)) ? localContext : deps.service().mergedTranscript(s, localContext));
292
+ // Python `_sanitize_messages_for_api`: the model never sees display-only rows or a replayed cancelled prompt.
293
+ const apiHistory = sanitizeMessagesForApi(previousContext);
294
+ const activeTurnToken = buildActiveTurnToken(streamId, s.pending_started_at);
295
+ const sidecar = deps.sidecar();
296
+ const partialText = this.registry.partialText.get(streamId) ?? [];
297
+ const reasoningText = this.registry.reasoningText.get(streamId) ?? [];
298
+ const liveToolCalls = this.registry.liveToolCalls.get(streamId) ?? [];
299
+ let tokenSent = false;
300
+ let firstTokenAt = null;
301
+ const titles = new ReasoningTitleTracker();
302
+ let capturedTerminalError = null;
303
+ const controller = new AbortController();
304
+ this.abortControllers.set(streamId, controller);
305
+ const workspaceCtx = workspaceContextPrefix(opts.workspace);
306
+ const userMessage = await this.buildUserMessage(workspaceCtx, msgText, opts.attachments ?? [], opts.workspace, sessionId, s, opts, controller.signal);
307
+ if (activeRun)
308
+ activeRun.phase = 'running';
309
+ const settledAt = { value: false };
310
+ let failed = false;
311
+ try {
312
+ if (!sidecar)
313
+ throw new SidecarError('The Agent sidecar is not running; chat is unavailable until it starts.', { condition: 'sidecar_unavailable' });
314
+ // Python: budgets, reasoning config, personality and delivery context come from the profile's config.yaml; the
315
+ // system message carries the frozen session workspace, the WebUI guidance rides as the ephemeral prompt.
316
+ const cfg = (await deps.profileConfig?.(s.profile ?? null)) ?? {};
317
+ // The sidecar's per-session YOLO state does not survive its restarts: push the local flag (either way) before
318
+ // the turn so a stale sidecar-side enable cannot auto-approve a session the UI reports as guarded.
319
+ await this.withYoloLock(sessionId, async () => { try {
320
+ await sidecar.call('approval.set_yolo', { session_id: sessionId, enabled: deps.service().yolo(sessionId).yolo_enabled === true });
321
+ }
322
+ catch { /* the approval path re-asserts it */ } });
323
+ // Last yield before `chat.start`: a cancel that landed during the attachment, config or YOLO awaits must not
324
+ // start an Agent turn (the client also refuses an already-aborted signal outright).
325
+ if (this.registry.cancelled.has(streamId)) {
326
+ this.finalizeCancelled(s, streamId, 'Task cancelled before start.', opts.ephemeral);
327
+ put('cancel', this.cancelPayload('Cancelled before start'));
328
+ return;
329
+ }
330
+ const frozenWorkspace = str(s.created_workspace) || str(s.workspace);
331
+ const turnContext = {
332
+ system_message: workspaceSystemMessage(frozenWorkspace),
333
+ ephemeral_system_prompt: webuiEphemeralSystemPrompt({ config: cfg, personality: str(s.personality) || null, sessionId, profile: s.profile ?? null, workspace: frozenWorkspace, hermesHome: deps.profileHome(s.profile), homeDisplay: deps.homeDisplay?.() ?? '~/.hermes' }),
334
+ max_iterations: processWakeupMaxIterations(maxIterationsFromConfig(cfg), opts.source ?? 'webui', deps.env ?? {}),
335
+ max_tokens: maxTokensFromConfig(cfg),
336
+ reasoning_config: reasoningConfigFromConfig(cfg, opts.modelProvider ?? s.model_provider ?? null),
337
+ };
338
+ // Python `_public_prefill_context_status`: the session-recall prefill hook was dropped with TAL-245, so the
339
+ // context frame always reports the not-configured shape.
340
+ const persistentBefore = persistentStateSnapshot(deps.profileHome(s.profile));
341
+ put('context_status', { session_id: sessionId, prefill: { status: 'not_configured', source: 'none', label: '', message_count: 0 } });
342
+ const result = await sidecar.call('chat.start', {
343
+ profile_home: deps.profileHome(s.profile), session_id: sessionId, stream_id: streamId, workspace: opts.workspace, model: opts.model ?? '', model_provider: opts.modelProvider,
344
+ user_message: userMessage, ...turnContext, conversation_history: apiHistory, enabled_toolsets: deps.toolsetsFor(s),
345
+ }, {
346
+ signal: controller.signal,
347
+ timeoutMs: 0,
348
+ onStream: (frame) => {
349
+ const data = isDict(frame.data) ? frame.data : {};
350
+ switch (frame.event) {
351
+ case 'token': {
352
+ tokenSent = true;
353
+ firstTokenAt ??= deps.now();
354
+ // Visible text closes the reasoning segment: a stable title snapshot (or a clear) goes out first.
355
+ const stable = titles.stableSnapshot(reasoningText.join(''));
356
+ if (stable !== null)
357
+ put('reasoning', { text: '', titles: stable });
358
+ partialText.push(str(data.text));
359
+ put('token', { text: str(data.text) });
360
+ return;
361
+ }
362
+ case 'reasoning': {
363
+ if (partialText.length && !reasoningText.length)
364
+ titles.resetSegment();
365
+ reasoningText.push(str(data.text));
366
+ // Python `reasoning_event_payload`: bold or first-line titles ride on the delta they complete.
367
+ put('reasoning', titles.prepare(reasoningEventPayload(str(data.text), reasoningText.join(''))));
368
+ return;
369
+ }
370
+ case 'steer_pending':
371
+ for (const record of this.takeConsumedSteers(streamId, str(data.text), { keepLeftovers: true }))
372
+ put('steer_consumed', record);
373
+ return;
374
+ case 'tool':
375
+ liveToolCalls.push({ name: data.name, args: data.args ?? {}, tid: str(data.tid), done: false });
376
+ put('tool', data);
377
+ return;
378
+ case 'tool_complete':
379
+ for (let i = liveToolCalls.length - 1; i >= 0; i -= 1) {
380
+ const tc = liveToolCalls[i];
381
+ if (tc.done)
382
+ continue;
383
+ if ((str(data.tid) && tc.tid === str(data.tid)) || (!tc.tid && tc.name === data.name)) {
384
+ tc.done = true;
385
+ tc.snippet = data.preview;
386
+ break;
387
+ }
388
+ }
389
+ put('tool_complete', data);
390
+ return;
391
+ // Python: the live chat frame carries the queue head plus depth, not the entry that just arrived.
392
+ case 'approval': {
393
+ deps.pending.submitApproval(sessionId, { ...data, session_id: sessionId });
394
+ const { pending, pending_count } = deps.pending.approvalPending(sessionId);
395
+ put('approval', { ...(pending ?? data), pending_count });
396
+ return;
397
+ }
398
+ case 'clarify': {
399
+ deps.pending.submitClarify(sessionId, { ...data, session_id: sessionId, kind: 'clarify' });
400
+ const head = deps.pending.clarifyHeadFrame(sessionId);
401
+ if (head)
402
+ put('clarify', head);
403
+ return;
404
+ }
405
+ case 'clarify_resolved': {
406
+ const { head } = deps.pending.resolveClarify(sessionId, str(data.clarify_id));
407
+ if (head)
408
+ put('clarify', head);
409
+ return;
410
+ }
411
+ case 'status':
412
+ if (data.kind === 'terminal_error' && !capturedTerminalError)
413
+ capturedTerminalError = str(data.message);
414
+ return;
415
+ default:
416
+ put(frame.event, data);
417
+ }
418
+ },
419
+ });
420
+ settledAt.value = true;
421
+ if (this.registry.cancelled.has(streamId) || result.status === 'cancelled') {
422
+ this.finalizeCancelled(s, streamId, 'Task cancelled.', opts.ephemeral);
423
+ put('cancel', this.cancelFrame(sessionId));
424
+ return;
425
+ }
426
+ if (opts.ephemeral) {
427
+ let answer = '';
428
+ for (let i = result.messages.length - 1; i >= 0; i -= 1) {
429
+ const m = result.messages[i];
430
+ if (m.role === 'assistant') {
431
+ answer = str(m.content);
432
+ break;
433
+ }
434
+ }
435
+ opts.onDone?.(answer);
436
+ // Python `_ephemeral_session_payload`: only role and content leave the server for a btw turn.
437
+ put('done', { session: { session_id: sessionId, messages: (result.messages).map((m) => ({ role: m.role, content: m.content })) }, usage: { input_tokens: 0, output_tokens: 0 }, ephemeral: true, answer });
438
+ try {
439
+ rmSync(deps.store.pathFor(sessionId), { force: true });
440
+ }
441
+ catch { /* ignore */ }
442
+ deps.store.sessions.delete(sessionId);
443
+ return;
444
+ }
445
+ // Writeback only while this stream still owns the session (Python `_stream_writeback_is_current`).
446
+ let current;
447
+ try {
448
+ current = deps.store.get(sessionId);
449
+ }
450
+ catch {
451
+ current = s;
452
+ }
453
+ if (current.active_stream_id !== streamId) {
454
+ deps.log(`[webui] Skipping stale stream writeback for session ${sessionId} stream ${streamId}; active_stream_id=${current.active_stream_id ?? ''}`);
455
+ return;
456
+ }
457
+ s = current;
458
+ // Python `_maybe_inject_max_iteration_summary_fallback`: an exhausted tool budget leaves the closing
459
+ // explanation in `final_response` only, so it becomes the turn's assistant answer before anything else reads it.
460
+ const resultMessages = result.tool_limit_reached ? injectMaxIterationSummaryFallback(result.messages, result.final_response) : result.messages;
461
+ // Python `_assistant_reply_added_after_current_turn`: replayed history never counts as this turn's answer (the
462
+ // sidecar reports `completed` whenever a failed run still carries messages).
463
+ // Python's second chance: a turn that emitted no new row still counts when the merged transcript it produced
464
+ // ends on a final answer (the current user row or trailing tool activity makes it "lacking").
465
+ const mergedForCheck = () => mergeDisplayMessagesAfterAgentResult(previousMessages, previousContext, resultMessages, msgText, { source: opts.source ?? 'webui', activeTurnToken, now: deps.now() });
466
+ const assistantAdded = assistantReplyAddedAfterCurrentTurn(resultMessages, previousContext, msgText) || !sessionLacksFinalAssistantAnswer(mergedForCheck());
467
+ const lastErr = result.error ?? capturedTerminalError ?? '';
468
+ // Python `_turn_transcript_lacks_final_assistant_answer`: a partial result with no final answer is a silent failure even if tokens streamed.
469
+ const stalePartial = result.result_status === 'partial' && !assistantAdded;
470
+ if (result.status === 'error' || (!assistantAdded && !tokenSent) || stalePartial) {
471
+ const classification = classifyProviderError(lastErr, { silentFailure: !lastErr });
472
+ const errStr = lastErr || `${classification.label}.`;
473
+ const payload = providerErrorPayload(errStr, classification.type, classification.hint, deps.redactEnabled());
474
+ this.persistError(s, streamId, classification.label, payload, activeTurnToken);
475
+ payload.session = redactSessionData(this.terminalSessionPayload(s), deps.redactEnabled());
476
+ payload.session_id = s.session_id;
477
+ payload.old_session_id = sessionId;
478
+ put('apperror', payload);
479
+ failed = true;
480
+ return;
481
+ }
482
+ // ── settle the transcript ──
483
+ s.messages = mergeDisplayMessagesAfterAgentResult(previousMessages, previousContext, resultMessages, msgText, { source: opts.source ?? 'webui', activeTurnToken, now: deps.now() });
484
+ s.context_messages = dedupeContext(resultMessages);
485
+ for (const m of s.messages) {
486
+ if (m.role !== 'assistant')
487
+ continue;
488
+ if (typeof m.content === 'string')
489
+ m.content = stripXmlToolCalls(m.content);
490
+ }
491
+ if (result.compressed) {
492
+ s.compression_anchor_visible_idx = Math.max(0, previousMessages.length - 1);
493
+ put('compressed', { session_id: sessionId, old_session_id: sessionId, new_session_id: sessionId, continuation_session_id: sessionId, message: 'Compression finished' });
494
+ }
495
+ const now = deps.now();
496
+ for (const m of s.messages)
497
+ m.timestamp ??= now;
498
+ if (['Untitled', 'New Chat', ''].includes(str(s.title)))
499
+ s.title = titleFrom(s.messages, s.title);
500
+ const usage = result.usage;
501
+ const prevInputTokens = s.input_tokens || 0;
502
+ const prevCacheReadTokens = s.cache_read_tokens || 0;
503
+ if (usage.prompt_tokens > 0)
504
+ s.input_tokens = usage.prompt_tokens;
505
+ if (usage.completion_tokens > 0)
506
+ s.output_tokens = usage.completion_tokens;
507
+ if (usage.estimated_cost_usd !== null)
508
+ s.estimated_cost = usage.estimated_cost_usd;
509
+ if (usage.cache_read_tokens > 0)
510
+ s.cache_read_tokens = usage.cache_read_tokens;
511
+ if (usage.cache_write_tokens > 0)
512
+ s.cache_write_tokens = usage.cache_write_tokens;
513
+ if (typeof result.context.context_length === 'number')
514
+ s.context_length = result.context.context_length;
515
+ if (typeof result.context.threshold_tokens === 'number')
516
+ s.threshold_tokens = result.context.threshold_tokens;
517
+ if (typeof result.context.last_prompt_tokens === 'number')
518
+ s.last_prompt_tokens = result.context.last_prompt_tokens;
519
+ s.tool_calls = extractToolCallsFromMessages(s.messages, liveToolCalls);
520
+ s.active_stream_id = null;
521
+ s.pending_user_message = null;
522
+ s.pending_attachments = [];
523
+ s.pending_started_at = null;
524
+ s.pending_user_source = null;
525
+ const attachments = opts.attachments ?? [];
526
+ if (attachments.length) {
527
+ const names = attachments.map((a) => str(a.name || a.filename)).filter(Boolean);
528
+ for (let i = s.messages.length - 1; i >= 0; i -= 1) {
529
+ const m = s.messages[i];
530
+ if (m.role === 'user') {
531
+ const content = messageText(m.content);
532
+ const base = msgText.includes('\n\n[Attached files:') ? (msgText.split('\n\n[Attached files:')[0] ?? '').trim() : msgText;
533
+ if (content.includes(base.slice(0, 60)) || msgText.includes(content.slice(0, 60)))
534
+ m.attachments = names;
535
+ break;
536
+ }
537
+ }
538
+ }
539
+ const prevAssistants = previousMessages.filter((m) => m.role === 'assistant').length;
540
+ const reasoning = reasoningText.join('');
541
+ let asstIdx = 0;
542
+ for (const m of s.messages) {
543
+ if (m.role !== 'assistant')
544
+ continue;
545
+ const turnIdx = asstIdx;
546
+ asstIdx += 1;
547
+ if (turnIdx < prevAssistants)
548
+ continue;
549
+ const existing = str(m.reasoning);
550
+ if (typeof m.content === 'string' && m.content) {
551
+ const [content, merged] = splitThinkingFromContent(m.content, existing);
552
+ m.content = content;
553
+ if (merged)
554
+ m.reasoning = merged;
555
+ }
556
+ if (turnIdx === prevAssistants && reasoning.trim() && !m.reasoning)
557
+ m.reasoning = reasoning.trim();
558
+ }
559
+ const duration = Math.max(0, now - previousStartedAt(s, activeRun));
560
+ for (let i = s.messages.length - 1; i >= 0; i -= 1) {
561
+ const m = s.messages[i];
562
+ if (m.role === 'assistant') {
563
+ m._turnDuration = Math.round(duration * 1000) / 1000;
564
+ if (usage.completion_tokens && duration > 0)
565
+ m._turnTps = Math.round((usage.completion_tokens / duration) * 10) / 10;
566
+ if (result.model)
567
+ m._usedModel = result.model;
568
+ break;
569
+ }
570
+ }
571
+ deps.store.save(s);
572
+ deps.pending.clearApprovals(sessionId);
573
+ deps.pending.clearClarifies(sessionId);
574
+ deps.events.publish('session_done', { profile: s.profile, sessionId: s.session_id });
575
+ // Python `_persistent_state_changes`: toast memory/skill files the agent wrote during the turn.
576
+ try {
577
+ const changes = persistentStateChanges(persistentBefore, persistentStateSnapshot(deps.profileHome(s.profile)));
578
+ if (changes.memory_saved)
579
+ put('state_saved', { session_id: sessionId, kind: 'memory', action: 'saved' });
580
+ for (const change of changes.skills)
581
+ put('state_saved', { session_id: sessionId, kind: 'skill', action: change.action, name: change.name });
582
+ }
583
+ catch (error) {
584
+ deps.log(`[webui] persistent state change detection failed for ${sessionId}: ${error.message}`);
585
+ }
586
+ // Python usage payload: session counters plus the per-turn timing and cache-hit figures the clients label with.
587
+ const cacheHit = (cacheRead, prompt) => (cacheRead > 0 && prompt > 0 ? Math.min(100, Math.round((cacheRead / prompt) * 100)) : null);
588
+ const inputTokens = s.input_tokens || 0;
589
+ const cacheReadTokens = s.cache_read_tokens || 0;
590
+ const turnInput = Math.max(0, inputTokens - prevInputTokens);
591
+ const turnCacheRead = Math.max(0, cacheReadTokens - prevCacheReadTokens);
592
+ const doneUsage = {
593
+ input_tokens: s.input_tokens, output_tokens: s.output_tokens, estimated_cost: s.estimated_cost, cache_read_tokens: s.cache_read_tokens, cache_write_tokens: s.cache_write_tokens,
594
+ cache_hit_percent: cacheHit(cacheReadTokens, inputTokens), turn_cache_hit_percent: cacheHit(turnCacheRead, turnInput), duration_seconds: Math.round(duration * 1000) / 1000,
595
+ context_length: s.context_length ?? 0, threshold_tokens: s.threshold_tokens ?? 0, last_prompt_tokens: s.last_prompt_tokens ?? 0,
596
+ };
597
+ if (usage.completion_tokens && duration > 0)
598
+ doneUsage.tps = Math.round((usage.completion_tokens / duration) * 10) / 10;
599
+ if (firstTokenAt !== null)
600
+ doneUsage.ttft_ms = Math.max(0, Math.round((firstTokenAt - previousStartedAt(s, activeRun)) * 1000));
601
+ const usedModel = str(result.model) || str(opts.model) || str(s.model);
602
+ if (usedModel)
603
+ doneUsage.used_model = usedModel;
604
+ const donePayload = {
605
+ session: redactSessionData(this.terminalSessionPayload(s), deps.redactEnabled()),
606
+ usage: doneUsage,
607
+ };
608
+ if (result.tool_limit_reached) {
609
+ donePayload.terminal_state = 'tool_limit_reached';
610
+ donePayload.terminal_reason = 'max_iterations';
611
+ }
612
+ put('done', donePayload);
613
+ for (const [event, payload] of this.finalizeSteerEvents(streamId, result.pending_steer))
614
+ put(event, payload);
615
+ // The turn is over: release admission before the title work so a follow-up message is accepted while the
616
+ // (up to two) title prompts run; the channel and journal stay open for the `title` events (Python retired the
617
+ // worker before its daemon-thread title generation).
618
+ this.registry.activeRuns.delete(streamId);
619
+ this.settledStreams.add(streamId);
620
+ await this.backgroundTitle(s, put);
621
+ // Python: the last non-error assistant reply with content, else "(no answer produced)".
622
+ if (opts.onDone) {
623
+ let answer = '';
624
+ for (let i = s.messages.length - 1; i >= 0; i -= 1) {
625
+ const m = s.messages[i];
626
+ if (m.role !== 'assistant' || m._error)
627
+ continue;
628
+ const text = messageText(m.content).trim();
629
+ if (text) {
630
+ answer = text;
631
+ break;
632
+ }
633
+ }
634
+ opts.onDone(answer || '(no answer produced)');
635
+ }
636
+ }
637
+ catch (error) {
638
+ if (settledAt.value && !(error instanceof SidecarError)) {
639
+ deps.log(`[webui] ERROR settling turn ${streamId}\n${error instanceof Error ? (error.stack ?? error.message) : String(error)}`);
640
+ }
641
+ failed = true;
642
+ if (this.registry.cancelled.has(streamId)) {
643
+ this.finalizeCancelled(s, streamId, 'Task cancelled.', opts.ephemeral);
644
+ put('cancel', this.cancelFrame(sessionId));
645
+ return;
646
+ }
647
+ const message = error instanceof Error ? error.message : String(error);
648
+ const condition = error instanceof SidecarError ? error.condition : undefined;
649
+ const classification = classifyProviderError(message, { condition });
650
+ const payload = providerErrorPayload(message, classification.type, classification.hint, deps.redactEnabled());
651
+ if (condition === 'sidecar_unavailable' || condition === 'agent_runtime_stale') {
652
+ payload.type = condition === 'agent_runtime_stale' ? 'agent_runtime_stale' : 'sidecar_unavailable';
653
+ payload.condition = condition;
654
+ }
655
+ try {
656
+ let current = s;
657
+ try {
658
+ current = deps.store.get(sessionId);
659
+ }
660
+ catch {
661
+ current = s;
662
+ }
663
+ if (current.active_stream_id === streamId) {
664
+ this.persistError(current, streamId, classification.label, payload, activeTurnToken);
665
+ payload.session = redactSessionData(this.terminalSessionPayload(current), deps.redactEnabled());
666
+ }
667
+ }
668
+ catch (persistError) {
669
+ deps.log(`[webui] WARNING: failed to persist turn error for ${sessionId}: ${persistError.message}`);
670
+ }
671
+ payload.session_id = sessionId;
672
+ payload.old_session_id = sessionId;
673
+ put('apperror', payload);
674
+ }
675
+ finally {
676
+ this.teardown(sessionId, streamId);
677
+ // After persistence and teardown: a background route's failure cleanup deletes the hidden session, which must
678
+ // not race the error writeback above (a re-saved session would resurface in the sidebar).
679
+ if (failed)
680
+ opts.onFailed?.();
681
+ }
682
+ }
683
+ /**
684
+ * Python `_build_user_message`: image attachments are embedded as native `image_url` parts only when the Agent's
685
+ * resolved image mode for this model is `native` (text mode routes them through the Agent's vision tool path), and
686
+ * only after the bytes are read through an anchored descriptor and sniffed as a real image format.
687
+ */
688
+ async buildUserMessage(workspaceCtx, msgText, attachments, workspace, sessionId, s, opts, signal) {
689
+ const text = workspaceCtx + msgText;
690
+ const candidates = attachments.filter((att) => str(att.path).trim() && str(att.mime).trim().startsWith('image/'));
691
+ if (!candidates.length)
692
+ return text;
693
+ const sidecar = this.deps.sidecar();
694
+ if (!sidecar)
695
+ return text;
696
+ // A cancel that landed before this point is final: never start the lookup or wait on it.
697
+ if (signal.aborted)
698
+ return text;
699
+ try {
700
+ // A cancelled turn must not sit behind this lookup: the abort wins the race and `run()` then takes the cancelled path.
701
+ const lookup = sidecar.call('text.image_mode', { profile_home: this.deps.profileHome(s.profile), provider: str(opts.modelProvider ?? s.model_provider), model: str(opts.model ?? s.model) }, { signal, timeoutMs: IMAGE_MODE_TIMEOUT_MS });
702
+ const mode = await Promise.race([lookup, new Promise((_, reject) => { if (signal.aborted) {
703
+ reject(new Error('turn cancelled'));
704
+ return;
705
+ } signal.addEventListener('abort', () => { reject(new Error('turn cancelled')); }, { once: true }); })]);
706
+ // Python `_resolve_image_input_mode`: a canonical "text" is honoured only with an explicit text signal or a model
707
+ // KNOWN to be text-only; an unknown/custom model forwards natively and lets the Agent's retry guard downgrade.
708
+ if (mode.mode !== 'native') {
709
+ const cfg = (await this.deps.profileConfig?.(s.profile ?? null)) ?? {};
710
+ if (explicitTextSignal(cfg) || mode.supports_vision === false)
711
+ return text;
712
+ }
713
+ }
714
+ catch (error) {
715
+ if (!signal.aborted)
716
+ this.deps.log(`[webui] image mode lookup failed for ${sessionId}: ${error.message}`);
717
+ return text;
718
+ }
719
+ const parts = [{ type: 'text', text }];
720
+ let images = 0;
721
+ const roots = [workspace, this.deps.attachmentDir(sessionId)].map((r) => resolvePathLikePython(r));
722
+ for (const att of candidates) {
723
+ const target = resolvePathLikePython(str(att.path).trim());
724
+ const root = roots.find((r) => isWithin(target, r));
725
+ if (!root)
726
+ continue;
727
+ let fd;
728
+ try {
729
+ fd = openAnchoredFd(root, target, { wantDir: false });
730
+ }
731
+ catch {
732
+ continue;
733
+ }
734
+ try {
735
+ const st = fstatSync(fd);
736
+ // A hard link to a file outside the roots passes the pathname walk; refuse it on the inode like the share and media paths.
737
+ if (!st.isFile() || st.nlink > 1 || st.size <= 0 || st.size > 20 * 1024 * 1024)
738
+ continue;
739
+ const bytes = readFileSync(fd);
740
+ const sniffed = sniffImageMime(bytes, str(att.mime));
741
+ if (!sniffed)
742
+ continue;
743
+ parts.push({ type: 'image_url', image_url: { url: `data:${sniffed};base64,${bytes.toString('base64')}` } });
744
+ images += 1;
745
+ }
746
+ catch { /* skip unreadable */ }
747
+ finally {
748
+ closeSync(fd);
749
+ }
750
+ }
751
+ return images ? parts : text;
752
+ }
753
+ /**
754
+ * Python `_session_payload_with_terminal_window`: the same visible-message window contract as `GET /api/session?
755
+ * msg_limit=` (renderable rows, limited payload shape, windowed tool calls, todo state) with the full count.
756
+ */
757
+ terminalSessionPayload(s) {
758
+ const payload = s.compact({ includeRuntime: true, activeStreamIds: this.registry.liveIds });
759
+ const [window, offset] = messageWindowForDisplay(s.messages, TERMINAL_SSE_VISIBLE_MESSAGE_LIMIT, null);
760
+ const limited = messagesForLimitedPayload(window);
761
+ payload.messages = limited;
762
+ payload.message_count = s.messages.length;
763
+ payload._messages_offset = offset;
764
+ payload._messages_truncated = offset > 0;
765
+ attachTodoState(payload, s.messages);
766
+ payload.tool_calls = toolCallsForMessageWindow(s.tool_calls, offset, limited.length);
767
+ return payload;
768
+ }
769
+ /** Python `_materialize_pending_user_turn_before_error` + error message append + save. */
770
+ persistError(s, streamId, label, payload, activeTurnToken) {
771
+ const startedAt = s.pending_started_at;
772
+ this.materializePendingUserTurn(s, activeTurnToken);
773
+ const duration = typeof startedAt === 'number' && startedAt > 0 ? Math.max(0, this.deps.now() - startedAt) : null;
774
+ s.active_stream_id = null;
775
+ s.pending_user_message = null;
776
+ s.pending_attachments = [];
777
+ s.pending_started_at = null;
778
+ s.pending_user_source = null;
779
+ this.appendPartialSnapshot(s, streamId);
780
+ const hint = str(payload.hint);
781
+ const errorMessage = { role: 'assistant', content: `**${label}:** ${str(payload.message) || label}${hint ? `\n\n*${hint}*` : ''}`, timestamp: Math.trunc(this.deps.now()), _error: true };
782
+ if (duration !== null)
783
+ errorMessage._turnDuration = Math.round(duration * 1000) / 1000;
784
+ if (payload.type === 'compression_exhausted') {
785
+ // Python `stamp_compression_exhausted_recovery`: durable recovery metadata on the session, the marker, and the frame.
786
+ const recovery = stampCompressionExhaustedRecovery(s, str(payload.message) || label, str(payload.details));
787
+ errorMessage._compressionRecovery = recovery;
788
+ payload.compression_recovery = recovery;
789
+ payload.recommended_recovery_action = recovery.recommended_action;
790
+ }
791
+ if (payload.details)
792
+ errorMessage.provider_details = payload.details;
793
+ if (payload.type === 'cancelled')
794
+ errorMessage.provider_details_label = 'Cancellation details';
795
+ else if (payload.type === 'interrupted')
796
+ errorMessage.provider_details_label = 'Interruption details';
797
+ s.messages.push(errorMessage);
798
+ try {
799
+ this.deps.store.save(s);
800
+ }
801
+ catch (error) {
802
+ this.deps.log(`[webui] WARNING: failed to save error turn for ${s.session_id}: ${error.message}`);
803
+ }
804
+ this.deps.pending.clearApprovals(s.session_id);
805
+ this.deps.pending.clearClarifies(s.session_id);
806
+ this.deps.events.publish('session_error', { profile: s.profile, sessionId: s.session_id });
807
+ }
808
+ materializePendingUserTurn(s, activeTurnToken) {
809
+ const pendingText = str(s.pending_user_message);
810
+ if (!pendingText)
811
+ return false;
812
+ const recoveredTs = typeof s.pending_started_at === 'number' && s.pending_started_at > 0 ? s.pending_started_at : this.deps.now();
813
+ const source = s.pending_user_source ?? 'webui';
814
+ const attachments = [...s.pending_attachments];
815
+ if (activeTurnToken) {
816
+ for (let i = s.messages.length - 1; i >= 0; i -= 1) {
817
+ const m = s.messages[i];
818
+ if (m.role === 'user' && m._active_turn_token === activeTurnToken) {
819
+ if (!s.messages.slice(i + 1).some((later) => later.role === 'user'))
820
+ return false;
821
+ break;
822
+ }
823
+ }
824
+ }
825
+ const last = s.messages[s.messages.length - 1];
826
+ // Python `_synthesize_user_message_on_cancel`: a worker that already merged this prompt (same text, not older than the pending start) wins.
827
+ if (last?.role === 'user' && messageText(last.content).trim() === pendingText.trim() && Math.trunc(Number(last.timestamp)) >= Math.trunc(recoveredTs))
828
+ return false;
829
+ const recovered = { role: 'user', content: pendingText, timestamp: recoveredTs, _recovered: true };
830
+ if (source !== 'webui')
831
+ recovered._source = source;
832
+ if (attachments.length)
833
+ recovered.attachments = attachments;
834
+ s.messages.push(recovered);
835
+ if (s.context_messages.length)
836
+ s.context_messages.push({ ...recovered });
837
+ return true;
838
+ }
839
+ appendPartialSnapshot(s, streamId) {
840
+ const partial = buildPartialMessage((this.registry.partialText.get(streamId) ?? []).join(''), (this.registry.reasoningText.get(streamId) ?? []).join(''), this.registry.liveToolCalls.get(streamId) ?? [], this.deps.now());
841
+ if (!partial)
842
+ return;
843
+ const key = messageIdentity(partial);
844
+ if (key !== null && s.messages.some((m) => m._partial && messageIdentity(m) === key))
845
+ return;
846
+ s.messages.push(partial);
847
+ }
848
+ cancelPayload(message = 'Cancelled by user') {
849
+ return { type: 'cancelled', message, hint: cancelledTurnHint(this.deps.agentName()) };
850
+ }
851
+ /** Python `_emit_cancel_event`: the terminal frame carries the settled session so tabs can render the partial without a reload. */
852
+ cancelFrame(sessionId) {
853
+ let snapshot = null;
854
+ try {
855
+ snapshot = redactSessionData(this.terminalSessionPayload(this.deps.store.get(sessionId)), this.deps.redactEnabled());
856
+ }
857
+ catch {
858
+ snapshot = null;
859
+ }
860
+ return { ...this.cancelPayload(), status: 'cancelled', session_id: sessionId, ...(snapshot ? { session: snapshot } : {}) };
861
+ }
862
+ /** Python `_finalize_cancelled_turn`/`_persist_cancelled_turn`: only while this stream still owns writeback. */
863
+ finalizeCancelled(s, streamId, message, ephemeral) {
864
+ if (this.registry.writebackOwners.get(s.session_id) !== streamId)
865
+ return false;
866
+ let current = s;
867
+ try {
868
+ current = this.deps.store.get(s.session_id);
869
+ }
870
+ catch {
871
+ return false;
872
+ }
873
+ if (current.active_stream_id !== null && current.active_stream_id !== streamId)
874
+ return false;
875
+ if (ephemeral) {
876
+ try {
877
+ rmSync(this.deps.store.pathFor(current.session_id), { force: true });
878
+ }
879
+ catch { /* ignore */ }
880
+ this.deps.store.sessions.delete(current.session_id);
881
+ return true;
882
+ }
883
+ if (current.messages.some((m) => m._error && str(m.content).startsWith('**Task cancelled:**')) && current.active_stream_id === null && !current.pending_user_message)
884
+ return true;
885
+ this.materializePendingUserTurn(current, buildActiveTurnToken(streamId, current.pending_started_at));
886
+ current.active_stream_id = null;
887
+ current.pending_user_message = null;
888
+ current.pending_attachments = [];
889
+ current.pending_started_at = null;
890
+ current.pending_user_source = null;
891
+ this.appendPartialSnapshot(current, streamId);
892
+ const text = message.trim().endsWith('.') ? message.trim() : `${message.trim()}.`;
893
+ current.messages.push({ role: 'assistant', content: `**Task cancelled:** ${text}\n\n*${cancelledTurnHint(this.deps.agentName())}*`, _error: true, provider_details: text, provider_details_label: 'Cancellation details', timestamp: Math.trunc(this.deps.now()) });
894
+ try {
895
+ this.deps.store.save(current);
896
+ }
897
+ catch {
898
+ return false;
899
+ }
900
+ this.deps.pending.clearApprovals(current.session_id);
901
+ this.deps.pending.clearClarifies(current.session_id);
902
+ this.deps.events.publish('session_cancel', { profile: current.profile, sessionId: current.session_id });
903
+ return true;
904
+ }
905
+ /** Push a frame onto the session's live chat stream from outside the turn (Python gateway notify callbacks). */
906
+ emitToSession(sessionId, event, data) {
907
+ this.sessionPuts.get(sessionId)?.(event, data);
908
+ }
909
+ teardown(sessionId, streamId) {
910
+ const writer = this.writers.get(streamId);
911
+ if (writer) {
912
+ try {
913
+ writer.close();
914
+ }
915
+ catch { /* ignore */ }
916
+ this.writers.delete(streamId);
917
+ }
918
+ this.abortControllers.delete(streamId);
919
+ this.steers.delete(streamId);
920
+ this.settledStreams.delete(streamId);
921
+ this.registry.retire(streamId, this.deps.now());
922
+ this.registry.forgetOwner(streamId);
923
+ this.registry.clearWritebackOwnerIfOwned(sessionId, streamId);
924
+ // Admission was released before the title work, so a successor turn may already own the session and have parked
925
+ // its own prompts: only the stream that still owns the session clears them.
926
+ const successor = this.registry.activeRunStreamForSession(sessionId);
927
+ if (!successor || successor === streamId) {
928
+ this.sessionPuts.delete(sessionId);
929
+ this.deps.pending.clearApprovals(sessionId);
930
+ this.deps.pending.clearClarifies(sessionId);
931
+ }
932
+ try {
933
+ this.deps.onTurnEnd?.(sessionId);
934
+ }
935
+ catch { /* best effort */ }
936
+ }
937
+ /** Journal files with a live writer (retention must leave them alone). */
938
+ activeJournalPaths() {
939
+ return new Set([...this.writers.values()].map((w) => this.deps.journal.pathFor(w.sessionId, w.runId)));
940
+ }
941
+ // ── title ────────────────────────────────────────────────────────────────
942
+ /** Python `_generate_llm_session_title_via_aux`: aux-route statuses (`llm_aux`, `llm_aux_retry`, `llm_*_aux`). */
943
+ async llmTitle(s, userText, assistantText) {
944
+ const sidecar = this.deps.sidecar();
945
+ if (!sidecar)
946
+ return { title: '', status: 'llm_error_aux', rawPreview: '' };
947
+ const [qa, prompts] = titlePrompts(userText, assistantText);
948
+ let status = 'llm_error_aux';
949
+ for (const [idx, prompt] of prompts.entries()) {
950
+ let raw = '';
951
+ try {
952
+ const result = await sidecar.call('aux.complete', { profile_home: this.deps.profileHome(s.profile), task: 'title_generation', messages: [{ role: 'system', content: prompt }, { role: 'user', content: qa }], max_tokens: 64 }, { timeoutMs: 15_000 });
953
+ raw = str(result.text);
954
+ }
955
+ catch (error) {
956
+ this.deps.log(`[webui] title generation failed for ${s.session_id}: ${error.message}`);
957
+ status = 'llm_error_aux';
958
+ continue;
959
+ }
960
+ if (!raw) {
961
+ status = 'llm_empty_aux';
962
+ continue;
963
+ }
964
+ const title = sanitizeGeneratedTitle(raw);
965
+ if (!title)
966
+ return { title: '', status: 'llm_invalid_aux', rawPreview: raw.slice(0, 120) };
967
+ if (titleLanguageMismatch(userText, title))
968
+ return { title: '', status: 'llm_language_mismatch_aux', rawPreview: raw.slice(0, 120) };
969
+ return { title, status: idx === 0 ? 'llm_aux' : 'llm_aux_retry', rawPreview: '' };
970
+ }
971
+ return { title: '', status, rawPreview: '' };
972
+ }
973
+ /** Python `generate_session_title_for_session`: on-demand title from the persisted transcript; never touches `llm_title_generated`. */
974
+ async generateTitle(s, opts = {}) {
975
+ const [userText, assistantText] = opts.preferLatest ? latestExchangeSnippets(s.messages) : firstExchangeSnippets(s.messages, { scanPastConsecutiveUsers: true });
976
+ if (!userText)
977
+ return { title: null, status: 'empty_user_message', rawPreview: '' };
978
+ if (!this.deps.titleGenerationEnabled())
979
+ return { title: null, status: 'title_generation_disabled', rawPreview: '' };
980
+ let next = '';
981
+ let llmStatus = 'empty_assistant_message';
982
+ let rawPreview = '';
983
+ if (assistantText)
984
+ ({ title: next, status: llmStatus, rawPreview } = await this.llmTitle(s, userText, assistantText));
985
+ if (next)
986
+ return { title: next, status: llmStatus, rawPreview };
987
+ const fallback = fallbackTitleFromExchange(userText, assistantText);
988
+ if (fallback && !isGenericFallbackTitle(fallback))
989
+ return { title: fallback, status: `local_summary:${llmStatus}`, rawPreview };
990
+ return { title: null, status: llmStatus || 'empty_title', rawPreview };
991
+ }
992
+ /** Python `_run_background_title_update` (spawned only when `_background_title_generation_inputs` is eligible). */
993
+ async backgroundTitle(s, put) {
994
+ const sessionId = s.session_id;
995
+ const placeholder = str(s.title).trim();
996
+ const invalidExisting = looksInvalidGeneratedTitle(s.title);
997
+ const eligible = ['Untitled', 'New Chat', ''].includes(placeholder) || placeholder === titleFrom(s.messages, '') || invalidExisting;
998
+ const [userText, assistantText] = firstExchangeSnippets(s.messages);
999
+ // Python `_put_title_status`: empty reason/title/raw_preview keys are omitted.
1000
+ const status = (status, reason = '', title = '', rawPreview = '') => {
1001
+ put('title_status', { session_id: sessionId, status, ...(reason ? { reason } : {}), ...(title ? { title } : {}), ...(rawPreview ? { raw_preview: rawPreview } : {}) });
1002
+ };
1003
+ try {
1004
+ if (!eligible || (s.llm_title_generated && !invalidExisting) || !userText || !assistantText)
1005
+ return;
1006
+ if (s.manual_title) {
1007
+ status('skipped', 'manual_title', placeholder);
1008
+ return;
1009
+ }
1010
+ if (!this.deps.titleGenerationEnabled()) {
1011
+ status('skipped', 'title_generation_disabled', placeholder);
1012
+ return;
1013
+ }
1014
+ const generated = await this.llmTitle(s, userText, assistantText);
1015
+ const { status: llmStatus, rawPreview } = generated;
1016
+ let next = generated.title;
1017
+ let source = llmStatus;
1018
+ if (!next) {
1019
+ const fallback = fallbackTitleFromExchange(userText, assistantText);
1020
+ if (fallback && !isGenericFallbackTitle(fallback)) {
1021
+ next = fallback;
1022
+ source = 'fallback';
1023
+ }
1024
+ }
1025
+ const fallbackReason = source === 'fallback' && llmStatus ? `local_summary:${llmStatus}` : 'local_summary';
1026
+ let current = s;
1027
+ try {
1028
+ current = this.deps.store.get(sessionId);
1029
+ }
1030
+ catch {
1031
+ current = s;
1032
+ }
1033
+ let effective = str(current.title).trim();
1034
+ let wrote = false;
1035
+ if (next) {
1036
+ const stillAuto = effective === placeholder || ['Untitled', 'New Chat', ''].includes(effective) || effective === titleFrom(current.messages, '') || looksInvalidGeneratedTitle(current.title);
1037
+ if (current.manual_title || !stillAuto) {
1038
+ status('skipped', 'manual_title', effective);
1039
+ return;
1040
+ }
1041
+ if (next !== effective) {
1042
+ current.title = next;
1043
+ markSessionTitleGenerated(current);
1044
+ this.deps.store.save(current, { touchUpdatedAt: false });
1045
+ // Python `sync_session_title` after generation: the state.db row follows when `sync_to_insights` is on.
1046
+ await this.deps.syncTitle?.(current);
1047
+ this.deps.events.publish('title', { profile: current.profile, sessionId });
1048
+ effective = next;
1049
+ wrote = true;
1050
+ }
1051
+ }
1052
+ if (wrote) {
1053
+ status(source, source === 'fallback' ? fallbackReason : llmStatus, effective, rawPreview);
1054
+ put('title', { session_id: sessionId, title: effective });
1055
+ }
1056
+ else
1057
+ status('skipped', source || 'unchanged', effective, rawPreview);
1058
+ }
1059
+ finally {
1060
+ put('stream_end', { session_id: sessionId });
1061
+ }
1062
+ }
1063
+ // ── cancel / steer ───────────────────────────────────────────────────────
1064
+ /** Python `cancel_stream`: persist the partial, mark cancelled, interrupt the Agent, release admission. */
1065
+ async cancel(streamId) {
1066
+ const channel = this.registry.peek(streamId);
1067
+ const run = this.registry.activeRuns.get(streamId);
1068
+ if (!channel && !run)
1069
+ return false;
1070
+ // Python popped the run before its title work: a cancel that lands after `done` (while the title prompts still run)
1071
+ // is not a cancellation, so it neither journals a `cancel` frame nor marks the completed run interrupted.
1072
+ if (!run && this.settledStreams.has(streamId))
1073
+ return false;
1074
+ const sessionId = this.registry.ownerSessionId(streamId) ?? run?.session_id ?? null;
1075
+ this.registry.cancelled.add(streamId);
1076
+ if (run) {
1077
+ run.phase = 'cancelling';
1078
+ run.cancelled_at = this.deps.now();
1079
+ }
1080
+ // Python `_finalize_webui_steers` drained the Agent's pending steer text at cancel time: the interrupt reply
1081
+ // carries it so queued steers settle as consumed / leftover before the terminal row.
1082
+ let leftover = '';
1083
+ const sidecar = this.deps.sidecar();
1084
+ if (sidecar) {
1085
+ try {
1086
+ leftover = str((await sidecar.call('chat.interrupt', { stream_id: streamId }, { timeoutMs: 5_000 })).pending_steer);
1087
+ }
1088
+ catch {
1089
+ leftover = '';
1090
+ }
1091
+ }
1092
+ if (sessionId) {
1093
+ let current = null;
1094
+ try {
1095
+ current = this.deps.store.get(sessionId);
1096
+ }
1097
+ catch {
1098
+ current = null;
1099
+ }
1100
+ if (current?.active_stream_id === streamId) {
1101
+ this.finalizeCancelled(current, streamId, 'Task cancelled.', run?.ephemeral);
1102
+ }
1103
+ }
1104
+ // The worker may already have written its terminal row while the interrupt was in flight; the stream is then
1105
+ // gone from the registry and this path must not add a second one.
1106
+ if (channel && this.registry.streams.has(streamId)) {
1107
+ const writer = this.writers.get(streamId) ?? null;
1108
+ const emit = (event, data) => {
1109
+ let eventId = null;
1110
+ if (writer) {
1111
+ try {
1112
+ eventId = writer.appendSseEvent(event, data).event_id;
1113
+ }
1114
+ catch {
1115
+ eventId = null;
1116
+ }
1117
+ }
1118
+ channel.put([event, data, eventId]);
1119
+ };
1120
+ if ((this.steers.get(streamId) ?? []).length)
1121
+ for (const [event, data] of this.finalizeSteerEvents(streamId, leftover))
1122
+ emit(event, data);
1123
+ emit('cancel', sessionId ? this.cancelFrame(sessionId) : this.cancelPayload());
1124
+ this.registry.streams.delete(streamId);
1125
+ this.registry.liveIds.delete(streamId);
1126
+ }
1127
+ this.abortControllers.get(streamId)?.abort();
1128
+ return true;
1129
+ }
1130
+ async steer(sessionId, text, displayText, steerId) {
1131
+ let s = null;
1132
+ try {
1133
+ s = this.deps.store.get(sessionId, { metadataOnly: true });
1134
+ }
1135
+ catch {
1136
+ s = null;
1137
+ }
1138
+ const activeStreamId = s?.active_stream_id ?? null;
1139
+ // Python: an unknown session has no live worker and no cached agent, so the lookup order yields `no_cached_agent`
1140
+ // (200) before `session_not_found` is ever reachable. A known idle session keeps its cached agent → `not_running`.
1141
+ if (!s)
1142
+ return { accepted: false, fallback: 'no_cached_agent', stream_id: null };
1143
+ if (!activeStreamId)
1144
+ return { accepted: false, fallback: 'not_running', stream_id: null };
1145
+ if (!this.registry.liveIds.has(activeStreamId))
1146
+ return { accepted: false, fallback: 'stream_dead', stream_id: null };
1147
+ const sidecar = this.deps.sidecar();
1148
+ if (!sidecar)
1149
+ return { accepted: false, fallback: 'no_cached_agent', stream_id: null };
1150
+ // Register provisionally before the RPC: the Agent may apply the steer and finish the turn before the steer
1151
+ // reply arrives, and the turn's finalisation must then already see the record. A rejection removes it again.
1152
+ const record = { steer_id: steerId, session_id: sessionId, stream_id: activeStreamId, text, display_text: displayText || text, created_at: this.deps.now() };
1153
+ const records = this.steers.get(activeStreamId) ?? [];
1154
+ records.push(record);
1155
+ this.steers.set(activeStreamId, records);
1156
+ const withdraw = () => {
1157
+ const current = this.steers.get(activeStreamId);
1158
+ if (!current)
1159
+ return;
1160
+ const index = current.indexOf(record);
1161
+ if (index >= 0)
1162
+ current.splice(index, 1);
1163
+ if (!current.length)
1164
+ this.steers.delete(activeStreamId);
1165
+ };
1166
+ let result;
1167
+ try {
1168
+ result = await sidecar.call('chat.steer', { stream_id: activeStreamId, text });
1169
+ }
1170
+ catch {
1171
+ withdraw();
1172
+ return { accepted: false, fallback: 'steer_error', stream_id: activeStreamId };
1173
+ }
1174
+ // An accepted steer whose turn finalised while the reply was in flight was already reported by that finalisation.
1175
+ if (!result.accepted)
1176
+ withdraw();
1177
+ return { accepted: result.accepted, fallback: result.accepted ? null : (result.fallback ?? 'not_running'), stream_id: activeStreamId, steer_id: steerId };
1178
+ }
1179
+ /** Python `_webui_steer_events_before` for terminal frames: cancel/apperror flush consumed steers and leftovers first. */
1180
+ takeSteerEventsBefore(streamId, event) {
1181
+ if (event !== 'cancel' && event !== 'apperror')
1182
+ return [];
1183
+ if (!(this.steers.get(streamId) ?? []).length)
1184
+ return [];
1185
+ return this.finalizeSteerEvents(streamId, '');
1186
+ }
1187
+ /** Python `_pending_webui_steer_suffix_start`: the pending text is the newline-joined tail of the records; earlier ones are consumed. */
1188
+ static pendingSteerSuffixStart(records, pendingText) {
1189
+ if (!pendingText)
1190
+ return records.length;
1191
+ for (let start = 0; start <= records.length; start += 1) {
1192
+ const suffix = records.slice(start).map((r) => r.text).join('\n');
1193
+ if (suffix && (pendingText === suffix || pendingText.endsWith(`\n${suffix}`)))
1194
+ return start;
1195
+ }
1196
+ return null;
1197
+ }
1198
+ consumedSteerPayload(record) {
1199
+ return { ...record, agent_text: record.text, text: record.display_text || record.text, consumed_at: this.deps.now() };
1200
+ }
1201
+ /**
1202
+ * Python `_take_consumed_webui_steers` (live, `keepLeftovers`) and `_finalize_webui_steers` (terminal): records the
1203
+ * Agent no longer holds pending are consumed; at the end the still-pending tail is reported as leftovers.
1204
+ */
1205
+ takeConsumedSteers(streamId, pendingText, opts = {}) {
1206
+ const records = this.steers.get(streamId) ?? [];
1207
+ const start = TurnRunner.pendingSteerSuffixStart(records, pendingText);
1208
+ if (opts.keepLeftovers) {
1209
+ if (start === null || start <= 0)
1210
+ return [];
1211
+ const consumed = records.splice(0, start);
1212
+ return consumed.map((r) => this.consumedSteerPayload(r));
1213
+ }
1214
+ this.steers.delete(streamId);
1215
+ if (start === null)
1216
+ return [];
1217
+ return records.slice(0, start).map((r) => this.consumedSteerPayload(r));
1218
+ }
1219
+ /** Terminal steer bookkeeping for cancel/error paths: consumed records first, then `pending_steer_leftover` for the rest. */
1220
+ finalizeSteerEvents(streamId, leftoverText) {
1221
+ const records = this.steers.get(streamId) ?? [];
1222
+ this.steers.delete(streamId);
1223
+ const start = TurnRunner.pendingSteerSuffixStart(records, leftoverText);
1224
+ if (start === null)
1225
+ return leftoverText ? [['pending_steer_leftover', { stream_id: streamId, text: leftoverText }]] : [];
1226
+ const events = records.slice(0, start).map((r) => ['steer_consumed', this.consumedSteerPayload(r)]);
1227
+ const leftovers = records.slice(start);
1228
+ for (const r of leftovers)
1229
+ events.push(['pending_steer_leftover', { ...r, leftover_at: this.deps.now() }]);
1230
+ const matched = leftovers.map((r) => r.text).join('\n');
1231
+ const unmatched = matched ? leftoverText.slice(0, leftoverText.length - matched.length).replace(/\n+$/, '') : leftoverText;
1232
+ if (unmatched)
1233
+ events.push(['pending_steer_leftover', { stream_id: streamId, text: unmatched }]);
1234
+ return events;
1235
+ }
1236
+ // ── approvals / clarify ──────────────────────────────────────────────────
1237
+ async respondApproval(sessionId, choice, approvalId, enableYolo) {
1238
+ // Python `_enable_session_yolo_and_release_pending`: a stale card id while another approval is parked
1239
+ // must not commit YOLO for the whole session — fail closed with the relay envelope instead.
1240
+ if (enableYolo && approvalId && !this.deps.pending.hasApprovalId(sessionId, approvalId) && this.deps.pending.hasPendingApproval(sessionId)) {
1241
+ return { ok: false, choice, relayed: false, code: 'gateway_run_unavailable', error: GATEWAY_APPROVAL_RELAY_UNAVAILABLE, yolo_enabled: this.deps.service().yolo(sessionId).yolo_enabled === true, _status: 409 };
1242
+ }
1243
+ // The mirrored prompt stays queued until the sidecar acknowledges the answer: a relay failure leaves it
1244
+ // answerable (503) instead of hiding a prompt the Agent is still blocked on.
1245
+ const { entry, found } = this.deps.pending.peekApproval(sessionId, approvalId);
1246
+ const sidecar = this.deps.sidecar();
1247
+ const relayFailure = (message) => ({ ok: false, choice, error: `The Agent sidecar did not accept the approval (${message}); retry in a moment.`, _status: 503 });
1248
+ let s = null;
1249
+ try {
1250
+ s = this.deps.store.get(sessionId, { metadataOnly: true });
1251
+ }
1252
+ catch {
1253
+ s = null;
1254
+ }
1255
+ const profileHome = this.deps.profileHome(s?.profile ?? null);
1256
+ let resolved = false;
1257
+ if (entry) {
1258
+ if (!sidecar)
1259
+ return relayFailure('not running');
1260
+ try {
1261
+ const result = await sidecar.call('approval.respond', { profile_home: profileHome, session_id: sessionId, choice: choice, request_id: entry ? str(entry.request_id) || null : null });
1262
+ resolved = result.ok;
1263
+ }
1264
+ catch (error) {
1265
+ this.deps.log(`[webui] approval relay failed for ${sessionId}: ${error.message}`);
1266
+ return relayFailure(str(error.message));
1267
+ }
1268
+ if (resolved && entry)
1269
+ this.deps.pending.resolveApproval(sessionId, str(entry.approval_id));
1270
+ }
1271
+ let yoloEnabled;
1272
+ if (enableYolo) {
1273
+ // The local flag is authoritative (it is re-pushed to the sidecar at every turn start); a running sidecar must
1274
+ // still confirm now so parked Agent waiters are released before YOLO is reported as on.
1275
+ const failure = await this.withYoloLock(sessionId, async () => {
1276
+ if (sidecar) {
1277
+ try {
1278
+ await sidecar.call('approval.set_yolo', { session_id: sessionId, enabled: true });
1279
+ }
1280
+ catch (error) {
1281
+ return relayFailure(str(error.message));
1282
+ }
1283
+ }
1284
+ this.deps.service().setYolo(sessionId, true);
1285
+ return null;
1286
+ });
1287
+ if (failure)
1288
+ return failure;
1289
+ yoloEnabled = true;
1290
+ this.deps.pending.clearApprovals(sessionId);
1291
+ }
1292
+ if (!found && !this.deps.pending.hasPendingApproval(sessionId))
1293
+ return { ok: true, choice, stale_cleared: true, ...(enableYolo ? { yolo_enabled: yoloEnabled } : {}) };
1294
+ return { ok: resolved || !approvalId, choice, ...(enableYolo && (resolved || !approvalId) ? { yolo_enabled: yoloEnabled } : {}) };
1295
+ }
1296
+ withYoloLock(sessionId, fn) {
1297
+ const run = (this.yoloChains.get(sessionId) ?? Promise.resolve()).then(fn);
1298
+ const settled = run.catch(() => undefined).then(() => { if (this.yoloChains.get(sessionId) === settled)
1299
+ this.yoloChains.delete(sessionId); });
1300
+ this.yoloChains.set(sessionId, settled);
1301
+ return run;
1302
+ }
1303
+ /** Python `set_session_yolo_enabled(False)`: the sidecar must drop its per-session YOLO state before the local flag clears. */
1304
+ disableYolo(sessionId) {
1305
+ return this.withYoloLock(sessionId, async () => {
1306
+ const sidecar = this.deps.sidecar();
1307
+ if (sidecar) {
1308
+ try {
1309
+ await sidecar.call('approval.set_yolo', { session_id: sessionId, enabled: false });
1310
+ }
1311
+ catch (error) {
1312
+ return { ok: false, error: `The Agent sidecar did not disable YOLO (${str(error.message)}); retry in a moment.`, yolo_enabled: this.deps.service().yolo(sessionId).yolo_enabled === true, _status: 503 };
1313
+ }
1314
+ }
1315
+ return this.deps.service().setYolo(sessionId, false);
1316
+ });
1317
+ }
1318
+ /** `stale` when no such prompt is queued; `ok:false` without `stale` when the sidecar did not acknowledge (prompt retained). */
1319
+ async respondClarify(sessionId, clarifyId, response) {
1320
+ const entry = this.deps.pending.peekClarify(sessionId, clarifyId);
1321
+ if (!entry)
1322
+ return { ok: false, stale: true };
1323
+ const sidecar = this.deps.sidecar();
1324
+ if (!sidecar)
1325
+ return { ok: false, error: 'The Agent sidecar is not running; retry in a moment.' };
1326
+ let ok = false;
1327
+ try {
1328
+ ok = (await sidecar.call('clarify.respond', { session_id: sessionId, clarify_id: str(entry.clarify_id), response })).ok;
1329
+ }
1330
+ catch (error) {
1331
+ return { ok: false, error: `The Agent sidecar did not accept the answer (${str(error.message)}); retry in a moment.` };
1332
+ }
1333
+ if (!ok)
1334
+ return { ok: false, stale: true };
1335
+ const { head } = this.deps.pending.resolveClarify(sessionId, str(entry.clarify_id));
1336
+ if (head)
1337
+ this.emitToSession(sessionId, 'clarify', head);
1338
+ return { ok: true };
1339
+ }
1340
+ }
1341
+ function previousStartedAt(s, run) {
1342
+ return typeof s.pending_started_at === 'number' && s.pending_started_at > 0 ? s.pending_started_at : (run?.started_at ?? Date.now() / 1000);
1343
+ }
1344
+ function dedupeContext(messages) {
1345
+ const out = [];
1346
+ let lastKey = null;
1347
+ for (const m of messages) {
1348
+ const key = messageIdentity(m);
1349
+ if (key !== null && key === lastKey && m.role === 'assistant')
1350
+ continue;
1351
+ out.push(m);
1352
+ lastKey = key;
1353
+ }
1354
+ return out;
1355
+ }
1356
+ export { HttpFailure };
1357
+ //# sourceMappingURL=turn.js.map