thursday-agent 0.2.0 → 0.4.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 (541) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +5 -2
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/prerender-manifest.json +5 -5
  5. package/.next/routes-manifest.json +24 -6
  6. package/.next/server/app/_global-error/page.js +2 -2
  7. package/.next/server/app/_global-error/page.js.nft.json +1 -1
  8. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  9. package/.next/server/app/_global-error.html +1 -1
  10. package/.next/server/app/_global-error.rsc +2 -2
  11. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
  13. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next/server/app/_not-found/page.js +2 -2
  15. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  16. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  17. package/.next/server/app/_not-found.html +1 -1
  18. package/.next/server/app/_not-found.rsc +14 -14
  19. package/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
  20. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +12 -12
  21. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  22. package/.next/server/app/api/artifact/route.js +3 -3
  23. package/.next/server/app/api/artifact/route.js.nft.json +1 -1
  24. package/.next/server/app/api/bot/memory/files/route/app-paths-manifest.json +3 -0
  25. package/.next/server/app/api/bot/memory/files/route.js +10 -0
  26. package/.next/server/app/api/bot/memory/files/route.js.nft.json +1 -0
  27. package/.next/server/app/api/bot/memory/files/route_client-reference-manifest.js +3 -0
  28. package/.next/server/app/api/bot/memory/route/app-paths-manifest.json +3 -0
  29. package/.next/server/app/api/bot/memory/route.js +14 -0
  30. package/.next/server/app/api/bot/{notes → memory}/route.js.nft.json +1 -1
  31. package/.next/server/app/api/bot/memory/route_client-reference-manifest.js +3 -0
  32. package/.next/server/app/api/bot/route.js +6 -6
  33. package/.next/server/app/api/bot/route.js.nft.json +1 -1
  34. package/.next/server/app/api/bot/task/[id]/route.js +8 -8
  35. package/.next/server/app/api/bot/task/[id]/route.js.nft.json +1 -1
  36. package/.next/server/app/api/bot/task/route.js +8 -8
  37. package/.next/server/app/api/bot/task/route.js.nft.json +1 -1
  38. package/.next/server/app/api/config/route.js +5 -5
  39. package/.next/server/app/api/config/route.js.nft.json +1 -1
  40. package/.next/server/app/api/events/route.js +4 -5
  41. package/.next/server/app/api/events/route.js.nft.json +1 -1
  42. package/.next/server/app/api/file/[...path]/route.js +3 -3
  43. package/.next/server/app/api/file/[...path]/route.js.nft.json +1 -1
  44. package/.next/server/app/api/llm-model/catalog/route.js +9 -8
  45. package/.next/server/app/api/llm-model/catalog/route.js.nft.json +1 -1
  46. package/.next/server/app/api/llm-model/credits/route/app-paths-manifest.json +3 -0
  47. package/.next/server/app/api/llm-model/credits/route/build-manifest.json +12 -0
  48. package/.next/server/app/api/llm-model/credits/route/server-reference-manifest.json +4 -0
  49. package/.next/server/app/api/llm-model/credits/route.js +16 -0
  50. package/.next/server/app/api/llm-model/credits/route.js.map +5 -0
  51. package/.next/server/app/api/llm-model/credits/route.js.nft.json +1 -0
  52. package/.next/server/app/api/llm-model/credits/route_client-reference-manifest.js +3 -0
  53. package/.next/server/app/api/llm-model/route.js +10 -8
  54. package/.next/server/app/api/llm-model/route.js.nft.json +1 -1
  55. package/.next/server/app/api/llm-model/subscription/route/app-paths-manifest.json +3 -0
  56. package/.next/server/app/api/llm-model/subscription/route/build-manifest.json +12 -0
  57. package/.next/server/app/api/llm-model/subscription/route/server-reference-manifest.json +4 -0
  58. package/.next/server/app/api/llm-model/subscription/route.js +15 -0
  59. package/.next/server/app/api/llm-model/subscription/route.js.map +5 -0
  60. package/.next/server/app/api/llm-model/subscription/route.js.nft.json +1 -0
  61. package/.next/server/app/api/llm-model/subscription/route_client-reference-manifest.js +3 -0
  62. package/.next/server/app/api/mcp/[name]/route.js +4 -4
  63. package/.next/server/app/api/mcp/[name]/route.js.nft.json +1 -1
  64. package/.next/server/app/api/mcp/oauth/callback/route.js +5 -5
  65. package/.next/server/app/api/mcp/oauth/callback/route.js.nft.json +1 -1
  66. package/.next/server/app/api/mcp/route.js +4 -4
  67. package/.next/server/app/api/mcp/route.js.nft.json +1 -1
  68. package/.next/server/app/api/mcp/tools/route.js +4 -4
  69. package/.next/server/app/api/mcp/tools/route.js.nft.json +1 -1
  70. package/.next/server/app/api/memory/edit/route/app-paths-manifest.json +3 -0
  71. package/.next/server/app/api/memory/edit/route/build-manifest.json +12 -0
  72. package/.next/server/app/api/memory/edit/route/server-reference-manifest.json +4 -0
  73. package/.next/server/app/api/memory/edit/route.js +24 -0
  74. package/.next/server/app/api/memory/edit/route.js.map +5 -0
  75. package/.next/server/app/api/memory/edit/route.js.nft.json +1 -0
  76. package/.next/server/app/api/memory/{tidy → edit}/route_client-reference-manifest.js +1 -1
  77. package/.next/server/app/api/memory/route.js +6 -7
  78. package/.next/server/app/api/memory/route.js.nft.json +1 -1
  79. package/.next/server/app/api/skills/[source]/[name]/route.js +3 -3
  80. package/.next/server/app/api/skills/[source]/[name]/route.js.nft.json +1 -1
  81. package/.next/server/app/api/skills/route.js +3 -3
  82. package/.next/server/app/api/skills/route.js.nft.json +1 -1
  83. package/.next/server/app/api/thursday/call/route.js +9 -7
  84. package/.next/server/app/api/thursday/call/route.js.nft.json +1 -1
  85. package/.next/server/app/api/thursday/skills/route.js +9 -7
  86. package/.next/server/app/api/thursday/skills/route.js.nft.json +1 -1
  87. package/.next/server/app/api/thursday/tool-call/route.js +17 -15
  88. package/.next/server/app/api/thursday/tool-call/route.js.nft.json +1 -1
  89. package/.next/server/app/api/workspace/route.js +4 -5
  90. package/.next/server/app/api/workspace/route.js.nft.json +1 -1
  91. package/.next/server/app/artifact/[...path]/page/server-reference-manifest.json +1 -1
  92. package/.next/server/app/artifact/[...path]/page.js +5 -6
  93. package/.next/server/app/artifact/[...path]/page.js.nft.json +1 -1
  94. package/.next/server/app/artifact/[...path]/page_client-reference-manifest.js +1 -1
  95. package/.next/server/app/favicon.ico/route.js +3 -4
  96. package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
  97. package/.next/server/app/favicon.ico.body +0 -0
  98. package/.next/server/app/page/server-reference-manifest.json +57 -90
  99. package/.next/server/app/page.js +10 -10
  100. package/.next/server/app/page.js.nft.json +1 -1
  101. package/.next/server/app/page_client-reference-manifest.js +1 -1
  102. package/.next/server/app-paths-manifest.json +5 -2
  103. package/.next/server/chunks/[externals]__0dzuk6_._.js +3 -0
  104. package/.next/server/chunks/[root-of-the-server]__01dkp4a._.js +3 -0
  105. package/.next/server/chunks/[root-of-the-server]__02va5wz._.js +1 -1
  106. package/.next/server/chunks/[root-of-the-server]__02xjoto._.js +3 -0
  107. package/.next/server/chunks/[root-of-the-server]__04q--0f._.js +8 -0
  108. package/.next/server/chunks/[root-of-the-server]__0bl-e63._.js +3 -0
  109. package/.next/server/chunks/[root-of-the-server]__0ce8nf8._.js +5 -0
  110. package/.next/server/chunks/[root-of-the-server]__0ffbg85._.js +3 -0
  111. package/.next/server/chunks/[root-of-the-server]__0j7z0r3._.js +12 -0
  112. package/.next/server/chunks/[root-of-the-server]__0jq4ohy._.js +8 -0
  113. package/.next/server/chunks/[root-of-the-server]__0ls6_8y._.js +8 -0
  114. package/.next/server/chunks/[root-of-the-server]__0ncs9uu._.js +3 -0
  115. package/.next/server/chunks/[root-of-the-server]__0pa2n68._.js +3 -0
  116. package/.next/server/chunks/[root-of-the-server]__0q0vvt2._.js +5 -0
  117. package/.next/server/chunks/[root-of-the-server]__1-ga0kh._.js +5 -0
  118. package/.next/server/chunks/[root-of-the-server]__11n256_._.js +5 -0
  119. package/.next/server/chunks/[root-of-the-server]__12cabg9._.js +3 -0
  120. package/.next/server/chunks/[root-of-the-server]__1339jnv._.js +8 -0
  121. package/.next/server/chunks/[root-of-the-server]__15bjnu5._.js +5 -0
  122. package/.next/server/chunks/[root-of-the-server]__18_teo8._.js +8 -0
  123. package/.next/server/chunks/[root-of-the-server]__1_04mcf._.js +5 -0
  124. package/.next/server/chunks/[root-of-the-server]__1anc1p9._.js +3 -0
  125. package/.next/server/chunks/[root-of-the-server]__1ccon1w._.js +5 -5
  126. package/.next/server/chunks/[root-of-the-server]__1fizn31._.js +3 -0
  127. package/.next/server/chunks/[root-of-the-server]__1llj3-b._.js +3 -0
  128. package/.next/server/chunks/[root-of-the-server]__1orn-5b._.js +12 -0
  129. package/.next/server/chunks/[root-of-the-server]__1p8m3f4._.js +3 -0
  130. package/.next/server/chunks/[root-of-the-server]__1wa1k7r._.js +8 -0
  131. package/.next/server/chunks/_0-73of8._.js +5 -5
  132. package/.next/server/chunks/_0-fjql4._.js +26 -0
  133. package/.next/server/chunks/_00xh_q0._.js +25 -0
  134. package/.next/server/chunks/_051qh8u._.js +3 -0
  135. package/.next/server/chunks/{node_modules_next_04q-o58._.js → _05dla15._.js} +2 -2
  136. package/.next/server/chunks/_05h2cii._.js +1 -1
  137. package/.next/server/chunks/_0c9j6rb._.js +49 -0
  138. package/.next/server/chunks/_0fzqt2y._.js +134 -0
  139. package/.next/server/chunks/{_0vnutv9._.js → _0ioz3nd._.js} +1 -1
  140. package/.next/server/chunks/_0iwfby5._.js +3 -0
  141. package/.next/server/chunks/_0mecv0f._.js +26 -0
  142. package/.next/server/chunks/_0mh8j4m._.js +3 -0
  143. package/.next/server/chunks/_0n74zsf._.js +4 -4
  144. package/.next/server/chunks/_0obojvv._.js +3 -0
  145. package/.next/server/chunks/_0recufk._.js +49 -0
  146. package/.next/server/chunks/_0rnf-6n._.js +4 -4
  147. package/.next/server/chunks/_0sb7tlz._.js +3 -0
  148. package/.next/server/chunks/_0u10wmw._.js +5 -5
  149. package/.next/server/chunks/_0u4aiy2._.js +3 -0
  150. package/.next/server/chunks/_0wp4le3._.js +1 -1
  151. package/.next/server/chunks/_0y5fg-x._.js +1 -1
  152. package/.next/server/chunks/_1230wbx._.js +10 -0
  153. package/.next/server/chunks/_12nheja._.js +3 -0
  154. package/.next/server/chunks/_13510xh._.js +3 -0
  155. package/.next/server/chunks/_15hq89o._.js +1 -1
  156. package/.next/server/chunks/_17l5ygt._.js +49 -0
  157. package/.next/server/chunks/_18-1wcc._.js +5 -5
  158. package/.next/server/chunks/_1_5mnq4._.js +4 -4
  159. package/.next/server/chunks/_1_9mjx-._.js +3 -0
  160. package/.next/server/chunks/{_0pppbb5._.js → _1c78vw5._.js} +166 -37
  161. package/.next/server/chunks/_1d0j961._.js +6 -0
  162. package/.next/server/chunks/_1d5zmy8._.js +1 -1
  163. package/.next/server/chunks/_1drgfx4._.js +4 -4
  164. package/.next/server/chunks/_1f46eie._.js +3 -0
  165. package/.next/server/chunks/_1h5_8sk._.js +3 -0
  166. package/.next/server/chunks/_1hdnxb7._.js +3 -0
  167. package/.next/server/chunks/_1j8k_j5._.js +3 -0
  168. package/.next/server/chunks/_1krd9rf._.js +3 -0
  169. package/.next/server/chunks/_1nc0eui._.js +11 -0
  170. package/.next/server/chunks/_1nula3v._.js +3 -0
  171. package/.next/server/chunks/_1pf4qe-._.js +3 -0
  172. package/.next/server/chunks/_1php4eq._.js +1 -1
  173. package/.next/server/chunks/_1qxheoa._.js +3 -0
  174. package/.next/server/chunks/_1x6kd1r._.js +3 -0
  175. package/.next/server/chunks/_1yuv4vp._.js +1 -1
  176. package/.next/server/chunks/_1zjrdag._.js +3 -0
  177. package/.next/server/chunks/_next-internal_server_app_api_bot_memory_files_route_actions_1_xd_8f.js +3 -0
  178. package/.next/server/chunks/_next-internal_server_app_api_bot_memory_route_actions_1a8xfr7.js +3 -0
  179. package/.next/server/chunks/_next-internal_server_app_api_llm-model_credits_route_actions_0v424pb.js +3 -0
  180. package/.next/server/chunks/_next-internal_server_app_api_llm-model_subscription_route_actions_0-udcm5.js +3 -0
  181. package/.next/server/chunks/_next-internal_server_app_api_memory_edit_route_actions_02sfnmr.js +3 -0
  182. package/.next/server/chunks/config_ts_1g4sz5o._.js +1 -1
  183. package/.next/server/chunks/features_0rpfxbk._.js +3 -0
  184. package/.next/server/chunks/features_1qf3cei._.js +3 -0
  185. package/.next/server/chunks/features_bot_0zt92oh._.js +3 -0
  186. package/.next/server/chunks/features_bot_bot_query_ts_12mgkl2._.js +3 -0
  187. package/.next/server/chunks/features_memory_1uqvksn._.js +6 -0
  188. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0a1rd09._.js → node_modules_consola_dist_chunks_prompt_mjs_0i7cvml._.js} +1 -1
  189. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_06067w6._.js → node_modules_consola_dist_chunks_prompt_mjs_0icqaqw._.js} +1 -1
  190. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_096ais_._.js → node_modules_consola_dist_chunks_prompt_mjs_0r28-sm._.js} +1 -1
  191. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_02s3hjt._.js → node_modules_consola_dist_chunks_prompt_mjs_0v_7drq._.js} +1 -1
  192. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1xoq2rl._.js +3 -0
  193. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0tvkfgh._.js → node_modules_consola_dist_chunks_prompt_mjs_1y49572._.js} +1 -1
  194. package/.next/server/chunks/ssr/[root-of-the-server]__092knlj._.js +3 -0
  195. package/.next/server/chunks/ssr/[root-of-the-server]__09q9med._.js +1 -1
  196. package/.next/server/chunks/ssr/[root-of-the-server]__0hg5gib._.js +1 -1
  197. package/.next/server/chunks/ssr/[root-of-the-server]__0i8ie7f._.js +3 -0
  198. package/.next/server/chunks/ssr/{[root-of-the-server]__0jsmmb-._.js → [root-of-the-server]__0mz33a7._.js} +1 -1
  199. package/.next/server/chunks/ssr/[root-of-the-server]__0s9dq5u._.js +3 -0
  200. package/.next/server/chunks/ssr/{[root-of-the-server]__0m1b5ky._.js → [root-of-the-server]__0s9l2sv._.js} +1 -1
  201. package/.next/server/chunks/ssr/[root-of-the-server]__0tbkdlx._.js +3 -0
  202. package/.next/server/chunks/ssr/[root-of-the-server]__0vnmv-s._.js +3 -0
  203. package/.next/server/chunks/ssr/{[root-of-the-server]__0f6s48x._.js → [root-of-the-server]__0xpcv9w._.js} +3 -3
  204. package/.next/server/chunks/ssr/[root-of-the-server]__0xsog8n._.js +3 -0
  205. package/.next/server/chunks/ssr/[root-of-the-server]__0xtssj3._.js +1 -1
  206. package/.next/server/chunks/ssr/[root-of-the-server]__10-dz5o._.js +61 -0
  207. package/.next/server/chunks/ssr/[root-of-the-server]__15_uhip._.js +3 -0
  208. package/.next/server/chunks/ssr/[root-of-the-server]__16jc196._.js +1 -1
  209. package/.next/server/chunks/ssr/[root-of-the-server]__1gtlw6v._.js +43 -0
  210. package/.next/server/chunks/ssr/[root-of-the-server]__1mzi-ju._.js +1 -1
  211. package/.next/server/chunks/ssr/[root-of-the-server]__1ox7hbk._.js +4 -0
  212. package/.next/server/chunks/ssr/{[root-of-the-server]__1qfqvmg._.js → [root-of-the-server]__202p40z._.js} +2 -2
  213. package/.next/server/chunks/ssr/{_1mznrxk._.js → _001_ms1._.js} +3 -3
  214. package/.next/server/chunks/ssr/_01n3o0m._.js +3 -0
  215. package/.next/server/chunks/ssr/_0ap2fzn._.js +3 -0
  216. package/.next/server/chunks/ssr/_0fz4mf_._.js +3 -0
  217. package/.next/server/chunks/ssr/_0gb4gmf._.js +3 -0
  218. package/.next/server/chunks/ssr/_0h42fze._.js +3 -0
  219. package/.next/server/chunks/ssr/_0h7zdni._.js +1 -1
  220. package/.next/server/chunks/ssr/_0i_6q1w._.js +41 -0
  221. package/.next/server/chunks/ssr/_0ilna52._.js +42 -0
  222. package/.next/server/chunks/ssr/_0n3toaw._.js +3 -0
  223. package/.next/server/chunks/ssr/{_13punpm._.js → _0q0zan6._.js} +2 -2
  224. package/.next/server/chunks/ssr/{_077-g6s._.js → _0s99bgb._.js} +3 -3
  225. package/.next/server/chunks/ssr/_0zcjrbg._.js +3 -0
  226. package/.next/server/chunks/ssr/_0zhoc3r._.js +3 -0
  227. package/.next/server/chunks/ssr/{_1lip4th._.js → _1-_etts._.js} +2 -2
  228. package/.next/server/chunks/ssr/_1-teee5._.js +3 -3
  229. package/.next/server/chunks/ssr/_13fke0x._.js +3 -0
  230. package/.next/server/chunks/ssr/_15j2_8f._.js +3 -0
  231. package/.next/server/chunks/ssr/_1b2mjwr._.js +3 -0
  232. package/.next/server/chunks/ssr/{_09ja_w1._.js → _1f-dg-6._.js} +2 -2
  233. package/.next/server/chunks/ssr/_1fojd41._.js +4 -0
  234. package/.next/server/chunks/ssr/_1gd8q8l._.js +3 -0
  235. package/.next/server/chunks/ssr/{_1m3bsfr._.js → _1h0rw11._.js} +2 -2
  236. package/.next/server/chunks/ssr/_1jhd09t._.js +3 -0
  237. package/.next/server/chunks/ssr/{_040sk32._.js → _1lz_cq2._.js} +84 -88
  238. package/.next/server/chunks/ssr/_1nnsk7z._.js +3 -0
  239. package/.next/server/chunks/ssr/_1sji22n._.js +3 -0
  240. package/.next/server/chunks/ssr/{_0x_nu5e._.js → _1vi_uo7._.js} +2 -2
  241. package/.next/server/chunks/ssr/_1vyd1ji._.js +1 -1
  242. package/.next/server/chunks/ssr/features_bot_1s_-2vj._.js +3 -0
  243. package/.next/server/chunks/ssr/features_config_components_config-setting_tsx_0xfwqdg._.js +1 -1
  244. package/.next/server/chunks/ssr/features_connectors_components_mcp-setting_tsx_0jmbgvp._.js +1 -1
  245. package/.next/server/chunks/ssr/{node_modules_0kxz4gt._.js → node_modules_00etblx._.js} +1 -1
  246. package/.next/server/chunks/ssr/{node_modules_18_dk7_._.js → node_modules_04jf_m8._.js} +1 -1
  247. package/.next/server/chunks/ssr/{node_modules_0_n241r._.js → node_modules_08dhkiq._.js} +1 -1
  248. package/.next/server/chunks/ssr/{node_modules_0n1kj4p._.js → node_modules_0940ns5._.js} +1 -1
  249. package/.next/server/chunks/ssr/{node_modules_17lrt3y._.js → node_modules_09q35v_._.js} +1 -1
  250. package/.next/server/chunks/ssr/{node_modules_1toafc-._.js → node_modules_0_-_a--._.js} +1 -1
  251. package/.next/server/chunks/ssr/{node_modules_0lrjk5t._.js → node_modules_0arus10._.js} +1 -1
  252. package/.next/server/chunks/ssr/{node_modules_04n7mjs._.js → node_modules_0b__sgl._.js} +1 -1
  253. package/.next/server/chunks/ssr/{node_modules_0iz1rzd._.js → node_modules_0cxaxk9._.js} +1 -1
  254. package/.next/server/chunks/ssr/{node_modules_0ndvxub._.js → node_modules_0g7fzk3._.js} +1 -1
  255. package/.next/server/chunks/ssr/{node_modules_0kl7-tn._.js → node_modules_0had7xd._.js} +1 -1
  256. package/.next/server/chunks/ssr/{node_modules_19dp7ko._.js → node_modules_0pvd7q-._.js} +1 -1
  257. package/.next/server/chunks/ssr/node_modules_0x3-ply._.js +3 -0
  258. package/.next/server/chunks/ssr/{node_modules_1dwguw_._.js → node_modules_0yugy6m._.js} +1 -1
  259. package/.next/server/chunks/ssr/{node_modules_004qlr4._.js → node_modules_14om4ar._.js} +1 -1
  260. package/.next/server/chunks/ssr/{node_modules_1q0p85z._.js → node_modules_14q6g4n._.js} +1 -1
  261. package/.next/server/chunks/ssr/node_modules_17bwo43._.js +55 -0
  262. package/.next/server/chunks/ssr/{node_modules_1hd-5tv._.js → node_modules_198nmll._.js} +1 -1
  263. package/.next/server/chunks/ssr/{node_modules_0m93zeg._.js → node_modules_1_pc9kq._.js} +1 -1
  264. package/.next/server/chunks/ssr/{node_modules_1n5ygqe._.js → node_modules_1aaxuef._.js} +1 -1
  265. package/.next/server/chunks/ssr/{node_modules_01qnv-a._.js → node_modules_1cx_ql6._.js} +1 -1
  266. package/.next/server/chunks/ssr/{node_modules_1zu988w._.js → node_modules_1cz2hle._.js} +1 -1
  267. package/.next/server/chunks/ssr/{node_modules_1xyd7ov._.js → node_modules_1efrqxi._.js} +1 -1
  268. package/.next/server/chunks/ssr/{node_modules_13r44jq._.js → node_modules_1eg7m6c._.js} +1 -1
  269. package/.next/server/chunks/ssr/{node_modules_08d8bke._.js → node_modules_1gdh31p._.js} +1 -1
  270. package/.next/server/chunks/ssr/{node_modules_1hn8z1f._.js → node_modules_1hpopmo._.js} +1 -1
  271. package/.next/server/chunks/ssr/{node_modules_20yebtj._.js → node_modules_1qpjvy5._.js} +1 -1
  272. package/.next/server/chunks/ssr/{node_modules_09xhur7._.js → node_modules_1vh5ntv._.js} +1 -1
  273. package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0m9a4y0._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_1b748ic._.js} +1 -1
  274. package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_13w_wy9._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_1z4hm5e._.js} +1 -1
  275. package/.next/server/chunks/ssr/node_modules_@vercel_oidc_dist_06mk8id._.js +3 -0
  276. package/.next/server/chunks/ssr/{node_modules_next_dist_0w6mzq5._.js → node_modules_next_dist_0drixxt._.js} +4 -4
  277. package/.next/server/chunks/ssr/node_modules_next_dist_1n3w9lb._.js +2 -2
  278. package/.next/server/instrumentation.js +1 -1
  279. package/.next/server/middleware-build-manifest.js +3 -3
  280. package/.next/server/pages/404.html +1 -1
  281. package/.next/server/pages/500.html +1 -1
  282. package/.next/server/server-reference-manifest.js +1 -1
  283. package/.next/server/server-reference-manifest.json +58 -91
  284. package/.next/static/chunks/00rd4-v8r7axp.js +1 -0
  285. package/.next/static/chunks/08qe4as2q428z.js +3 -0
  286. package/.next/static/chunks/{31ttnjt6g35a0.js → 0ev4hyghq0mbb.js} +1 -1
  287. package/.next/static/chunks/0fl3gwe4-l6tz.js +1 -0
  288. package/.next/static/chunks/0gnqdjn2lvdad.js +2 -0
  289. package/.next/static/chunks/0hwap1-p0m5sa.js +1 -0
  290. package/.next/static/chunks/0r5qcctujhxli.js +1 -0
  291. package/.next/static/chunks/0w52rrrmuj9r9.js +1 -0
  292. package/.next/static/chunks/{07kl352c-lvcz.js → 0wcprt5ot5she.js} +1 -1
  293. package/.next/static/chunks/10teww7wt-f6h.js +1 -0
  294. package/.next/static/chunks/15m575979un5f.js +38 -0
  295. package/.next/static/chunks/17p6tk6k88dq6.js +1 -0
  296. package/.next/static/chunks/{3_h9txt3tq8t8.js → 1dkjg87pxk0n9.js} +1 -1
  297. package/.next/static/chunks/1jd09sxcjddu1.js +1 -0
  298. package/.next/static/chunks/1kq8h8wi_e0bc.js +53 -0
  299. package/.next/static/chunks/{18vhba44l1fc9.js → 1w73ece_6bheu.js} +1 -1
  300. package/.next/static/chunks/{3ard_noqapnw1.js → 214ya3n5gxeih.js} +1 -1
  301. package/.next/static/chunks/22_zkas21rpna.js +1 -0
  302. package/.next/static/chunks/2h0sd2vumhb15.js +1 -0
  303. package/.next/static/chunks/2t4vhi3m8ahya.js +1 -0
  304. package/.next/static/chunks/2tigdtz3dpcy8.js +1 -0
  305. package/.next/static/chunks/2u7xt6mxg44lf.js +1 -0
  306. package/.next/static/chunks/39j0fvpybzabl.js +40 -0
  307. package/.next/static/chunks/3akmqm77muj4-.js +1 -0
  308. package/.next/static/chunks/3cq5g494yuke8.css +1 -0
  309. package/.next/static/chunks/3woj-xd7ki5_i.js +1 -0
  310. package/.next/static/chunks/3xk7yqgomx7yq.js +1 -0
  311. package/.next/static/chunks/3zmcsnuo25zvq.js +1 -0
  312. package/.next/static/media/favicon.37o1pjldwi2uc.ico +0 -0
  313. package/README.md +47 -22
  314. package/app/api/bot/memory/files/route.ts +14 -0
  315. package/app/api/bot/memory/route.ts +5 -0
  316. package/app/api/events/app-event.ts +3 -7
  317. package/app/api/llm-model/credits/route.ts +11 -0
  318. package/app/api/llm-model/route.ts +6 -1
  319. package/app/api/llm-model/subscription/route.ts +11 -0
  320. package/app/api/memory/edit/route.ts +7 -0
  321. package/app/api/memory/route.ts +21 -0
  322. package/app/api/query-key.ts +19 -4
  323. package/bin/port.mjs +65 -0
  324. package/bin/thursday.mjs +5 -33
  325. package/components/ui/shiny-text.tsx +48 -134
  326. package/database/migrations/{20260909060813_optimal_sabretooth → 20260910173049_numerous_spirit}/migration.sql +24 -34
  327. package/database/migrations/{20260909060813_optimal_sabretooth → 20260910173049_numerous_spirit}/snapshot.json +71 -145
  328. package/database/migrations/20260911030320_opposite_jack_power/migration.sql +1 -0
  329. package/database/migrations/{20260909063821_bitter_tenebrous → 20260911030320_opposite_jack_power}/snapshot.json +57 -184
  330. package/docs/how-it-works.md +11 -7
  331. package/docs/images/face-dark.png +0 -0
  332. package/docs/images/face-light.png +0 -0
  333. package/docs/images/keeps-going.png +0 -0
  334. package/features/ai/chatgpt.ts +639 -0
  335. package/features/ai/components/chatgpt-sign-in.tsx +66 -0
  336. package/features/ai/components/model-picker.tsx +17 -1
  337. package/features/ai/components/provider-icon.tsx +1 -0
  338. package/features/ai/load-tools.ts +78 -56
  339. package/features/ai/model.schema.ts +232 -34
  340. package/features/ai/model.ts +169 -24
  341. package/features/ai/prompts/bot.prompt.ts +207 -114
  342. package/features/ai/prompts/memory-edit.prompt.ts +33 -0
  343. package/features/ai/prompts/prompt-helper.ts +45 -26
  344. package/features/ai/prompts/thursday.prompt.ts +57 -27
  345. package/features/ai/tools/bot.tool.ts +35 -52
  346. package/features/ai/tools/connected.ts +17 -4
  347. package/features/ai/tools/memory.tool.ts +181 -117
  348. package/features/ai/tools/search.tool.ts +136 -47
  349. package/features/ai/tools/tool-name.ts +2 -6
  350. package/features/ai/tools/workspace.tool.ts +5 -1
  351. package/features/bot/bot.action.ts +14 -19
  352. package/features/bot/bot.memory.ts +89 -0
  353. package/features/bot/bot.query.ts +37 -57
  354. package/features/bot/bot.run.ts +512 -242
  355. package/features/bot/bot.runner.ts +434 -120
  356. package/features/bot/bot.schema.ts +104 -19
  357. package/features/bot/bot.seed.ts +89 -16
  358. package/features/bot/components/bot-mark.tsx +122 -20
  359. package/features/bot/components/bot-room.tsx +214 -132
  360. package/features/bot/components/bot-setting.tsx +790 -402
  361. package/features/bot/components/bot-tool.tsx +2 -7
  362. package/features/bot/components/mark-palette.tsx +54 -0
  363. package/features/bot/components/path-chips.tsx +1 -1
  364. package/features/bot/components/task-badge.tsx +26 -6
  365. package/features/bot/components/task-reply.tsx +22 -33
  366. package/features/bot/components/task-setting.tsx +20 -17
  367. package/features/bot/task.query.ts +240 -34
  368. package/features/bot/task.store.ts +76 -13
  369. package/features/config/components/config-setting.tsx +265 -34
  370. package/features/config/components/models-badge.tsx +42 -0
  371. package/features/config/config.action.ts +12 -0
  372. package/features/config/config.const.ts +38 -2
  373. package/features/connectors/mcp.manager.ts +10 -2
  374. package/features/intro/components/intro.tsx +47 -46
  375. package/features/memory/components/memory-edit.tsx +340 -0
  376. package/features/memory/components/memory-setting.tsx +75 -77
  377. package/features/memory/memory.action.ts +3 -43
  378. package/features/memory/memory.edit.ts +87 -0
  379. package/features/memory/memory.query.ts +24 -42
  380. package/features/memory/memory.schema.ts +29 -106
  381. package/features/settings/components/settings.tsx +2 -0
  382. package/features/settings/settings.alert.ts +3 -1
  383. package/features/thursday/components/ascii-orb.tsx +106 -90
  384. package/features/thursday/components/boot.tsx +7 -0
  385. package/features/thursday/components/call-log.tsx +219 -92
  386. package/features/thursday/components/face.tsx +6 -4
  387. package/features/thursday/components/tab-state.tsx +97 -0
  388. package/features/thursday/components/thursday-mark.tsx +4 -1
  389. package/features/thursday/components/thursday-setting.tsx +17 -47
  390. package/features/thursday/components/thursday.tsx +208 -98
  391. package/features/thursday/thursday.action.ts +12 -17
  392. package/features/thursday/thursday.query.ts +61 -70
  393. package/features/thursday/thursday.schema.ts +20 -7
  394. package/features/thursday/tool-line.ts +85 -12
  395. package/features/thursday/use-thursday.ts +87 -60
  396. package/features/workspace/components/file-view.tsx +4 -2
  397. package/features/workspace/workspace.ts +110 -26
  398. package/lib/limits.ts +1 -1
  399. package/lib/oauth-page.ts +27 -0
  400. package/lib/realtime/openai.ts +2 -2
  401. package/lib/realtime/realtime.protocol.ts +1 -1
  402. package/lib/realtime/realtime.tap.ts +122 -0
  403. package/lib/sandbox.ts +59 -36
  404. package/package.json +1 -1
  405. package/skills/browser/SKILL.md +55 -34
  406. package/.next/server/app/api/bot/notes/route/app-paths-manifest.json +0 -3
  407. package/.next/server/app/api/bot/notes/route.js +0 -14
  408. package/.next/server/app/api/bot/notes/route_client-reference-manifest.js +0 -3
  409. package/.next/server/app/api/memory/tidy/route/app-paths-manifest.json +0 -3
  410. package/.next/server/app/api/memory/tidy/route.js +0 -22
  411. package/.next/server/app/api/memory/tidy/route.js.nft.json +0 -1
  412. package/.next/server/chunks/0d-b_next_dist_server_app-render_after-task-async-storage_external_0-exn7p.js +0 -3
  413. package/.next/server/chunks/[root-of-the-server]__0-npkh_._.js +0 -3
  414. package/.next/server/chunks/[root-of-the-server]__005var5._.js +0 -3
  415. package/.next/server/chunks/[root-of-the-server]__019dx9f._.js +0 -5
  416. package/.next/server/chunks/[root-of-the-server]__02_2sm-._.js +0 -5
  417. package/.next/server/chunks/[root-of-the-server]__04_8spk._.js +0 -3
  418. package/.next/server/chunks/[root-of-the-server]__05luceg._.js +0 -3
  419. package/.next/server/chunks/[root-of-the-server]__06t77t4._.js +0 -11
  420. package/.next/server/chunks/[root-of-the-server]__0bed6hu._.js +0 -8
  421. package/.next/server/chunks/[root-of-the-server]__0cro2dz._.js +0 -5
  422. package/.next/server/chunks/[root-of-the-server]__0dupqt7._.js +0 -5
  423. package/.next/server/chunks/[root-of-the-server]__0emr6go._.js +0 -3
  424. package/.next/server/chunks/[root-of-the-server]__0le4d-p._.js +0 -3
  425. package/.next/server/chunks/[root-of-the-server]__0m8ywyo._.js +0 -10
  426. package/.next/server/chunks/[root-of-the-server]__0nfuugi._.js +0 -5
  427. package/.next/server/chunks/[root-of-the-server]__0p32jfz._.js +0 -12
  428. package/.next/server/chunks/[root-of-the-server]__0phdgco._.js +0 -8
  429. package/.next/server/chunks/[root-of-the-server]__0wvgrsl._.js +0 -3
  430. package/.next/server/chunks/[root-of-the-server]__10qshki._.js +0 -3
  431. package/.next/server/chunks/[root-of-the-server]__11j52t0._.js +0 -5
  432. package/.next/server/chunks/[root-of-the-server]__1692zrv._.js +0 -3
  433. package/.next/server/chunks/[root-of-the-server]__16thabu._.js +0 -3
  434. package/.next/server/chunks/[root-of-the-server]__173nhwf._.js +0 -8
  435. package/.next/server/chunks/[root-of-the-server]__1_aoyr9._.js +0 -3
  436. package/.next/server/chunks/[root-of-the-server]__1bn73ih._.js +0 -10
  437. package/.next/server/chunks/[root-of-the-server]__1bqhuh9._.js +0 -8
  438. package/.next/server/chunks/[root-of-the-server]__1cqizfq._.js +0 -5
  439. package/.next/server/chunks/[root-of-the-server]__1e-ci13._.js +0 -3
  440. package/.next/server/chunks/[root-of-the-server]__1fboj2j._.js +0 -3
  441. package/.next/server/chunks/[root-of-the-server]__1lr7j9t._.js +0 -5
  442. package/.next/server/chunks/[root-of-the-server]__1oli73c._.js +0 -3
  443. package/.next/server/chunks/[root-of-the-server]__1txedx6._.js +0 -8
  444. package/.next/server/chunks/[root-of-the-server]__1untqv8._.js +0 -5
  445. package/.next/server/chunks/[root-of-the-server]__1zgpz1w._.js +0 -3
  446. package/.next/server/chunks/_00_584r._.js +0 -293
  447. package/.next/server/chunks/_012yhfm._.js +0 -39
  448. package/.next/server/chunks/_06mxlgn._.js +0 -3
  449. package/.next/server/chunks/_07rtlxv._.js +0 -3
  450. package/.next/server/chunks/_0a351g7._.js +0 -6
  451. package/.next/server/chunks/_0kjjmag._.js +0 -3
  452. package/.next/server/chunks/_0nn99o2._.js +0 -3
  453. package/.next/server/chunks/_0zuzmt_._.js +0 -39
  454. package/.next/server/chunks/_106co2o._.js +0 -3
  455. package/.next/server/chunks/_12bbv5i._.js +0 -100
  456. package/.next/server/chunks/_15q1pig._.js +0 -3
  457. package/.next/server/chunks/_19wg5yn._.js +0 -138
  458. package/.next/server/chunks/_1bt95p9._.js +0 -55
  459. package/.next/server/chunks/_1cbk7ms._.js +0 -26
  460. package/.next/server/chunks/_1drv2et._.js +0 -40
  461. package/.next/server/chunks/_1ic3bew._.js +0 -5
  462. package/.next/server/chunks/_1mxn_lb._.js +0 -3
  463. package/.next/server/chunks/_1rysr-r._.js +0 -10
  464. package/.next/server/chunks/_1u3rwbr._.js +0 -3
  465. package/.next/server/chunks/_1x7p319._.js +0 -6
  466. package/.next/server/chunks/_next-internal_server_app_api_bot_notes_route_actions_0qcsyu7.js +0 -3
  467. package/.next/server/chunks/_next-internal_server_app_api_memory_tidy_route_actions_0_cfuom.js +0 -3
  468. package/.next/server/chunks/features_bot_task_query_ts_11xuzl3._.js +0 -3
  469. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_0gzvgfv._.js +0 -3
  470. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_0vorg6w._.js +0 -3
  471. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1tpvamf._.js +0 -3
  472. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1vmaq9f._.js +0 -3
  473. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1y9-wcr._.js +0 -3
  474. package/.next/server/chunks/ssr/[externals]_node_crypto_1kn79-7._.js +0 -3
  475. package/.next/server/chunks/ssr/[root-of-the-server]__0iuoa-r._.js +0 -3
  476. package/.next/server/chunks/ssr/[root-of-the-server]__0un-erv._.js +0 -4
  477. package/.next/server/chunks/ssr/[root-of-the-server]__1-mnhyd._.js +0 -92
  478. package/.next/server/chunks/ssr/[root-of-the-server]__1bww500._.js +0 -3
  479. package/.next/server/chunks/ssr/[root-of-the-server]__1vcdast._.js +0 -3
  480. package/.next/server/chunks/ssr/[root-of-the-server]__1xwnu-h._.js +0 -3
  481. package/.next/server/chunks/ssr/_0-mc7ro._.js +0 -3
  482. package/.next/server/chunks/ssr/_01p5r6-._.js +0 -3
  483. package/.next/server/chunks/ssr/_03webdr._.js +0 -3
  484. package/.next/server/chunks/ssr/_09-0k3z._.js +0 -3
  485. package/.next/server/chunks/ssr/_0f_b_9s._.js +0 -3
  486. package/.next/server/chunks/ssr/_0fafgvl._.js +0 -3
  487. package/.next/server/chunks/ssr/_0sf1isi._.js +0 -3
  488. package/.next/server/chunks/ssr/_0su6_08._.js +0 -3
  489. package/.next/server/chunks/ssr/_0ti51xc._.js +0 -3
  490. package/.next/server/chunks/ssr/_0w5bgak._.js +0 -3
  491. package/.next/server/chunks/ssr/_0y84qo6._.js +0 -3
  492. package/.next/server/chunks/ssr/_1ciyoo3._.js +0 -3
  493. package/.next/server/chunks/ssr/_1e-dgz0._.js +0 -4
  494. package/.next/server/chunks/ssr/_1fq7zgb._.js +0 -3
  495. package/.next/server/chunks/ssr/_1g6l4uj._.js +0 -3
  496. package/.next/server/chunks/ssr/_1ruzehw._.js +0 -3
  497. package/.next/server/chunks/ssr/features_bot_components_bot-setting_tsx_01ajft6._.js +0 -4
  498. package/.next/server/chunks/ssr/features_config_components_14jqgy4._.js +0 -3
  499. package/.next/server/chunks/ssr/features_memory_components_memory-setting_tsx_0oa43sw._.js +0 -4
  500. package/.next/server/chunks/ssr/node_modules_zod_v4_classic_external_15fmmz3.js +0 -55
  501. package/.next/static/chunks/05b6nx_o3_f_n.js +0 -1
  502. package/.next/static/chunks/0dmns-gi0raa4.js +0 -1
  503. package/.next/static/chunks/0f9sq-7q_yxyl.js +0 -1
  504. package/.next/static/chunks/0oc4_xoia77dg.css +0 -1
  505. package/.next/static/chunks/0pheo8m3a-rzi.js +0 -1
  506. package/.next/static/chunks/19i8h2t-5evjx.js +0 -1
  507. package/.next/static/chunks/1c5fcj473025q.js +0 -1
  508. package/.next/static/chunks/1pi_hbmjw-f-z.js +0 -1
  509. package/.next/static/chunks/1twt6k09vg4np.js +0 -1
  510. package/.next/static/chunks/1ytnip8wdng93.js +0 -1
  511. package/.next/static/chunks/28gotuisldmom.js +0 -1
  512. package/.next/static/chunks/2998p0y7k1c8z.js +0 -3
  513. package/.next/static/chunks/29gyb3nubj51j.js +0 -53
  514. package/.next/static/chunks/2l233xfycjpd8.js +0 -1
  515. package/.next/static/chunks/2p5n7pa3c40t-.js +0 -2
  516. package/.next/static/chunks/2ru1q132af7h7.js +0 -1
  517. package/.next/static/chunks/2t8wqycwg3y04.js +0 -2
  518. package/.next/static/chunks/2uxum7qbyeyxd.js +0 -1
  519. package/.next/static/chunks/32z_ypgtchw48.js +0 -1
  520. package/.next/static/chunks/33pyjb3xd1iez.js +0 -1
  521. package/.next/static/chunks/3omud5xunrk1f.js +0 -1
  522. package/.next/static/chunks/42dnrkbph9e-1.js +0 -1
  523. package/.next/static/media/favicon.2vob68tjqpejf.ico +0 -0
  524. package/database/migrations/20260909063821_bitter_tenebrous/migration.sql +0 -1
  525. package/features/ai/prompts/memory-tidy.prompt.ts +0 -129
  526. package/features/ai/tools/tidy.tool.ts +0 -22
  527. package/features/bot/bot.notes.ts +0 -120
  528. package/features/memory/components/memory-tidy.tsx +0 -395
  529. package/features/memory/components/memory-view.tsx +0 -84
  530. package/features/memory/memory.tidy.ts +0 -332
  531. package/features/memory/tidy.query.ts +0 -135
  532. package/features/thursday/use-call-title.ts +0 -31
  533. /package/.next/server/app/api/bot/{notes → memory/files}/route/build-manifest.json +0 -0
  534. /package/.next/server/app/api/bot/{notes → memory/files}/route/server-reference-manifest.json +0 -0
  535. /package/.next/server/app/api/bot/{notes → memory/files}/route.js.map +0 -0
  536. /package/.next/server/app/api/{memory/tidy → bot/memory}/route/build-manifest.json +0 -0
  537. /package/.next/server/app/api/{memory/tidy → bot/memory}/route/server-reference-manifest.json +0 -0
  538. /package/.next/server/app/api/{memory/tidy → bot/memory}/route.js.map +0 -0
  539. /package/.next/static/{YR4NWcDDuzwLbTTU7U7Vr → kaTO0UHqF1bMnWgCDik2f}/_buildManifest.js +0 -0
  540. /package/.next/static/{YR4NWcDDuzwLbTTU7U7Vr → kaTO0UHqF1bMnWgCDik2f}/_clientMiddlewareManifest.js +0 -0
  541. /package/.next/static/{YR4NWcDDuzwLbTTU7U7Vr → kaTO0UHqF1bMnWgCDik2f}/_ssgManifest.js +0 -0
