thursday-agent 0.1.2 → 0.3.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 (573) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +5 -1
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/prerender-manifest.json +5 -5
  5. package/.next/routes-manifest.json +27 -3
  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.html +1 -1
  9. package/.next/server/app/_global-error.rsc +2 -2
  10. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
  12. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/server/app/_not-found/page.js +2 -2
  14. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  15. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/.next/server/app/_not-found.html +1 -1
  17. package/.next/server/app/_not-found.rsc +14 -14
  18. package/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
  19. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +12 -12
  20. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  21. package/.next/server/app/api/artifact/route.js +3 -3
  22. package/.next/server/app/api/artifact/route.js.nft.json +1 -1
  23. package/.next/server/app/api/bot/memory/files/route/app-paths-manifest.json +3 -0
  24. package/.next/server/app/api/bot/memory/files/route.js +10 -0
  25. package/.next/server/app/api/bot/memory/files/route.js.nft.json +1 -0
  26. package/.next/server/app/api/bot/memory/files/route_client-reference-manifest.js +3 -0
  27. package/.next/server/app/api/bot/memory/route/app-paths-manifest.json +3 -0
  28. package/.next/server/app/api/bot/memory/route/build-manifest.json +12 -0
  29. package/.next/server/app/api/bot/memory/route/server-reference-manifest.json +4 -0
  30. package/.next/server/app/api/bot/memory/route.js +14 -0
  31. package/.next/server/app/api/bot/memory/route.js.map +5 -0
  32. package/.next/server/app/api/bot/memory/route.js.nft.json +1 -0
  33. package/.next/server/app/api/bot/memory/route_client-reference-manifest.js +3 -0
  34. package/.next/server/app/api/bot/route.js +7 -7
  35. package/.next/server/app/api/bot/route.js.nft.json +1 -1
  36. package/.next/server/app/api/bot/task/[id]/route.js +9 -9
  37. package/.next/server/app/api/bot/task/[id]/route.js.nft.json +1 -1
  38. package/.next/server/app/api/bot/task/route.js +8 -8
  39. package/.next/server/app/api/bot/task/route.js.nft.json +1 -1
  40. package/.next/server/app/api/config/route.js +6 -6
  41. package/.next/server/app/api/config/route.js.nft.json +1 -1
  42. package/.next/server/app/api/events/route.js +4 -5
  43. package/.next/server/app/api/events/route.js.nft.json +1 -1
  44. package/.next/server/app/api/file/[...path]/route.js +3 -3
  45. package/.next/server/app/api/file/[...path]/route.js.nft.json +1 -1
  46. package/.next/server/app/api/llm-model/catalog/route.js +10 -9
  47. package/.next/server/app/api/llm-model/catalog/route.js.nft.json +1 -1
  48. package/.next/server/app/api/llm-model/credits/route/app-paths-manifest.json +3 -0
  49. package/.next/server/app/api/llm-model/credits/route/build-manifest.json +12 -0
  50. package/.next/server/app/api/llm-model/credits/route/server-reference-manifest.json +4 -0
  51. package/.next/server/app/api/llm-model/credits/route.js +16 -0
  52. package/.next/server/app/api/llm-model/credits/route.js.map +5 -0
  53. package/.next/server/app/api/llm-model/credits/route.js.nft.json +1 -0
  54. package/.next/server/app/api/llm-model/credits/route_client-reference-manifest.js +3 -0
  55. package/.next/server/app/api/llm-model/route.js +10 -8
  56. package/.next/server/app/api/llm-model/route.js.nft.json +1 -1
  57. package/.next/server/app/api/llm-model/subscription/route/app-paths-manifest.json +3 -0
  58. package/.next/server/app/api/llm-model/subscription/route/build-manifest.json +12 -0
  59. package/.next/server/app/api/llm-model/subscription/route/server-reference-manifest.json +4 -0
  60. package/.next/server/app/api/llm-model/subscription/route.js +15 -0
  61. package/.next/server/app/api/llm-model/subscription/route.js.map +5 -0
  62. package/.next/server/app/api/llm-model/subscription/route.js.nft.json +1 -0
  63. package/.next/server/app/api/llm-model/subscription/route_client-reference-manifest.js +3 -0
  64. package/.next/server/app/api/mcp/[name]/route.js +5 -5
  65. package/.next/server/app/api/mcp/[name]/route.js.nft.json +1 -1
  66. package/.next/server/app/api/mcp/oauth/callback/route.js +6 -6
  67. package/.next/server/app/api/mcp/oauth/callback/route.js.nft.json +1 -1
  68. package/.next/server/app/api/mcp/route.js +5 -5
  69. package/.next/server/app/api/mcp/route.js.nft.json +1 -1
  70. package/.next/server/app/api/mcp/tools/route.js +5 -5
  71. package/.next/server/app/api/mcp/tools/route.js.nft.json +1 -1
  72. package/.next/server/app/api/memory/edit/route/app-paths-manifest.json +3 -0
  73. package/.next/server/app/api/memory/edit/route/build-manifest.json +12 -0
  74. package/.next/server/app/api/memory/edit/route/server-reference-manifest.json +4 -0
  75. package/.next/server/app/api/memory/edit/route.js +24 -0
  76. package/.next/server/app/api/memory/edit/route.js.map +5 -0
  77. package/.next/server/app/api/memory/edit/route.js.nft.json +1 -0
  78. package/.next/server/app/api/memory/{tidy → edit}/route_client-reference-manifest.js +1 -1
  79. package/.next/server/app/api/memory/route.js +7 -8
  80. package/.next/server/app/api/memory/route.js.nft.json +1 -1
  81. package/.next/server/app/api/skills/[source]/[name]/route.js +3 -3
  82. package/.next/server/app/api/skills/[source]/[name]/route.js.nft.json +1 -1
  83. package/.next/server/app/api/skills/route.js +3 -3
  84. package/.next/server/app/api/skills/route.js.nft.json +1 -1
  85. package/.next/server/app/api/thursday/call/route.js +9 -7
  86. package/.next/server/app/api/thursday/call/route.js.nft.json +1 -1
  87. package/.next/server/app/api/thursday/skills/route.js +9 -7
  88. package/.next/server/app/api/thursday/skills/route.js.nft.json +1 -1
  89. package/.next/server/app/api/thursday/tool-call/route.js +16 -15
  90. package/.next/server/app/api/thursday/tool-call/route.js.nft.json +1 -1
  91. package/.next/server/app/api/workspace/route.js +4 -5
  92. package/.next/server/app/api/workspace/route.js.nft.json +1 -1
  93. package/.next/server/app/artifact/[...path]/page/server-reference-manifest.json +1 -1
  94. package/.next/server/app/artifact/[...path]/page.js +5 -6
  95. package/.next/server/app/artifact/[...path]/page.js.nft.json +1 -1
  96. package/.next/server/app/artifact/[...path]/page_client-reference-manifest.js +1 -1
  97. package/.next/server/app/favicon.ico/route.js +3 -4
  98. package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
  99. package/.next/server/app/favicon.ico.body +0 -0
  100. package/.next/server/app/page/server-reference-manifest.json +66 -77
  101. package/.next/server/app/page.js +11 -11
  102. package/.next/server/app/page.js.nft.json +1 -1
  103. package/.next/server/app/page_client-reference-manifest.js +1 -1
  104. package/.next/server/app-paths-manifest.json +5 -1
  105. package/.next/server/chunks/[externals]__0dzuk6_._.js +3 -0
  106. package/.next/server/chunks/[root-of-the-server]__01dkp4a._.js +3 -0
  107. package/.next/server/chunks/[root-of-the-server]__02va5wz._.js +3 -0
  108. package/.next/server/chunks/[root-of-the-server]__02xjoto._.js +3 -0
  109. package/.next/server/chunks/[root-of-the-server]__04q--0f._.js +8 -0
  110. package/.next/server/chunks/[root-of-the-server]__0bl-e63._.js +3 -0
  111. package/.next/server/chunks/[root-of-the-server]__0ce8nf8._.js +5 -0
  112. package/.next/server/chunks/[root-of-the-server]__0ffbg85._.js +3 -0
  113. package/.next/server/chunks/[root-of-the-server]__0j7z0r3._.js +12 -0
  114. package/.next/server/chunks/[root-of-the-server]__0jq4ohy._.js +8 -0
  115. package/.next/server/chunks/[root-of-the-server]__0ls6_8y._.js +8 -0
  116. package/.next/server/chunks/[root-of-the-server]__0ncs9uu._.js +3 -0
  117. package/.next/server/chunks/[root-of-the-server]__0pa2n68._.js +3 -0
  118. package/.next/server/chunks/[root-of-the-server]__0q0vvt2._.js +5 -0
  119. package/.next/server/chunks/[root-of-the-server]__1-ga0kh._.js +5 -0
  120. package/.next/server/chunks/[root-of-the-server]__11n256_._.js +5 -0
  121. package/.next/server/chunks/[root-of-the-server]__12cabg9._.js +3 -0
  122. package/.next/server/chunks/[root-of-the-server]__1339jnv._.js +8 -0
  123. package/.next/server/chunks/[root-of-the-server]__15bjnu5._.js +5 -0
  124. package/.next/server/chunks/[root-of-the-server]__18_teo8._.js +8 -0
  125. package/.next/server/chunks/[root-of-the-server]__1_04mcf._.js +5 -0
  126. package/.next/server/chunks/[root-of-the-server]__1anc1p9._.js +3 -0
  127. package/.next/server/chunks/[root-of-the-server]__1ccon1w._.js +10 -0
  128. package/.next/server/chunks/[root-of-the-server]__1fizn31._.js +3 -0
  129. package/.next/server/chunks/[root-of-the-server]__1llj3-b._.js +3 -0
  130. package/.next/server/chunks/[root-of-the-server]__1orn-5b._.js +11 -0
  131. package/.next/server/chunks/[root-of-the-server]__1p8m3f4._.js +3 -0
  132. package/.next/server/chunks/[root-of-the-server]__1wa1k7r._.js +8 -0
  133. package/.next/server/chunks/{_1hzrsvg._.js → _0-73of8._.js} +6 -6
  134. package/.next/server/chunks/{_1cbk7ms._.js → _0-fjql4._.js} +10 -10
  135. package/.next/server/chunks/_00xh_q0._.js +27 -0
  136. package/.next/server/chunks/{_0y8is-s._.js → _03a0897._.js} +1 -1
  137. package/.next/server/chunks/{_0pppbb5._.js → _03kp0jp._.js} +166 -35
  138. package/.next/server/chunks/_051qh8u._.js +3 -0
  139. package/.next/server/chunks/{node_modules_next_04q-o58._.js → _05dla15._.js} +2 -2
  140. package/.next/server/chunks/_05h2cii._.js +1 -1
  141. package/.next/server/chunks/_0c9j6rb._.js +49 -0
  142. package/.next/server/chunks/_0fzqt2y._.js +74 -44
  143. package/.next/server/chunks/{_0kl37zl._.js → _0ioz3nd._.js} +1 -1
  144. package/.next/server/chunks/_0iwfby5._.js +3 -0
  145. package/.next/server/chunks/{_0hin38_._.js → _0khihjk._.js} +5 -5
  146. package/.next/server/chunks/_0mecv0f._.js +26 -0
  147. package/.next/server/chunks/_0mh8j4m._.js +3 -0
  148. package/.next/server/chunks/{_1icgbi8._.js → _0n74zsf._.js} +2 -2
  149. package/.next/server/chunks/_0obojvv._.js +3 -0
  150. package/.next/server/chunks/_0recufk._.js +49 -0
  151. package/.next/server/chunks/{_1t8fv7b._.js → _0rnf-6n._.js} +2 -2
  152. package/.next/server/chunks/_0sb7tlz._.js +3 -0
  153. package/.next/server/chunks/{_0jgm3b8._.js → _0u10wmw._.js} +6 -6
  154. package/.next/server/chunks/{_12nr4xc._.js → _0u4aiy2._.js} +2 -2
  155. package/.next/server/chunks/_0wp4le3._.js +1 -1
  156. package/.next/server/chunks/_0y5fg-x._.js +1 -1
  157. package/.next/server/chunks/_1230wbx._.js +10 -0
  158. package/.next/server/chunks/_12nheja._.js +3 -0
  159. package/.next/server/chunks/_13510xh._.js +3 -0
  160. package/.next/server/chunks/_15hq89o._.js +1 -1
  161. package/.next/server/chunks/_17l5ygt._.js +49 -0
  162. package/.next/server/chunks/{_0ks6_sg._.js → _18-1wcc._.js} +6 -6
  163. package/.next/server/chunks/_1_5mnq4._.js +18 -0
  164. package/.next/server/chunks/{_0tzfesa._.js → _1_9mjx-._.js} +1 -1
  165. package/.next/server/chunks/_1d0j961._.js +6 -0
  166. package/.next/server/chunks/_1d5zmy8._.js +1 -1
  167. package/.next/server/chunks/_1drgfx4._.js +18 -0
  168. package/.next/server/chunks/{_1skgy7b._.js → _1e48czp._.js} +5 -5
  169. package/.next/server/chunks/_1f46eie._.js +3 -0
  170. package/.next/server/chunks/_1h5_8sk._.js +3 -0
  171. package/.next/server/chunks/_1hdnxb7._.js +3 -0
  172. package/.next/server/chunks/_1j8k_j5._.js +3 -0
  173. package/.next/server/chunks/{_10qam6o._.js → _1kg70wc._.js} +5 -5
  174. package/.next/server/chunks/_1krd9rf._.js +3 -0
  175. package/.next/server/chunks/_1nc0eui._.js +11 -0
  176. package/.next/server/chunks/_1nula3v._.js +3 -0
  177. package/.next/server/chunks/_1pf4qe-._.js +3 -0
  178. package/.next/server/chunks/_1php4eq._.js +1 -1
  179. package/.next/server/chunks/_1qxheoa._.js +3 -0
  180. package/.next/server/chunks/_1x6kd1r._.js +3 -0
  181. package/.next/server/chunks/_1yuv4vp._.js +1 -1
  182. package/.next/server/chunks/_1zjrdag._.js +3 -0
  183. package/.next/server/chunks/_next-internal_server_app_api_bot_memory_files_route_actions_1_xd_8f.js +3 -0
  184. package/.next/server/chunks/_next-internal_server_app_api_bot_memory_route_actions_1a8xfr7.js +3 -0
  185. package/.next/server/chunks/_next-internal_server_app_api_llm-model_credits_route_actions_0v424pb.js +3 -0
  186. package/.next/server/chunks/_next-internal_server_app_api_llm-model_subscription_route_actions_0-udcm5.js +3 -0
  187. package/.next/server/chunks/_next-internal_server_app_api_memory_edit_route_actions_02sfnmr.js +3 -0
  188. package/.next/server/chunks/config_ts_1g4sz5o._.js +1 -1
  189. package/.next/server/chunks/features_0rpfxbk._.js +3 -0
  190. package/.next/server/chunks/features_1qf3cei._.js +3 -0
  191. package/.next/server/chunks/features_bot_0zt92oh._.js +3 -0
  192. package/.next/server/chunks/features_bot_bot_query_ts_12mgkl2._.js +1 -1
  193. package/.next/server/chunks/features_memory_1uqvksn._.js +6 -0
  194. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0tf290y._.js → node_modules_consola_dist_chunks_prompt_mjs_0i7cvml._.js} +1 -1
  195. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0228f43._.js → node_modules_consola_dist_chunks_prompt_mjs_0icqaqw._.js} +1 -1
  196. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0ir-vo2._.js → node_modules_consola_dist_chunks_prompt_mjs_0j61-ma._.js} +1 -1
  197. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0otw8ef._.js → node_modules_consola_dist_chunks_prompt_mjs_0r28-sm._.js} +1 -1
  198. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_0v_7drq._.js +3 -0
  199. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_11zjo-1._.js +3 -0
  200. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1pycqsq._.js +3 -0
  201. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1xoq2rl._.js +3 -0
  202. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1y49572._.js +3 -0
  203. package/.next/server/chunks/ssr/[externals]__0dizgek._.js +3 -0
  204. package/.next/server/chunks/ssr/[root-of-the-server]__09q9med._.js +1 -1
  205. package/.next/server/chunks/ssr/[root-of-the-server]__0_69wqr._.js +3 -0
  206. package/.next/server/chunks/ssr/[root-of-the-server]__0hg5gib._.js +1 -1
  207. package/.next/server/chunks/ssr/[root-of-the-server]__0i8ie7f._.js +3 -0
  208. package/.next/server/chunks/ssr/{[root-of-the-server]__0jsmmb-._.js → [root-of-the-server]__0mz33a7._.js} +1 -1
  209. package/.next/server/chunks/ssr/[root-of-the-server]__0tbkdlx._.js +3 -0
  210. package/.next/server/chunks/ssr/[root-of-the-server]__0vnmv-s._.js +3 -0
  211. package/.next/server/chunks/ssr/{[root-of-the-server]__0f6s48x._.js → [root-of-the-server]__0xpcv9w._.js} +3 -3
  212. package/.next/server/chunks/ssr/[root-of-the-server]__0xtssj3._.js +1 -1
  213. package/.next/server/chunks/ssr/[root-of-the-server]__0zkv7z_._.js +3 -0
  214. package/.next/server/chunks/ssr/[root-of-the-server]__10-dz5o._.js +57 -0
  215. package/.next/server/chunks/ssr/[root-of-the-server]__15_uhip._.js +3 -0
  216. package/.next/server/chunks/ssr/[root-of-the-server]__16jc196._.js +1 -1
  217. package/.next/server/chunks/ssr/[root-of-the-server]__1cyjywu._.js +3 -0
  218. package/.next/server/chunks/ssr/[root-of-the-server]__1eh7hid._.js +3 -0
  219. package/.next/server/chunks/ssr/[root-of-the-server]__1gtlw6v._.js +43 -0
  220. package/.next/server/chunks/ssr/[root-of-the-server]__1mzi-ju._.js +1 -1
  221. package/.next/server/chunks/ssr/[root-of-the-server]__1ox7hbk._.js +4 -0
  222. package/.next/server/chunks/ssr/[root-of-the-server]__1pduf5n._.js +3 -0
  223. package/.next/server/chunks/ssr/{_1mznrxk._.js → _001_ms1._.js} +3 -3
  224. package/.next/server/chunks/ssr/_01_6m32._.js +3 -0
  225. package/.next/server/chunks/ssr/_01n3o0m._.js +3 -0
  226. package/.next/server/chunks/ssr/_07uchnc._.js +3 -0
  227. package/.next/server/chunks/ssr/_0ap2fzn._.js +3 -0
  228. package/.next/server/chunks/ssr/_0c-ql2m._.js +3 -0
  229. package/.next/server/chunks/ssr/_0h42fze._.js +3 -0
  230. package/.next/server/chunks/ssr/_0h7zdni._.js +1 -1
  231. package/.next/server/chunks/ssr/_0i_6q1w._.js +41 -0
  232. package/.next/server/chunks/ssr/_0kbkmar._.js +3 -0
  233. package/.next/server/chunks/ssr/_0lu1n_x._.js +3 -0
  234. package/.next/server/chunks/ssr/{_077-g6s._.js → _0mb8zc8._.js} +3 -3
  235. package/.next/server/chunks/ssr/_0mo2w06._.js +3 -0
  236. package/.next/server/chunks/ssr/{_13punpm._.js → _0q0zan6._.js} +2 -2
  237. package/.next/server/chunks/ssr/{_0gd4vaa._.js → _0u2jv_x._.js} +94 -58
  238. package/.next/server/chunks/ssr/_0y0tiht._.js +4 -0
  239. package/.next/server/chunks/ssr/_0zhoc3r._.js +3 -0
  240. package/.next/server/chunks/ssr/_1-teee5._.js +3 -3
  241. package/.next/server/chunks/ssr/_13fke0x._.js +3 -0
  242. package/.next/server/chunks/ssr/{_0q_2f2x._.js → _13z3f85._.js} +2 -2
  243. package/.next/server/chunks/ssr/_14_lw10._.js +3 -0
  244. package/.next/server/chunks/ssr/_15j2_8f._.js +3 -0
  245. package/.next/server/chunks/ssr/{_0lc_7c8._.js → _1eoqg_g._.js} +2 -2
  246. package/.next/server/chunks/ssr/_1sji22n._.js +3 -0
  247. package/.next/server/chunks/ssr/_1spzm8z._.js +3 -0
  248. package/.next/server/chunks/ssr/{_19_o9d-._.js → _1vi_uo7._.js} +2 -2
  249. package/.next/server/chunks/ssr/_1vyd1ji._.js +3 -0
  250. package/.next/server/chunks/ssr/_1xvgn77._.js +3 -0
  251. package/.next/server/chunks/ssr/_20kd_n0._.js +42 -0
  252. package/.next/server/chunks/ssr/features_bot_1s_-2vj._.js +3 -0
  253. package/.next/server/chunks/ssr/features_config_components_config-setting_tsx_0xfwqdg._.js +1 -1
  254. package/.next/server/chunks/ssr/features_connectors_components_mcp-setting_tsx_0jmbgvp._.js +1 -1
  255. package/.next/server/chunks/ssr/lib_date-like_ts_02is0pw._.js +3 -0
  256. package/.next/server/chunks/ssr/lib_realtime_12wg_dd._.js +27 -0
  257. package/.next/server/chunks/ssr/lib_realtime_15h_v1v._.js +27 -0
  258. package/.next/server/chunks/ssr/{node_modules_1d-is1o._.js → node_modules_063xwpp._.js} +1 -1
  259. package/.next/server/chunks/ssr/{node_modules_1ub40gk._.js → node_modules_0bvraul._.js} +1 -1
  260. package/.next/server/chunks/ssr/{node_modules_1nhw9qg._.js → node_modules_0eu1va6._.js} +1 -1
  261. package/.next/server/chunks/ssr/{node_modules_0xwx4u-._.js → node_modules_0h50b8n._.js} +1 -1
  262. package/.next/server/chunks/ssr/{node_modules_043-236._.js → node_modules_0h5hnr1._.js} +1 -1
  263. package/.next/server/chunks/ssr/{node_modules_1t0y5jt._.js → node_modules_0h7u60n._.js} +1 -1
  264. package/.next/server/chunks/ssr/{node_modules_1k29j1j._.js → node_modules_0lm7_vm._.js} +1 -1
  265. package/.next/server/chunks/ssr/{node_modules_16i4o_3._.js → node_modules_0mwym0g._.js} +1 -1
  266. package/.next/server/chunks/ssr/{node_modules_1y8_3vg._.js → node_modules_0rjg6t1._.js} +1 -1
  267. package/.next/server/chunks/ssr/node_modules_0x3-ply._.js +3 -0
  268. package/.next/server/chunks/ssr/{node_modules_108wd2o._.js → node_modules_0y_cded._.js} +1 -1
  269. package/.next/server/chunks/ssr/{node_modules_1jatz7w._.js → node_modules_0z5tsu8._.js} +1 -1
  270. package/.next/server/chunks/ssr/{node_modules_0ls96je._.js → node_modules_0zp9r92._.js} +1 -1
  271. package/.next/server/chunks/ssr/{node_modules_06kvvuo._.js → node_modules_1-hc6uv._.js} +1 -1
  272. package/.next/server/chunks/ssr/{node_modules_1_588_4._.js → node_modules_113tz0l._.js} +1 -1
  273. package/.next/server/chunks/ssr/{node_modules_0ipn1ho._.js → node_modules_124bue5._.js} +1 -1
  274. package/.next/server/chunks/ssr/node_modules_17bwo43._.js +55 -0
  275. package/.next/server/chunks/ssr/{node_modules_0_j7-g4._.js → node_modules_1_n3ztw._.js} +1 -1
  276. package/.next/server/chunks/ssr/{node_modules_20ynonm._.js → node_modules_1bc7gu8._.js} +1 -1
  277. package/.next/server/chunks/ssr/{node_modules_1hn_sd5._.js → node_modules_1bgb6-f._.js} +1 -1
  278. package/.next/server/chunks/ssr/{node_modules_0bue6gy._.js → node_modules_1h4a4pp._.js} +1 -1
  279. package/.next/server/chunks/ssr/{node_modules_0fbw5-u._.js → node_modules_1i7u1ia._.js} +1 -1
  280. package/.next/server/chunks/ssr/{node_modules_1ee8gav._.js → node_modules_1juq3hs._.js} +1 -1
  281. package/.next/server/chunks/ssr/{node_modules_0_fvxnt._.js → node_modules_1l0qtdu._.js} +1 -1
  282. package/.next/server/chunks/ssr/{node_modules_13f2md6._.js → node_modules_1t9cyee._.js} +1 -1
  283. package/.next/server/chunks/ssr/{node_modules_0y2-av4._.js → node_modules_1uuj9x1._.js} +1 -1
  284. package/.next/server/chunks/ssr/{node_modules_00d6675._.js → node_modules_1vqb6po._.js} +1 -1
  285. package/.next/server/chunks/ssr/{node_modules_1v0077l._.js → node_modules_1xx28h1._.js} +1 -1
  286. package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_092s8nx._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0d3skpl._.js} +1 -1
  287. package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_1nvexgu._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0i44kk5._.js} +1 -1
  288. package/.next/server/chunks/ssr/node_modules_@vercel_oidc_dist_0qe30a_._.js +3 -0
  289. package/.next/server/chunks/ssr/node_modules_consola_dist_chunks_prompt_mjs_05cwqv2._.js +47 -0
  290. package/.next/server/chunks/ssr/{node_modules_next_dist_0w6mzq5._.js → node_modules_next_dist_0drixxt._.js} +4 -4
  291. package/.next/server/chunks/ssr/node_modules_next_dist_1n3w9lb._.js +2 -2
  292. package/.next/server/instrumentation.js +1 -1
  293. package/.next/server/middleware-build-manifest.js +3 -3
  294. package/.next/server/pages/404.html +1 -1
  295. package/.next/server/pages/500.html +1 -1
  296. package/.next/server/server-reference-manifest.js +1 -1
  297. package/.next/server/server-reference-manifest.json +67 -78
  298. package/.next/static/chunks/{31ttnjt6g35a0.js → 0ev4hyghq0mbb.js} +1 -1
  299. package/.next/static/chunks/0r5qcctujhxli.js +1 -0
  300. package/.next/static/chunks/0tmgrnlje14-s.js +3 -0
  301. package/.next/static/chunks/0x79tx04dxbpp.js +1 -0
  302. package/.next/static/chunks/0xzgvc0fkdb6t.js +1 -0
  303. package/.next/static/chunks/10teww7wt-f6h.js +1 -0
  304. package/.next/static/chunks/14hgtsjg-kael.css +1 -0
  305. package/.next/static/chunks/17donp69y4do7.js +40 -0
  306. package/.next/static/chunks/1dkjg87pxk0n9.js +28 -0
  307. package/.next/static/chunks/1ekc7ul556kbl.js +1 -0
  308. package/.next/static/chunks/{1khakdu_iuedc.js → 1grhjo7a5a0_-.js} +1 -1
  309. package/.next/static/chunks/1kq8h8wi_e0bc.js +53 -0
  310. package/.next/static/chunks/1okqiww3pnwka.js +1 -0
  311. package/.next/static/chunks/26-pr2c9ro222.js +1 -0
  312. package/.next/static/chunks/{2ihkf6l2jowo9.js → 280t0_e99i276.js} +3 -3
  313. package/.next/static/chunks/28cizcdaabd4h.js +1 -0
  314. package/.next/static/chunks/2a0k6jlq0_v1u.js +2 -0
  315. package/.next/static/chunks/2fzs9fm8yarvq.js +1 -0
  316. package/.next/static/chunks/2h0sd2vumhb15.js +1 -0
  317. package/.next/static/chunks/2hw2f6zmt7fmt.js +1 -0
  318. package/.next/static/chunks/2t4vhi3m8ahya.js +1 -0
  319. package/.next/static/chunks/2tigdtz3dpcy8.js +1 -0
  320. package/.next/static/chunks/3111pj5wnjgos.js +1 -0
  321. package/.next/static/chunks/3786h5vayt80-.js +28 -0
  322. package/.next/static/chunks/{3aquirz5h71da.js → 3agpgkwaa321p.js} +1 -1
  323. package/.next/static/chunks/3cfi_q6d_wtci.js +1 -0
  324. package/.next/static/chunks/3ok9_x74rip3v.js +1 -0
  325. package/.next/static/chunks/3xk7yqgomx7yq.js +1 -0
  326. package/.next/static/chunks/3xpbkl66ssib5.js +38 -0
  327. package/.next/static/media/favicon.37o1pjldwi2uc.ico +0 -0
  328. package/README.md +6 -4
  329. package/app/api/bot/memory/files/route.ts +14 -0
  330. package/app/api/bot/memory/route.ts +5 -0
  331. package/app/api/events/app-event.client.ts +24 -0
  332. package/app/api/events/app-event.server.ts +45 -0
  333. package/app/api/events/app-event.ts +42 -0
  334. package/app/api/llm-model/catalog/route.ts +12 -0
  335. package/app/api/llm-model/credits/route.ts +11 -0
  336. package/app/api/llm-model/route.ts +26 -0
  337. package/app/api/llm-model/subscription/route.ts +11 -0
  338. package/app/api/memory/edit/route.ts +7 -0
  339. package/app/api/memory/route.ts +21 -0
  340. package/app/api/query-key.ts +159 -0
  341. package/app/api/thursday/tool-call/route.ts +67 -0
  342. package/bin/thursday.mjs +3 -0
  343. package/components/ui/button-group.tsx +45 -0
  344. package/components/ui/custom-icon.tsx +259 -0
  345. package/components/ui/dropdown-menu.tsx +271 -0
  346. package/components/ui/folded-text.tsx +68 -0
  347. package/components/ui/input-group.tsx +157 -0
  348. package/components/ui/json-view.tsx +240 -0
  349. package/components/ui/shiny-text.tsx +55 -0
  350. package/components/ui/text-type.tsx +213 -0
  351. package/components/ui/theme-sync.tsx +9 -0
  352. package/database/migrations/{20260908030742_rainy_marvel_zombies → 20260910173049_numerous_spirit}/migration.sql +30 -33
  353. package/database/migrations/{20260908030742_rainy_marvel_zombies → 20260910173049_numerous_spirit}/snapshot.json +124 -145
  354. package/database/migrations/20260911030320_opposite_jack_power/migration.sql +1 -0
  355. package/database/migrations/20260911030320_opposite_jack_power/snapshot.json +1158 -0
  356. package/docs/how-it-works.md +59 -0
  357. package/docs/images/keeps-going.png +0 -0
  358. package/docs/images/task-thread.png +0 -0
  359. package/docs/images/two-minds.png +0 -0
  360. package/features/ai/chatgpt.ts +617 -0
  361. package/features/ai/components/chatgpt-sign-in.tsx +66 -0
  362. package/features/ai/components/model-picker.tsx +17 -1
  363. package/features/ai/components/provider-icon.tsx +1 -0
  364. package/features/ai/load-tools.ts +105 -39
  365. package/features/ai/model.schema.ts +232 -34
  366. package/features/ai/model.ts +169 -24
  367. package/features/ai/prompts/bot.prompt.ts +245 -59
  368. package/features/ai/prompts/memory-edit.prompt.ts +33 -0
  369. package/features/ai/prompts/prompt-helper.ts +75 -30
  370. package/features/ai/prompts/thursday.prompt.ts +42 -17
  371. package/features/ai/tools/bot.tool.ts +32 -30
  372. package/features/ai/tools/connected.ts +17 -4
  373. package/features/ai/tools/mcp.tool.ts +5 -5
  374. package/features/ai/tools/memory.tool.ts +142 -74
  375. package/features/ai/tools/search.tool.ts +136 -47
  376. package/features/ai/tools/studio.tool.ts +5 -5
  377. package/features/ai/tools/tool-name.ts +2 -3
  378. package/features/ai/tools/workspace.tool.ts +33 -4
  379. package/features/bot/bot.action.ts +24 -7
  380. package/features/bot/bot.memory.ts +89 -0
  381. package/features/bot/bot.query.ts +41 -6
  382. package/features/bot/bot.run.ts +516 -210
  383. package/features/bot/bot.runner.ts +463 -93
  384. package/features/bot/bot.schema.ts +84 -7
  385. package/features/bot/bot.seed.ts +89 -16
  386. package/features/bot/components/bot-mark.tsx +122 -20
  387. package/features/bot/components/bot-room.tsx +519 -391
  388. package/features/bot/components/bot-setting.tsx +832 -341
  389. package/features/bot/components/bot-tool.tsx +2 -6
  390. package/features/bot/components/mark-palette.tsx +54 -0
  391. package/features/bot/components/path-chips.tsx +1 -1
  392. package/features/bot/components/task-badge.tsx +26 -6
  393. package/features/bot/components/task-reply.tsx +22 -33
  394. package/features/bot/components/task-setting.tsx +20 -17
  395. package/features/bot/task.query.ts +233 -33
  396. package/features/bot/task.store.ts +53 -9
  397. package/features/config/components/config-setting.tsx +265 -34
  398. package/features/config/components/models-badge.tsx +42 -0
  399. package/features/config/config.action.ts +12 -0
  400. package/features/config/config.const.ts +38 -2
  401. package/features/connectors/mcp.manager.ts +10 -2
  402. package/features/intro/components/intro.tsx +47 -46
  403. package/features/memory/components/memory-edit.tsx +338 -0
  404. package/features/memory/components/memory-setting.tsx +86 -79
  405. package/features/memory/memory.action.ts +3 -43
  406. package/features/memory/memory.edit.ts +87 -0
  407. package/features/memory/memory.query.ts +33 -44
  408. package/features/memory/memory.schema.ts +50 -97
  409. package/features/settings/components/settings.tsx +2 -0
  410. package/features/settings/settings.alert.ts +3 -1
  411. package/features/skills/components/skills-setting.tsx +9 -1
  412. package/features/thursday/components/ascii-orb.tsx +106 -90
  413. package/features/thursday/components/boot.tsx +7 -0
  414. package/features/thursday/components/call-log.tsx +219 -92
  415. package/features/thursday/components/face.tsx +6 -4
  416. package/features/thursday/components/tab-state.tsx +97 -0
  417. package/features/thursday/components/thursday-mark.tsx +4 -1
  418. package/features/thursday/components/thursday-setting.tsx +17 -47
  419. package/features/thursday/components/thursday.tsx +208 -95
  420. package/features/thursday/thursday.action.ts +12 -17
  421. package/features/thursday/thursday.query.ts +61 -70
  422. package/features/thursday/thursday.schema.ts +20 -7
  423. package/features/thursday/tool-line.ts +85 -11
  424. package/features/thursday/use-thursday.ts +86 -59
  425. package/features/workspace/components/file-view.tsx +4 -2
  426. package/features/workspace/workspace.ts +230 -13
  427. package/hooks/use-draft.ts +37 -0
  428. package/hooks/use-hotkey.ts +105 -0
  429. package/hooks/use-object-state.ts +51 -0
  430. package/hooks/use-on-visible.ts +72 -0
  431. package/hooks/use-theme.ts +106 -0
  432. package/hooks/use-wake-word.ts +262 -0
  433. package/lib/limits.ts +1 -1
  434. package/lib/oauth-page.ts +27 -0
  435. package/lib/realtime/openai.ts +2 -2
  436. package/lib/realtime/realtime.driver.ts +34 -1
  437. package/lib/realtime/realtime.protocol.ts +7 -1
  438. package/lib/realtime/realtime.tap.ts +122 -0
  439. package/lib/sandbox.ts +60 -29
  440. package/lib/tokens.ts +19 -0
  441. package/package.json +1 -1
  442. package/skills/browser/SKILL.md +55 -34
  443. package/.next/server/app/api/memory/tidy/route/app-paths-manifest.json +0 -3
  444. package/.next/server/app/api/memory/tidy/route.js +0 -23
  445. package/.next/server/app/api/memory/tidy/route.js.nft.json +0 -1
  446. package/.next/server/chunks/[root-of-the-server]__019dx9f._.js +0 -5
  447. package/.next/server/chunks/[root-of-the-server]__02_2sm-._.js +0 -5
  448. package/.next/server/chunks/[root-of-the-server]__02kt8yn._.js +0 -10
  449. package/.next/server/chunks/[root-of-the-server]__05luceg._.js +0 -3
  450. package/.next/server/chunks/[root-of-the-server]__06t77t4._.js +0 -11
  451. package/.next/server/chunks/[root-of-the-server]__0bed6hu._.js +0 -8
  452. package/.next/server/chunks/[root-of-the-server]__0dupqt7._.js +0 -5
  453. package/.next/server/chunks/[root-of-the-server]__0h59di-._.js +0 -5
  454. package/.next/server/chunks/[root-of-the-server]__0j_n5l3._.js +0 -3
  455. package/.next/server/chunks/[root-of-the-server]__0le4d-p._.js +0 -3
  456. package/.next/server/chunks/[root-of-the-server]__0m8ywyo._.js +0 -10
  457. package/.next/server/chunks/[root-of-the-server]__0nfuugi._.js +0 -5
  458. package/.next/server/chunks/[root-of-the-server]__0phdgco._.js +0 -8
  459. package/.next/server/chunks/[root-of-the-server]__0qjqtwo._.js +0 -5
  460. package/.next/server/chunks/[root-of-the-server]__0t_dqaj._.js +0 -3
  461. package/.next/server/chunks/[root-of-the-server]__0wvgrsl._.js +0 -3
  462. package/.next/server/chunks/[root-of-the-server]__0yk6s3w._.js +0 -5
  463. package/.next/server/chunks/[root-of-the-server]__0z3gelv._.js +0 -3
  464. package/.next/server/chunks/[root-of-the-server]__10qshki._.js +0 -3
  465. package/.next/server/chunks/[root-of-the-server]__113fqg5._.js +0 -12
  466. package/.next/server/chunks/[root-of-the-server]__11w_7_3._.js +0 -3
  467. package/.next/server/chunks/[root-of-the-server]__16thabu._.js +0 -3
  468. package/.next/server/chunks/[root-of-the-server]__173nhwf._.js +0 -8
  469. package/.next/server/chunks/[root-of-the-server]__1_0q_9w._.js +0 -3
  470. package/.next/server/chunks/[root-of-the-server]__1_aoyr9._.js +0 -3
  471. package/.next/server/chunks/[root-of-the-server]__1bqhuh9._.js +0 -8
  472. package/.next/server/chunks/[root-of-the-server]__1cqizfq._.js +0 -5
  473. package/.next/server/chunks/[root-of-the-server]__1dow0bz._.js +0 -3
  474. package/.next/server/chunks/[root-of-the-server]__1e-ci13._.js +0 -3
  475. package/.next/server/chunks/[root-of-the-server]__1gmn1cs._.js +0 -10
  476. package/.next/server/chunks/[root-of-the-server]__1lr7j9t._.js +0 -5
  477. package/.next/server/chunks/[root-of-the-server]__1mzp_el._.js +0 -3
  478. package/.next/server/chunks/[root-of-the-server]__1txedx6._.js +0 -8
  479. package/.next/server/chunks/[root-of-the-server]__1u_qvc7._.js +0 -3
  480. package/.next/server/chunks/[root-of-the-server]__1zgpz1w._.js +0 -3
  481. package/.next/server/chunks/_012yhfm._.js +0 -39
  482. package/.next/server/chunks/_06mxlgn._.js +0 -3
  483. package/.next/server/chunks/_07rtlxv._.js +0 -3
  484. package/.next/server/chunks/_0a351g7._.js +0 -6
  485. package/.next/server/chunks/_0kjjmag._.js +0 -3
  486. package/.next/server/chunks/_0zmestz._.js +0 -55
  487. package/.next/server/chunks/_0zuzmt_._.js +0 -39
  488. package/.next/server/chunks/_12bbv5i._.js +0 -96
  489. package/.next/server/chunks/_15q1pig._.js +0 -3
  490. package/.next/server/chunks/_1drv2et._.js +0 -36
  491. package/.next/server/chunks/_1ic3bew._.js +0 -5
  492. package/.next/server/chunks/_1mxn_lb._.js +0 -3
  493. package/.next/server/chunks/_1uj4_io._.js +0 -10
  494. package/.next/server/chunks/_1x7p319._.js +0 -6
  495. package/.next/server/chunks/_1yldgo4._.js +0 -55
  496. package/.next/server/chunks/_2050gwn._.js +0 -255
  497. package/.next/server/chunks/_next-internal_server_app_api_memory_tidy_route_actions_0_cfuom.js +0 -3
  498. package/.next/server/chunks/features_0scmr4i._.js +0 -3
  499. package/.next/server/chunks/features_bot_task_query_ts_11xuzl3._.js +0 -3
  500. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_0vorg6w._.js +0 -3
  501. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_0ykklj0._.js +0 -3
  502. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1ryg3wh._.js +0 -3
  503. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1vmaq9f._.js +0 -3
  504. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1vwbnq_._.js +0 -3
  505. package/.next/server/chunks/ssr/[externals]_node_crypto_1kn79-7._.js +0 -3
  506. package/.next/server/chunks/ssr/[root-of-the-server]__01id-v3._.js +0 -3
  507. package/.next/server/chunks/ssr/[root-of-the-server]__0b-0i32._.js +0 -3
  508. package/.next/server/chunks/ssr/[root-of-the-server]__0dfwvr4._.js +0 -4
  509. package/.next/server/chunks/ssr/[root-of-the-server]__0iuoa-r._.js +0 -3
  510. package/.next/server/chunks/ssr/[root-of-the-server]__1-mnhyd._.js +0 -90
  511. package/.next/server/chunks/ssr/[root-of-the-server]__185grso._.js +0 -3
  512. package/.next/server/chunks/ssr/[root-of-the-server]__1minz8_._.js +0 -3
  513. package/.next/server/chunks/ssr/_0-mc7ro._.js +0 -3
  514. package/.next/server/chunks/ssr/_01p5r6-._.js +0 -3
  515. package/.next/server/chunks/ssr/_09-0k3z._.js +0 -3
  516. package/.next/server/chunks/ssr/_0ay6b01._.js +0 -3
  517. package/.next/server/chunks/ssr/_0cvjbuz._.js +0 -3
  518. package/.next/server/chunks/ssr/_0fafgvl._.js +0 -3
  519. package/.next/server/chunks/ssr/_0j4v8of._.js +0 -3
  520. package/.next/server/chunks/ssr/_0l81bzy._.js +0 -3
  521. package/.next/server/chunks/ssr/_0nso8tp._.js +0 -3
  522. package/.next/server/chunks/ssr/_0sf1isi._.js +0 -3
  523. package/.next/server/chunks/ssr/_0ti51xc._.js +0 -3
  524. package/.next/server/chunks/ssr/_0y84qo6._.js +0 -3
  525. package/.next/server/chunks/ssr/_10uvw3l._.js +0 -3
  526. package/.next/server/chunks/ssr/_1dcxzqe._.js +0 -3
  527. package/.next/server/chunks/ssr/_1e-dgz0._.js +0 -4
  528. package/.next/server/chunks/ssr/_1fq7zgb._.js +0 -3
  529. package/.next/server/chunks/ssr/_1jbv7m1._.js +0 -3
  530. package/.next/server/chunks/ssr/features_bot_components_bot-setting_tsx_01ajft6._.js +0 -4
  531. package/.next/server/chunks/ssr/features_config_components_14jqgy4._.js +0 -3
  532. package/.next/server/chunks/ssr/features_memory_components_memory-setting_tsx_0oa43sw._.js +0 -4
  533. package/.next/server/chunks/ssr/lib_realtime_0aetuyp._.js +0 -24
  534. package/.next/server/chunks/ssr/lib_realtime_18k_aj-._.js +0 -24
  535. package/.next/server/chunks/ssr/node_modules_zod_v4_classic_errors_05xv7vp.js +0 -3
  536. package/.next/server/chunks/ssr/node_modules_zod_v4_classic_external_15fmmz3.js +0 -55
  537. package/.next/static/chunks/0f9sq-7q_yxyl.js +0 -1
  538. package/.next/static/chunks/0tydnn2kybibk.js +0 -1
  539. package/.next/static/chunks/0yysv1gzin17t.js +0 -1
  540. package/.next/static/chunks/1fr5m75pwtyic.js +0 -1
  541. package/.next/static/chunks/1gy3bbv7usnjo.js +0 -1
  542. package/.next/static/chunks/1keib_gt066gc.js +0 -1
  543. package/.next/static/chunks/1ytnip8wdng93.js +0 -1
  544. package/.next/static/chunks/21rk8w7bda-su.js +0 -1
  545. package/.next/static/chunks/247d-r95j8xs0.js +0 -1
  546. package/.next/static/chunks/28g42tw8tsvhd.js +0 -1
  547. package/.next/static/chunks/29gyb3nubj51j.js +0 -53
  548. package/.next/static/chunks/2cwjgkt_hoyhj.js +0 -22
  549. package/.next/static/chunks/2fq_86jvq9ymq.js +0 -2
  550. package/.next/static/chunks/2rbf1fpd8n80-.js +0 -22
  551. package/.next/static/chunks/2ukj8k69y89um.js +0 -1
  552. package/.next/static/chunks/2uxum7qbyeyxd.js +0 -1
  553. package/.next/static/chunks/2wngeetfnzxkp.js +0 -2
  554. package/.next/static/chunks/388-t2v72jh39.css +0 -1
  555. package/.next/static/chunks/3fqvc0cg-atq_.js +0 -1
  556. package/.next/static/chunks/3p4egvfjxpatb.js +0 -1
  557. package/.next/static/chunks/3qe1w1ei-huhl.js +0 -1
  558. package/.next/static/chunks/3xxa2j56-f3nm.js +0 -3
  559. package/.next/static/chunks/3y40riycdip-i.js +0 -1
  560. package/.next/static/chunks/45a6zbtkelph9.js +0 -1
  561. package/.next/static/media/favicon.2vob68tjqpejf.ico +0 -0
  562. package/features/ai/prompts/memory-tidy.prompt.ts +0 -123
  563. package/features/ai/tools/tidy.tool.ts +0 -22
  564. package/features/memory/components/memory-tidy.tsx +0 -395
  565. package/features/memory/memory.tidy.ts +0 -332
  566. package/features/memory/tidy.query.ts +0 -135
  567. package/features/thursday/use-call-title.ts +0 -31
  568. /package/.next/server/app/api/{memory/tidy → bot/memory/files}/route/build-manifest.json +0 -0
  569. /package/.next/server/app/api/{memory/tidy → bot/memory/files}/route/server-reference-manifest.json +0 -0
  570. /package/.next/server/app/api/{memory/tidy → bot/memory/files}/route.js.map +0 -0
  571. /package/.next/static/{X34BUd9bfdH27ripuwwBm → x1cCqY0kWpR_VcYx_lWO5}/_buildManifest.js +0 -0
  572. /package/.next/static/{X34BUd9bfdH27ripuwwBm → x1cCqY0kWpR_VcYx_lWO5}/_clientMiddlewareManifest.js +0 -0
  573. /package/.next/static/{X34BUd9bfdH27ripuwwBm → x1cCqY0kWpR_VcYx_lWO5}/_ssgManifest.js +0 -0
