thursday-agent 0.4.0 → 0.6.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 (657) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +3 -2
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/prerender-manifest.json +3 -3
  5. package/.next/required-server-files.json +8 -1
  6. package/.next/routes-manifest.json +18 -6
  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 +1 -1
  10. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  14. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  15. package/.next/server/app/_not-found.html +1 -1
  16. package/.next/server/app/_not-found.rsc +13 -13
  17. package/.next/server/app/_not-found.segments/_full.segment.rsc +13 -13
  18. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +12 -12
  19. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  20. package/.next/server/app/api/artifact/route.js +10 -6
  21. package/.next/server/app/api/artifact/route.js.nft.json +1 -1
  22. package/.next/server/app/api/bot/memory/files/route.js +6 -5
  23. package/.next/server/app/api/bot/memory/files/route.js.nft.json +1 -1
  24. package/.next/server/app/api/bot/memory/route.js +8 -8
  25. package/.next/server/app/api/bot/memory/route.js.nft.json +1 -1
  26. package/.next/server/app/api/bot/route.js +8 -8
  27. package/.next/server/app/api/bot/route.js.nft.json +1 -1
  28. package/.next/server/app/api/bot/thread/[id]/route/app-paths-manifest.json +3 -0
  29. package/.next/server/app/api/bot/thread/[id]/route.js +15 -0
  30. package/.next/server/app/api/bot/thread/[id]/route.js.nft.json +1 -0
  31. package/.next/server/app/api/bot/thread/[id]/route_client-reference-manifest.js +3 -0
  32. package/.next/server/app/api/bot/thread/route/app-paths-manifest.json +3 -0
  33. package/.next/server/app/api/bot/thread/route.js +15 -0
  34. package/.next/server/app/api/bot/thread/route.js.nft.json +1 -0
  35. package/.next/server/app/api/bot/thread/route_client-reference-manifest.js +3 -0
  36. package/.next/server/app/api/config/route.js +7 -7
  37. package/.next/server/app/api/config/route.js.nft.json +1 -1
  38. package/.next/server/app/api/events/route.js +3 -3
  39. package/.next/server/app/api/events/route.js.nft.json +1 -1
  40. package/.next/server/app/api/file/[...path]/route.js +5 -5
  41. package/.next/server/app/api/file/[...path]/route.js.nft.json +1 -1
  42. package/.next/server/app/api/llm-model/catalog/route.js +8 -8
  43. package/.next/server/app/api/llm-model/catalog/route.js.nft.json +1 -1
  44. package/.next/server/app/api/llm-model/credits/route.js +8 -8
  45. package/.next/server/app/api/llm-model/credits/route.js.nft.json +1 -1
  46. package/.next/server/app/api/llm-model/route.js +8 -8
  47. package/.next/server/app/api/llm-model/route.js.nft.json +1 -1
  48. package/.next/server/app/api/llm-model/subscription/route.js +8 -8
  49. package/.next/server/app/api/llm-model/subscription/route.js.nft.json +1 -1
  50. package/.next/server/app/api/mcp/[name]/route.js +6 -6
  51. package/.next/server/app/api/mcp/[name]/route.js.nft.json +1 -1
  52. package/.next/server/app/api/mcp/oauth/callback/route.js +4 -4
  53. package/.next/server/app/api/mcp/oauth/callback/route.js.nft.json +1 -1
  54. package/.next/server/app/api/mcp/route.js +6 -6
  55. package/.next/server/app/api/mcp/route.js.nft.json +1 -1
  56. package/.next/server/app/api/mcp/tools/route.js +6 -6
  57. package/.next/server/app/api/mcp/tools/route.js.nft.json +1 -1
  58. package/.next/server/app/api/memory/edit/route.js +13 -13
  59. package/.next/server/app/api/memory/edit/route.js.nft.json +1 -1
  60. package/.next/server/app/api/memory/route.js +7 -6
  61. package/.next/server/app/api/memory/route.js.nft.json +1 -1
  62. package/.next/server/app/api/skills/[source]/[name]/route.js +6 -6
  63. package/.next/server/app/api/skills/[source]/[name]/route.js.nft.json +1 -1
  64. package/.next/server/app/api/skills/route.js +6 -6
  65. package/.next/server/app/api/skills/route.js.nft.json +1 -1
  66. package/.next/server/app/api/thursday/call/route.js +10 -11
  67. package/.next/server/app/api/thursday/call/route.js.nft.json +1 -1
  68. package/.next/server/app/api/thursday/skills/route.js +10 -11
  69. package/.next/server/app/api/thursday/skills/route.js.nft.json +1 -1
  70. package/.next/server/app/api/thursday/tool-call/route.js +15 -15
  71. package/.next/server/app/api/thursday/tool-call/route.js.nft.json +1 -1
  72. package/.next/server/app/api/workspace/missing/route/app-paths-manifest.json +3 -0
  73. package/.next/server/app/api/workspace/missing/route/build-manifest.json +12 -0
  74. package/.next/server/app/api/workspace/missing/route/server-reference-manifest.json +4 -0
  75. package/.next/server/app/api/workspace/missing/route.js +12 -0
  76. package/.next/server/app/api/workspace/missing/route.js.map +5 -0
  77. package/.next/server/app/api/workspace/missing/route.js.nft.json +1 -0
  78. package/.next/server/app/api/workspace/missing/route_client-reference-manifest.js +3 -0
  79. package/.next/server/app/api/workspace/route.js +5 -4
  80. package/.next/server/app/api/workspace/route.js.nft.json +1 -1
  81. package/.next/server/app/artifact/[...path]/page/server-reference-manifest.json +1 -1
  82. package/.next/server/app/artifact/[...path]/page.js +4 -3
  83. package/.next/server/app/artifact/[...path]/page.js.nft.json +1 -1
  84. package/.next/server/app/artifact/[...path]/page_client-reference-manifest.js +1 -1
  85. package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
  86. package/.next/server/app/page/server-reference-manifest.json +67 -45
  87. package/.next/server/app/page.js +10 -9
  88. package/.next/server/app/page.js.nft.json +1 -1
  89. package/.next/server/app/page_client-reference-manifest.js +1 -1
  90. package/.next/server/app-paths-manifest.json +3 -2
  91. package/.next/server/chunks/[root-of-the-server]__02846fb._.js +8 -0
  92. package/.next/server/chunks/[root-of-the-server]__049f521._.js +3 -0
  93. package/.next/server/chunks/[root-of-the-server]__05luceg._.js +3 -0
  94. package/.next/server/chunks/[root-of-the-server]__06--3q3._.js +8 -0
  95. package/.next/server/chunks/[root-of-the-server]__07bnah_._.js +3 -0
  96. package/.next/server/chunks/[root-of-the-server]__08eae20._.js +8 -0
  97. package/.next/server/chunks/[root-of-the-server]__0_ue8oe._.js +3 -0
  98. package/.next/server/chunks/[root-of-the-server]__0c9fgca._.js +5 -0
  99. package/.next/server/chunks/[root-of-the-server]__0g-ueei._.js +8 -0
  100. package/.next/server/chunks/[root-of-the-server]__0h_x6q6._.js +5 -0
  101. package/.next/server/chunks/{[root-of-the-server]__0pa2n68._.js → [root-of-the-server]__0kxrvxf._.js} +2 -2
  102. package/.next/server/chunks/{[root-of-the-server]__0q0vvt2._.js → [root-of-the-server]__0ov3iir._.js} +2 -2
  103. package/.next/server/chunks/[root-of-the-server]__0qwtlmx._.js +8 -0
  104. package/.next/server/chunks/[root-of-the-server]__0sm5xvm._.js +3 -0
  105. package/.next/server/chunks/[root-of-the-server]__0ye3da2._.js +3 -0
  106. package/.next/server/chunks/[root-of-the-server]__0zp35ul._.js +5 -0
  107. package/.next/server/chunks/[root-of-the-server]__144u9d_._.js +3 -0
  108. package/.next/server/chunks/[root-of-the-server]__18-2tsq._.js +5 -0
  109. package/.next/server/chunks/[root-of-the-server]__1e-ci13._.js +3 -0
  110. package/.next/server/chunks/[root-of-the-server]__1e2365e._.js +8 -0
  111. package/.next/server/chunks/[root-of-the-server]__1fix932._.js +8 -0
  112. package/.next/server/chunks/{[root-of-the-server]__0ce8nf8._.js → [root-of-the-server]__1g3t7vn._.js} +3 -3
  113. package/.next/server/chunks/[root-of-the-server]__1kp-83u._.js +5 -0
  114. package/.next/server/chunks/[root-of-the-server]__1kv8p3e._.js +5 -0
  115. package/.next/server/chunks/[root-of-the-server]__1orn-5b._.js +2 -3
  116. package/.next/server/chunks/_02szwk8._.js +5 -0
  117. package/.next/server/chunks/_05h2cii._.js +1 -1
  118. package/.next/server/chunks/_06c9kie._.js +216 -0
  119. package/.next/server/chunks/{_1x6kd1r._.js → _07y2gj-._.js} +1 -1
  120. package/.next/server/chunks/_0a43a-0._.js +3 -0
  121. package/.next/server/chunks/_0bujjey._.js +5 -0
  122. package/.next/server/chunks/_0c9j6rb._.js +1 -1
  123. package/.next/server/chunks/_0dkd549._.js +3 -0
  124. package/.next/server/chunks/_0ej1kvv._.js +3 -0
  125. package/.next/server/chunks/_0f6ts7z._.js +3 -0
  126. package/.next/server/chunks/_0gm9hyv._.js +55 -0
  127. package/.next/server/chunks/_0i55en5._.js +25 -0
  128. package/.next/server/chunks/_0k50n27._.js +18 -0
  129. package/.next/server/chunks/_0khihjk._.js +3 -3
  130. package/.next/server/chunks/_0n74zsf._.js +5 -5
  131. package/.next/server/chunks/_0nvm9ef._.js +3 -0
  132. package/.next/server/chunks/_0p56fn0._.js +3 -0
  133. package/.next/server/chunks/_0recufk._.js +1 -1
  134. package/.next/server/chunks/_0xbkz10._.js +3 -0
  135. package/.next/server/chunks/_10tkr26._.js +3 -0
  136. package/.next/server/chunks/{_0ioz3nd._.js → _11u2kp7._.js} +1 -1
  137. package/.next/server/chunks/_11ujpya._.js +3 -0
  138. package/.next/server/chunks/_127nx_p._.js +5 -0
  139. package/.next/server/chunks/_17l5ygt._.js +1 -1
  140. package/.next/server/chunks/_19uauj6._.js +3 -0
  141. package/.next/server/chunks/_1_5mnq4._.js +5 -5
  142. package/.next/server/chunks/_1abl6t9._.js +55 -0
  143. package/.next/server/chunks/_1d0j961._.js +3 -3
  144. package/.next/server/chunks/_1dbixxz._.js +3 -0
  145. package/.next/server/chunks/_1dc_d1q._.js +98 -0
  146. package/.next/server/chunks/_1drgfx4._.js +5 -5
  147. package/.next/server/chunks/_1e48czp._.js +3 -3
  148. package/.next/server/chunks/_1f31u5x._.js +5 -0
  149. package/.next/server/chunks/_1kg70wc._.js +3 -3
  150. package/.next/server/chunks/_1klbcft._.js +5 -0
  151. package/.next/server/chunks/_1krd9rf._.js +4 -1
  152. package/.next/server/chunks/_1kwmzab._.js +3 -0
  153. package/.next/server/chunks/{_1pf4qe-._.js → _1oxj9fa._.js} +1 -1
  154. package/.next/server/chunks/{_1f46eie._.js → _1pcmdbb._.js} +1 -1
  155. package/.next/server/chunks/_1qgix-j._.js +3 -0
  156. package/.next/server/chunks/{lib_logger_ts_1exwjut._.js → _1rpchta._.js} +5 -5
  157. package/.next/server/chunks/_1vsej-s._.js +5 -0
  158. package/.next/server/chunks/_1yx0p_s._.js +5 -0
  159. package/.next/server/chunks/{_0rnf-6n._.js → _20fsr_z._.js} +6 -6
  160. package/.next/server/chunks/_2128hkj._.js +5 -0
  161. package/.next/server/chunks/_next-internal_server_app_api_bot_thread_[id]_route_actions_1fstjn2.js +3 -0
  162. package/.next/server/chunks/_next-internal_server_app_api_bot_thread_route_actions_003ccso.js +3 -0
  163. package/.next/server/chunks/_next-internal_server_app_api_workspace_missing_route_actions_1fidi6p.js +3 -0
  164. package/.next/server/chunks/config_ts_1g4sz5o._.js +1 -1
  165. package/.next/server/chunks/features_0-cnt0l._.js +4 -0
  166. package/.next/server/chunks/features_0hdaci0._.js +4 -0
  167. package/.next/server/chunks/features_1e39j7-._.js +8 -0
  168. package/.next/server/chunks/features_bot_bot_query_ts_12mgkl2._.js +1 -1
  169. package/.next/server/chunks/features_memory_1uqvksn._.js +1 -1
  170. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0r28-sm._.js → node_modules_consola_dist_chunks_prompt_mjs_0a7-pbw._.js} +1 -1
  171. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0v_7drq._.js → node_modules_consola_dist_chunks_prompt_mjs_0gzvgfv._.js} +1 -1
  172. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0i7cvml._.js → node_modules_consola_dist_chunks_prompt_mjs_107pxfn._.js} +1 -1
  173. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0icqaqw._.js → node_modules_consola_dist_chunks_prompt_mjs_15hbel8._.js} +1 -1
  174. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_18i-23e._.js +3 -0
  175. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1bewgf3._.js +3 -0
  176. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1kipdeo._.js +3 -0
  177. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1nlww1m._.js +3 -0
  178. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1q77nfx._.js +3 -0
  179. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1xqdzx_._.js +3 -0
  180. package/.next/server/chunks/{node_modules_zod_v4_1801ax7._.js → node_modules_zod_v4_0lkux5-._.js} +2 -2
  181. package/.next/server/chunks/{node_modules_zod_v4_1cha_bw._.js → node_modules_zod_v4_1jmr9cq._.js} +2 -2
  182. package/.next/server/chunks/{node_modules_zod_v4_0ubdflj._.js → node_modules_zod_v4_1yrxmek._.js} +2 -2
  183. package/.next/server/chunks/node_modules_zod_v4_classic_errors_0tp0y-q.js +3 -0
  184. package/.next/server/chunks/ssr/[root-of-the-server]__09q9med._.js +1 -1
  185. package/.next/server/chunks/ssr/[root-of-the-server]__0bxsfyn._.js +101 -0
  186. package/.next/server/chunks/ssr/[root-of-the-server]__0lyi8i2._.js +1 -1
  187. package/.next/server/chunks/ssr/[root-of-the-server]__16jc196._.js +1 -1
  188. package/.next/server/chunks/ssr/[root-of-the-server]__172cq4x._.js +3 -0
  189. package/.next/server/chunks/ssr/[root-of-the-server]__18dthbp._.js +3 -0
  190. package/.next/server/chunks/ssr/[root-of-the-server]__18e68w8._.js +3 -0
  191. package/.next/server/chunks/ssr/[root-of-the-server]__1fll8dc._.js +3 -0
  192. package/.next/server/chunks/ssr/[root-of-the-server]__1mzi-ju._.js +1 -1
  193. package/.next/server/chunks/ssr/[root-of-the-server]__1nygfik._.js +5 -0
  194. package/.next/server/chunks/ssr/[root-of-the-server]__1sdphb0._.js +8 -0
  195. package/.next/server/chunks/ssr/[root-of-the-server]__1waa3rc._.js +100 -0
  196. package/.next/server/chunks/ssr/_00rehl_._.js +3 -0
  197. package/.next/server/chunks/ssr/{_1lz_cq2._.js → _02oc_h5._.js} +76 -89
  198. package/.next/server/chunks/ssr/_03_928f._.js +3 -0
  199. package/.next/server/chunks/ssr/_05yghx6._.js +3 -0
  200. package/.next/server/chunks/ssr/_08c4z-9._.js +3 -0
  201. package/.next/server/chunks/ssr/_0_qfdlw._.js +4 -0
  202. package/.next/server/chunks/ssr/_0c9e5w6._.js +9 -0
  203. package/.next/server/chunks/ssr/{_0s99bgb._.js → _0dh2e1j._.js} +9 -3
  204. package/.next/server/chunks/ssr/_0dr-ddg._.js +3 -0
  205. package/.next/server/chunks/ssr/_0eaog6_._.js +3 -0
  206. package/.next/server/chunks/ssr/_0evkpto._.js +3 -0
  207. package/.next/server/chunks/ssr/_0g_rlog._.js +3 -0
  208. package/.next/server/chunks/ssr/_0geg212._.js +3 -0
  209. package/.next/server/chunks/ssr/{_1gd8q8l._.js → _0hxt2ul._.js} +2 -2
  210. package/.next/server/chunks/ssr/_0i_6q1w._.js +2 -2
  211. package/.next/server/chunks/ssr/_0lu1n_x._.js +1 -1
  212. package/.next/server/chunks/ssr/{_15j2_8f._.js → _0wzaht-._.js} +2 -2
  213. package/.next/server/chunks/ssr/_0z-g0uy._.js +3 -0
  214. package/.next/server/chunks/ssr/_123ttsf._.js +3 -0
  215. package/.next/server/chunks/ssr/_123y-ah._.js +3 -0
  216. package/.next/server/chunks/ssr/_12w42gi._.js +3 -0
  217. package/.next/server/chunks/ssr/_161kqav._.js +3 -0
  218. package/.next/server/chunks/ssr/{_0ap2fzn._.js → _16c6_xm._.js} +2 -2
  219. package/.next/server/chunks/ssr/_18opah7._.js +3 -0
  220. package/.next/server/chunks/ssr/_18xqmny._.js +3 -0
  221. package/.next/server/chunks/ssr/_1_krfph._.js +3 -0
  222. package/.next/server/chunks/ssr/{_1-_etts._.js → _1anqots._.js} +2 -2
  223. package/.next/server/chunks/ssr/_1ey7qn-._.js +70 -0
  224. package/.next/server/chunks/ssr/_1g9vs7q._.js +3 -0
  225. package/.next/server/chunks/ssr/_1jmz33w._.js +3 -0
  226. package/.next/server/chunks/ssr/{_0n3toaw._.js → _1k0spz7._.js} +2 -2
  227. package/.next/server/chunks/ssr/_1rgtxhr._.js +5 -0
  228. package/.next/server/chunks/ssr/_1slp25y._.js +3 -0
  229. package/.next/server/chunks/ssr/{_1fojd41._.js → _1x9pskj._.js} +3 -3
  230. package/.next/server/chunks/ssr/_1y6b5mr._.js +101 -0
  231. package/.next/server/chunks/ssr/_1ztk7p6._.js +7 -0
  232. package/.next/server/chunks/ssr/components_ui_tabs_tsx_0ej40b4._.js +3 -0
  233. package/.next/server/chunks/ssr/features_bot_thread_query_ts_023eklt._.js +4 -0
  234. package/.next/server/chunks/ssr/features_config_components_config-setting_tsx_0xfwqdg._.js +1 -1
  235. package/.next/server/chunks/ssr/features_connectors_components_mcp-setting_tsx_0jmbgvp._.js +1 -1
  236. package/.next/server/chunks/ssr/{_1-teee5._.js → lib_logger_ts_0-olsze._.js} +2 -7
  237. package/.next/server/chunks/ssr/{node_modules_0cxaxk9._.js → node_modules_00_51_v._.js} +1 -1
  238. package/.next/server/chunks/ssr/{node_modules_04jf_m8._.js → node_modules_022sipm._.js} +1 -1
  239. package/.next/server/chunks/ssr/{node_modules_0g7fzk3._.js → node_modules_0332gw4._.js} +1 -1
  240. package/.next/server/chunks/ssr/{node_modules_1cx_ql6._.js → node_modules_04vxchl._.js} +1 -1
  241. package/.next/server/chunks/ssr/{node_modules_1vh5ntv._.js → node_modules_0746rzi._.js} +1 -1
  242. package/.next/server/chunks/ssr/{node_modules_0yugy6m._.js → node_modules_0d5uzof._.js} +1 -1
  243. package/.next/server/chunks/ssr/{node_modules_09q35v_._.js → node_modules_0exkwcm._.js} +1 -1
  244. package/.next/server/chunks/ssr/{node_modules_08dhkiq._.js → node_modules_0fr-ml8._.js} +1 -1
  245. package/.next/server/chunks/ssr/{node_modules_14q6g4n._.js → node_modules_0mxvg8h._.js} +1 -1
  246. package/.next/server/chunks/ssr/{node_modules_0arus10._.js → node_modules_0qapgjr._.js} +1 -1
  247. package/.next/server/chunks/ssr/{node_modules_1eg7m6c._.js → node_modules_0qbfyjh._.js} +1 -1
  248. package/.next/server/chunks/ssr/{node_modules_1cz2hle._.js → node_modules_0t-9nup._.js} +1 -1
  249. package/.next/server/chunks/ssr/{node_modules_17bwo43._.js → node_modules_0tusx4d._.js} +3 -3
  250. package/.next/server/chunks/ssr/{node_modules_1efrqxi._.js → node_modules_0vsxbzp._.js} +1 -1
  251. package/.next/server/chunks/ssr/{node_modules_0_-_a--._.js → node_modules_0vv0-fg._.js} +1 -1
  252. package/.next/server/chunks/ssr/{node_modules_1gdh31p._.js → node_modules_0wsvp00._.js} +1 -1
  253. package/.next/server/chunks/ssr/{node_modules_1hpopmo._.js → node_modules_1-urho9._.js} +1 -1
  254. package/.next/server/chunks/ssr/{node_modules_1_pc9kq._.js → node_modules_16pi_dd._.js} +1 -1
  255. package/.next/server/chunks/ssr/{node_modules_00etblx._.js → node_modules_1d53yiq._.js} +1 -1
  256. package/.next/server/chunks/ssr/{node_modules_1aaxuef._.js → node_modules_1g1eihe._.js} +1 -1
  257. package/.next/server/chunks/ssr/{node_modules_198nmll._.js → node_modules_1gpednh._.js} +1 -1
  258. package/.next/server/chunks/ssr/{node_modules_0pvd7q-._.js → node_modules_1gu1pph._.js} +1 -1
  259. package/.next/server/chunks/ssr/{node_modules_0b__sgl._.js → node_modules_1he1w8_._.js} +1 -1
  260. package/.next/server/chunks/ssr/{node_modules_1qpjvy5._.js → node_modules_1lnjapm._.js} +1 -1
  261. package/.next/server/chunks/ssr/{node_modules_0940ns5._.js → node_modules_1lynj9m._.js} +1 -1
  262. package/.next/server/chunks/ssr/{node_modules_0x3-ply._.js → node_modules_1seuurl._.js} +2 -2
  263. package/.next/server/chunks/ssr/{node_modules_14om4ar._.js → node_modules_1u6w1pb._.js} +1 -1
  264. package/.next/server/chunks/ssr/{node_modules_0had7xd._.js → node_modules_1xn6saw._.js} +1 -1
  265. package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_1b748ic._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_067_exa._.js} +1 -1
  266. package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_1z4hm5e._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0nrrdaq._.js} +1 -1
  267. package/.next/server/chunks/ssr/{node_modules_@vercel_oidc_dist_06mk8id._.js → node_modules_@vercel_oidc_dist_21a5haa._.js} +1 -1
  268. package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_trash_mjs_0px2gzr._.js +3 -0
  269. package/.next/server/middleware-build-manifest.js +3 -3
  270. package/.next/server/pages/404.html +1 -1
  271. package/.next/server/pages/500.html +1 -1
  272. package/.next/server/server-reference-manifest.js +1 -1
  273. package/.next/server/server-reference-manifest.json +68 -46
  274. package/.next/static/chunks/0dr16v67vj4n7.js +2 -0
  275. package/.next/static/chunks/0g4b348l0pve7.js +1 -0
  276. package/.next/static/chunks/0joan-69kbspd.js +1 -0
  277. package/.next/static/chunks/0vlgzqo9ka90v.js +1 -0
  278. package/.next/static/chunks/0zrupxgci5gqc.js +1 -0
  279. package/.next/static/chunks/10k2xvap03z20.css +1 -0
  280. package/.next/static/chunks/18mkhstgiqcbt.js +1 -0
  281. package/.next/static/chunks/{15m575979un5f.js → 1i-u2wzub_to1.js} +2 -2
  282. package/.next/static/chunks/1kp5nfp06nj9h.js +5 -0
  283. package/.next/static/chunks/1ohslk4568tm3.js +1 -0
  284. package/.next/static/chunks/1sbmwswc9k2-1.js +1 -0
  285. package/.next/static/chunks/1t9qms5pwwa3n.js +1 -0
  286. package/.next/static/chunks/1vj_5bnbxx84e.js +1 -0
  287. package/.next/static/chunks/1vqezbhjm3goq.js +1 -0
  288. package/.next/static/chunks/{3akmqm77muj4-.js → 1wlo979mx-y3w.js} +1 -1
  289. package/.next/static/chunks/26ql4y-43xwhp.js +1 -0
  290. package/.next/static/chunks/{0fl3gwe4-l6tz.js → 2_pnccjp6j1ca.js} +1 -1
  291. package/.next/static/chunks/2a51-ywz9b2a3.js +1 -0
  292. package/.next/static/chunks/2fvdgxih7x7hr.js +1 -0
  293. package/.next/static/chunks/2jlz8zp4jax_m.js +18 -0
  294. package/.next/static/chunks/2nvepds1fu2ux.js +1 -0
  295. package/.next/static/chunks/3addj3i4-izma.js +1 -0
  296. package/.next/static/chunks/3bfwpww3m3x4u.js +1 -0
  297. package/.next/static/chunks/3gfh7nxwusbt0.js +1 -0
  298. package/.next/static/chunks/3gwa44cvz4qoi.js +1 -0
  299. package/.next/static/chunks/{38gweqdo4jpfj.js → 3izg0a3k87q1-.js} +5 -5
  300. package/.next/static/chunks/3zk6q3-3f9swq.js +99 -0
  301. package/.next/static/chunks/3zkg5iup3v4a2.js +12 -0
  302. package/.next/static/chunks/405m0bqtoihev.js +1 -0
  303. package/.next/static/chunks/42wcg3wfg_m_p.js +1 -0
  304. package/README.md +27 -216
  305. package/app/api/events/app-event.ts +6 -4
  306. package/app/api/query-key.ts +25 -16
  307. package/bin/database.mjs +37 -0
  308. package/bin/thursday.mjs +44 -28
  309. package/components/ui/custom-icon.tsx +0 -60
  310. package/components/ui/radio-group.tsx +37 -0
  311. package/components/ui/shiny-text.tsx +33 -6
  312. package/database/migrations/{20260910173049_numerous_spirit → 20260916145701_clever_multiple_man}/migration.sql +80 -24
  313. package/database/migrations/{20260910173049_numerous_spirit → 20260916145701_clever_multiple_man}/snapshot.json +689 -110
  314. package/database/migrations/20260916160713_thread_cancelled/migration.sql +2 -0
  315. package/database/migrations/{20260911030320_opposite_jack_power → 20260916160713_thread_cancelled}/snapshot.json +706 -84
  316. package/docs/how-it-works.md +39 -36
  317. package/docs/images/keeps-going.png +0 -0
  318. package/docs/images/two-minds.png +0 -0
  319. package/docs/live-calls.md +231 -0
  320. package/docs/thread-rooms.md +71 -0
  321. package/features/ai/chatgpt.ts +1 -2
  322. package/features/ai/live.schema.ts +128 -0
  323. package/features/ai/load-tools.ts +148 -77
  324. package/features/ai/model.schema.ts +2 -120
  325. package/features/ai/model.ts +52 -88
  326. package/features/ai/prompts/bot.prompt.ts +91 -214
  327. package/features/ai/prompts/live.prompt.ts +265 -0
  328. package/features/ai/prompts/prompt-helper.ts +91 -32
  329. package/features/ai/prompts/thursday.prompt.ts +125 -172
  330. package/features/ai/tools/bot.tool.ts +38 -145
  331. package/features/ai/tools/call.tool.ts +1 -1
  332. package/features/ai/tools/connected.ts +5 -3
  333. package/features/ai/tools/mcp.tool.ts +7 -4
  334. package/features/ai/tools/memory.tool.ts +35 -31
  335. package/features/ai/tools/skills.tool.ts +36 -14
  336. package/features/ai/tools/studio.tool.ts +26 -21
  337. package/features/ai/tools/tool-name.ts +2 -5
  338. package/features/ai/tools/workspace.tool.ts +33 -10
  339. package/features/artifact/artifact.action.ts +2 -2
  340. package/features/artifact/artifact.query.ts +112 -61
  341. package/features/artifact/artifact.schema.ts +8 -6
  342. package/features/artifact/components/artifact-setting.tsx +83 -33
  343. package/features/bot/bot.action.ts +43 -36
  344. package/features/bot/bot.memory.ts +137 -20
  345. package/features/bot/bot.query.ts +28 -11
  346. package/features/bot/bot.run.ts +299 -636
  347. package/features/bot/bot.runner.ts +468 -844
  348. package/features/bot/bot.schema.ts +93 -82
  349. package/features/bot/bot.seed.ts +141 -64
  350. package/features/bot/components/bot-mark.tsx +324 -232
  351. package/features/bot/components/bot-room.tsx +1613 -697
  352. package/features/bot/components/bot-roster.tsx +8 -13
  353. package/features/bot/components/bot-setting.tsx +58 -42
  354. package/features/bot/components/bot-tool.tsx +55 -87
  355. package/features/bot/components/mark-palette.tsx +35 -32
  356. package/features/bot/components/path-chips.tsx +33 -3
  357. package/features/bot/components/thread-badge.tsx +34 -0
  358. package/features/bot/components/thread-reply.tsx +639 -0
  359. package/features/bot/components/thread-setting.tsx +674 -0
  360. package/features/bot/mark.const.ts +79 -31
  361. package/features/bot/room.query.ts +889 -0
  362. package/features/bot/room.schema.ts +64 -0
  363. package/features/bot/seed-bots.ts +2 -1
  364. package/features/bot/thread.query.ts +971 -0
  365. package/features/bot/thread.store.ts +587 -0
  366. package/features/config/components/voice-key.tsx +113 -88
  367. package/features/config/config.const.ts +5 -9
  368. package/features/connectors/mcp.schema.ts +0 -2
  369. package/features/intro/components/intro.tsx +169 -139
  370. package/features/memory/components/memory-setting.tsx +2 -7
  371. package/features/memory/memory.schema.ts +14 -23
  372. package/features/settings/components/setting-ui.tsx +0 -44
  373. package/features/settings/components/settings.tsx +12 -12
  374. package/features/settings/settings.alert.ts +3 -3
  375. package/features/settings/settings.store.ts +1 -1
  376. package/features/skills/skills.schema.ts +0 -2
  377. package/features/thursday/components/ascii-field.tsx +24 -44
  378. package/features/thursday/components/ascii-orb.tsx +359 -358
  379. package/features/thursday/components/call-log.tsx +46 -44
  380. package/features/thursday/components/face.tsx +35 -10
  381. package/features/thursday/components/tab-state.tsx +3 -3
  382. package/features/thursday/components/thursday-mark.tsx +1 -0
  383. package/features/thursday/components/thursday-setting.tsx +631 -332
  384. package/features/thursday/components/thursday.tsx +106 -41
  385. package/features/thursday/components/voice-picker.tsx +255 -0
  386. package/features/thursday/thursday.action.ts +94 -68
  387. package/features/thursday/thursday.query.ts +49 -30
  388. package/features/thursday/thursday.schema.ts +27 -30
  389. package/features/thursday/thursday.store.ts +11 -7
  390. package/features/thursday/tool-call.ts +3 -3
  391. package/features/thursday/tool-line.ts +5 -3
  392. package/features/thursday/use-thursday.ts +725 -450
  393. package/features/workspace/workspace.query.ts +17 -0
  394. package/features/workspace/workspace.ts +103 -50
  395. package/lib/live/live.schema.ts +47 -0
  396. package/lib/live/live.server.ts +155 -0
  397. package/lib/live/live.session.ts +574 -0
  398. package/lib/{realtime/realtime.tap.ts → live/live.tap.ts} +41 -12
  399. package/lib/live/live.transport.ts +152 -0
  400. package/lib/protocol/use-server-pages.ts +9 -5
  401. package/lib/utils.ts +21 -20
  402. package/package.json +2 -2
  403. package/public/voices/alloy.ogg +0 -0
  404. package/public/voices/ash.ogg +0 -0
  405. package/public/voices/ballad.ogg +0 -0
  406. package/public/voices/beacon.ogg +0 -0
  407. package/public/voices/bossa.ogg +0 -0
  408. package/public/voices/cedar.ogg +0 -0
  409. package/public/voices/cinder.ogg +0 -0
  410. package/public/voices/coral.ogg +0 -0
  411. package/public/voices/delta.ogg +0 -0
  412. package/public/voices/echo.ogg +0 -0
  413. package/public/voices/gleam.ogg +0 -0
  414. package/public/voices/marin.ogg +0 -0
  415. package/public/voices/meridian.ogg +0 -0
  416. package/public/voices/quartz.ogg +0 -0
  417. package/public/voices/ripple.ogg +0 -0
  418. package/public/voices/sage.ogg +0 -0
  419. package/public/voices/shimmer.ogg +0 -0
  420. package/public/voices/stone.ogg +0 -0
  421. package/public/voices/tempo.ogg +0 -0
  422. package/public/voices/verse.ogg +0 -0
  423. package/public/voices/vesper.ogg +0 -0
  424. package/public/voices/willow.ogg +0 -0
  425. package/scripts/artifact-paths.test.mts +25 -0
  426. package/scripts/bot-context.test.mts +1408 -0
  427. package/scripts/live-history.test.mts +115 -0
  428. package/scripts/live-session.test.mts +948 -0
  429. package/server.js +1 -1
  430. package/skills/browser/SKILL.md +17 -6
  431. package/skills/computer/SKILL.md +1 -1
  432. package/skills/find-skills/SKILL.md +3 -8
  433. package/skills/interactive-page/SKILL.md +12 -7
  434. package/skills/interactive-page/references/diagram.md +89 -0
  435. package/skills/interactive-page/scripts/archify/LICENSE +22 -0
  436. package/skills/interactive-page/scripts/archify/README.md +25 -0
  437. package/skills/interactive-page/scripts/archify/assets/JetBrainsMono-OFL.txt +93 -0
  438. package/skills/interactive-page/scripts/archify/assets/template.html +14934 -0
  439. package/skills/interactive-page/scripts/archify/bin/archify.mjs +2126 -0
  440. package/skills/interactive-page/scripts/archify/bin/open-artifact.mjs +86 -0
  441. package/skills/interactive-page/scripts/archify/bin/preview.mjs +653 -0
  442. package/skills/interactive-page/scripts/archify/bin/visual-check.mjs +829 -0
  443. package/skills/interactive-page/scripts/archify/delta/architecture-delta.mjs +1223 -0
  444. package/skills/interactive-page/scripts/archify/examples/agent-run.lifecycle.json +60 -0
  445. package/skills/interactive-page/scripts/archify/examples/agent-tool-call.workflow.json +94 -0
  446. package/skills/interactive-page/scripts/archify/examples/async-job-roundtrip.sequence.json +61 -0
  447. package/skills/interactive-page/scripts/archify/examples/cache-miss-request.sequence.json +82 -0
  448. package/skills/interactive-page/scripts/archify/examples/checkout-platform.base.architecture.json +31 -0
  449. package/skills/interactive-page/scripts/archify/examples/checkout-platform.head.architecture.json +31 -0
  450. package/skills/interactive-page/scripts/archify/examples/deployment-release.lifecycle.json +49 -0
  451. package/skills/interactive-page/scripts/archify/examples/event-stream.dataflow.json +57 -0
  452. package/skills/interactive-page/scripts/archify/examples/incident-response.workflow.json +64 -0
  453. package/skills/interactive-page/scripts/archify/examples/product-analytics.dataflow.json +76 -0
  454. package/skills/interactive-page/scripts/archify/examples/production-deployment.architecture.json +71 -0
  455. package/skills/interactive-page/scripts/archify/examples/release-delivery.workflow.json +62 -0
  456. package/skills/interactive-page/scripts/archify/examples/web-app.architecture.json +46 -0
  457. package/skills/interactive-page/scripts/archify/migrations/workflow-v2.mjs +279 -0
  458. package/skills/interactive-page/scripts/archify/recipes/scenarios.mjs +284 -0
  459. package/skills/interactive-page/scripts/archify/references/authoring-contract.md +243 -0
  460. package/skills/interactive-page/scripts/archify/references/delivery-contract.md +120 -0
  461. package/skills/interactive-page/scripts/archify/references/viewer-runtime.md +45 -0
  462. package/skills/interactive-page/scripts/archify/renderers/architecture/grid.mjs +62 -0
  463. package/skills/interactive-page/scripts/archify/renderers/architecture/render-architecture.mjs +1078 -0
  464. package/skills/interactive-page/scripts/archify/renderers/dataflow/README.md +104 -0
  465. package/skills/interactive-page/scripts/archify/renderers/dataflow/render-dataflow.mjs +483 -0
  466. package/skills/interactive-page/scripts/archify/renderers/lifecycle/README.md +115 -0
  467. package/skills/interactive-page/scripts/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
  468. package/skills/interactive-page/scripts/archify/renderers/sequence/README.md +114 -0
  469. package/skills/interactive-page/scripts/archify/renderers/sequence/render-sequence.mjs +464 -0
  470. package/skills/interactive-page/scripts/archify/renderers/shared/brand-marks.mjs +563 -0
  471. package/skills/interactive-page/scripts/archify/renderers/shared/cli.mjs +218 -0
  472. package/skills/interactive-page/scripts/archify/renderers/shared/desktop-readability.mjs +26 -0
  473. package/skills/interactive-page/scripts/archify/renderers/shared/diagnostics.mjs +127 -0
  474. package/skills/interactive-page/scripts/archify/renderers/shared/engineering-profiles.mjs +157 -0
  475. package/skills/interactive-page/scripts/archify/renderers/shared/generated-brand-marks.mjs +3 -0
  476. package/skills/interactive-page/scripts/archify/renderers/shared/generated-validators.mjs +13 -0
  477. package/skills/interactive-page/scripts/archify/renderers/shared/geometry.mjs +1423 -0
  478. package/skills/interactive-page/scripts/archify/renderers/shared/i18n.mjs +595 -0
  479. package/skills/interactive-page/scripts/archify/renderers/shared/layout-report.mjs +40 -0
  480. package/skills/interactive-page/scripts/archify/renderers/shared/legend.mjs +217 -0
  481. package/skills/interactive-page/scripts/archify/renderers/shared/output-path.mjs +340 -0
  482. package/skills/interactive-page/scripts/archify/renderers/shared/repository-evidence.mjs +238 -0
  483. package/skills/interactive-page/scripts/archify/renderers/shared/repository-location.mjs +58 -0
  484. package/skills/interactive-page/scripts/archify/renderers/shared/text-fit.mjs +49 -0
  485. package/skills/interactive-page/scripts/archify/renderers/shared/utils.mjs +232 -0
  486. package/skills/interactive-page/scripts/archify/renderers/shared/validator.mjs +86 -0
  487. package/skills/interactive-page/scripts/archify/renderers/workflow/README.md +223 -0
  488. package/skills/interactive-page/scripts/archify/renderers/workflow/render-workflow.mjs +35 -0
  489. package/skills/interactive-page/scripts/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
  490. package/skills/interactive-page/scripts/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
  491. package/skills/interactive-page/scripts/archify/schemas/README.md +211 -0
  492. package/skills/interactive-page/scripts/archify/schemas/architecture.schema.json +178 -0
  493. package/skills/interactive-page/scripts/archify/schemas/common.schema.json +115 -0
  494. package/skills/interactive-page/scripts/archify/schemas/dataflow.schema.json +243 -0
  495. package/skills/interactive-page/scripts/archify/schemas/lifecycle.schema.json +266 -0
  496. package/skills/interactive-page/scripts/archify/schemas/sequence.schema.json +223 -0
  497. package/skills/interactive-page/scripts/archify/schemas/workflow.schema.json +428 -0
  498. package/skills/interactive-page/scripts/archify/scripts/check-render-output.mjs +916 -0
  499. package/skills/interactive-page/scripts/archify/scripts/render-examples.mjs +26 -0
  500. package/skills/interactive-page/scripts/bundle-artifact.sh +9 -8
  501. package/skills/skill-creator/SKILL.md +4 -5
  502. package/.next/server/app/api/bot/task/[id]/route/app-paths-manifest.json +0 -3
  503. package/.next/server/app/api/bot/task/[id]/route.js +0 -16
  504. package/.next/server/app/api/bot/task/[id]/route.js.nft.json +0 -1
  505. package/.next/server/app/api/bot/task/[id]/route_client-reference-manifest.js +0 -3
  506. package/.next/server/app/api/bot/task/route/app-paths-manifest.json +0 -3
  507. package/.next/server/app/api/bot/task/route.js +0 -16
  508. package/.next/server/app/api/bot/task/route.js.nft.json +0 -1
  509. package/.next/server/app/api/bot/task/route_client-reference-manifest.js +0 -3
  510. package/.next/server/chunks/[root-of-the-server]__01dkp4a._.js +0 -3
  511. package/.next/server/chunks/[root-of-the-server]__02va5wz._.js +0 -3
  512. package/.next/server/chunks/[root-of-the-server]__02xjoto._.js +0 -3
  513. package/.next/server/chunks/[root-of-the-server]__04q--0f._.js +0 -8
  514. package/.next/server/chunks/[root-of-the-server]__0bl-e63._.js +0 -3
  515. package/.next/server/chunks/[root-of-the-server]__0ffbg85._.js +0 -3
  516. package/.next/server/chunks/[root-of-the-server]__0jq4ohy._.js +0 -8
  517. package/.next/server/chunks/[root-of-the-server]__0ls6_8y._.js +0 -8
  518. package/.next/server/chunks/[root-of-the-server]__0ncs9uu._.js +0 -3
  519. package/.next/server/chunks/[root-of-the-server]__1-ga0kh._.js +0 -5
  520. package/.next/server/chunks/[root-of-the-server]__11n256_._.js +0 -5
  521. package/.next/server/chunks/[root-of-the-server]__12cabg9._.js +0 -3
  522. package/.next/server/chunks/[root-of-the-server]__1339jnv._.js +0 -8
  523. package/.next/server/chunks/[root-of-the-server]__18_teo8._.js +0 -8
  524. package/.next/server/chunks/[root-of-the-server]__1_04mcf._.js +0 -5
  525. package/.next/server/chunks/[root-of-the-server]__1anc1p9._.js +0 -3
  526. package/.next/server/chunks/[root-of-the-server]__1ccon1w._.js +0 -10
  527. package/.next/server/chunks/[root-of-the-server]__1fizn31._.js +0 -3
  528. package/.next/server/chunks/[root-of-the-server]__1llj3-b._.js +0 -3
  529. package/.next/server/chunks/[root-of-the-server]__1p8m3f4._.js +0 -3
  530. package/.next/server/chunks/[root-of-the-server]__1wa1k7r._.js +0 -8
  531. package/.next/server/chunks/_0-73of8._.js +0 -55
  532. package/.next/server/chunks/_0-fjql4._.js +0 -26
  533. package/.next/server/chunks/_00xh_q0._.js +0 -25
  534. package/.next/server/chunks/_03a0897._.js +0 -3
  535. package/.next/server/chunks/_051qh8u._.js +0 -3
  536. package/.next/server/chunks/_05sovvj._.js +0 -3
  537. package/.next/server/chunks/_0fzqt2y._.js +0 -134
  538. package/.next/server/chunks/_0mecv0f._.js +0 -26
  539. package/.next/server/chunks/_0mh8j4m._.js +0 -3
  540. package/.next/server/chunks/_0obojvv._.js +0 -3
  541. package/.next/server/chunks/_0sb7tlz._.js +0 -3
  542. package/.next/server/chunks/_0sbynis._.js +0 -3
  543. package/.next/server/chunks/_0u10wmw._.js +0 -55
  544. package/.next/server/chunks/_0u4aiy2._.js +0 -3
  545. package/.next/server/chunks/_0wp4le3._.js +0 -3
  546. package/.next/server/chunks/_0y5fg-x._.js +0 -3
  547. package/.next/server/chunks/_1230wbx._.js +0 -10
  548. package/.next/server/chunks/_12nheja._.js +0 -3
  549. package/.next/server/chunks/_13510xh._.js +0 -3
  550. package/.next/server/chunks/_15hq89o._.js +0 -3
  551. package/.next/server/chunks/_18-1wcc._.js +0 -55
  552. package/.next/server/chunks/_1_9mjx-._.js +0 -3
  553. package/.next/server/chunks/_1c78vw5._.js +0 -229
  554. package/.next/server/chunks/_1h5_8sk._.js +0 -3
  555. package/.next/server/chunks/_1hdnxb7._.js +0 -3
  556. package/.next/server/chunks/_1j8k_j5._.js +0 -3
  557. package/.next/server/chunks/_1nula3v._.js +0 -3
  558. package/.next/server/chunks/_1php4eq._.js +0 -3
  559. package/.next/server/chunks/_1yuv4vp._.js +0 -3
  560. package/.next/server/chunks/_1zjrdag._.js +0 -3
  561. package/.next/server/chunks/_next-internal_server_app_api_bot_task_[id]_route_actions_1_k_8u3.js +0 -3
  562. package/.next/server/chunks/_next-internal_server_app_api_bot_task_route_actions_0xln6ra.js +0 -3
  563. package/.next/server/chunks/features_0rpfxbk._.js +0 -3
  564. package/.next/server/chunks/features_1qf3cei._.js +0 -3
  565. package/.next/server/chunks/features_bot_0zt92oh._.js +0 -3
  566. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1xoq2rl._.js +0 -3
  567. package/.next/server/chunks/ssr/[externals]__0dizgek._.js +0 -3
  568. package/.next/server/chunks/ssr/[root-of-the-server]__092knlj._.js +0 -3
  569. package/.next/server/chunks/ssr/[root-of-the-server]__0mz33a7._.js +0 -3
  570. package/.next/server/chunks/ssr/[root-of-the-server]__0s9dq5u._.js +0 -3
  571. package/.next/server/chunks/ssr/[root-of-the-server]__0s9l2sv._.js +0 -3
  572. package/.next/server/chunks/ssr/[root-of-the-server]__0vnmv-s._.js +0 -3
  573. package/.next/server/chunks/ssr/[root-of-the-server]__0xsog8n._.js +0 -3
  574. package/.next/server/chunks/ssr/[root-of-the-server]__10-dz5o._.js +0 -61
  575. package/.next/server/chunks/ssr/[root-of-the-server]__1gtlw6v._.js +0 -43
  576. package/.next/server/chunks/ssr/[root-of-the-server]__1ox7hbk._.js +0 -4
  577. package/.next/server/chunks/ssr/[root-of-the-server]__202p40z._.js +0 -3
  578. package/.next/server/chunks/ssr/_001_ms1._.js +0 -28
  579. package/.next/server/chunks/ssr/_01n3o0m._.js +0 -3
  580. package/.next/server/chunks/ssr/_0fz4mf_._.js +0 -3
  581. package/.next/server/chunks/ssr/_0gb4gmf._.js +0 -3
  582. package/.next/server/chunks/ssr/_0h42fze._.js +0 -3
  583. package/.next/server/chunks/ssr/_0h7zdni._.js +0 -3
  584. package/.next/server/chunks/ssr/_0ilna52._.js +0 -42
  585. package/.next/server/chunks/ssr/_0q0zan6._.js +0 -70
  586. package/.next/server/chunks/ssr/_0zcjrbg._.js +0 -3
  587. package/.next/server/chunks/ssr/_0zhoc3r._.js +0 -3
  588. package/.next/server/chunks/ssr/_13fke0x._.js +0 -3
  589. package/.next/server/chunks/ssr/_18cymkq._.js +0 -3
  590. package/.next/server/chunks/ssr/_1b2mjwr._.js +0 -3
  591. package/.next/server/chunks/ssr/_1f-dg-6._.js +0 -3
  592. package/.next/server/chunks/ssr/_1h0rw11._.js +0 -3
  593. package/.next/server/chunks/ssr/_1jhd09t._.js +0 -3
  594. package/.next/server/chunks/ssr/_1nnsk7z._.js +0 -3
  595. package/.next/server/chunks/ssr/_1ov37if._.js +0 -3
  596. package/.next/server/chunks/ssr/_1sji22n._.js +0 -3
  597. package/.next/server/chunks/ssr/_1vi_uo7._.js +0 -3
  598. package/.next/server/chunks/ssr/_1vyd1ji._.js +0 -3
  599. package/.next/server/chunks/ssr/_1xx4zlb._.js +0 -3
  600. package/.next/server/chunks/ssr/components_ui_1y1l7yj._.js +0 -3
  601. package/.next/server/chunks/ssr/features_bot_1s_-2vj._.js +0 -3
  602. package/.next/server/chunks/ssr/lib_realtime_12wg_dd._.js +0 -27
  603. package/.next/server/chunks/ssr/lib_realtime_15h_v1v._.js +0 -27
  604. package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_0npjf8-._.js +0 -3
  605. package/.next/static/chunks/00rd4-v8r7axp.js +0 -1
  606. package/.next/static/chunks/08qe4as2q428z.js +0 -3
  607. package/.next/static/chunks/0gnqdjn2lvdad.js +0 -2
  608. package/.next/static/chunks/0hwap1-p0m5sa.js +0 -1
  609. package/.next/static/chunks/0r5qcctujhxli.js +0 -1
  610. package/.next/static/chunks/0w52rrrmuj9r9.js +0 -1
  611. package/.next/static/chunks/0wcprt5ot5she.js +0 -1
  612. package/.next/static/chunks/10teww7wt-f6h.js +0 -1
  613. package/.next/static/chunks/17p6tk6k88dq6.js +0 -1
  614. package/.next/static/chunks/18a96rkhi4_yd.js +0 -1
  615. package/.next/static/chunks/1dkjg87pxk0n9.js +0 -28
  616. package/.next/static/chunks/1jd09sxcjddu1.js +0 -1
  617. package/.next/static/chunks/1w73ece_6bheu.js +0 -18
  618. package/.next/static/chunks/214ya3n5gxeih.js +0 -1
  619. package/.next/static/chunks/22_zkas21rpna.js +0 -1
  620. package/.next/static/chunks/2fh4-3z_q8b_w.js +0 -1
  621. package/.next/static/chunks/2h0sd2vumhb15.js +0 -1
  622. package/.next/static/chunks/2t4vhi3m8ahya.js +0 -1
  623. package/.next/static/chunks/2tigdtz3dpcy8.js +0 -1
  624. package/.next/static/chunks/2u7xt6mxg44lf.js +0 -1
  625. package/.next/static/chunks/3786h5vayt80-.js +0 -28
  626. package/.next/static/chunks/39j0fvpybzabl.js +0 -40
  627. package/.next/static/chunks/3cq5g494yuke8.css +0 -1
  628. package/.next/static/chunks/3hibbjnt85-k6.js +0 -1
  629. package/.next/static/chunks/3woj-xd7ki5_i.js +0 -1
  630. package/.next/static/chunks/3xk7yqgomx7yq.js +0 -1
  631. package/.next/static/chunks/3zmcsnuo25zvq.js +0 -1
  632. package/database/migrations/20260911030320_opposite_jack_power/migration.sql +0 -1
  633. package/docs/images/task-thread.png +0 -0
  634. package/features/bot/components/task-badge.tsx +0 -42
  635. package/features/bot/components/task-reply.tsx +0 -328
  636. package/features/bot/components/task-setting.tsx +0 -524
  637. package/features/bot/task.query.ts +0 -976
  638. package/features/bot/task.store.ts +0 -489
  639. package/lib/realtime/client-secret.ts +0 -86
  640. package/lib/realtime/open-session.ts +0 -31
  641. package/lib/realtime/openai.ts +0 -35
  642. package/lib/realtime/realtime.audio.ts +0 -196
  643. package/lib/realtime/realtime.driver.ts +0 -489
  644. package/lib/realtime/realtime.protocol.ts +0 -393
  645. package/lib/realtime/realtime.schema.ts +0 -37
  646. package/lib/realtime/realtime.session.ts +0 -106
  647. package/lib/realtime/realtime.transport.ts +0 -384
  648. package/lib/realtime/xai.ts +0 -39
  649. /package/.next/server/app/api/bot/{task → thread}/[id]/route/build-manifest.json +0 -0
  650. /package/.next/server/app/api/bot/{task → thread}/[id]/route/server-reference-manifest.json +0 -0
  651. /package/.next/server/app/api/bot/{task → thread}/[id]/route.js.map +0 -0
  652. /package/.next/server/app/api/bot/{task → thread}/route/build-manifest.json +0 -0
  653. /package/.next/server/app/api/bot/{task → thread}/route/server-reference-manifest.json +0 -0
  654. /package/.next/server/app/api/bot/{task → thread}/route.js.map +0 -0
  655. /package/.next/static/{kaTO0UHqF1bMnWgCDik2f → 0qzGoKn2wTERVZtblX1tp}/_buildManifest.js +0 -0
  656. /package/.next/static/{kaTO0UHqF1bMnWgCDik2f → 0qzGoKn2wTERVZtblX1tp}/_clientMiddlewareManifest.js +0 -0
  657. /package/.next/static/{kaTO0UHqF1bMnWgCDik2f → 0qzGoKn2wTERVZtblX1tp}/_ssgManifest.js +0 -0