@@ -1,8 +1,10 @@
1
1
  "use client";
2
2
 
3
+ import { format } from "date-fns";
3
4
  import {
4
5
  ArrowUp,
5
6
  Check,
7
+ ChevronDown,
6
8
  ChevronLeft,
7
9
  ChevronRight,
8
10
  ChevronsRight,
@@ -10,6 +12,7 @@ import {
10
12
  History,
11
13
  Loader2,
12
14
  Plus,
15
+ RotateCw,
13
16
  X,
14
17
  } from "lucide-react";
15
18
  import {
@@ -27,16 +30,15 @@ import { Bubble, BubbleContent } from "@/components/ui/bubble";
27
30
  import { Button } from "@/components/ui/button";
28
31
  import { FoldedText } from "@/components/ui/folded-text";
29
32
  import { Markdown } from "@/components/ui/markdown";
30
- import ShinyText from "@/components/ui/shiny-text";
33
+ import { ShinyText, type ShinyTone } from "@/components/ui/shiny-text";
31
34
  import { Textarea } from "@/components/ui/textarea";
32
35
  import { toast } from "@/components/ui/toast";
33
- import { BOT_RUN } from "@/config";
34
- import { markReportedAction, startTaskAction } from "@/features/bot/bot.action";
36
+ import { startTaskAction } from "@/features/bot/bot.action";
35
37
  import {
36
38
  type Bot,
37
39
  type BotIcon,
38
40
  DEFAULT_BOT,
39
- isBudgetAsk,
41
+ isAppStop,
40
42
  TASK_CONTINUE,
41
43
  } from "@/features/bot/bot.schema";
42
44
  import { BotMark } from "@/features/bot/components/bot-mark";
@@ -47,7 +49,6 @@ import { openSettings } from "@/features/settings/settings.store";
47
49
  import { ThursdayMark } from "@/features/thursday/components/thursday-mark";
48
50
  import { FileViewer } from "@/features/workspace/components/file-view";
49
51
  import { shortAgo, toDate } from "@/lib/date-like";
50
- import { unwrapResult } from "@/lib/protocol/result";
51
52
  import { useServerAction } from "@/lib/protocol/use-server-action";
52
53
  import { revalidate, useServerRoute } from "@/lib/protocol/use-server-route";
53
54
  import {
@@ -71,6 +72,7 @@ import {
71
72
  type TaskViewStatus,
72
73
  threadItems,
73
74
  useBotTasks,
75
+ useSeenOnDetail,
74
76
  } from "../task.store";
75
77
  import { BotTool } from "./bot-tool";
76
78
 
@@ -178,41 +180,23 @@ export const BotRoom = memo(function BotRoom() {
178
180
  }
179
181
  }, [tasks, handoff]);
180
182
 
181
- // Opening a finished thread marks it reported. The key includes updatedAt: a
182
- // follow-up request finishes again and that result is unread. markReported does not
183
- // move `updatedAt`, so the set only bridges the gap until the next poll.
184
- const marked = useRef(new Set<string>());
185
- useEffect(() => {
186
- if (!current || current.relayed) return;
187
- if (current.status !== "done" && current.status !== "failed") return;
188
- const key = `${current.id}@${toDate(current.updatedAt).getTime()}`;
189
- if (marked.current.has(key)) return;
190
- marked.current.add(key);
191
- // unwrapResult folds Result failures and rejections into one path; on failure
192
- // release the key so the task can be marked again.
193
- void markReportedAction([current.id])
194
- .then(unwrapResult)
195
- .then(() => revalidate(queryKey.tasks))
196
- .catch((cause) => {
197
- marked.current.delete(key);
198
- toast.add({
199
- type: "error",
200
- title: "Could not mark the task as read",
201
- description: errorToString(cause),
202
- });
203
- });
204
- }, [current]);
183
+ // Reading a thread is reading its ending; that is what clears its dot.
184
+ useSeenOnDetail(open ? current : null);
205
185
 
206
186
  const busy = tasks.filter((entry) => entry.status === "working").length;
207
187
  const attention = newest.filter(needsYou);
208
188
  const pending = attention.length;
189
+ const unread = newest.filter(isUnread);
190
+ const failed = unread.filter((entry) => entry.status === "failed").length;
209
191
 
210
192
  const closeCompose = useCallback(() => setComposing(false), []);
211
193
 
212
194
  return (
213
- <div className="pointer-events-none absolute right-5 bottom-5 z-10 flex w-132 max-w-[calc(100vw-2.5rem)] flex-col items-end gap-2">
195
+ // As wide as the resting pill may grow: 80% of the window. The open room
196
+ // keeps its own 33rem inside it.
197
+ <div className="pointer-events-none absolute right-5 bottom-5 z-10 flex w-[min(80vw,calc(100vw-2.5rem))] flex-col items-end gap-2">
214
198
  {open ? (
215
- <div className="pointer-events-auto flex max-h-[min(44rem,78vh)] w-full animate-in flex-col overflow-hidden rounded-3xl bg-background/75 shadow-2xl shadow-black/6 ring-1 ring-border/50 backdrop-blur-xl fade-in slide-in-from-bottom-1 duration-200">
199
+ <div className="pointer-events-auto flex max-h-[min(44rem,78vh)] w-132 max-w-full animate-in flex-col overflow-hidden rounded-3xl bg-background/75 shadow-2xl shadow-black/6 ring-1 ring-border/50 backdrop-blur-xl fade-in slide-in-from-bottom-1 duration-200">
216
200
  {current ? (
217
201
  <>
218
202
  <ThreadHeader
@@ -267,6 +251,8 @@ export const BotRoom = memo(function BotRoom() {
267
251
  count={tasks.length}
268
252
  busy={busy}
269
253
  pending={pending}
254
+ unread={unread.length}
255
+ failed={failed}
270
256
  composing={composing}
271
257
  onCompose={() => setComposing(true)}
272
258
  onCloseCompose={closeCompose}
@@ -275,8 +261,10 @@ export const BotRoom = memo(function BotRoom() {
275
261
  setOpen(true);
276
262
  }}
277
263
  onOpen={() => {
278
- // A single waiting task opens straight into its thread.
279
- if (!picked && attention.length === 1) setPicked(attention[0].id);
264
+ // One thing to look at opens straight into its thread: a single
265
+ // question, or with none waiting, a single answer nobody has read.
266
+ const only = attention.length ? attention : unread;
267
+ if (!picked && only.length === 1) setPicked(only[0].id);
280
268
  setOpen(true);
281
269
  }}
282
270
  />
@@ -441,7 +429,7 @@ function crewOf(
441
429
  };
442
430
  }
443
431
 
444
- /** Blanks the question in the reply box when it already is the last thread line (budget stops). */
432
+ /** Blanks the question in the reply box when it already is the last thread line (stops the app made). */
445
433
  function askFor(task: TaskView): TaskView["ask"] {
446
434
  if (!task.ask?.question) return task.ask;
447
435
  const last = task.lines.at(-1);
@@ -451,46 +439,69 @@ function askFor(task: TaskView): TaskView["ask"] {
451
439
  }
452
440
 
453
441
  /**
454
- * A task that cannot move until the user answers: a question, or a budget stop.
442
+ * A task that cannot move until the user answers: a question, or a stop the app made.
455
443
  *
456
444
  * A finished job is not on this list. It has nothing for the user to do, and
457
- * counting it here made the chip hold itself open over a report nobody had to
458
- * act on. A report says itself once, in the sentence, and then it is a row in
445
+ * counting it here made the chip hold itself open over an answer nobody had to
446
+ * act on. An answer says itself once, in the sentence, and then it is a row in
459
447
  * the room like every other one.
460
448
  */
461
449
  const needsYou = (task: TaskView) => task.status === "waiting";
462
450
 
451
+ /** An ending nobody has opened. It needs the user too, to read rather than to answer. */
452
+ const isUnread = (task: TaskView) =>
453
+ (task.status === "done" || task.status === "failed") && !task.seen;
454
+
463
455
  /**
464
456
  * What the room itself is doing, and nothing else — the right side of the pill.
465
457
  *
466
- * Only `waiting on you` sweeps. A shine means something is happening right now,
467
- * which is what the words beside the faces are for; the spinner already says a
468
- * job is running, and two things sweeping in one pill read as a loading screen.
458
+ * What is still moving shines, like every other running line: a wait in amber,
459
+ * running jobs in muted ink beside the spinner. An ending does not an answer
460
+ * nobody has opened is something to read, not something happening.
469
461
  */
470
462
  function restingState({
471
463
  count,
472
464
  busy,
473
465
  pending,
466
+ unread,
467
+ failed,
474
468
  }: {
475
469
  count: number;
476
470
  busy: number;
477
471
  pending: number;
478
- }): { text: string; tone: string; shine: boolean } {
472
+ /** Endings nobody has opened, failures included. */
473
+ unread: number;
474
+ failed: number;
475
+ }): { text: string; tone: string; shine: ShinyTone | null } {
479
476
  if (pending > 0)
480
477
  return {
481
478
  text: pending === 1 ? "waiting on you" : `${pending} waiting on you`,
482
479
  tone: WAITING_INK,
483
- shine: true,
480
+ shine: "waiting",
481
+ };
482
+ // Unread outranks running: a job still going will say so again, and an answer
483
+ // left unopened will not.
484
+ if (failed > 0)
485
+ return {
486
+ text: failed === 1 ? "1 failed" : `${failed} failed`,
487
+ tone: "text-destructive",
488
+ shine: null,
489
+ };
490
+ if (unread > 0)
491
+ return {
492
+ text: unread === 1 ? "1 new answer" : `${unread} new answers`,
493
+ tone: WAITING_INK,
494
+ shine: null,
484
495
  };
485
496
  if (busy > 0)
486
497
  return {
487
498
  text: busy === 1 ? "working" : `${busy} running`,
488
499
  tone: "text-muted-foreground",
489
- shine: false,
500
+ shine: "muted",
490
501
  };
491
502
  if (count > 0)
492
- return { text: "all done", tone: "text-muted-foreground", shine: false };
493
- return { text: "no jobs yet", tone: "text-muted-foreground", shine: false };
503
+ return { text: "all done", tone: "text-muted-foreground", shine: null };
504
+ return { text: "no jobs yet", tone: "text-muted-foreground", shine: null };
494
505
  }
495
506
 
496
507
  /**
@@ -520,6 +531,8 @@ function Chip({
520
531
  count,
521
532
  busy,
522
533
  pending,
534
+ unread,
535
+ failed,
523
536
  composing,
524
537
  onCompose,
525
538
  onCloseCompose,
@@ -535,13 +548,15 @@ function Chip({
535
548
  count: number;
536
549
  busy: number;
537
550
  pending: number;
551
+ unread: number;
552
+ failed: number;
538
553
  composing: boolean;
539
554
  onCompose: () => void;
540
555
  onCloseCompose: () => void;
541
556
  onPick: (id: string) => void;
542
557
  onOpen: () => void;
543
558
  }) {
544
- const state = restingState({ count, busy, pending });
559
+ const state = restingState({ count, busy, pending, unread, failed });
545
560
  const grown = composing || pending > 0;
546
561
 
547
562
  return (
@@ -550,8 +565,10 @@ function Chip({
550
565
  // Not `overflow-hidden`: a hand-off bubble stands above the row, outside
551
566
  // this box. The growing part clips itself instead.
552
567
  "pointer-events-auto w-fit max-w-full bg-background/78 ring-1 ring-border/50 backdrop-blur-md transition-shadow duration-300",
568
+ // A card with questions in it keeps the room's width: only the resting
569
+ // row grows with the corner, and a reply's long lines would stretch it.
553
570
  grown
554
- ? "min-w-96 rounded-3xl shadow-lg shadow-black/8"
571
+ ? "max-w-[min(33rem,100%)] min-w-96 rounded-3xl shadow-lg shadow-black/8"
555
572
  : "rounded-full shadow-sm shadow-black/3",
556
573
  )}
557
574
  >
@@ -607,18 +624,12 @@ function Chip({
607
624
  the right. `ml-auto` keeps it there when the crew says nothing. */}
608
625
  <span className="ml-auto flex h-7 shrink-0 items-center gap-1.5">
609
626
  {state.shine ? (
610
- // Neither waiting nor running is a state at rest, but only one of
611
- // them needs you. The colours are classes because they differ per
612
- // theme, and ShinyText reads the variables they set.
613
- <span className="block [--rest:var(--color-amber-700)] [--shine:var(--color-amber-400)] dark:[--rest:var(--color-amber-400)] dark:[--shine:var(--color-amber-100)]">
614
- <ShinyText
615
- text={state.text}
616
- speed={2.6}
617
- color="var(--rest)"
618
- shineColor="var(--shine)"
619
- className="truncate text-[14px] leading-5 tracking-[-0.15px]"
620
- />
621
- </span>
627
+ <ShinyText
628
+ text={state.text}
629
+ tone={state.shine}
630
+ speed={2.6}
631
+ className="block truncate text-[14px] leading-5 tracking-[-0.15px]"
632
+ />
622
633
  ) : (
623
634
  <span
624
635
  key={state.text}
@@ -633,12 +644,14 @@ function Chip({
633
644
  {/* Always 16px, empty or not: a glyph that comes and goes moves the
634
645
  sentence's right end even when the sentence has not changed. */}
635
646
  <span className="grid size-4 shrink-0 place-items-center">
636
- {pending === 0 && busy > 0 && (
647
+ {pending === 0 && unread === 0 && busy > 0 && (
637
648
  <Loader2 className="size-4 animate-spin text-muted-foreground/70" />
638
649
  )}
639
- {pending === 0 && busy === 0 && count > 0 && (
640
- <Check className="size-4 text-muted-foreground/60" />
641
- )}
650
+ {pending === 0 &&
651
+ failed === 0 &&
652
+ (unread > 0 || (busy === 0 && count > 0)) && (
653
+ <Check className="size-4 text-muted-foreground/60" />
654
+ )}
642
655
  </span>
643
656
  </span>
644
657
  </button>
@@ -702,8 +715,13 @@ function Compose({ bots, onDone }: { bots?: Bot[]; onDone: () => void }) {
702
715
  });
703
716
 
704
717
  // A fresh install has no rows and still has a worker (bot.schema DEFAULT_BOT).
718
+ // Switched-off bots are left out for the same reason no model is shown one —
719
+ // but the fallback answers "no rows", not "every row off", so switching them
720
+ // all off leaves nothing to pick rather than conjuring a worker.
705
721
  const roster: BotRef[] = bots?.length
706
- ? bots.map((bot) => ({ name: bot.name, icon: bot.icon }))
722
+ ? bots
723
+ .filter((bot) => !bot.disabled)
724
+ .map((bot) => ({ name: bot.name, icon: bot.icon }))
707
725
  : [{ name: DEFAULT_BOT.name, icon: DEFAULT_BOT.icon }];
708
726
 
709
727
  const send = () => {
@@ -833,6 +851,9 @@ const GHOSTS: CrewFace[] = ["alto", "brio", "cinder", "delta"].map(
833
851
  /** A single face reads as one worker, not as a crew; below this the row is padded out. */
834
852
  const FLOOR = 3;
835
853
 
854
+ /** The awake halo, in the mark's own view-box units: about 2px of blur at 28px. */
855
+ const AWAKE_GLOW = 20;
856
+
836
857
  /**
837
858
  * The crew, and what each of them is doing.
838
859
  *
@@ -872,25 +893,12 @@ function Crew({
872
893
  face.standIn && "opacity-35",
873
894
  face.awake && "-translate-y-0.5 scale-110",
874
895
  )}
875
- style={{
876
- zIndex: crew.length - index,
877
- color: face.icon?.color ?? undefined,
878
- // The glow is the bot's own colour, so who is moving reads before
879
- // any word does. currentColor keeps it right when nobody chose one.
880
- filter: face.awake
881
- ? "drop-shadow(0 0 5px color-mix(in srgb, currentColor 55%, transparent))"
882
- : undefined,
883
- }}
896
+ style={{ zIndex: crew.length - index }}
884
897
  >
885
- <BotMark
886
- size={28}
887
- seed={face.name}
888
- vary={face.name}
889
- color={face.icon?.color}
890
- shape={face.icon?.shape}
891
- outline={face.icon?.outline}
892
- notify={face.waiting}
893
- />
898
+ {/* The halo is the mark's own (bot-mark `glow`), not a filter on
899
+ this box: a filter here also lands on the bubble above, which
900
+ came out tinted in the bot's colour and wearing its blur. */}
901
+ <CrewMark face={face} />
894
902
  {bubble && <HandoffBubble handoff={bubble} />}
895
903
  </span>
896
904
  {face.word && (
@@ -900,8 +908,6 @@ function Crew({
900
908
  <ShinyText
901
909
  text={face.word}
902
910
  speed={2.6}
903
- color="var(--muted-foreground)"
904
- shineColor="var(--foreground)"
905
911
  className="truncate text-[13px] leading-5 tracking-[-0.1px]"
906
912
  />
907
913
  </span>
@@ -926,6 +932,30 @@ function Crew({
926
932
  );
927
933
  }
928
934
 
935
+ /**
936
+ * One crew face. Awake, it wears a halo in its own colour, so who is moving
937
+ * reads before any word does; the options object is memoized because a new one
938
+ * every render re-derives the silhouette behind it.
939
+ */
940
+ function CrewMark({ face }: { face: CrewFace }) {
941
+ const glow = useMemo(
942
+ () => (face.awake ? { glow: AWAKE_GLOW } : undefined),
943
+ [face.awake],
944
+ );
945
+ return (
946
+ <BotMark
947
+ size={28}
948
+ seed={face.name}
949
+ vary={face.name}
950
+ color={face.icon?.color}
951
+ shape={face.icon?.shape}
952
+ outline={face.icon?.outline}
953
+ notify={face.waiting}
954
+ options={glow}
955
+ />
956
+ );
957
+ }
958
+
929
959
  /**
930
960
  * A hand-off, over the face it landed on. The mark at its head is whoever gave
931
961
  * it away — Thursday, another bot, or nobody at all when it was you.
@@ -1100,7 +1130,7 @@ const STATE_LOOK: Record<TaskViewStatus, string> = {
1100
1130
 
1101
1131
  function State({ task }: { task: TaskView }) {
1102
1132
  const look =
1103
- task.status === "done" && task.relayed
1133
+ task.status === "done" && task.seen
1104
1134
  ? "text-muted-foreground"
1105
1135
  : STATE_LOOK[task.status];
1106
1136
 
@@ -1109,8 +1139,6 @@ function State({ task }: { task: TaskView }) {
1109
1139
  <ShinyText
1110
1140
  text="working"
1111
1141
  speed={2.2}
1112
- color="var(--muted-foreground)"
1113
- shineColor="var(--foreground)"
1114
1142
  className="shrink-0 font-mono text-[10px]"
1115
1143
  />
1116
1144
  );
@@ -1224,18 +1252,17 @@ function TaskList({
1224
1252
 
1225
1253
  function TaskRow({ task, onPick }: { task: TaskView; onPick: () => void }) {
1226
1254
  const attention = needsYou(task);
1227
- // secondLine runs plainText over the whole report. Every sync rebuilds each TaskView
1255
+ // secondLine runs plainText over the whole answer. Every sync rebuilds each TaskView
1228
1256
  // (task.store), so depend on the fields that change the line, not on `task`.
1229
1257
  const last = task.lines.at(-1);
1230
1258
  const line = useMemo(
1231
1259
  () => secondLine(task),
1232
- [task.status, task.outcome, task.relayed, task.ask, last?.id],
1260
+ [task.status, task.outcome, task.seen, task.ask, last?.id],
1233
1261
  );
1234
1262
  const [answer, answering] = useAnswerTask();
1235
1263
  const [sending, setSending] = useState<string | null>(null);
1236
1264
  // Options are answered inline, without opening the thread.
1237
1265
  const options = task.status === "waiting" ? (task.ask?.options ?? []) : [];
1238
- const budget = isBudgetAsk(task.ask);
1239
1266
 
1240
1267
  return (
1241
1268
  // The row is not itself a button: the option buttons cannot nest inside one.
@@ -1287,8 +1314,6 @@ function TaskRow({ task, onPick }: { task: TaskView; onPick: () => void }) {
1287
1314
  <ShinyText
1288
1315
  text={line.text}
1289
1316
  speed={2.2}
1290
- color="var(--muted-foreground)"
1291
- shineColor="var(--foreground)"
1292
1317
  className={cn(
1293
1318
  "min-w-0 flex-1 truncate text-[12px] leading-4",
1294
1319
  line.tone,
@@ -1322,21 +1347,12 @@ function TaskRow({ task, onPick }: { task: TaskView; onPick: () => void }) {
1322
1347
  await answer(task, option);
1323
1348
  setSending(null);
1324
1349
  }}
1325
- className={cn(
1326
- "h-7 gap-1.5 rounded-full bg-background px-3 text-[12px]",
1327
- // A budget stop is not a question, so it does not take the waiting colour.
1328
- !budget && "border-amber-500/35",
1329
- )}
1350
+ className="h-7 gap-1.5 rounded-full border-amber-500/35 bg-background px-3 text-[12px]"
1330
1351
  >
1331
- {budget && option === TASK_CONTINUE && (
1352
+ {option === TASK_CONTINUE && (
1332
1353
  <ChevronsRight className="size-3.5 text-muted-foreground" />
1333
1354
  )}
1334
1355
  {option}
1335
- {budget && option === TASK_CONTINUE && (
1336
- <span className="font-mono text-[10px] text-muted-foreground">
1337
- +{BOT_RUN.steps} steps
1338
- </span>
1339
- )}
1340
1356
  </Button>
1341
1357
  ))}
1342
1358
  </div>
@@ -1348,29 +1364,28 @@ function TaskRow({ task, onPick }: { task: TaskView; onPick: () => void }) {
1348
1364
  /** Second row of a task line: the question, the outcome, or the bot's last step. */
1349
1365
  function secondLine(task: TaskView): { text: string; tone: string } {
1350
1366
  if (task.status === "waiting" && task.ask) {
1351
- // Budget stops are not questions; keep them out of the waiting colour.
1352
- if (isBudgetAsk(task.ask)) {
1353
- return {
1354
- text: plainText(task.outcome ?? task.ask.question),
1355
- tone: "text-muted-foreground",
1356
- };
1357
- }
1367
+ // A budget stop is not a question, but it waits on the user exactly as one
1368
+ // does, so it carries the waiting colour too; only the words differ.
1358
1369
  return {
1359
- text: task.ask.question,
1370
+ text: isAppStop(task.ask)
1371
+ ? plainText(task.outcome ?? task.ask.question)
1372
+ : task.ask.question,
1360
1373
  tone: WAITING_INK,
1361
1374
  };
1362
1375
  }
1363
1376
  // Reports are markdown; keep only the text.
1377
+ // An ending the user has opened steps back; red stays red, only quieter.
1378
+ const had = task.seen;
1364
1379
  if (task.status === "failed") {
1365
1380
  return {
1366
1381
  text: plainText(task.outcome ?? "Failed"),
1367
- tone: "text-destructive",
1382
+ tone: had ? "text-destructive/70" : "text-destructive",
1368
1383
  };
1369
1384
  }
1370
1385
  if (task.status === "done") {
1371
1386
  return {
1372
1387
  text: plainText(task.outcome ?? "Done"),
1373
- tone: task.relayed ? "text-muted-foreground" : "text-foreground",
1388
+ tone: had ? "text-muted-foreground" : "text-foreground",
1374
1389
  };
1375
1390
  }
1376
1391
  const last = lastSaid(task);
@@ -1434,20 +1449,37 @@ export function Conversation({
1434
1449
  <Group key={item.key} group={item.group} task={task} />
1435
1450
  ),
1436
1451
  )}
1437
- {task.status === "working" && task.lines.length === 0 && (
1438
- <ShinyText
1439
- text={`${task.bot.name} is taking it on…`}
1440
- speed={2.2}
1441
- color="var(--muted-foreground)"
1442
- shineColor="var(--foreground)"
1443
- className="block px-1 font-mono text-[10px]"
1444
- />
1445
- )}
1452
+ {task.status === "working" && <NextMove task={task} />}
1446
1453
  </div>
1447
1454
  </FileViewer>
1448
1455
  );
1449
1456
  }
1450
1457
 
1458
+ /**
1459
+ * The foot of a running thread while nothing on it is moving: the bot is
1460
+ * writing its next step. A tool mid-call already shines on its own row, so this
1461
+ * steps aside for it.
1462
+ */
1463
+ function NextMove({ task }: { task: TaskView }) {
1464
+ if (task.lines.some((line) => line.tool && line.tool.results === undefined)) {
1465
+ return null;
1466
+ }
1467
+ const last = task.lines.at(-1);
1468
+ // After a hand-off, in either direction, the move is the receiver's
1469
+ const who = last?.kind === "ask" && last.to ? last.to : last?.bot;
1470
+ return (
1471
+ <ShinyText
1472
+ text={
1473
+ who
1474
+ ? `${who.name} is on the next step…`
1475
+ : `${task.bot.name} is taking it on…`
1476
+ }
1477
+ speed={2.2}
1478
+ className="block px-1 font-mono text-[10px]"
1479
+ />
1480
+ );
1481
+ }
1482
+
1451
1483
  /** The delegated request, folded to three lines (FoldedText). */
1452
1484
  function Request({ task }: { task: TaskView }) {
1453
1485
  return (
@@ -1544,7 +1576,7 @@ function Turn({
1544
1576
  return (
1545
1577
  <div className={cn("flex gap-2.5", side === "end" && "flex-row-reverse")}>
1546
1578
  {mark}
1547
- {/* One cap on the turn's column, not one per block inside it: a report and
1579
+ {/* One cap on the turn's column, not one per block inside it: an answer and
1548
1580
  a one-line remark from the same bot then end on the same edge. The
1549
1581
  answering side hugs that edge, so the two sides face each other. */}
1550
1582
  <div
@@ -1601,6 +1633,8 @@ function Group({ group, task }: { group: ChatterGroup; task: TaskView }) {
1601
1633
  {runs(group.lines).map((run) =>
1602
1634
  run.kind === "tools" ? (
1603
1635
  <Steps key={run.key} lines={run.lines} taskId={task.id} />
1636
+ ) : run.kind === "stops" ? (
1637
+ <Stops key={run.key} lines={run.lines} />
1604
1638
  ) : (
1605
1639
  run.lines.map((line) => (
1606
1640
  <Line key={line.id} line={line} taskId={task.id} />
@@ -1636,7 +1670,7 @@ function Line({ line, taskId }: { line: Chatter; taskId: string }) {
1636
1670
  );
1637
1671
  }
1638
1672
 
1639
- // Passing remarks are muted; the report is the one thing here at full weight.
1673
+ // Passing remarks are muted; the answer is the one thing here at full weight.
1640
1674
  if (!isOutcome(line)) {
1641
1675
  return (
1642
1676
  <p className="px-1 text-[12.5px] leading-relaxed break-keep text-muted-foreground">
@@ -1669,7 +1703,7 @@ function Line({ line, taskId }: { line: Chatter; taskId: string }) {
1669
1703
  const failed = line.kind === "error";
1670
1704
 
1671
1705
  return (
1672
- // The report is not a card. It sits in a thread that is already in a box,
1706
+ // The answer is not a card. It sits in a thread that is already in a box,
1673
1707
  // in a section that is another: a fourth border reads as a second chat
1674
1708
  // window. What tells it from a passing remark is that it is the only prose
1675
1709
  // here at foreground weight, plus the files it names — and its copy button
@@ -1690,7 +1724,7 @@ function Line({ line, taskId }: { line: Chatter; taskId: string }) {
1690
1724
  {line.text}
1691
1725
  </Markdown>
1692
1726
  {/* The files it names, and the copy — one row, because the end of the
1693
- report is where a reader is when they want either. */}
1727
+ answer is where a reader is when they want either. */}
1694
1728
  <div className="mt-2 flex items-center gap-2">
1695
1729
  <PathChips text={line.text} className="min-w-0" />
1696
1730
  {!failed && (
@@ -1703,7 +1737,7 @@ function Line({ line, taskId }: { line: Chatter; taskId: string }) {
1703
1737
  );
1704
1738
  }
1705
1739
 
1706
- /** Copies the report. The icon confirms; a toast only reports failure. */
1740
+ /** Copies the answer. The icon confirms; a toast only reports failure. */
1707
1741
  function CopyReport({ text }: { text: string }) {
1708
1742
  const [copied, setCopied] = useState(false);
1709
1743
  const back = useRef<ReturnType<typeof setTimeout> | null>(null);
@@ -1718,7 +1752,7 @@ function CopyReport({ text }: { text: string }) {
1718
1752
  return (
1719
1753
  <button
1720
1754
  type="button"
1721
- aria-label="Copy the report"
1755
+ aria-label="Copy the answer"
1722
1756
  onClick={async () => {
1723
1757
  try {
1724
1758
  await navigator.clipboard.writeText(text);
@@ -1728,7 +1762,7 @@ function CopyReport({ text }: { text: string }) {
1728
1762
  } catch (cause) {
1729
1763
  toast.add({
1730
1764
  type: "error",
1731
- title: "Could not copy the report",
1765
+ title: "Could not copy the answer",
1732
1766
  description: errorToString(cause),
1733
1767
  });
1734
1768
  }
@@ -1764,17 +1798,65 @@ function Steps({ lines, taskId }: { lines: Chatter[]; taskId: string }) {
1764
1798
  );
1765
1799
  }
1766
1800
 
1767
- /** Splits one speaker's lines into runs of tool calls and runs of everything else. */
1801
+ /**
1802
+ * Where the app stopped the run (bot.runner parkTask): a failed model call, a
1803
+ * restart, a closed browser. Muted and in the bot's turn, since the bot goes on
1804
+ * from here. The same reason in a row is one line with a count; opening it lists
1805
+ * each stop by the time it happened.
1806
+ */
1807
+ function Stops({ lines }: { lines: Chatter[] }) {
1808
+ return (
1809
+ <details className="group min-w-0 px-1 text-muted-foreground">
1810
+ <summary className="flex cursor-pointer list-none items-center gap-1.5 text-[12.5px] leading-relaxed outline-none [&::-webkit-details-marker]:hidden">
1811
+ <RotateCw className="size-3 shrink-0" />
1812
+ <span className="min-w-0 truncate">{leadOf(lines[0].text)}</span>
1813
+ {lines.length > 1 && (
1814
+ <span className="shrink-0 font-mono text-[10px] text-muted-foreground/70">
1815
+ ×{lines.length}
1816
+ </span>
1817
+ )}
1818
+ <ChevronDown className="size-2.5 shrink-0 text-muted-foreground/70 transition-transform group-open:rotate-180" />
1819
+ </summary>
1820
+ <div className="mt-1 space-y-1 pl-4.5 text-[11px] leading-relaxed">
1821
+ {lines.map((line) => (
1822
+ <p key={line.id} className="flex gap-2 break-keep">
1823
+ {line.at && (
1824
+ <span className="shrink-0 font-mono text-muted-foreground/70 tabular-nums">
1825
+ {format(toDate(line.at), "HH:mm:ss")}
1826
+ </span>
1827
+ )}
1828
+ <span className="min-w-0">{line.text}</span>
1829
+ </p>
1830
+ ))}
1831
+ </div>
1832
+ </details>
1833
+ );
1834
+ }
1835
+
1836
+ /** What stopped it, without the words behind it: a stop names those in parentheses (bot.runner parkTask). */
1837
+ const leadOf = (text: string) => text.split(" (")[0].replace(/\.$/, "");
1838
+
1839
+ /** Splits one speaker's lines into runs of tool calls, of stops, and of everything else. */
1768
1840
  type Run =
1769
1841
  | { kind: "tools"; key: string; lines: Chatter[] }
1842
+ | { kind: "stops"; key: string; lines: Chatter[] }
1770
1843
  | { kind: "said"; key: string; lines: Chatter[] };
1771
1844
 
1772
1845
  function runs(lines: Chatter[]): Run[] {
1773
1846
  const out: Run[] = [];
1774
1847
  for (const line of lines) {
1775
- const kind = line.kind === "tool" && line.tool ? "tools" : "said";
1848
+ const kind =
1849
+ line.kind === "tool" && line.tool
1850
+ ? "tools"
1851
+ : line.kind === "stop"
1852
+ ? "stops"
1853
+ : "said";
1776
1854
  const open = out.at(-1);
1777
- if (open && open.kind === kind) open.lines.push(line);
1855
+ // Stops fold only while the reason repeats; another reason is a line of its own
1856
+ const same =
1857
+ open?.kind === kind &&
1858
+ (kind !== "stops" || leadOf(open.lines[0].text) === leadOf(line.text));
1859
+ if (open && same) open.lines.push(line);
1778
1860
  else out.push({ kind, key: line.id, lines: [line] });
1779
1861
  }
1780
1862
  return out;