@@ -1,10 +1,12 @@
1
1
  import {
2
+ type FinishReason,
2
3
  generateText,
3
4
  hasToolCall,
4
5
  type LanguageModel,
5
6
  type LanguageModelUsage,
6
7
  type ModelMessage,
7
8
  type PrepareStepResult,
9
+ pruneMessages,
8
10
  type StepResult,
9
11
  stepCountIs,
10
12
  ToolLoopAgent,
@@ -14,33 +16,50 @@ import {
14
16
  import { BOT_RUN } from "@/config";
15
17
  import { loadTools } from "@/features/ai/load-tools";
16
18
  import {
19
+ compactBudget,
17
20
  getTextModel,
21
+ type ModelFailure,
18
22
  modelErrorToString,
23
+ modelFailureOf,
19
24
  resolveDefaultModel,
20
25
  } from "@/features/ai/model";
21
- import { loadBotPrompt } from "@/features/ai/prompts/bot.prompt";
22
26
  import {
27
+ buildHandoff,
28
+ chainOf,
29
+ loadBotPrompt,
30
+ } from "@/features/ai/prompts/bot.prompt";
31
+ import {
32
+ answerAccepted,
23
33
  askBackSpec,
24
34
  askBotSpec,
25
- reportAccepted,
26
35
  } from "@/features/ai/tools/bot.tool";
27
36
  import { TOOL_NAMES } from "@/features/ai/tools/tool-name";
28
37
  import {
38
+ COMPACT_AT_MIN,
29
39
  type JobBot,
30
40
  NO_TOKENS,
31
41
  type TokenUsage,
32
42
  } from "@/features/bot/bot.schema";
33
- import { closeJobShell } from "@/features/workspace/workspace";
43
+ import {
44
+ closeHiddenBrowser,
45
+ filesOnDisk,
46
+ openBotFolder,
47
+ openJobScratch,
48
+ } from "@/features/workspace/workspace";
34
49
  import { logger } from "@/lib/logger";
35
50
  import { publicError } from "@/lib/public-error";
36
51
  import { estimateTokens } from "@/lib/tokens";
37
- import { clip } from "@/lib/utils";
38
52
  import { findJobBot } from "./bot.query";
39
- import { optionsOf, toolLine } from "./task.query";
53
+ import {
54
+ findTask,
55
+ listWrittenPaths,
56
+ optionsOf,
57
+ writtenPathsIn,
58
+ } from "./task.query";
40
59
 
41
60
  /**
42
61
  * The bot loop behind `delegate`: one model with its own prompt, tools and
43
- * context. Only the `report` reaches Thursday; every event goes through
62
+ * context. Only the `answer` reaches Thursday; every event goes through
44
63
  * `emit` and the runner (bot.runner) writes it to the task's thread.
45
64
  */
46
65
 
@@ -50,7 +69,14 @@ export type BotEvent =
50
69
  | { type: "text"; text: string }
51
70
  /** A tool call. `id` is where the result lands. */
52
71
  | { type: "tool"; id: string; name: string; input: unknown }
53
- | { type: "tool-result"; id: string; name: string; output: unknown }
72
+ | {
73
+ type: "tool-result";
74
+ id: string;
75
+ name: string;
76
+ output: unknown;
77
+ /** The tool threw; `output` is what it said. */
78
+ error?: boolean;
79
+ }
54
80
  /**
55
81
  * A step finished; `messages` is what the model sees next time.
56
82
  * `budget` is where this run compacts (config BOT_RUN).
@@ -84,11 +110,23 @@ export type BotEvent =
84
110
  */
85
111
  | { type: "waiting"; id: string | null; question: string; options: string[] }
86
112
  /**
87
- * The only thing that reaches Thursday. `complete` is what the model said
88
- * (bot.tool reportSpec), or false when the step cap forced the report.
113
+ * The only thing that reaches Thursday. `stopped` is the app's word, never the
114
+ * model's: the step cap forced this answer, the answer was refused on the last
115
+ * step, the output limit cut it off, or the run ended with nothing handed back.
116
+ * A stopped job waits to be continued; any other answer ends it.
89
117
  */
90
- | { type: "report"; text: string; complete: boolean }
91
- | { type: "error"; message: string };
118
+ | { type: "answer"; text: string; stopped: boolean }
119
+ /**
120
+ * The run broke. `failure` is whose it is to fix (model.ts modelFailureOf) — a
121
+ * retry's, a compaction's, or a person's — and `budget` where to compact next
122
+ * time, when the model refused the context as too long.
123
+ */
124
+ | {
125
+ type: "error";
126
+ message: string;
127
+ failure: ModelFailure;
128
+ budget?: number;
129
+ };
92
130
 
93
131
  /** The event as the thread sees it: which bot, inside which `ask_bot` call. */
94
132
  export type TaskEvent = BotEvent & { bot: string; parent: string | null };
@@ -98,8 +136,17 @@ export type RunOptions = {
98
136
  depth?: number;
99
137
  /** The `ask_bot` call this run answers; null for the job's own bot. */
100
138
  parent?: string | null;
101
- /** The job this runs inside. The shell pins its browser session to it (workspace.ts). */
139
+ /**
140
+ * The job this runs inside, for every seat on it — a borrowed bot's too. Its row
141
+ * names the one folder they all work in (workspace.ts jobScratch).
142
+ */
102
143
  taskId?: string | null;
144
+ /**
145
+ * The browser session this seat's shell drives (workspace.ts jobShellEnv): the
146
+ * job's own when unset, a borrowed bot's under its call, so two seats never click
147
+ * in one window.
148
+ */
149
+ session?: string | null;
103
150
  /**
104
151
  * Drained once per step boundary; each string goes in front of the model as
105
152
  * a user turn before the next step. Only the job's own bot has one.
@@ -111,18 +158,12 @@ export type RunOptions = {
111
158
  };
112
159
 
113
160
  /**
114
- * Both limits come from config BOT_RUN. At the step cap the last step is
115
- * forced to `report` (lastStep) and the job goes to `waiting`. Past the
116
- * context budget the run compacts (compact) and continues from the summary.
161
+ * At the step cap the last step is forced to `answer` (lastStep) and the job goes
162
+ * to `waiting`. The context budget is the model's own window where that can be
163
+ * known (model.ts compactBudget); past it the run compacts and carries on from
164
+ * the summary.
117
165
  */
118
166
  const MAX_STEPS = BOT_RUN.steps;
119
- const CONTEXT_BUDGET = BOT_RUN.compactAt;
120
-
121
- /** A borrowed bot cannot borrow another. */
122
- const MAX_DEPTH = 1;
123
-
124
- /** `ask_back` calls a borrowed bot gets per part. At zero prepareStep removes the tool instead of having it refuse. */
125
- const MAX_ASK_BACK = 3;
126
167
 
127
168
  /**
128
169
  * Where a run starts: a job's thread (the opening message first time, past
@@ -131,7 +172,16 @@ const MAX_ASK_BACK = 3;
131
172
  */
132
173
  export type RunInput =
133
174
  | { bot: string; messages: ModelMessage[] }
134
- | { bot: string; request: string; context?: string | null; askedBy: string };
175
+ | {
176
+ bot: string;
177
+ request: string;
178
+ context?: string | null;
179
+ askedBy: string;
180
+ /** What the borrowing bot inherited, handed down under the part (bot.prompt buildHandoff). */
181
+ chain: string;
182
+ /** Every bot above this one on the job, the holder first; `askedBy` is the last. */
183
+ above: string[];
184
+ };
135
185
 
136
186
  export async function runBot(
137
187
  input: RunInput,
@@ -148,46 +198,97 @@ export async function runBot(
148
198
  await emit({
149
199
  type: "error",
150
200
  message: `No bot named "${input.bot}". Use a name from the list.`,
201
+ failure: "fatal",
202
+ });
203
+ return;
204
+ }
205
+ // Switched off is not a bot to pick — the backstop for every fresh start
206
+ // (`delegate`, `ask_bot`, the screen). A resume carries `messages` instead and
207
+ // is let through: its thread already exists and the user can still answer it.
208
+ if (bot.disabled && "request" in input) {
209
+ await emit({
210
+ type: "error",
211
+ message: `${bot.name} is switched off. Use a name from the list.`,
212
+ failure: "fatal",
151
213
  });
152
214
  return;
153
215
  }
154
216
 
155
217
  // Tools are built on the model: web search runs on this bot's model (load-tools).
156
218
  const model = await resolveModel(bot);
219
+ // The job's row: the same one for every seat on the job, a borrowed bot's too
220
+ const row = options.taskId ? await findTask(options.taskId) : null;
221
+ // One folder per job, not per bot: bots borrowed with `ask_bot` work inside
222
+ // the same job and share its material (workspace.ts jobScratch).
223
+ const [scratch, own] = await Promise.all([
224
+ options.taskId ? openJobScratch(options.taskId, row?.label ?? "job") : null,
225
+ openBotFolder(name),
226
+ ]);
157
227
  const [prompt, tools] = await Promise.all([
158
228
  loadBotPrompt(
159
229
  name,
160
230
  bot.systemPrompt,
161
- "askedBy" in input ? input.askedBy : null,
231
+ "askedBy" in input
232
+ ? {
233
+ askedBy: input.askedBy,
234
+ above: input.above,
235
+ canBorrow: depth < BOT_RUN.depth,
236
+ }
237
+ : null,
238
+ { scratch, own },
162
239
  ),
163
240
  loadTools({
164
241
  target: "bot",
165
242
  bot: name,
166
- taskId: options.taskId,
243
+ // A borrowed bot drives a browser session of its own; everyone else the job's
244
+ session: options.session ?? options.taskId,
167
245
  model,
168
246
  }),
169
247
  ]);
170
- const budget = CONTEXT_BUDGET;
248
+ // What the owner set, else the model's own window, else the constant (model.ts
249
+ // compactBudget), and never above where this job last fit: a context the model
250
+ // refused as too long lowers the job's own number (bot.runner parkTask). That
251
+ // number is the holder's; a borrowed bot runs on a model of its own.
252
+ const budget = Math.min(
253
+ await compactBudget(model.ref, bot.compactAt),
254
+ ("askedBy" in input ? 0 : row?.contextBudget) || Number.POSITIVE_INFINITY,
255
+ );
171
256
 
172
257
  const history: ModelMessage[] =
173
258
  "messages" in input
174
259
  ? input.messages
175
- : [{ role: "user", content: taskPrompt(input) }];
260
+ : [
261
+ {
262
+ role: "user",
263
+ content: buildHandoff({
264
+ chain: input.chain,
265
+ bots: [...input.above, name],
266
+ did: input.context ?? null,
267
+ part: input.request,
268
+ }),
269
+ },
270
+ ];
176
271
 
177
272
  /** Remaining `ask_back` calls for a borrowed bot; the tool counts it down. */
178
- const asks = { left: MAX_ASK_BACK };
273
+ const asks = { left: BOT_RUN.askBack };
274
+ // A borrowed bot asks back instead of asking Thursday. Any seat above the
275
+ // depth limit may borrow in turn, handing down the chain it was handed.
276
+ const seatTools =
277
+ "askedBy" in input ? withAskBack(tools, asks, options) : tools;
179
278
  const agentTools =
180
- depth >= MAX_DEPTH
181
- ? withAskBack(tools, asks, options)
182
- : withAskBot(tools, {
279
+ depth < BOT_RUN.depth
280
+ ? withAskBot(seatTools, {
183
281
  name,
184
282
  model: model.model,
185
283
  instructions: prompt.text,
186
284
  peers: prompt.peers,
187
285
  depth,
286
+ chain: chainOf(history[0]),
287
+ above: "askedBy" in input ? input.above : [],
188
288
  options,
189
289
  emit,
190
- });
290
+ })
291
+ : seatTools;
191
292
 
192
293
  /**
193
294
  * A compaction decided in `prepareStep` is emitted from the loop at
@@ -201,22 +302,27 @@ export async function runBot(
201
302
  interjections: string[];
202
303
  } = { compact: null, interjections: [] };
203
304
 
305
+ /** Stops the stream when the model goes quiet (config BOT_RUN.silenceMs). */
306
+ const quiet = silenceWatch(BOT_RUN.silenceMs);
307
+ /** Context size the latest step went out at; an overflow shrinks the budget from it. */
308
+ let sent = 0;
309
+
204
310
  const agent = new ToolLoopAgent({
205
311
  model: model.model,
206
312
  instructions: prompt.text,
207
313
  tools: agentTools,
208
- // The loop ends on an accepted report or at the step cap (the last step is
209
- // forced to report). A refused report does not end it. `ask_thursday` has
314
+ // The loop ends on an accepted answer or at the step cap (the last step is
315
+ // forced to answer). A refused answer does not end it. `ask_thursday` has
210
316
  // no execute, but invalid arguments come back as a tool-error and the loop
211
317
  // would go on, so the stop is on the call itself.
212
318
  stopWhen: [
213
319
  stepCountIs(MAX_STEPS),
214
- reportAccepted_,
320
+ answerAccepted_,
215
321
  hasToolCall(TOOL_NAMES.ask_thursday),
216
322
  ],
217
323
  prepareStep: async ({ stepNumber, steps, messages }) => {
218
324
  let step = lastStep(stepNumber);
219
- // Remove `ask_back` once spent. The last step is already narrowed to `report`.
325
+ // Remove `ask_back` once spent. The last step is already narrowed to `answer`.
220
326
  if (!step.activeTools && asks.left <= 0) {
221
327
  step = {
222
328
  ...step,
@@ -229,24 +335,29 @@ export async function runBot(
229
335
  // estimate cannot see; the estimate covers a provider that reports none.
230
336
  const measured = steps.at(-1)?.usage.inputTokens ?? 0;
231
337
  const size = Math.max(measured, sizeOf(messages));
338
+ sent = size;
232
339
  let next = messages;
233
- // Nothing past the opening is nothing to compact
234
- if (size > budget && messages.length >= 2) {
340
+ // The opening survives every compaction, so past it and one summary there is nothing to compact
341
+ if (size > budget && messages.length > 2) {
342
+ // One long call that sends nothing until it is done, and bounds itself
343
+ quiet.hold();
235
344
  const summary = await compact(model.model, agentTools, messages, {
236
345
  signal: options.signal,
237
346
  budget,
238
- });
347
+ }).finally(quiet.release);
239
348
  logger.debug(
240
349
  `compacted ${messages.length} messages at ${size} tokens (budget ${budget})`,
241
350
  );
351
+ // Which files the job has is read off its rows and the disk, never asked of the model
352
+ const text = `${summary.text}${await filesUnder(row?.id ?? null, messages, scratch)}`;
242
353
  pending.compact = {
243
354
  type: "compact",
244
- text: summary.text,
355
+ text,
245
356
  usage: summary.usage,
246
357
  messages: messages.length,
247
358
  tokens: size,
248
359
  };
249
- next = [{ role: "user", content: summary.text }];
360
+ next = [messages[0], { role: "user", content: text }];
250
361
  }
251
362
 
252
363
  // Notes said during the last step go in as user turns after everything
@@ -269,9 +380,15 @@ export async function runBot(
269
380
  // Step messages arrive by callback and the stream by loop, with no ordering
270
381
  // between them: the callback queues, the loop takes at `finish-step`.
271
382
  const steps = stepQueue();
383
+ // The job stopping and the model going quiet end the stream the same way;
384
+ // which of the two it was is read at `abort`
385
+ const stop = options.signal
386
+ ? AbortSignal.any([options.signal, quiet.signal])
387
+ : quiet.signal;
388
+ quiet.touch();
272
389
  const result = await agent.stream({
273
390
  messages: history,
274
- abortSignal: options.signal,
391
+ abortSignal: stop,
275
392
  onStepEnd: (step) =>
276
393
  steps.push({
277
394
  messages: step.response.messages,
@@ -280,19 +397,44 @@ export async function runBot(
280
397
  });
281
398
 
282
399
  let writing = "";
283
- /** The last chunk of prose; used only if the run ends without a report. */
400
+ /** The last chunk of prose; used only if the run ends without an answer. */
284
401
  let last: string | null = null;
285
402
  /** The `ask_thursday` call the loop stopped on. */
286
403
  let asked: { id: string; question: string; options: string[] } | null = null;
287
- /** The report (bot.tool reportSpec). */
288
- let reported: { text: string; complete: boolean } | null = null;
404
+ /** The answer (bot.tool answerSpec). */
405
+ let answered: { text: string; stopped: boolean } | null = null;
406
+ /** Index of the step being read; `lastStep` narrows the one at MAX_STEPS - 1. */
407
+ let stepAt = -1;
408
+ /** Why the latest step ended; prose cut off at the output limit is not an answer. */
409
+ let finish: FinishReason | null = null;
410
+ /** Tool calls still running: the model is not expected to send anything meanwhile. */
411
+ const working = new Set<string>();
412
+
413
+ /** A break as the runner takes it; an overflow carries where to compact next time. */
414
+ const failed = (cause: unknown): Extract<BotEvent, { type: "error" }> => {
415
+ const failure = modelFailureOf(cause);
416
+ const message = modelErrorToString(cause);
417
+ if (failure !== "overflow") return { type: "error", message, failure };
418
+ const shrunk = Math.floor(
419
+ Math.min(budget, sent || budget) * BOT_RUN.overflowShrink,
420
+ );
421
+ return {
422
+ type: "error",
423
+ message,
424
+ failure,
425
+ budget: Math.max(COMPACT_AT_MIN, shrunk),
426
+ };
427
+ };
289
428
 
290
429
  // An abort ends the stream without a finish; release any waiting take.
291
- options.signal?.addEventListener("abort", () => steps.end(), { once: true });
430
+ stop.addEventListener("abort", () => steps.end(), { once: true });
292
431
  try {
293
432
  for await (const part of result.fullStream) {
433
+ // Anything at all is the model still there
434
+ quiet.touch();
294
435
  switch (part.type) {
295
436
  case "start-step":
437
+ stepAt += 1;
296
438
  if (pending.compact) {
297
439
  await emit(pending.compact);
298
440
  pending.compact = null;
@@ -326,13 +468,23 @@ export async function runBot(
326
468
  question: String(args.question ?? ""),
327
469
  options: optionsOf(args.options),
328
470
  };
329
- } else if (part.toolName === TOOL_NAMES.report) {
330
- const args = part.input as { result?: unknown; complete?: unknown };
331
- reported = {
471
+ } else if (part.toolName === TOOL_NAMES.answer) {
472
+ const args = part.input as { result?: unknown };
473
+ answered = {
332
474
  text: String(args.result ?? "").trim(),
333
- complete: args.complete !== false,
475
+ // The last step offers nothing but `answer`: an answer there is the cap's doing
476
+ stopped: stepAt >= MAX_STEPS - 1,
334
477
  };
335
478
  }
479
+ // A tool at work sends nothing, and bounds itself (bash, connected
480
+ // tools, a borrowed bot's own watch); `ask_thursday` never runs
481
+ if (
482
+ !part.providerExecuted &&
483
+ part.toolName !== TOOL_NAMES.ask_thursday
484
+ ) {
485
+ working.add(part.toolCallId);
486
+ quiet.hold();
487
+ }
336
488
  await emit({
337
489
  type: "tool",
338
490
  id: part.toolCallId,
@@ -343,16 +495,17 @@ export async function runBot(
343
495
  }
344
496
 
345
497
  case "tool-result":
346
- // A refused report is not the ending, unless the step cap ends the run
347
- // on this step; then the text still reaches the user, never as complete.
498
+ if (working.delete(part.toolCallId)) quiet.release();
499
+ // A refused answer is not the ending, unless the step cap ends the run
500
+ // on this step; then the text still reaches the user, as a stop.
348
501
  if (
349
- part.toolName === TOOL_NAMES.report &&
350
- reported &&
351
- !reportAccepted(part.output)
502
+ part.toolName === TOOL_NAMES.answer &&
503
+ answered &&
504
+ !answerAccepted(part.output)
352
505
  ) {
353
- reported = {
354
- text: `${reported.text}\n\n${outputText(part.output)}`,
355
- complete: false,
506
+ answered = {
507
+ text: `${answered.text}\n\n${outputText(part.output)}`,
508
+ stopped: true,
356
509
  };
357
510
  }
358
511
  // A provider-executed tool (web search) lands here too; the step's own
@@ -365,72 +518,109 @@ export async function runBot(
365
518
  });
366
519
  break;
367
520
 
521
+ case "tool-error":
522
+ if (working.delete(part.toolCallId)) quiet.release();
523
+ // The model reads it and carries on; the row shows it now rather than
524
+ // when the step ends
525
+ await emit({
526
+ type: "tool-result",
527
+ id: part.toolCallId,
528
+ name: part.toolName,
529
+ output: modelErrorToString(part.error),
530
+ error: true,
531
+ });
532
+ break;
533
+
368
534
  case "finish-step": {
535
+ finish = part.finishReason;
369
536
  const step = await steps.take();
370
537
  if (step) await emit({ type: "step", ...step, budget });
371
538
  break;
372
539
  }
373
540
 
374
- case "error":
541
+ case "abort":
542
+ // The job was stopped: whoever stopped it writes the row (bot.runner)
543
+ if (options.signal?.aborted) return;
544
+ // Otherwise the model went quiet (silenceWatch)
375
545
  await emit({
376
546
  type: "error",
377
- message: modelErrorToString(part.error),
547
+ message: part.reason ?? "The model stopped sending.",
548
+ failure: "transient",
378
549
  });
379
550
  return;
380
551
 
552
+ case "error":
553
+ await emit(failed(part.error));
554
+ return;
555
+
381
556
  default:
382
557
  break;
383
558
  }
384
559
  }
385
560
  } finally {
386
561
  steps.end();
562
+ quiet.end();
387
563
  }
388
564
 
389
565
  try {
390
566
  await result.response;
391
567
  } catch (cause) {
392
- await emit({ type: "error", message: modelErrorToString(cause) });
568
+ // Stopped between steps: whoever stopped it writes the row
569
+ if (options.signal?.aborted) return;
570
+ await emit(failed(cause));
393
571
  return;
394
572
  }
395
573
 
396
- // Asking wins over reporting: the loop halts on the ask.
574
+ // Asking wins over answering: the loop halts on the ask.
397
575
  if (asked) {
398
576
  await emit({ type: "waiting", ...asked });
399
577
  return;
400
578
  }
401
579
 
402
- if (reported) {
403
- await emit({ type: "report", ...reported });
580
+ if (answered) {
581
+ await emit({ type: "answer", ...answered });
582
+ return;
583
+ }
584
+
585
+ // The provider's filter stopped the answer: there is nothing to continue past
586
+ if (finish === "content-filter") {
587
+ await emit({
588
+ type: "error",
589
+ message: "The provider's content filter stopped the response.",
590
+ failure: "fatal",
591
+ });
404
592
  return;
405
593
  }
406
594
 
407
- // The sdk stops on a step with no tool call regardless of `stopWhen`. The
408
- // prose is passed on as a report, never as complete.
595
+ // The sdk stops on a step with no tool call regardless of `stopWhen`. Prose
596
+ // there is an answer given without the tool and ends the job like one;
597
+ // nothing at all, prose cut off at the output limit, or prose on the step the
598
+ // cap narrowed to `answer`, is a stop — continuing picks up where it broke off.
409
599
  await emit({
410
- type: "report",
600
+ type: "answer",
411
601
  text: last ?? "Stopped without handing anything back.",
412
- complete: false,
602
+ stopped: last === null || finish === "length" || stepAt >= MAX_STEPS - 1,
413
603
  });
414
604
  }
415
605
 
416
- /** The last step ended on a report the tool accepted. `hasToolCall` would also stop on a refused one. */
417
- const reportAccepted_ = ({ steps }: { steps: StepResult<ToolSet>[] }) =>
606
+ /** The last step ended on an answer the tool accepted. `hasToolCall` would also stop on a refused one. */
607
+ const answerAccepted_ = ({ steps }: { steps: StepResult<ToolSet>[] }) =>
418
608
  steps
419
609
  .at(-1)
420
610
  ?.toolResults.some(
421
611
  (result) =>
422
- result.toolName === TOOL_NAMES.report && reportAccepted(result.output),
612
+ result.toolName === TOOL_NAMES.answer && answerAccepted(result.output),
423
613
  ) ?? false;
424
614
 
425
615
  /**
426
- * The step before the cap is narrowed to `report` and required to call it,
616
+ * The step before the cap is narrowed to `answer` and required to call it,
427
617
  * so a run out of budget ends with something a person can act on.
428
618
  */
429
619
  function lastStep(stepNumber: number): NonNullable<PrepareStepResult<ToolSet>> {
430
620
  if (stepNumber < MAX_STEPS - 1) return {};
431
621
  return {
432
- activeTools: [TOOL_NAMES.report],
433
- toolChoice: { type: "tool", toolName: TOOL_NAMES.report },
622
+ activeTools: [TOOL_NAMES.answer],
623
+ toolChoice: { type: "tool", toolName: TOOL_NAMES.answer },
434
624
  };
435
625
  }
436
626
 
@@ -452,15 +642,18 @@ function sizeOf(messages: ModelMessage[]): number {
452
642
  /** Instructions for compacting. The summary replaces everything above it, opening included, so it must restate the job. */
453
643
  const compactInstructions = (
454
644
  words: number,
455
- ) => `Compact your context. Everything above is your own work on this job so far; after this message it is replaced by what you write now, and you carry on from that alone. Write what a fresh copy of you needs to continue without re-reading anything:
645
+ ) => `Compact your context. The first message above the job, the call it came from, and anything handed over with it — stays exactly as it is. Everything after it is replaced by what you write now, and you carry on from that first message and this alone; nothing else above can be read again. Write it for a fresh copy of you that has your tools, your instructions and that first message, but has seen none of the rest.
456
646
 
457
- - The job as it was given the request, quoted, and the details from the call that matter.
458
- - What you have done and what you found, with the exact values: numbers, names, urls, dates, ids, error messages.
459
- - Files you wrote or read, by path, and what each holds.
460
- - What is still left to do, in order, and any decision or answer you are waiting on.
461
- - What did not work, so it is not tried again.
647
+ Cover all of it, and prefer a fact over a description of a fact:
462
648
 
463
- Plain text, under ${words} words. No preamble and no headings about the summary itselfstart with the job.`;
649
+ - **Where the job stands against that first message.** Do not restate the job or the call; say what has changed since a choice made, a detail settled, an answer that came back, a correction, anything you are still waiting on.
650
+ - **What is already true.** Every value you have — numbers, names, dates, ids, urls, prices, times — written out, not referred to. A value you leave out is one the next steps go and fetch again.
651
+ - **Where you are.** Which page the browser is on by url, whether you are signed in and to what, what is running, what you were in the middle of. Element refs do not survive this: name what to look for, never a ref.
652
+ - **Files.** Every path you wrote or read, and one line on what each holds.
653
+ - **What did not work**, and why: the url that 404s, the endpoint that needs POST not GET, the selector that is not there, the site that refuses a headless browser, the argument a tool rejected. This is the part that stops the next steps repeating an hour of yours.
654
+ - **What is left**, in the order you would do it.
655
+
656
+ Plain text, under ${words} words. No preamble, no headings about the summary itself, no account of how you have been going.`;
464
657
 
465
658
  /** Word cap for a summary, scaled to the budget it stands in for (config BOT_RUN.summaryWords). */
466
659
  const summaryWords = (budget: number) => {
@@ -468,15 +661,51 @@ const summaryWords = (budget: number) => {
468
661
  return Math.min(max, Math.max(min, Math.round(budget / perTokens)));
469
662
  };
470
663
 
471
- /** Prefix of the first message after a compaction (and on a resume). */
664
+ /** Prefix of the summary that follows the first message after a compaction (and on a resume). */
472
665
  const COMPACT_PREAMBLE =
473
- "The context was compacted here. Below is your own summary of the job up to this point; continue from it.";
666
+ "Everything between the first message and here was compacted. Below is your own summary of it; carry on from the first message and this.";
667
+
668
+ /** Heads the file list under a compaction summary (filesUnder). */
669
+ const FILES_HEAD =
670
+ "Files this job has on disk now — open one rather than fetching again what is in it:";
671
+
672
+ /**
673
+ * The files a job has on disk, as lines under its compaction summary. Read off the
674
+ * job's rows — every `write_file`, its borrowed bots' included — and off its folder,
675
+ * never asked of the model: a path a summary leaves out is work the next steps redo.
676
+ * A borrowed run has no rows of its own and reads the messages it is compacting.
677
+ * A listing that fails costs the list, never the compaction.
678
+ */
679
+ async function filesUnder(
680
+ taskId: string | null,
681
+ messages: ModelMessage[],
682
+ folder: string | null,
683
+ ): Promise<string> {
684
+ try {
685
+ const written = taskId
686
+ ? await listWrittenPaths(taskId)
687
+ : writtenPathsIn(messages.map((message) => message.content));
688
+ const files = await filesOnDisk(written, folder);
689
+ if (!files.length) return "";
690
+ const shown = files.slice(-BOT_RUN.compactFiles);
691
+ const older = files.length - shown.length;
692
+ const lines = shown.map((path) => `- ${path}`);
693
+ if (older) lines.unshift(`- …and ${older} older ones`);
694
+ return `\n\n${FILES_HEAD}\n${lines.join("\n")}`;
695
+ } catch (cause) {
696
+ logger.warn("files under the summary could not be listed", cause);
697
+ return "";
698
+ }
699
+ }
474
700
 
475
701
  /**
476
702
  * The model summarizes its own context and continues from the summary. Tools
477
703
  * are passed with calling off: providers refuse a history of tool calls
478
- * without the tools that made them. A failure throws and the runner closes
479
- * the run as `failed`; the thread stays.
704
+ * without the tools that made them. A context too long to summarise whole —
705
+ * the very thing a compaction is for — is tried once more without the tool
706
+ * calls and results but the last few. A failure throws with its cause kept:
707
+ * the runner parks what a retry or a smaller budget can fix (bot.runner
708
+ * parkTask) and fails the rest; the thread stays.
480
709
  */
481
710
  async function compact(
482
711
  model: LanguageModel,
@@ -484,38 +713,67 @@ async function compact(
484
713
  messages: ModelMessage[],
485
714
  options: { signal?: AbortSignal; budget: number },
486
715
  ): Promise<{ text: string; usage: TokenUsage }> {
716
+ let failure: unknown;
487
717
  try {
488
- const { text, usage } = await generateText({
489
- model,
490
- tools,
491
- toolChoice: "none",
492
- messages: [
493
- ...messages,
494
- {
495
- role: "user",
496
- content: compactInstructions(summaryWords(options.budget)),
497
- },
498
- ],
499
- abortSignal: options.signal,
500
- });
501
- const summary = text.trim();
502
- if (summary) {
503
- return {
504
- text: `${COMPACT_PREAMBLE}\n\n${summary}`,
505
- usage: usageOf(usage),
506
- };
507
- }
718
+ return await summarize(model, tools, messages, options);
508
719
  } catch (cause) {
509
720
  if (options.signal?.aborted) throw cause;
510
- throw new Error(
511
- `Could not compact the context (${modelErrorToString(cause)}).`,
512
- );
721
+ failure = cause;
722
+ }
723
+ if (modelFailureOf(failure) === "overflow") {
724
+ // pruneMessages drops a call together with its result, so the thread stays whole
725
+ const lighter = pruneMessages({
726
+ messages,
727
+ toolCalls: "before-last-4-messages",
728
+ emptyMessages: "remove",
729
+ });
730
+ try {
731
+ return await summarize(model, tools, lighter, options);
732
+ } catch (cause) {
733
+ if (options.signal?.aborted) throw cause;
734
+ failure = cause;
735
+ }
513
736
  }
514
737
  throw new Error(
515
- "The summary of the context came back empty, so there is nothing to carry on from.",
738
+ `Could not compact the context (${modelErrorToString(failure)}).`,
739
+ { cause: failure },
516
740
  );
517
741
  }
518
742
 
743
+ /** One summarising call. Nothing streams back, so its whole length is one silence (config BOT_RUN.silenceMs). */
744
+ async function summarize(
745
+ model: LanguageModel,
746
+ tools: ToolSet,
747
+ messages: ModelMessage[],
748
+ options: { signal?: AbortSignal; budget: number },
749
+ ): Promise<{ text: string; usage: TokenUsage }> {
750
+ const { text, usage } = await generateText({
751
+ model,
752
+ tools,
753
+ toolChoice: "none",
754
+ messages: [
755
+ ...messages,
756
+ {
757
+ role: "user",
758
+ content: compactInstructions(summaryWords(options.budget)),
759
+ },
760
+ ],
761
+ abortSignal: options.signal,
762
+ timeout: BOT_RUN.silenceMs,
763
+ });
764
+ const summary = text.trim();
765
+ if (!summary) {
766
+ // A blank reply is the model's hiccup, not the job's: worth another try
767
+ throw Object.assign(
768
+ new Error(
769
+ "The summary of the context came back empty, so there is nothing to carry on from.",
770
+ ),
771
+ { isRetryable: true },
772
+ );
773
+ }
774
+ return { text: `${COMPACT_PREAMBLE}\n\n${summary}`, usage: usageOf(usage) };
775
+ }
776
+
519
777
  /** Step messages handed from `onStepEnd` to the loop. A take that arrives before the push waits. */
520
778
  type FinishedStep = { messages: ModelMessage[]; usage: TokenUsage };
521
779
 
@@ -548,6 +806,51 @@ function stepQueue() {
548
806
  };
549
807
  }
550
808
 
809
+ /**
810
+ * The clock behind `BOT_RUN.silenceMs`: aborts its signal once nothing has
811
+ * arrived for that long. Held while something other than the model does the
812
+ * work — a tool, a compaction — since those send nothing and bound themselves;
813
+ * `touch` starts it over. Not the sdk's `timeout.chunkMs`: that clock keeps
814
+ * running while tools execute, so a long command or a borrowed bot's whole run
815
+ * would trip it.
816
+ */
817
+ function silenceWatch(ms: number) {
818
+ const stop = new AbortController();
819
+ let timer: ReturnType<typeof setTimeout> | undefined;
820
+ let held = 0;
821
+ let ended = false;
822
+ const arm = () => {
823
+ clearTimeout(timer);
824
+ timer = undefined;
825
+ if (ended || held > 0) return;
826
+ timer = setTimeout(() => {
827
+ stop.abort(
828
+ new DOMException(
829
+ `The model sent nothing for ${Math.round(ms / 60_000)} minutes.`,
830
+ "TimeoutError",
831
+ ),
832
+ );
833
+ }, ms);
834
+ };
835
+ return {
836
+ signal: stop.signal,
837
+ touch: arm,
838
+ hold: () => {
839
+ held += 1;
840
+ arm();
841
+ },
842
+ release: () => {
843
+ held = Math.max(0, held - 1);
844
+ arm();
845
+ },
846
+ /** The run is over; nothing is watched any more. */
847
+ end: () => {
848
+ ended = true;
849
+ arm();
850
+ },
851
+ };
852
+ }
853
+
551
854
  /** The sdk gives undefined when unknown; 0 sums correctly. */
552
855
  const usageOf = (usage: LanguageModelUsage): TokenUsage => ({
553
856
  input: usage.inputTokens ?? 0,
@@ -562,7 +865,7 @@ function resolveModel(bot: JobBot) {
562
865
  return resolveDefaultModel().then(getTextModel);
563
866
  }
564
867
 
565
- /** A whole run as one string; this is all `ask_bot` receives. An incomplete report still comes back, marked as such. */
868
+ /** A whole run as one string; this is all `ask_bot` receives. A stopped run still comes back, marked as such. */
566
869
  export async function runBotToText(
567
870
  input: Extract<RunInput, { request: string }>,
568
871
  options: RunOptions,
@@ -572,10 +875,10 @@ export async function runBotToText(
572
875
  await runBot(input, {
573
876
  ...options,
574
877
  emit: async (event) => {
575
- if (event.type === "report") {
576
- outcome = event.complete
577
- ? event.text
578
- : `${event.text}\n\n(As far as they got — they ran out of steps.)`;
878
+ if (event.type === "answer") {
879
+ outcome = event.stopped
880
+ ? `${event.text}\n\n(As far as they got — they stopped before finishing.)`
881
+ : event.text;
579
882
  }
580
883
  if (event.type === "error") failure = event.message;
581
884
  await options.emit(event);
@@ -602,6 +905,10 @@ function withAskBot(
602
905
  /** Bots it can call; itself excluded (bot.prompt). */
603
906
  peers: string[];
604
907
  depth: number;
908
+ /** The chain this bot inherited, handed down under the part it hands over. */
909
+ chain: string;
910
+ /** Bots above this one on the job, which are blocked waiting on it. */
911
+ above: string[];
605
912
  options: RunOptions;
606
913
  emit: (event: BotEvent) => Promise<void>;
607
914
  },
@@ -614,10 +921,16 @@ function withAskBot(
614
921
  description: askBotSpec.description,
615
922
  inputSchema: askBotSpec.parameters,
616
923
  execute: async ({ bot, request, context }, call) => {
617
- // The borrowed bot gets its own browser session under this call's id,
618
- // closed when the call ends; the runner closes the job's own.
619
- const taskId = asker.options.taskId
620
- ? `${asker.options.taskId}-${call.toolCallId.slice(0, 8)}`
924
+ // A bot above is blocked waiting on this very call; borrowing it back never returns
925
+ if (bot === asker.name || asker.above.includes(bot)) {
926
+ return `${bot} is already on this job, above you and waiting on your part. Pick another bot, or do it yourself.`;
927
+ }
928
+ // The borrowed bot works in the job's folder but drives a browser session
929
+ // of its own, under this call's id. When it answers, the same rule as a
930
+ // job's end: what it showed on their screen stays, what nobody can see
931
+ // closes (workspace.ts closeHiddenBrowser).
932
+ const session = asker.options.taskId
933
+ ? `${asker.options.session ?? asker.options.taskId}-${call.toolCallId.slice(0, 8)}`
621
934
  : null;
622
935
  const exchanges: { question: string; answer: string }[] = [];
623
936
  const answer = async (question: string) => {
@@ -637,12 +950,22 @@ function withAskBot(
637
950
  };
638
951
  try {
639
952
  const outcome = await runBotToText(
640
- { bot, request, context, askedBy: asker.name },
953
+ {
954
+ bot,
955
+ request,
956
+ context,
957
+ askedBy: asker.name,
958
+ chain: asker.chain,
959
+ above: [...asker.above, asker.name],
960
+ },
641
961
  {
642
962
  ...asker.options,
963
+ // This call's signal, not the job's: a stream that ends — the job
964
+ // stopping, or the model going quiet — ends the part it waited on
965
+ signal: call.abortSignal ?? asker.options.signal,
643
966
  depth: asker.depth + 1,
644
967
  parent: call.toolCallId,
645
- taskId,
968
+ session,
646
969
  answer,
647
970
  },
648
971
  );
@@ -650,7 +973,7 @@ function withAskBot(
650
973
  ? `${outcome}\n\n${exchangeLines(bot, exchanges)}`
651
974
  : outcome;
652
975
  } finally {
653
- if (taskId) void closeJobShell(taskId);
976
+ if (session) void closeHiddenBrowser(session);
654
977
  }
655
978
  },
656
979
  }),
@@ -658,7 +981,7 @@ function withAskBot(
658
981
  return agentTools;
659
982
  }
660
983
 
661
- /** Appended under the report: what the borrowed bot asked and what this bot answered. */
984
+ /** Appended under the answer: what the borrowed bot asked and what this bot answered. */
662
985
  function exchangeLines(
663
986
  bot: string,
664
987
  exchanges: { question: string; answer: string }[],
@@ -740,6 +1063,8 @@ async function answerBack(input: {
740
1063
  },
741
1064
  ],
742
1065
  abortSignal: input.signal,
1066
+ // One-shot: nothing streams back, so its whole length is one silence
1067
+ timeout: BOT_RUN.silenceMs,
743
1068
  });
744
1069
  return {
745
1070
  text:
@@ -758,90 +1083,83 @@ async function answerBack(input: {
758
1083
  }
759
1084
 
760
1085
  /**
761
- * The stored thread cut down for a resume: tool results dropped, tool calls
762
- * kept as one line each (task.query toolLine), ask/answer/report exchanges
763
- * kept as text. The opening always, then only the last `keep` messages.
764
- * Output is user/assistant text only, so a run stopped mid-step has no
765
- * dangling tool call.
1086
+ * The stored thread as a resuming run reads it: the rows as they were written,
1087
+ * each tool call with its result, so a resumed run carries on the conversation
1088
+ * it was having and a provider's prefix cache still matches from its first call.
1089
+ * The one repair is a call whose result never arrived the run was stopped
1090
+ * between the two (a closed browser, a restart, a model that went quiet) and a
1091
+ * call with no result is refused outright — which gets one saying so, right
1092
+ * after it. An `ask_bot` call cut off that way carries what the borrowed bot
1093
+ * had written by then, so its part is picked up rather than asked for again
1094
+ * from nothing. Nothing else changes: `compact` is the one thing that shortens
1095
+ * a bot's own thread.
766
1096
  */
767
- export function condenseThread(
1097
+ export function resumeThread(
768
1098
  thread: ModelMessage[],
769
- keep = BOT_RUN.resumeMessages,
1099
+ /** Borrowed bots' last lines, by `ask_bot` call id (task.query listBorrowedTails). */
1100
+ borrowed: ReadonlyMap<string, { bot: string; tail: string }> = new Map(),
770
1101
  ): ModelMessage[] {
771
- const [opening, ...rest] = thread;
772
- const flat: { role: "user" | "assistant"; content: string }[] = [];
773
- const say = (role: "user" | "assistant", text: string) => {
774
- const line = text.trim();
775
- if (!line) return;
776
- const last = flat.at(-1);
777
- // Merge consecutive same-role messages; some providers require alternation.
778
- if (last?.role === role) last.content = `${last.content}\n\n${line}`;
779
- else flat.push({ role, content: line });
780
- };
781
-
782
- for (const message of rest) {
783
- if (message.role === "user") {
784
- say(
785
- "user",
786
- typeof message.content === "string"
787
- ? message.content
788
- : message.content
789
- .flatMap((part) => (part.type === "text" ? [part.text] : []))
790
- .join("\n"),
791
- );
792
- } else if (message.role === "assistant") {
793
- if (typeof message.content === "string") {
794
- say("assistant", message.content);
795
- continue;
796
- }
797
- for (const part of message.content) {
798
- if (part.type === "text") say("assistant", part.text);
799
- else if (part.type === "tool-call") say("assistant", callNote(part));
800
- }
801
- } else if (message.role === "tool") {
802
- for (const part of message.content) {
803
- if (part.type !== "tool-result") continue;
804
- if (
805
- part.toolName === TOOL_NAMES.ask_thursday ||
806
- part.toolName === TOOL_NAMES.ask_back
807
- ) {
808
- // Thursday, or the bot that borrowed this one
809
- say("user", outputText(part.output));
810
- } else if (part.toolName === TOOL_NAMES.ask_bot) {
811
- say(
812
- "assistant",
813
- `(they answered: ${clip(outputText(part.output), 600)})`,
814
- );
815
- }
816
- // Other tool results are dropped
817
- }
1102
+ const answered = new Set<string>();
1103
+ for (const message of thread) {
1104
+ // A provider-run tool (web search) answers inside the assistant message
1105
+ if (message.role !== "tool" && message.role !== "assistant") continue;
1106
+ if (typeof message.content === "string") continue;
1107
+ for (const part of message.content) {
1108
+ if (part.type === "tool-result") answered.add(part.toolCallId);
818
1109
  }
819
1110
  }
820
1111
 
821
- const tail = flat.slice(-keep);
822
- return opening ? [opening, ...tail] : tail;
1112
+ const out: ModelMessage[] = [];
1113
+ let owed: Extract<ModelMessage, { role: "tool" }>["content"] = [];
1114
+ for (const message of thread) {
1115
+ if (owed.length && message.role === "tool") {
1116
+ out.push({ ...message, content: [...owed, ...message.content] });
1117
+ owed = [];
1118
+ continue;
1119
+ }
1120
+ if (owed.length) {
1121
+ out.push({ role: "tool", content: owed });
1122
+ owed = [];
1123
+ }
1124
+ out.push(message);
1125
+ if (message.role !== "assistant" || typeof message.content === "string") {
1126
+ continue;
1127
+ }
1128
+ owed = message.content.flatMap((part) =>
1129
+ part.type === "tool-call" && !answered.has(part.toolCallId)
1130
+ ? [
1131
+ {
1132
+ type: "tool-result" as const,
1133
+ toolCallId: part.toolCallId,
1134
+ toolName: part.toolName,
1135
+ output: {
1136
+ type: "text" as const,
1137
+ value: interrupted(part, borrowed),
1138
+ },
1139
+ },
1140
+ ]
1141
+ : [],
1142
+ );
1143
+ }
1144
+ if (owed.length) out.push({ role: "tool", content: owed });
1145
+ return out;
823
1146
  }
824
1147
 
825
- /**
826
- * One tool call as one line for a resuming model. Conversation calls read as
827
- * conversation; the rest use the same `toolLine` the screen draws (task.query).
828
- */
829
- function callNote(part: { toolName: string; input: unknown }): string {
830
- const args = (part.input ?? {}) as Record<string, unknown>;
831
- if (part.toolName === TOOL_NAMES.ask_thursday) {
832
- return `(asked Thursday: ${String(args.question ?? "")})`;
833
- }
834
- if (part.toolName === TOOL_NAMES.ask_back) {
835
- return `(asked back: ${String(args.question ?? "")})`;
836
- }
837
- if (part.toolName === TOOL_NAMES.ask_bot) {
838
- return `(asked ${String(args.bot ?? "")} for: ${String(args.request ?? "")})`;
839
- }
840
- if (part.toolName === TOOL_NAMES.report) {
841
- // A mid-thread report is a stretch that ended; the follow-up under it needs it.
842
- return `(handed back: ${clip(String(args.result ?? ""), 300)})`;
843
- }
844
- return `[${part.toolName} ${toolLine(part.toolName, args)}]`;
1148
+ /** What a resumed run reads where a call's result never arrived (resumeThread). */
1149
+ const INTERRUPTED =
1150
+ "No result: the run was stopped before this call returned. Check what it did before relying on it.";
1151
+
1152
+ /** INTERRUPTED, or for a borrowed bot's part, what it had written when the run stopped. */
1153
+ function interrupted(
1154
+ call: { toolCallId: string; toolName: string },
1155
+ borrowed: ReadonlyMap<string, { bot: string; tail: string }>,
1156
+ ): string {
1157
+ const cut =
1158
+ call.toolName === TOOL_NAMES.ask_bot
1159
+ ? borrowed.get(call.toolCallId)
1160
+ : undefined;
1161
+ if (!cut) return INTERRUPTED;
1162
+ return `No result: the run was stopped before ${cut.bot} answered, and they are not working on it any more. What they had written by then:\n\n${cut.tail}\n\nCheck what they did before relying on it, then finish the part yourself or hand them what is left.`;
845
1163
  }
846
1164
 
847
1165
  /** Just the text out of the shape a tool result reached the model in (ai-sdk ToolResultOutput). */
@@ -855,15 +1173,3 @@ function outputText(output: unknown): string {
855
1173
  }
856
1174
  return JSON.stringify(output ?? "");
857
1175
  }
858
-
859
- /** The borrowed bot's first user message: the request plus what the asking bot already knows. */
860
- function taskPrompt(input: {
861
- request: string;
862
- context?: string | null;
863
- askedBy: string;
864
- }) {
865
- const context = input.context?.trim();
866
- return context
867
- ? `${input.request}\n\n## What ${input.askedBy} already knows\n\n${context}`
868
- : input.request;
869
- }