@@ -1,13 +1,12 @@
1
1
  import {
2
2
  type FinishReason,
3
3
  generateText,
4
- hasToolCall,
5
4
  type LanguageModel,
6
5
  type LanguageModelUsage,
7
6
  type ModelMessage,
8
7
  type PrepareStepResult,
8
+ type ProviderMetadata,
9
9
  pruneMessages,
10
- type StepResult,
11
10
  stepCountIs,
12
11
  ToolLoopAgent,
13
12
  type ToolSet,
@@ -18,57 +17,43 @@ import { loadTools } from "@/features/ai/load-tools";
18
17
  import {
19
18
  compactBudget,
20
19
  getTextModel,
21
- type ModelFailure,
20
+ isContextOverflow,
21
+ isProviderRefusal,
22
22
  modelErrorToString,
23
- modelFailureOf,
24
23
  resolveDefaultModel,
25
24
  } from "@/features/ai/model";
26
- import {
27
- buildHandoff,
28
- chainOf,
29
- loadBotPrompt,
30
- } from "@/features/ai/prompts/bot.prompt";
31
- import {
32
- answerAccepted,
33
- askBackSpec,
34
- askBotSpec,
35
- } from "@/features/ai/tools/bot.tool";
25
+ import { loadBotPrompt } from "@/features/ai/prompts/bot.prompt";
26
+ import { sendMessageSpec } from "@/features/ai/tools/bot.tool";
36
27
  import { TOOL_NAMES } from "@/features/ai/tools/tool-name";
37
28
  import {
38
29
  COMPACT_AT_MIN,
39
30
  type JobBot,
40
- NO_TOKENS,
41
31
  type TokenUsage,
42
32
  } from "@/features/bot/bot.schema";
43
33
  import {
44
- closeHiddenBrowser,
34
+ botBrowserSession,
45
35
  filesOnDisk,
46
- openBotFolder,
36
+ openBotFolders,
47
37
  openJobScratch,
48
38
  } from "@/features/workspace/workspace";
49
39
  import { logger } from "@/lib/logger";
50
- import { publicError } from "@/lib/public-error";
51
40
  import { estimateTokens } from "@/lib/tokens";
52
41
  import { findJobBot } from "./bot.query";
53
- import {
54
- findTask,
55
- listWrittenPaths,
56
- optionsOf,
57
- writtenPathsIn,
58
- } from "./task.query";
59
-
60
- /**
61
- * The bot loop behind `delegate`: one model with its own prompt, tools and
62
- * context. Only the `answer` reaches Thursday; every event goes through
63
- * `emit` and the runner (bot.runner) writes it to the task's thread.
64
- */
42
+ import { findThread, listWrittenPaths, writtenPathsIn } from "./thread.query";
65
43
 
66
- /** One thing that happened in a run. The runner adds which bot and inside which `ask_bot` call. */
44
+ /** Events from one participant turn; the runner persists them in that participant's transcript. */
67
45
  export type BotEvent =
68
46
  /** A finished chunk of prose. */
69
47
  | { type: "text"; text: string }
70
48
  /** A tool call. `id` is where the result lands. */
71
- | { type: "tool"; id: string; name: string; input: unknown }
49
+ | {
50
+ type: "tool";
51
+ id: string;
52
+ name: string;
53
+ input: unknown;
54
+ providerExecuted?: boolean;
55
+ providerOptions?: ProviderMetadata;
56
+ }
72
57
  | {
73
58
  type: "tool-result";
74
59
  id: string;
@@ -99,211 +84,169 @@ export type BotEvent =
99
84
  messages: number;
100
85
  tokens: number;
101
86
  }
102
- /** Words from the user read before this step (`RunOptions.notes`). Stored as a user row between two steps. */
103
- | { type: "interjection"; text: string }
104
- /** This bot answered a borrowed bot's `ask_back` (answerBack). No row of its own; `usage` is charged to the job. */
105
- | { type: "answered"; question: string; text: string; usage: TokenUsage }
106
- /**
107
- * Stopped for an answer. `id` is the `ask_thursday` call the answer becomes
108
- * the result of; null when asking whether to continue, then the answer is a
109
- * fresh user turn.
110
- */
111
- | { type: "waiting"; id: string | null; question: string; options: string[] }
87
+ /** Normal turn end; interruption is a runtime fact, separate from room completion. */
88
+ | { type: "turn-end"; text: string; stopped: boolean }
112
89
  /**
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.
90
+ * The run broke, in words a person can act on; `budget` is where to compact next
91
+ * time, when the model refused the context as too long; `retry` whether another try
92
+ * may pass (config BOT_RUN.retryMs), false when only a person can change the outcome.
117
93
  */
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
- };
94
+ | { type: "error"; message: string; budget?: number; retry: boolean };
130
95
 
131
- /** The event as the thread sees it: which bot, inside which `ask_bot` call. */
132
- export type TaskEvent = BotEvent & { bot: string; parent: string | null };
96
+ /** The event as the thread sees it: which participant and continuation. */
97
+ export type ThreadEvent = BotEvent & { bot: string; parent: string | null };
133
98
 
134
99
  export type RunOptions = {
135
100
  signal?: AbortSignal;
136
- depth?: number;
137
- /** The `ask_bot` call this run answers; null for the job's own bot. */
138
101
  parent?: string | null;
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
- */
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
- */
102
+ threadId?: string | null;
149
103
  session?: string | null;
150
- /**
151
- * Drained once per step boundary; each string goes in front of the model as
152
- * a user turn before the next step. Only the job's own bot has one.
153
- */
154
- notes?: () => string[];
155
- /** Answers a borrowed run's `ask_back` from the asking bot's context (answerBack). Without it the tool is not attached. */
156
- answer?: (question: string) => Promise<string>;
157
- emit: (event: TaskEvent) => Promise<void>;
104
+ caller: string;
105
+ owner: string;
106
+ contextBudget?: number;
107
+ notes?: () => Promise<string[]>;
108
+ send: (input: {
109
+ id: string;
110
+ to: string;
111
+ text: string;
112
+ kind?: "message" | "question";
113
+ options?: string[] | null;
114
+ replyTo?: string | null;
115
+ }) => Promise<unknown>;
116
+ emit: (event: ThreadEvent) => Promise<void>;
158
117
  };
159
118
 
160
- /**
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.
165
- */
166
119
  const MAX_STEPS = BOT_RUN.steps;
167
-
168
- /**
169
- * Where a run starts: a job's thread (the opening message first time, past
170
- * stretches plus new turns on resume), or an `ask_bot` brief plus `context`
171
- * from the asking bot (`askedBy`).
172
- */
173
- export type RunInput =
174
- | { bot: string; messages: ModelMessage[] }
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
- };
120
+ export type RunInput = { bot: string; messages: ModelMessage[] };
185
121
 
186
122
  export async function runBot(
187
123
  input: RunInput,
188
124
  options: RunOptions,
189
125
  ): Promise<void> {
190
- const depth = options.depth ?? 0;
191
126
  const parent = options.parent ?? null;
192
- const name = input.bot.trim();
127
+ const bot = await findJobBot(input.bot.trim());
128
+ const name = bot?.name ?? input.bot.trim();
193
129
  const emit = (event: BotEvent) =>
194
130
  options.emit({ ...event, bot: name, parent });
195
131
 
196
- const bot = await findJobBot(name);
197
132
  if (!bot) {
198
133
  await emit({
199
134
  type: "error",
200
135
  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",
136
+ retry: false,
213
137
  });
214
138
  return;
215
139
  }
140
+ const history = input.messages;
216
141
 
217
142
  // Tools are built on the model: web search runs on this bot's model (load-tools).
218
143
  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),
144
+ const row = options.threadId ? await findThread(options.threadId) : null;
145
+ // Participants share thread files while retaining their own context and browser.
146
+ const [scratch, { own, artifacts }] = await Promise.all([
147
+ options.threadId
148
+ ? openJobScratch(options.threadId, row?.label ?? "job")
149
+ : null,
150
+ openBotFolders(name),
226
151
  ]);
227
152
  const [prompt, tools] = await Promise.all([
228
153
  loadBotPrompt(
229
154
  name,
230
155
  bot.systemPrompt,
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 },
156
+ { owner: options.owner, caller: options.caller, messageId: parent },
157
+ { scratch, own, artifacts },
239
158
  ),
240
159
  loadTools({
241
160
  target: "bot",
242
161
  bot: name,
243
- // A borrowed bot drives a browser session of its own; everyone else the job's
244
- session: options.session ?? options.taskId,
162
+ session:
163
+ options.session ??
164
+ (options.threadId ? botBrowserSession(options.threadId, name) : null),
245
165
  model,
246
166
  }),
247
167
  ]);
248
168
  // 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.
169
+ // compactBudget), and never above where this participant last fit: a context the
170
+ // model refused as too long lowers its own number (room.query
171
+ // lowerRoomContextBudget). The thread row's budget only draws the meter.
252
172
  const budget = Math.min(
253
173
  await compactBudget(model.ref, bot.compactAt),
254
- ("askedBy" in input ? 0 : row?.contextBudget) || Number.POSITIVE_INFINITY,
174
+ options.contextBudget || Number.POSITIVE_INFINITY,
255
175
  );
256
176
 
257
- const history: ModelMessage[] =
258
- "messages" in input
259
- ? input.messages
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
- ];
271
-
272
- /** Remaining `ask_back` calls for a borrowed bot; the tool counts it down. */
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;
278
- const agentTools =
279
- depth < BOT_RUN.depth
280
- ? withAskBot(seatTools, {
281
- name,
282
- model: model.model,
283
- instructions: prompt.text,
284
- peers: prompt.peers,
285
- depth,
286
- chain: chainOf(history[0]),
287
- above: "askedBy" in input ? input.above : [],
288
- options,
289
- emit,
290
- })
291
- : seatTools;
292
-
293
- /**
294
- * A compaction decided in `prepareStep` is emitted from the loop at
295
- * `start-step`, not from the callback: the sdk prepares step N+1 while the
296
- * loop may still be reading step N, and the row must sit between the two
297
- * steps (that position is what a resume restarts from, task.query listThread).
298
- */
299
- const pending: {
300
- compact: Extract<BotEvent, { type: "compact" }> | null;
301
- /** Emitted at the same place, after the compaction. */
302
- interjections: string[];
303
- } = { compact: null, interjections: [] };
304
-
305
- /** Stops the stream when the model goes quiet (config BOT_RUN.silenceMs). */
177
+ // A committed question to the user ends the turn; the answer brings the bot back (room.query tellRoom).
178
+ let asked = false;
179
+ const agentTools: ToolSet = {
180
+ ...tools,
181
+ [TOOL_NAMES.send_message]: tool({
182
+ description: sendMessageSpec.description,
183
+ inputSchema: sendMessageSpec.parameters,
184
+ execute: async (input, call) => {
185
+ const receipt = await options.send({ ...input, id: call.toolCallId });
186
+ if (input.kind === "question") asked = true;
187
+ return receipt;
188
+ },
189
+ }),
190
+ };
191
+ // Persist local calls before their side effects, and results before another model step.
192
+ // The stream can repeat these events; the writer deduplicates them by call ID.
193
+ const inFlight = new Set<Promise<unknown>>();
306
194
  const quiet = silenceWatch(BOT_RUN.silenceMs);
195
+ for (const [toolName, held] of Object.entries(agentTools)) {
196
+ const execute = held.execute;
197
+ if (!execute) continue;
198
+ held.execute = (args, call) => {
199
+ const execution = (async () => {
200
+ options.signal?.throwIfAborted();
201
+ await emit({
202
+ type: "tool",
203
+ id: call.toolCallId,
204
+ name: toolName,
205
+ input: args,
206
+ });
207
+ quiet.hold();
208
+ try {
209
+ options.signal?.throwIfAborted();
210
+ let output = await execute(args, call);
211
+ if (
212
+ output &&
213
+ typeof output === "object" &&
214
+ Symbol.asyncIterator in output
215
+ ) {
216
+ let last: unknown;
217
+ for await (const value of output as AsyncIterable<unknown>)
218
+ last = value;
219
+ output = last;
220
+ }
221
+ await emit({
222
+ type: "tool-result",
223
+ id: call.toolCallId,
224
+ name: toolName,
225
+ output,
226
+ });
227
+ return output;
228
+ } catch (cause) {
229
+ await emit({
230
+ type: "tool-result",
231
+ id: call.toolCallId,
232
+ name: toolName,
233
+ output: modelErrorToString(cause),
234
+ error: true,
235
+ });
236
+ throw cause;
237
+ } finally {
238
+ quiet.release();
239
+ }
240
+ })();
241
+ inFlight.add(execution);
242
+ void execution.finally(() => inFlight.delete(execution)).catch(() => {});
243
+ return execution;
244
+ };
245
+ }
246
+
247
+ // The SDK can prepare the next step before the consumer has stored this one.
248
+ let writtenStep: Promise<void> = Promise.resolve();
249
+ let acknowledgeStep = () => {};
307
250
  /** Context size the latest step went out at; an overflow shrinks the budget from it. */
308
251
  let sent = 0;
309
252
 
@@ -311,26 +254,11 @@ export async function runBot(
311
254
  model: model.model,
312
255
  instructions: prompt.text,
313
256
  tools: agentTools,
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
316
- // no execute, but invalid arguments come back as a tool-error and the loop
317
- // would go on, so the stop is on the call itself.
318
- stopWhen: [
319
- stepCountIs(MAX_STEPS),
320
- answerAccepted_,
321
- hasToolCall(TOOL_NAMES.ask_thursday),
322
- ],
257
+ stopWhen: [stepCountIs(MAX_STEPS), () => asked],
323
258
  prepareStep: async ({ stepNumber, steps, messages }) => {
324
- let step = lastStep(stepNumber);
325
- // Remove `ask_back` once spent. The last step is already narrowed to `answer`.
326
- if (!step.activeTools && asks.left <= 0) {
327
- step = {
328
- ...step,
329
- activeTools: Object.keys(agentTools).filter(
330
- (held) => held !== TOOL_NAMES.ask_back,
331
- ),
332
- };
333
- }
259
+ await writtenStep;
260
+ options.signal?.throwIfAborted();
261
+ const step: NonNullable<PrepareStepResult<ToolSet>> = {};
334
262
  // The provider's input count includes instructions and tool schemas the
335
263
  // estimate cannot see; the estimate covers a provider that reports none.
336
264
  const measured = steps.at(-1)?.usage.inputTokens ?? 0;
@@ -351,21 +279,20 @@ export async function runBot(
351
279
  );
352
280
  // Which files the job has is read off its rows and the disk, never asked of the model
353
281
  const text = `${summary.text}${await filesUnder(row?.id ?? null, messages, scratch)}`;
354
- pending.compact = {
282
+ await emit({
355
283
  type: "compact",
356
284
  text,
357
285
  usage: summary.usage,
358
286
  messages: messages.length,
359
287
  tokens: size,
360
- };
288
+ });
361
289
  next = [messages[0], { role: "user", content: text }];
362
290
  }
363
291
 
364
292
  // Notes said during the last step go in as user turns after everything
365
293
  // the model has (and after the summary). Not on the first step.
366
- const notes = stepNumber > 0 ? (options.notes?.() ?? []) : [];
294
+ const notes = stepNumber > 0 ? ((await options.notes?.()) ?? []) : [];
367
295
  if (notes.length) {
368
- pending.interjections.push(...notes);
369
296
  next = [
370
297
  ...next,
371
298
  ...notes.map((text) => ({ role: "user" as const, content: text })),
@@ -387,62 +314,74 @@ export async function runBot(
387
314
  ? AbortSignal.any([options.signal, quiet.signal])
388
315
  : quiet.signal;
389
316
  quiet.touch();
390
- const result = await agent.stream({
391
- messages: history,
392
- abortSignal: stop,
393
- onStepEnd: (step) =>
394
- steps.push({
395
- messages: step.response.messages,
396
- usage: usageOf(step.usage),
397
- }),
398
- });
317
+ const execution = new AbortController();
318
+ const signal = AbortSignal.any([stop, execution.signal]);
319
+ let result;
320
+ try {
321
+ result = await agent.stream({
322
+ messages: history,
323
+ abortSignal: signal,
324
+ onStepEnd: (step) => {
325
+ writtenStep = new Promise<void>((resolve) => {
326
+ acknowledgeStep = resolve;
327
+ });
328
+ steps.push({
329
+ messages: step.response.messages,
330
+ usage: usageOf(step.usage),
331
+ });
332
+ },
333
+ });
334
+ } catch (cause) {
335
+ quiet.end();
336
+ execution.abort();
337
+ await Promise.allSettled([...inFlight]);
338
+ throw cause;
339
+ }
399
340
 
400
341
  let writing = "";
401
- /** The last chunk of prose; used only if the run ends without an answer. */
402
- let last: string | null = null;
403
- /** The `ask_thursday` call the loop stopped on. */
404
- let asked: { id: string; question: string; options: string[] } | null = null;
405
- /** The answer (bot.tool answerSpec). */
406
- let answered: { text: string; stopped: boolean } | null = null;
407
- /** Index of the step being read; `lastStep` narrows the one at MAX_STEPS - 1. */
408
- let stepAt = -1;
342
+ /** Only prose from the final step is returned to the correspondent. */
343
+ let last = "";
409
344
  /** Why the latest step ended; prose cut off at the output limit is not an answer. */
410
345
  let finish: FinishReason | null = null;
411
- /** Tool calls still running: the model is not expected to send anything meanwhile. */
412
- const working = new Set<string>();
413
346
 
414
- /** A break as the runner takes it; an overflow carries where to compact next time. */
347
+ /**
348
+ * Every way the stream breaks, as the runner takes it: the words for the row, and
349
+ * where to compact next time when the context was refused as too long. The row
350
+ * keeps only the words, so the whole cause is logged here.
351
+ */
415
352
  const failed = (cause: unknown): Extract<BotEvent, { type: "error" }> => {
416
- const failure = modelFailureOf(cause);
353
+ logger.error(`${name}: the model run broke`, cause);
417
354
  const message = modelErrorToString(cause);
418
- if (failure !== "overflow") return { type: "error", message, failure };
355
+ const retry = !isProviderRefusal(cause);
356
+ if (!isContextOverflow(cause)) return { type: "error", message, retry };
419
357
  const shrunk = Math.floor(
420
358
  Math.min(budget, sent || budget) * BOT_RUN.overflowShrink,
421
359
  );
422
360
  return {
423
361
  type: "error",
424
362
  message,
425
- failure,
426
363
  budget: Math.max(COMPACT_AT_MIN, shrunk),
364
+ retry,
427
365
  };
428
366
  };
429
367
 
430
368
  // An abort ends the stream without a finish; release any waiting take.
431
- stop.addEventListener("abort", () => steps.end(), { once: true });
369
+ stop.addEventListener(
370
+ "abort",
371
+ () => {
372
+ steps.end();
373
+ acknowledgeStep();
374
+ },
375
+ { once: true },
376
+ );
432
377
  try {
433
378
  for await (const part of result.fullStream) {
434
379
  // Anything at all is the model still there
435
380
  quiet.touch();
436
381
  switch (part.type) {
437
382
  case "start-step":
438
- stepAt += 1;
439
- if (pending.compact) {
440
- await emit(pending.compact);
441
- pending.compact = null;
442
- }
443
- for (const text of pending.interjections.splice(0)) {
444
- await emit({ type: "interjection", text });
445
- }
383
+ last = "";
384
+ writing = "";
446
385
  break;
447
386
 
448
387
  case "text-delta":
@@ -453,62 +392,24 @@ export async function runBot(
453
392
  const text = writing.trim();
454
393
  writing = "";
455
394
  if (!text) break;
456
- last = text;
395
+ last = [last, text].filter(Boolean).join("\n\n");
457
396
  await emit({ type: "text", text });
458
397
  break;
459
398
  }
460
399
 
461
400
  case "tool-call": {
462
- if (part.toolName === TOOL_NAMES.ask_thursday) {
463
- const args = part.input as {
464
- question?: unknown;
465
- options?: unknown;
466
- };
467
- asked = {
468
- id: part.toolCallId,
469
- question: String(args.question ?? ""),
470
- options: optionsOf(args.options),
471
- };
472
- } else if (part.toolName === TOOL_NAMES.answer) {
473
- const args = part.input as { result?: unknown };
474
- answered = {
475
- text: String(args.result ?? "").trim(),
476
- // The last step offers nothing but `answer`: an answer there is the cap's doing
477
- stopped: stepAt >= MAX_STEPS - 1,
478
- };
479
- }
480
- // A tool at work sends nothing, and bounds itself (bash, connected
481
- // tools, a borrowed bot's own watch); `ask_thursday` never runs
482
- if (
483
- !part.providerExecuted &&
484
- part.toolName !== TOOL_NAMES.ask_thursday
485
- ) {
486
- working.add(part.toolCallId);
487
- quiet.hold();
488
- }
489
401
  await emit({
490
402
  type: "tool",
491
403
  id: part.toolCallId,
492
404
  name: part.toolName,
493
405
  input: part.input,
406
+ providerExecuted: part.providerExecuted,
407
+ providerOptions: part.providerMetadata,
494
408
  });
495
409
  break;
496
410
  }
497
411
 
498
412
  case "tool-result":
499
- if (working.delete(part.toolCallId)) quiet.release();
500
- // A refused answer is not the ending, unless the step cap ends the run
501
- // on this step; then the text still reaches the user, as a stop.
502
- if (
503
- part.toolName === TOOL_NAMES.answer &&
504
- answered &&
505
- !answerAccepted(part.output)
506
- ) {
507
- answered = {
508
- text: `${answered.text}\n\n${outputText(part.output)}`,
509
- stopped: true,
510
- };
511
- }
512
413
  // A provider-executed tool (web search) lands here too; the step's own
513
414
  // messages decide where it belongs (bot.runner).
514
415
  await emit({
@@ -520,7 +421,6 @@ export async function runBot(
520
421
  break;
521
422
 
522
423
  case "tool-error":
523
- if (working.delete(part.toolCallId)) quiet.release();
524
424
  // The model reads it and carries on; the row shows it now rather than
525
425
  // when the step ends
526
426
  await emit({
@@ -536,18 +436,16 @@ export async function runBot(
536
436
  finish = part.finishReason;
537
437
  const step = await steps.take();
538
438
  if (step) await emit({ type: "step", ...step, budget });
439
+ acknowledgeStep();
539
440
  break;
540
441
  }
541
442
 
542
443
  case "abort":
543
444
  // The job was stopped: whoever stopped it writes the row (bot.runner)
544
445
  if (options.signal?.aborted) return;
545
- // Otherwise the model went quiet (silenceWatch)
546
- await emit({
547
- type: "error",
548
- message: part.reason ?? "The model stopped sending.",
549
- failure: "transient",
550
- });
446
+ // Otherwise the model went quiet (silenceWatch). The sdk types `reason` as
447
+ // a string but hands over the signal's reason, the watch's DOMException.
448
+ await emit(failed(part.reason ?? "The model stopped sending."));
551
449
  return;
552
450
 
553
451
  case "error":
@@ -559,8 +457,11 @@ export async function runBot(
559
457
  }
560
458
  }
561
459
  } finally {
460
+ execution.abort();
562
461
  steps.end();
563
462
  quiet.end();
463
+ acknowledgeStep();
464
+ await Promise.allSettled([...inFlight]);
564
465
  }
565
466
 
566
467
  try {
@@ -572,59 +473,26 @@ export async function runBot(
572
473
  return;
573
474
  }
574
475
 
575
- // Asking wins over answering: the loop halts on the ask.
576
- if (asked) {
577
- await emit({ type: "waiting", ...asked });
578
- return;
579
- }
580
-
581
- if (answered) {
582
- await emit({ type: "answer", ...answered });
583
- return;
584
- }
585
-
586
476
  // The provider's filter stopped the answer: there is nothing to continue past
587
477
  if (finish === "content-filter") {
588
478
  await emit({
589
479
  type: "error",
590
480
  message: "The provider's content filter stopped the response.",
591
- failure: "fatal",
481
+ retry: false,
592
482
  });
593
483
  return;
594
484
  }
595
485
 
596
- // The sdk stops on a step with no tool call regardless of `stopWhen`. Prose
597
- // there is an answer given without the tool and ends the job like one;
598
- // nothing at all, prose cut off at the output limit, or prose on the step the
599
- // cap narrowed to `answer`, is a stop — continuing picks up where it broke off.
600
486
  await emit({
601
- type: "answer",
602
- text: last ?? "Stopped without handing anything back.",
603
- stopped: last === null || finish === "length" || stepAt >= MAX_STEPS - 1,
487
+ type: "turn-end",
488
+ text: last,
489
+ // A turn that asked the user ends on its tool call by design, not cut off
490
+ stopped:
491
+ !asked &&
492
+ (finish === "length" || finish === "error" || finish === "tool-calls"),
604
493
  });
605
494
  }
606
495
 
607
- /** The last step ended on an answer the tool accepted. `hasToolCall` would also stop on a refused one. */
608
- const answerAccepted_ = ({ steps }: { steps: StepResult<ToolSet>[] }) =>
609
- steps
610
- .at(-1)
611
- ?.toolResults.some(
612
- (result) =>
613
- result.toolName === TOOL_NAMES.answer && answerAccepted(result.output),
614
- ) ?? false;
615
-
616
- /**
617
- * The step before the cap is narrowed to `answer` and required to call it,
618
- * so a run out of budget ends with something a person can act on.
619
- */
620
- function lastStep(stepNumber: number): NonNullable<PrepareStepResult<ToolSet>> {
621
- if (stepNumber < MAX_STEPS - 1) return {};
622
- return {
623
- activeTools: [TOOL_NAMES.answer],
624
- toolChoice: { type: "tool", toolName: TOOL_NAMES.answer },
625
- };
626
- }
627
-
628
496
  /** Rough token weight of a message list. The provider's own count wins when it reports one (prepareStep). */
629
497
  function sizeOf(messages: ModelMessage[]): number {
630
498
  return messages.reduce(
@@ -643,11 +511,11 @@ function sizeOf(messages: ModelMessage[]): number {
643
511
  /** Instructions for compacting. The summary replaces everything above it, opening included, so it must restate the job. */
644
512
  const compactInstructions = (
645
513
  words: number,
646
- ) => `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.
514
+ ) => `Compact your context. The first message above — who handed you the job, and the job — 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.
647
515
 
648
516
  Cover all of it, and prefer a fact over a description of a fact:
649
517
 
650
- - **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.
518
+ - **Where the job stands against that first message.** Do not restate the job; say what has changed since — a choice made, a detail settled, an answer that came back, a correction, anything you are still waiting on.
651
519
  - **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.
652
520
  - **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.
653
521
  - **Files.** Every path you wrote or read, and one line on what each holds.
@@ -672,19 +540,19 @@ const FILES_HEAD =
672
540
 
673
541
  /**
674
542
  * The files a job has on disk, as lines under its compaction summary. Read off the
675
- * job's rows — every `write_file`, its borrowed bots' included — and off its folder,
543
+ * job's rows — every participant's `write_file` — and off its folder,
676
544
  * never asked of the model: a path a summary leaves out is work the next steps redo.
677
- * A borrowed run has no rows of its own and reads the messages it is compacting.
545
+ * Without a thread, the run reads only the messages it is compacting.
678
546
  * A listing that fails costs the list, never the compaction.
679
547
  */
680
548
  async function filesUnder(
681
- taskId: string | null,
549
+ threadId: string | null,
682
550
  messages: ModelMessage[],
683
551
  folder: string | null,
684
552
  ): Promise<string> {
685
553
  try {
686
- const written = taskId
687
- ? await listWrittenPaths(taskId)
554
+ const written = threadId
555
+ ? await listWrittenPaths(threadId)
688
556
  : writtenPathsIn(messages.map((message) => message.content));
689
557
  const files = await filesOnDisk(written, folder);
690
558
  if (!files.length) return "";
@@ -703,12 +571,11 @@ async function filesUnder(
703
571
  * The model summarizes its own context and continues from the summary. Tools
704
572
  * are passed with calling off: providers refuse a history of tool calls
705
573
  * without the tools that made them. The run's own instructions go too: the
706
- * summary is written by the bot the thread belongs to, and the provider's
574
+ * summary is written by the bot the transcript belongs to, and the provider's
707
575
  * cached prefix, instructions first, still matches the run's. A context too
708
576
  * long to summarise whole — the very thing a compaction is for — is tried once
709
577
  * more without the tool calls and results but the last few. A failure throws
710
- * with its cause kept: the runner parks what a retry or a smaller budget can
711
- * fix (bot.runner parkTask) and fails the rest; the thread stays.
578
+ * with its cause kept. The room pauses for manual recovery; the transcript stays.
712
579
  */
713
580
  async function compact(
714
581
  model: LanguageModel,
@@ -723,8 +590,8 @@ async function compact(
723
590
  if (options.signal?.aborted) throw cause;
724
591
  failure = cause;
725
592
  }
726
- if (modelFailureOf(failure) === "overflow") {
727
- // pruneMessages drops a call together with its result, so the thread stays whole
593
+ if (isContextOverflow(failure)) {
594
+ // pruneMessages drops a call together with its result, so the transcript stays whole
728
595
  const lighter = pruneMessages({
729
596
  messages,
730
597
  toolCalls: "before-last-4-messages",
@@ -800,7 +667,7 @@ function stepQueue() {
800
667
  /**
801
668
  * No more steps are coming; release waiting takes with `null`. Without
802
669
  * this, a `finish-step` whose `onStepEnd` never fires (abort, provider
803
- * error mid-step) parks the loop forever, and `answerTask` waits on
670
+ * error mid-step) parks the loop forever, and `answerThread` waits on
804
671
  * `run.done` while holding the job's lock (bot.runner).
805
672
  */
806
673
  end() {
@@ -815,7 +682,7 @@ function stepQueue() {
815
682
  * arrived for that long. Held while something other than the model does the
816
683
  * work — a tool, a compaction — since those send nothing and bound themselves;
817
684
  * `touch` starts it over. Not the sdk's `timeout.chunkMs`: that clock keeps
818
- * running while tools execute, so a long command or a borrowed bot's whole run
685
+ * running while tools execute, so a long command
819
686
  * would trip it.
820
687
  */
821
688
  function silenceWatch(ms: number) {
@@ -869,311 +736,107 @@ function resolveModel(bot: JobBot) {
869
736
  return resolveDefaultModel().then(getTextModel);
870
737
  }
871
738
 
872
- /** A whole run as one string; this is all `ask_bot` receives. A stopped run still comes back, marked as such. */
873
- export async function runBotToText(
874
- input: Extract<RunInput, { request: string }>,
875
- options: RunOptions,
876
- ): Promise<string> {
877
- let outcome = "";
878
- let failure: string | null = null;
879
- await runBot(input, {
880
- ...options,
881
- emit: async (event) => {
882
- if (event.type === "answer") {
883
- outcome = event.stopped
884
- ? `${event.text}\n\n(As far as they got — they stopped before finishing.)`
885
- : event.text;
886
- }
887
- if (event.type === "error") failure = event.message;
888
- await options.emit(event);
889
- },
890
- });
891
- if (failure) publicError(failure);
892
- return outcome;
893
- }
894
-
895
- /**
896
- * `ask_bot` gets its execute here, not in loadTools (loadTools importing this
897
- * file would be a cycle). With no peers the tool is absent rather than refusing.
898
- * The borrowed bot's events go to the same thread under this call's id, and
899
- * `answer` serves its `ask_back` from `call.messages`, the context that
900
- * produced this call. Exchanges ride back inside the result so this thread
901
- * keeps them.
902
- */
903
- function withAskBot(
904
- tools: ToolSet,
905
- asker: {
906
- name: string;
907
- model: LanguageModel;
908
- instructions: string;
909
- /** Bots it can call; itself excluded (bot.prompt). */
910
- peers: string[];
911
- depth: number;
912
- /** The chain this bot inherited, handed down under the part it hands over. */
913
- chain: string;
914
- /** Bots above this one on the job, which are blocked waiting on it. */
915
- above: string[];
916
- options: RunOptions;
917
- emit: (event: BotEvent) => Promise<void>;
918
- },
919
- ): ToolSet {
920
- if (asker.peers.length === 0) return tools;
921
-
922
- const agentTools: ToolSet = {
923
- ...tools,
924
- [TOOL_NAMES.ask_bot]: tool({
925
- description: askBotSpec.description,
926
- inputSchema: askBotSpec.parameters,
927
- execute: async ({ bot, request, context }, call) => {
928
- // A bot above is blocked waiting on this very call; borrowing it back never returns
929
- if (bot === asker.name || asker.above.includes(bot)) {
930
- return `${bot} is already on this job, above you and waiting on your part. Pick another bot, or do it yourself.`;
931
- }
932
- // The borrowed bot works in the job's folder but drives a browser session
933
- // of its own, under this call's id. When it answers, the same rule as a
934
- // job's end: what it showed on their screen stays, what nobody can see
935
- // closes (workspace.ts closeHiddenBrowser).
936
- const session = asker.options.taskId
937
- ? `${asker.options.session ?? asker.options.taskId}-${call.toolCallId.slice(0, 8)}`
938
- : null;
939
- const exchanges: { question: string; answer: string }[] = [];
940
- const answer = async (question: string) => {
941
- const reply = await answerBack({
942
- model: asker.model,
943
- instructions: asker.instructions,
944
- // Same tools, calling off (see compact).
945
- tools: agentTools,
946
- messages: call.messages,
947
- asked: { bot, request },
948
- question,
949
- signal: asker.options.signal,
950
- });
951
- await asker.emit({ type: "answered", question, ...reply });
952
- exchanges.push({ question, answer: reply.text });
953
- return reply.text;
954
- };
955
- try {
956
- const outcome = await runBotToText(
957
- {
958
- bot,
959
- request,
960
- context,
961
- askedBy: asker.name,
962
- chain: asker.chain,
963
- above: [...asker.above, asker.name],
964
- },
965
- {
966
- ...asker.options,
967
- // This call's signal, not the job's: a stream that ends — the job
968
- // stopping, or the model going quiet — ends the part it waited on
969
- signal: call.abortSignal ?? asker.options.signal,
970
- depth: asker.depth + 1,
971
- parent: call.toolCallId,
972
- session,
973
- answer,
974
- },
975
- );
976
- return exchanges.length
977
- ? `${outcome}\n\n${exchangeLines(bot, exchanges)}`
978
- : outcome;
979
- } finally {
980
- if (session) void closeHiddenBrowser(session);
981
- }
982
- },
983
- }),
984
- };
985
- return agentTools;
986
- }
987
-
988
- /** Appended under the answer: what the borrowed bot asked and what this bot answered. */
989
- function exchangeLines(
990
- bot: string,
991
- exchanges: { question: string; answer: string }[],
992
- ): string {
993
- return exchanges
994
- .map(
995
- ({ question, answer }) =>
996
- `(On the way ${bot} asked you: "${question}" — you answered: "${answer}")`,
997
- )
998
- .join("\n");
999
- }
1000
-
1001
- /**
1002
- * A borrowed bot's tools: `ask_thursday` removed (the bot above is blocked on
1003
- * this run, so a wait here never resolves), `ask_back` added when there is an
1004
- * `answer`. prepareStep removes it once `asks.left` reaches zero.
1005
- */
1006
- function withAskBack(
1007
- tools: ToolSet,
1008
- asks: { left: number },
1009
- options: RunOptions,
1010
- ): ToolSet {
1011
- const own = { ...tools };
1012
- delete own[TOOL_NAMES.ask_thursday];
1013
- const { answer } = options;
1014
- if (!answer) return own;
1015
- return {
1016
- ...own,
1017
- [TOOL_NAMES.ask_back]: tool({
1018
- description: askBackSpec.description,
1019
- inputSchema: askBackSpec.parameters,
1020
- execute: async ({ question }) => {
1021
- asks.left -= 1;
1022
- return answer(question);
1023
- },
1024
- }),
1025
- };
1026
- }
1027
-
1028
- /** Prompt for answering a borrowed bot's `ask_back`. The blocked call is restated because the sdk's `messages` stop just before it. */
1029
- const answerInstructions = (
1030
- asked: { bot: string; request: string },
1031
- question: string,
1032
- ) => `You handed one part of your job to ${asked.bot}:
1033
-
1034
- > ${asked.request.trim()}
1035
-
1036
- Before going on, they ask you:
1037
-
1038
- > ${question.trim()}
1039
-
1040
- Answer from what you already have — the request as it reached you, the call it came out of, what you found before asking them. A few lines, plain text, and nothing else: no tools run here. If it is not in what you have, say so rather than guess, and say which way to go without it.`;
1041
-
1042
- /**
1043
- * One call, no loop: the asking bot's model over its own context plus the
1044
- * question. A provider failure returns a fallback text instead of throwing;
1045
- * only an abort propagates.
1046
- */
1047
- async function answerBack(input: {
1048
- model: LanguageModel;
1049
- instructions: string;
1050
- tools: ToolSet;
1051
- messages: ModelMessage[];
1052
- asked: { bot: string; request: string };
1053
- question: string;
1054
- signal?: AbortSignal;
1055
- }): Promise<{ text: string; usage: TokenUsage }> {
1056
- try {
1057
- const { text, usage } = await generateText({
1058
- model: input.model,
1059
- system: input.instructions,
1060
- tools: input.tools,
1061
- toolChoice: "none",
1062
- messages: [
1063
- ...input.messages,
1064
- {
1065
- role: "user",
1066
- content: answerInstructions(input.asked, input.question),
1067
- },
1068
- ],
1069
- abortSignal: input.signal,
1070
- // One-shot: nothing streams back, so its whole length is one silence
1071
- timeout: BOT_RUN.silenceMs,
1072
- });
1073
- return {
1074
- text:
1075
- text.trim() ||
1076
- "They had nothing to add. Go on with your best reading, and say what you assumed in what you hand back.",
1077
- usage: usageOf(usage),
1078
- };
1079
- } catch (cause) {
1080
- if (input.signal?.aborted) throw cause;
1081
- logger.warn(`ask_back: ${modelErrorToString(cause)}`);
1082
- return {
1083
- text: `No answer came back (${modelErrorToString(cause)}). Go on with your best reading, and say what you assumed in what you hand back.`,
1084
- usage: NO_TOKENS,
1085
- };
1086
- }
1087
- }
1088
-
1089
- /**
1090
- * The stored thread as a resuming run reads it: the rows as they were written,
1091
- * each tool call with its result, so a resumed run carries on the conversation
1092
- * it was having and a provider's prefix cache still matches from its first call.
1093
- * The one repair is a call whose result never arrived — the run was stopped
1094
- * between the two (a closed browser, a restart, a model that went quiet) and a
1095
- * call with no result is refused outright — which gets one saying so, right
1096
- * after it. An `ask_bot` call cut off that way carries what the borrowed bot
1097
- * had written by then, so its part is picked up rather than asked for again
1098
- * from nothing. Nothing else changes: `compact` is the one thing that shortens
1099
- * a bot's own thread.
1100
- */
1101
- export function resumeThread(
1102
- thread: ModelMessage[],
1103
- /** Borrowed bots' last lines, by `ask_bot` call id (task.query listBorrowedTails). */
1104
- borrowed: ReadonlyMap<string, { bot: string; tail: string }> = new Map(),
739
+ /** Build a valid model projection without changing the recorded interruption history. */
740
+ export function resumeTranscript(
741
+ transcript: ModelMessage[],
742
+ receipts: ReadonlyMap<string, { messageId: string; to: string }> = new Map(),
1105
743
  ): ModelMessage[] {
1106
- const answered = new Set<string>();
1107
- for (const message of thread) {
1108
- // A provider-run tool (web search) answers inside the assistant message
744
+ type Result = Extract<
745
+ Extract<ModelMessage, { role: "tool" }>["content"][number],
746
+ { type: "tool-result" }
747
+ >;
748
+ const results = new Map<string, Result>();
749
+ const embedded = new Set<string>();
750
+ for (const message of transcript) {
1109
751
  if (message.role !== "tool" && message.role !== "assistant") continue;
1110
- if (typeof message.content === "string") continue;
752
+ if (!Array.isArray(message.content)) continue;
1111
753
  for (const part of message.content) {
1112
- if (part.type === "tool-result") answered.add(part.toolCallId);
754
+ if (part.type !== "tool-result") continue;
755
+ if (!results.has(part.toolCallId)) results.set(part.toolCallId, part);
756
+ if (message.role === "assistant") embedded.add(part.toolCallId);
1113
757
  }
1114
758
  }
1115
-
1116
759
  const out: ModelMessage[] = [];
1117
- let owed: Extract<ModelMessage, { role: "tool" }>["content"] = [];
1118
- for (const message of thread) {
1119
- if (owed.length && message.role === "tool") {
1120
- out.push({ ...message, content: [...owed, ...message.content] });
1121
- owed = [];
760
+ const used = new Set<string>();
761
+ for (const message of transcript) {
762
+ if (message.role === "tool") {
763
+ const other = message.content.filter(
764
+ (part) => part.type !== "tool-result",
765
+ );
766
+ if (other.length) out.push({ ...message, content: other });
1122
767
  continue;
1123
768
  }
1124
- if (owed.length) {
1125
- out.push({ role: "tool", content: owed });
1126
- owed = [];
1127
- }
1128
- out.push(message);
1129
769
  if (message.role !== "assistant" || typeof message.content === "string") {
770
+ out.push(message);
1130
771
  continue;
1131
772
  }
1132
- owed = message.content.flatMap((part) =>
1133
- part.type === "tool-call" && !answered.has(part.toolCallId)
1134
- ? [
1135
- {
773
+ const owed: Result[] = [];
774
+ const content: typeof message.content = [];
775
+ for (const part of message.content) {
776
+ if (part.type !== "tool-call") {
777
+ content.push(part);
778
+ continue;
779
+ }
780
+ if (used.has(part.toolCallId)) continue;
781
+ let input = part.input;
782
+ if (typeof input === "string") {
783
+ try {
784
+ input = JSON.parse(input);
785
+ } catch {
786
+ input = undefined;
787
+ }
788
+ }
789
+ if (!part.toolCallId || input === undefined) {
790
+ content.push({
791
+ type: "text",
792
+ text: "A tool argument stream was interrupted before a complete call was recorded. Inspect the saved work before continuing.",
793
+ });
794
+ continue;
795
+ }
796
+ const receipt =
797
+ part.toolName === TOOL_NAMES.send_message
798
+ ? receipts.get(part.toolCallId)
799
+ : undefined;
800
+ const result =
801
+ results.get(part.toolCallId) ??
802
+ (receipt
803
+ ? {
1136
804
  type: "tool-result" as const,
1137
805
  toolCallId: part.toolCallId,
1138
806
  toolName: part.toolName,
1139
- output: {
1140
- type: "text" as const,
1141
- value: interrupted(part, borrowed),
1142
- },
807
+ output: { type: "json" as const, value: receipt },
808
+ }
809
+ : undefined);
810
+ if (
811
+ part.providerExecuted &&
812
+ (!result || !embedded.has(part.toolCallId))
813
+ ) {
814
+ content.push({
815
+ type: "text",
816
+ text: result
817
+ ? `The provider operation ${part.toolName} returned this data before its native continuation was interrupted: ${JSON.stringify(result.output)}`
818
+ : `The provider operation ${part.toolName} was interrupted without a recorded result. Its remote execution state is unknown.`,
819
+ });
820
+ continue;
821
+ }
822
+ used.add(part.toolCallId);
823
+ content.push({ ...part, input });
824
+ if (!embedded.has(part.toolCallId))
825
+ owed.push(
826
+ result ?? {
827
+ type: "tool-result",
828
+ toolCallId: part.toolCallId,
829
+ toolName: part.toolName,
830
+ output: {
831
+ type: "text",
832
+ value:
833
+ "No result: execution was interrupted before a result was recorded. The outcome is unknown. Inspect the current state before repeating the action.",
1143
834
  },
1144
- ]
1145
- : [],
1146
- );
835
+ },
836
+ );
837
+ }
838
+ if (content.length) out.push({ ...message, content });
839
+ if (owed.length) out.push({ role: "tool", content: owed });
1147
840
  }
1148
- if (owed.length) out.push({ role: "tool", content: owed });
1149
841
  return out;
1150
842
  }
1151
-
1152
- /** What a resumed run reads where a call's result never arrived (resumeThread). */
1153
- const INTERRUPTED =
1154
- "No result: the run was stopped before this call returned. Check what it did before relying on it.";
1155
-
1156
- /** INTERRUPTED, or for a borrowed bot's part, what it had written when the run stopped. */
1157
- function interrupted(
1158
- call: { toolCallId: string; toolName: string },
1159
- borrowed: ReadonlyMap<string, { bot: string; tail: string }>,
1160
- ): string {
1161
- const cut =
1162
- call.toolName === TOOL_NAMES.ask_bot
1163
- ? borrowed.get(call.toolCallId)
1164
- : undefined;
1165
- if (!cut) return INTERRUPTED;
1166
- 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.`;
1167
- }
1168
-
1169
- /** Just the text out of the shape a tool result reached the model in (ai-sdk ToolResultOutput). */
1170
- function outputText(output: unknown): string {
1171
- if (typeof output === "string") return output;
1172
- const wrapped = output as { type?: string; value?: unknown } | null;
1173
- if (wrapped && typeof wrapped === "object" && "value" in wrapped) {
1174
- return typeof wrapped.value === "string"
1175
- ? wrapped.value
1176
- : JSON.stringify(wrapped.value ?? "");
1177
- }
1178
- return JSON.stringify(output ?? "");
1179
- }