thursday-agent 0.5.0 → 0.7.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 (667) 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 +5 -4
  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 +4 -4
  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 +15 -15
  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 +9 -10
  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 -5
  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 +11 -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]__0-06t3f._.js +8 -0
  92. package/.next/server/chunks/[root-of-the-server]__02846fb._.js +8 -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]__06l05n_._.js +3 -0
  96. package/.next/server/chunks/[root-of-the-server]__07bnah_._.js +3 -0
  97. package/.next/server/chunks/[root-of-the-server]__08eae20._.js +8 -0
  98. package/.next/server/chunks/[root-of-the-server]__0_ue8oe._.js +3 -0
  99. package/.next/server/chunks/[root-of-the-server]__0btqrct._.js +3 -0
  100. package/.next/server/chunks/[root-of-the-server]__0c9fgca._.js +5 -0
  101. package/.next/server/chunks/[root-of-the-server]__0h_x6q6._.js +5 -0
  102. package/.next/server/chunks/{[root-of-the-server]__0pa2n68._.js → [root-of-the-server]__0kxrvxf._.js} +2 -2
  103. package/.next/server/chunks/{[root-of-the-server]__0q0vvt2._.js → [root-of-the-server]__0ov3iir._.js} +2 -2
  104. package/.next/server/chunks/[root-of-the-server]__0qwtlmx._.js +8 -0
  105. package/.next/server/chunks/[root-of-the-server]__0sm5xvm._.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/_1gnjfli._.js +3 -0
  150. package/.next/server/chunks/_1kg70wc._.js +3 -3
  151. package/.next/server/chunks/_1klbcft._.js +5 -0
  152. package/.next/server/chunks/_1krd9rf._.js +4 -1
  153. package/.next/server/chunks/_1kwmzab._.js +3 -0
  154. package/.next/server/chunks/{_1pf4qe-._.js → _1oxj9fa._.js} +1 -1
  155. package/.next/server/chunks/{_1f46eie._.js → _1pcmdbb._.js} +1 -1
  156. package/.next/server/chunks/_1qgix-j._.js +3 -0
  157. package/.next/server/chunks/{lib_logger_ts_1exwjut._.js → _1rpchta._.js} +5 -5
  158. package/.next/server/chunks/_1vsej-s._.js +5 -0
  159. package/.next/server/chunks/_1yx0p_s._.js +5 -0
  160. package/.next/server/chunks/{_0rnf-6n._.js → _20fsr_z._.js} +6 -6
  161. package/.next/server/chunks/_2128hkj._.js +5 -0
  162. package/.next/server/chunks/_next-internal_server_app_api_bot_thread_[id]_route_actions_1fstjn2.js +3 -0
  163. package/.next/server/chunks/_next-internal_server_app_api_bot_thread_route_actions_003ccso.js +3 -0
  164. package/.next/server/chunks/_next-internal_server_app_api_workspace_missing_route_actions_1fidi6p.js +3 -0
  165. package/.next/server/chunks/config_ts_1g4sz5o._.js +1 -1
  166. package/.next/server/chunks/features_0-cnt0l._.js +4 -0
  167. package/.next/server/chunks/features_0hdaci0._.js +4 -0
  168. package/.next/server/chunks/features_1e39j7-._.js +8 -0
  169. package/.next/server/chunks/features_bot_bot_query_ts_12mgkl2._.js +1 -1
  170. package/.next/server/chunks/features_memory_1uqvksn._.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_0a7-pbw._.js} +1 -1
  172. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_17k8irj._.js → node_modules_consola_dist_chunks_prompt_mjs_0gzvgfv._.js} +1 -1
  173. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0i7cvml._.js → node_modules_consola_dist_chunks_prompt_mjs_107pxfn._.js} +1 -1
  174. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0icqaqw._.js → node_modules_consola_dist_chunks_prompt_mjs_15hbel8._.js} +1 -1
  175. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_18i-23e._.js +3 -0
  176. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1bewgf3._.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]__0-e011f._.js +3 -0
  185. package/.next/server/chunks/ssr/[root-of-the-server]__09q9med._.js +1 -1
  186. package/.next/server/chunks/ssr/[root-of-the-server]__0lyi8i2._.js +1 -1
  187. package/.next/server/chunks/ssr/[root-of-the-server]__0pd8yr7._.js +3 -0
  188. package/.next/server/chunks/ssr/[root-of-the-server]__16jc196._.js +1 -1
  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]__1mzi-ju._.js +1 -1
  192. package/.next/server/chunks/ssr/[root-of-the-server]__1nygfik._.js +5 -0
  193. package/.next/server/chunks/ssr/[root-of-the-server]__1sdphb0._.js +8 -0
  194. package/.next/server/chunks/ssr/[root-of-the-server]__1vdmfbe._.js +3 -0
  195. package/.next/server/chunks/ssr/[root-of-the-server]__1waa3rc._.js +85 -0
  196. package/.next/server/chunks/ssr/_0-2qsxo._.js +3 -0
  197. package/.next/server/chunks/ssr/_00rehl_._.js +3 -0
  198. package/.next/server/chunks/ssr/{_1lz_cq2._.js → _02oc_h5._.js} +76 -97
  199. package/.next/server/chunks/ssr/_05yghx6._.js +3 -0
  200. package/.next/server/chunks/ssr/_07nbwap._.js +3 -0
  201. package/.next/server/chunks/ssr/_08c4z-9._.js +3 -0
  202. package/.next/server/chunks/ssr/_0_qfdlw._.js +4 -0
  203. package/.next/server/chunks/ssr/{_0m2lsd1._.js → _0b6_jv-._.js} +2 -2
  204. package/.next/server/chunks/ssr/_0evkpto._.js +3 -0
  205. package/.next/server/chunks/ssr/_0geg212._.js +3 -0
  206. package/.next/server/chunks/ssr/{_1jdgjv9._.js → _0hxt2ul._.js} +2 -2
  207. package/.next/server/chunks/ssr/_0i_6q1w._.js +2 -2
  208. package/.next/server/chunks/ssr/_0lu1n_x._.js +1 -1
  209. package/.next/server/chunks/ssr/_0no7i1o._.js +3 -0
  210. package/.next/server/chunks/ssr/_0tzg--b._.js +3 -0
  211. package/.next/server/chunks/ssr/{_15j2_8f._.js → _0wzaht-._.js} +2 -2
  212. package/.next/server/chunks/ssr/_0z-g0uy._.js +3 -0
  213. package/.next/server/chunks/ssr/_114ylv7._.js +7 -0
  214. package/.next/server/chunks/ssr/_123y-ah._.js +3 -0
  215. package/.next/server/chunks/ssr/_12c-iwh._.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/_167hchk._.js +3 -0
  219. package/.next/server/chunks/ssr/{_0ap2fzn._.js → _16c6_xm._.js} +2 -2
  220. package/.next/server/chunks/ssr/_18opah7._.js +3 -0
  221. package/.next/server/chunks/ssr/_1_krfph._.js +3 -0
  222. package/.next/server/chunks/ssr/{_0rzucul._.js → _1_x5spz._.js} +3 -3
  223. package/.next/server/chunks/ssr/_1a61fc9._.js +3 -0
  224. package/.next/server/chunks/ssr/_1ey7qn-._.js +70 -0
  225. package/.next/server/chunks/ssr/_1g9vs7q._.js +3 -0
  226. package/.next/server/chunks/ssr/_1jmz33w._.js +3 -0
  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/_1w_t7h7._.js +101 -0
  230. package/.next/server/chunks/ssr/{_0s99bgb._.js → _1wyfaq_._.js} +9 -3
  231. package/.next/server/chunks/ssr/_200gt-m._.js +102 -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/features_workspace_00jz2hu._.js +8 -0
  237. package/.next/server/chunks/ssr/{_1-teee5._.js → lib_logger_ts_0-olsze._.js} +2 -7
  238. package/.next/server/chunks/ssr/{node_modules_05-n6dz._.js → node_modules_02ai7br._.js} +1 -1
  239. package/.next/server/chunks/ssr/{node_modules_1chvq5f._.js → node_modules_02deexj._.js} +1 -1
  240. package/.next/server/chunks/ssr/{node_modules_0m383r2._.js → node_modules_07za262._.js} +1 -1
  241. package/.next/server/chunks/ssr/{node_modules_07dsgkx._.js → node_modules_0_s9get._.js} +1 -1
  242. package/.next/server/chunks/ssr/{node_modules_1a0kmjl._.js → node_modules_0n3c7h0._.js} +1 -1
  243. package/.next/server/chunks/ssr/{node_modules_0zeg3x_._.js → node_modules_0od6jwr._.js} +1 -1
  244. package/.next/server/chunks/ssr/{node_modules_1y79i83._.js → node_modules_0rdt7pk._.js} +1 -1
  245. package/.next/server/chunks/ssr/{node_modules_17bwo43._.js → node_modules_0tusx4d._.js} +3 -3
  246. package/.next/server/chunks/ssr/{node_modules_04sksn9._.js → node_modules_0w1z7kz._.js} +1 -1
  247. package/.next/server/chunks/ssr/{node_modules_1uyk91w._.js → node_modules_0wdupfz._.js} +1 -1
  248. package/.next/server/chunks/ssr/{node_modules_0ifp389._.js → node_modules_1720w0a._.js} +1 -1
  249. package/.next/server/chunks/ssr/{node_modules_0u9da1h._.js → node_modules_18lsg7d._.js} +1 -1
  250. package/.next/server/chunks/ssr/{node_modules_1k6n600._.js → node_modules_1b94fxf._.js} +1 -1
  251. package/.next/server/chunks/ssr/{node_modules_20c28js._.js → node_modules_1ecdo1s._.js} +1 -1
  252. package/.next/server/chunks/ssr/{node_modules_0fkeku3._.js → node_modules_1fa9ag7._.js} +1 -1
  253. package/.next/server/chunks/ssr/{node_modules_1j8j4pt._.js → node_modules_1fv-_si._.js} +1 -1
  254. package/.next/server/chunks/ssr/{node_modules_0-dr_o7._.js → node_modules_1hf461g._.js} +1 -1
  255. package/.next/server/chunks/ssr/{node_modules_1lz0oxc._.js → node_modules_1m71ta-._.js} +1 -1
  256. package/.next/server/chunks/ssr/{node_modules_1t77e4n._.js → node_modules_1n93n-g._.js} +1 -1
  257. package/.next/server/chunks/ssr/{node_modules_16mkxuo._.js → node_modules_1n9lqu9._.js} +1 -1
  258. package/.next/server/chunks/ssr/{node_modules_1k7z2j6._.js → node_modules_1npnsvd._.js} +1 -1
  259. package/.next/server/chunks/ssr/{node_modules_056qgf-._.js → node_modules_1rn2r0n._.js} +1 -1
  260. package/.next/server/chunks/ssr/{node_modules_0x3-ply._.js → node_modules_1seuurl._.js} +2 -2
  261. package/.next/server/chunks/ssr/{node_modules_0bvopy2._.js → node_modules_1vq-mfk._.js} +1 -1
  262. package/.next/server/chunks/ssr/{node_modules_0iur59s._.js → node_modules_1vzyw1t._.js} +1 -1
  263. package/.next/server/chunks/ssr/{node_modules_1k8j8n0._.js → node_modules_1w359w4._.js} +1 -1
  264. package/.next/server/chunks/ssr/{node_modules_1x0ztf4._.js → node_modules_1yajbl6._.js} +1 -1
  265. package/.next/server/chunks/ssr/{node_modules_1-y7nni._.js → node_modules_20eqhy8._.js} +1 -1
  266. package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0bkwd0u._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_019sbis._.js} +1 -1
  267. package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0p9a9su._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0s6piq3._.js} +1 -1
  268. package/.next/server/chunks/ssr/{node_modules_@vercel_oidc_dist_1o553h-._.js → node_modules_@vercel_oidc_dist_05itbb1._.js} +1 -1
  269. package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_trash_mjs_0px2gzr._.js +3 -0
  270. package/.next/server/middleware-build-manifest.js +3 -3
  271. package/.next/server/pages/404.html +1 -1
  272. package/.next/server/pages/500.html +1 -1
  273. package/.next/server/server-reference-manifest.js +1 -1
  274. package/.next/server/server-reference-manifest.json +68 -46
  275. package/.next/static/chunks/07vqbk_9t4em4.js +2 -0
  276. package/.next/static/chunks/08krja1zx7nqg.js +1 -0
  277. package/.next/static/chunks/0g4b348l0pve7.js +1 -0
  278. package/.next/static/chunks/0qcuwdk132jjt.js +1 -0
  279. package/.next/static/chunks/0vlgzqo9ka90v.js +1 -0
  280. package/.next/static/chunks/{11r2yp_yh9_qn.js → 0z69gvysmave7.js} +1 -1
  281. package/.next/static/chunks/0zrupxgci5gqc.js +1 -0
  282. package/.next/static/chunks/12fvz1w5ows75.js +1 -0
  283. package/.next/static/chunks/14t03ned286iu.js +1 -0
  284. package/.next/static/chunks/156xxbkrz5lzu.js +18 -0
  285. package/.next/static/chunks/1fz14reod4lft.js +1 -0
  286. package/.next/static/chunks/1g23dbabl-vkr.js +1 -0
  287. package/.next/static/chunks/1ohslk4568tm3.js +1 -0
  288. package/.next/static/chunks/1qy_rc3oxfxok.js +1 -0
  289. package/.next/static/chunks/1sbmwswc9k2-1.js +1 -0
  290. package/.next/static/chunks/1vqezbhjm3goq.js +1 -0
  291. package/.next/static/chunks/1zea7qdl-7365.js +1 -0
  292. package/.next/static/chunks/1zvqumkg7kmo0.css +1 -0
  293. package/.next/static/chunks/26hpqt3y6bogl.js +12 -0
  294. package/.next/static/chunks/2_j5mqm6d-z18.js +99 -0
  295. package/.next/static/chunks/2dt6tu7a22svs.js +1 -0
  296. package/.next/static/chunks/2h5ls3d86kya6.js +1 -0
  297. package/.next/static/chunks/2wc6y1ht6dfuy.js +1 -0
  298. package/.next/static/chunks/{435atsnhs7ksu.js → 2xr0zpll7thyi.js} +2 -2
  299. package/.next/static/chunks/31jztc-yq30m7.js +1 -0
  300. package/.next/static/chunks/391af0i8eljzk.js +1 -0
  301. package/.next/static/chunks/3bfwpww3m3x4u.js +1 -0
  302. package/.next/static/chunks/{38gweqdo4jpfj.js → 3izg0a3k87q1-.js} +5 -5
  303. package/.next/static/chunks/3p2s2cjokx1aa.js +5 -0
  304. package/.next/static/chunks/{408ueu4hfvcg7.js → 41s3fml5oivsa.js} +1 -1
  305. package/README.md +45 -193
  306. package/app/api/events/app-event.ts +6 -4
  307. package/app/api/query-key.ts +25 -16
  308. package/bin/database.mjs +37 -0
  309. package/bin/thursday.mjs +44 -28
  310. package/components/ui/custom-icon.tsx +0 -60
  311. package/components/ui/radio-group.tsx +37 -0
  312. package/components/ui/shiny-text.tsx +33 -6
  313. package/database/migrations/{20260910173049_numerous_spirit → 20260916145701_clever_multiple_man}/migration.sql +80 -24
  314. package/database/migrations/{20260910173049_numerous_spirit → 20260916145701_clever_multiple_man}/snapshot.json +689 -110
  315. package/database/migrations/20260916160713_thread_cancelled/migration.sql +2 -0
  316. package/database/migrations/{20260911030320_opposite_jack_power → 20260916160713_thread_cancelled}/snapshot.json +706 -84
  317. package/docs/how-it-works.md +40 -35
  318. package/docs/images/keeps-going.png +0 -0
  319. package/docs/images/two-minds.png +0 -0
  320. package/docs/live-calls.md +236 -0
  321. package/docs/thread-rooms.md +71 -0
  322. package/features/ai/chatgpt.ts +1 -2
  323. package/features/ai/live.schema.ts +128 -0
  324. package/features/ai/load-tools.ts +152 -79
  325. package/features/ai/model.schema.ts +2 -120
  326. package/features/ai/model.ts +52 -88
  327. package/features/ai/prompts/bot.prompt.ts +91 -214
  328. package/features/ai/prompts/live.prompt.ts +169 -0
  329. package/features/ai/prompts/prompt-helper.ts +92 -32
  330. package/features/ai/prompts/thursday.prompt.ts +164 -190
  331. package/features/ai/tools/bot.tool.ts +38 -145
  332. package/features/ai/tools/call.tool.ts +33 -5
  333. package/features/ai/tools/connected.ts +5 -3
  334. package/features/ai/tools/mcp.tool.ts +7 -4
  335. package/features/ai/tools/memory.tool.ts +35 -31
  336. package/features/ai/tools/skills.tool.ts +11 -6
  337. package/features/ai/tools/studio.tool.ts +26 -21
  338. package/features/ai/tools/tool-name.ts +3 -5
  339. package/features/ai/tools/workspace.tool.ts +33 -10
  340. package/features/artifact/artifact.action.ts +2 -2
  341. package/features/artifact/artifact.query.ts +112 -61
  342. package/features/artifact/artifact.schema.ts +8 -6
  343. package/features/artifact/components/artifact-setting.tsx +83 -33
  344. package/features/bot/bot.action.ts +43 -36
  345. package/features/bot/bot.memory.ts +137 -20
  346. package/features/bot/bot.query.ts +28 -11
  347. package/features/bot/bot.run.ts +296 -744
  348. package/features/bot/bot.runner.ts +463 -864
  349. package/features/bot/bot.schema.ts +93 -82
  350. package/features/bot/bot.seed.ts +141 -64
  351. package/features/bot/components/bot-mark.tsx +324 -232
  352. package/features/bot/components/bot-room.tsx +1613 -697
  353. package/features/bot/components/bot-roster.tsx +8 -13
  354. package/features/bot/components/bot-setting.tsx +58 -42
  355. package/features/bot/components/bot-tool.tsx +55 -87
  356. package/features/bot/components/mark-palette.tsx +35 -32
  357. package/features/bot/components/path-chips.tsx +33 -3
  358. package/features/bot/components/thread-badge.tsx +34 -0
  359. package/features/bot/components/thread-reply.tsx +639 -0
  360. package/features/bot/components/thread-setting.tsx +674 -0
  361. package/features/bot/mark.const.ts +79 -31
  362. package/features/bot/room.query.ts +889 -0
  363. package/features/bot/room.schema.ts +64 -0
  364. package/features/bot/seed-bots.ts +2 -1
  365. package/features/bot/thread.query.ts +971 -0
  366. package/features/bot/thread.store.ts +587 -0
  367. package/features/config/components/voice-key.tsx +113 -88
  368. package/features/config/config.const.ts +5 -9
  369. package/features/connectors/mcp.schema.ts +0 -2
  370. package/features/intro/components/intro.tsx +169 -139
  371. package/features/memory/components/memory-setting.tsx +2 -7
  372. package/features/memory/memory.schema.ts +14 -23
  373. package/features/settings/components/setting-ui.tsx +0 -44
  374. package/features/settings/components/settings.tsx +12 -12
  375. package/features/settings/settings.alert.ts +3 -3
  376. package/features/settings/settings.store.ts +1 -1
  377. package/features/skills/skills.schema.ts +0 -2
  378. package/features/thursday/ascii.const.ts +74 -0
  379. package/features/thursday/components/ascii-field.tsx +24 -44
  380. package/features/thursday/components/ascii-orb.tsx +525 -381
  381. package/features/thursday/components/call-log.tsx +46 -44
  382. package/features/thursday/components/face.tsx +40 -10
  383. package/features/thursday/components/side-captions.tsx +273 -0
  384. package/features/thursday/components/tab-state.tsx +14 -6
  385. package/features/thursday/components/thursday-mark.tsx +1 -0
  386. package/features/thursday/components/thursday-setting.tsx +633 -333
  387. package/features/thursday/components/thursday.tsx +315 -274
  388. package/features/thursday/components/voice-picker.tsx +255 -0
  389. package/features/thursday/face.store.ts +5 -0
  390. package/features/thursday/thursday.action.ts +96 -72
  391. package/features/thursday/thursday.query.ts +49 -30
  392. package/features/thursday/thursday.schema.ts +37 -35
  393. package/features/thursday/thursday.store.ts +14 -7
  394. package/features/thursday/tool-call.ts +3 -3
  395. package/features/thursday/tool-line.ts +5 -3
  396. package/features/thursday/use-thursday.ts +801 -457
  397. package/features/workspace/file-kind.ts +9 -0
  398. package/features/workspace/workspace.query.ts +17 -0
  399. package/features/workspace/workspace.ts +82 -36
  400. package/hooks/use-hotkey.ts +3 -2
  401. package/lib/live/live.schema.ts +26 -0
  402. package/lib/live/live.server.ts +153 -0
  403. package/lib/live/live.session.ts +574 -0
  404. package/lib/{realtime/realtime.tap.ts → live/live.tap.ts} +41 -12
  405. package/lib/live/live.transport.ts +152 -0
  406. package/lib/protocol/use-server-pages.ts +9 -5
  407. package/lib/utils.ts +21 -20
  408. package/package.json +2 -2
  409. package/public/voices/alloy.ogg +0 -0
  410. package/public/voices/ash.ogg +0 -0
  411. package/public/voices/ballad.ogg +0 -0
  412. package/public/voices/beacon.ogg +0 -0
  413. package/public/voices/bossa.ogg +0 -0
  414. package/public/voices/cedar.ogg +0 -0
  415. package/public/voices/cinder.ogg +0 -0
  416. package/public/voices/coral.ogg +0 -0
  417. package/public/voices/delta.ogg +0 -0
  418. package/public/voices/echo.ogg +0 -0
  419. package/public/voices/gleam.ogg +0 -0
  420. package/public/voices/marin.ogg +0 -0
  421. package/public/voices/meridian.ogg +0 -0
  422. package/public/voices/quartz.ogg +0 -0
  423. package/public/voices/ripple.ogg +0 -0
  424. package/public/voices/sage.ogg +0 -0
  425. package/public/voices/shimmer.ogg +0 -0
  426. package/public/voices/stone.ogg +0 -0
  427. package/public/voices/tempo.ogg +0 -0
  428. package/public/voices/verse.ogg +0 -0
  429. package/public/voices/vesper.ogg +0 -0
  430. package/public/voices/willow.ogg +0 -0
  431. package/scripts/artifact-paths.test.mts +38 -0
  432. package/scripts/bot-context.test.mts +1408 -0
  433. package/scripts/live-history.test.mts +115 -0
  434. package/scripts/live-session.test.mts +938 -0
  435. package/server.js +1 -1
  436. package/skills/browser/SKILL.md +11 -1
  437. package/skills/computer/SKILL.md +1 -1
  438. package/skills/find-skills/SKILL.md +3 -8
  439. package/skills/interactive-page/SKILL.md +12 -7
  440. package/skills/interactive-page/references/diagram.md +89 -0
  441. package/skills/interactive-page/scripts/archify/LICENSE +22 -0
  442. package/skills/interactive-page/scripts/archify/README.md +25 -0
  443. package/skills/interactive-page/scripts/archify/assets/JetBrainsMono-OFL.txt +93 -0
  444. package/skills/interactive-page/scripts/archify/assets/template.html +14934 -0
  445. package/skills/interactive-page/scripts/archify/bin/archify.mjs +2126 -0
  446. package/skills/interactive-page/scripts/archify/bin/open-artifact.mjs +86 -0
  447. package/skills/interactive-page/scripts/archify/bin/preview.mjs +653 -0
  448. package/skills/interactive-page/scripts/archify/bin/visual-check.mjs +829 -0
  449. package/skills/interactive-page/scripts/archify/delta/architecture-delta.mjs +1223 -0
  450. package/skills/interactive-page/scripts/archify/examples/agent-run.lifecycle.json +60 -0
  451. package/skills/interactive-page/scripts/archify/examples/agent-tool-call.workflow.json +94 -0
  452. package/skills/interactive-page/scripts/archify/examples/async-job-roundtrip.sequence.json +61 -0
  453. package/skills/interactive-page/scripts/archify/examples/cache-miss-request.sequence.json +82 -0
  454. package/skills/interactive-page/scripts/archify/examples/checkout-platform.base.architecture.json +31 -0
  455. package/skills/interactive-page/scripts/archify/examples/checkout-platform.head.architecture.json +31 -0
  456. package/skills/interactive-page/scripts/archify/examples/deployment-release.lifecycle.json +49 -0
  457. package/skills/interactive-page/scripts/archify/examples/event-stream.dataflow.json +57 -0
  458. package/skills/interactive-page/scripts/archify/examples/incident-response.workflow.json +64 -0
  459. package/skills/interactive-page/scripts/archify/examples/product-analytics.dataflow.json +76 -0
  460. package/skills/interactive-page/scripts/archify/examples/production-deployment.architecture.json +71 -0
  461. package/skills/interactive-page/scripts/archify/examples/release-delivery.workflow.json +62 -0
  462. package/skills/interactive-page/scripts/archify/examples/web-app.architecture.json +46 -0
  463. package/skills/interactive-page/scripts/archify/migrations/workflow-v2.mjs +279 -0
  464. package/skills/interactive-page/scripts/archify/recipes/scenarios.mjs +284 -0
  465. package/skills/interactive-page/scripts/archify/references/authoring-contract.md +243 -0
  466. package/skills/interactive-page/scripts/archify/references/delivery-contract.md +120 -0
  467. package/skills/interactive-page/scripts/archify/references/viewer-runtime.md +45 -0
  468. package/skills/interactive-page/scripts/archify/renderers/architecture/grid.mjs +62 -0
  469. package/skills/interactive-page/scripts/archify/renderers/architecture/render-architecture.mjs +1078 -0
  470. package/skills/interactive-page/scripts/archify/renderers/dataflow/README.md +104 -0
  471. package/skills/interactive-page/scripts/archify/renderers/dataflow/render-dataflow.mjs +483 -0
  472. package/skills/interactive-page/scripts/archify/renderers/lifecycle/README.md +115 -0
  473. package/skills/interactive-page/scripts/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
  474. package/skills/interactive-page/scripts/archify/renderers/sequence/README.md +114 -0
  475. package/skills/interactive-page/scripts/archify/renderers/sequence/render-sequence.mjs +464 -0
  476. package/skills/interactive-page/scripts/archify/renderers/shared/brand-marks.mjs +563 -0
  477. package/skills/interactive-page/scripts/archify/renderers/shared/cli.mjs +218 -0
  478. package/skills/interactive-page/scripts/archify/renderers/shared/desktop-readability.mjs +26 -0
  479. package/skills/interactive-page/scripts/archify/renderers/shared/diagnostics.mjs +127 -0
  480. package/skills/interactive-page/scripts/archify/renderers/shared/engineering-profiles.mjs +157 -0
  481. package/skills/interactive-page/scripts/archify/renderers/shared/generated-brand-marks.mjs +3 -0
  482. package/skills/interactive-page/scripts/archify/renderers/shared/generated-validators.mjs +13 -0
  483. package/skills/interactive-page/scripts/archify/renderers/shared/geometry.mjs +1423 -0
  484. package/skills/interactive-page/scripts/archify/renderers/shared/i18n.mjs +595 -0
  485. package/skills/interactive-page/scripts/archify/renderers/shared/layout-report.mjs +40 -0
  486. package/skills/interactive-page/scripts/archify/renderers/shared/legend.mjs +217 -0
  487. package/skills/interactive-page/scripts/archify/renderers/shared/output-path.mjs +340 -0
  488. package/skills/interactive-page/scripts/archify/renderers/shared/repository-evidence.mjs +238 -0
  489. package/skills/interactive-page/scripts/archify/renderers/shared/repository-location.mjs +58 -0
  490. package/skills/interactive-page/scripts/archify/renderers/shared/text-fit.mjs +49 -0
  491. package/skills/interactive-page/scripts/archify/renderers/shared/utils.mjs +232 -0
  492. package/skills/interactive-page/scripts/archify/renderers/shared/validator.mjs +86 -0
  493. package/skills/interactive-page/scripts/archify/renderers/workflow/README.md +223 -0
  494. package/skills/interactive-page/scripts/archify/renderers/workflow/render-workflow.mjs +35 -0
  495. package/skills/interactive-page/scripts/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
  496. package/skills/interactive-page/scripts/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
  497. package/skills/interactive-page/scripts/archify/schemas/README.md +211 -0
  498. package/skills/interactive-page/scripts/archify/schemas/architecture.schema.json +178 -0
  499. package/skills/interactive-page/scripts/archify/schemas/common.schema.json +115 -0
  500. package/skills/interactive-page/scripts/archify/schemas/dataflow.schema.json +243 -0
  501. package/skills/interactive-page/scripts/archify/schemas/lifecycle.schema.json +266 -0
  502. package/skills/interactive-page/scripts/archify/schemas/sequence.schema.json +223 -0
  503. package/skills/interactive-page/scripts/archify/schemas/workflow.schema.json +428 -0
  504. package/skills/interactive-page/scripts/archify/scripts/check-render-output.mjs +916 -0
  505. package/skills/interactive-page/scripts/archify/scripts/render-examples.mjs +26 -0
  506. package/skills/interactive-page/scripts/bundle-artifact.sh +9 -8
  507. package/skills/skill-creator/SKILL.md +4 -5
  508. package/.next/server/app/api/bot/task/[id]/route/app-paths-manifest.json +0 -3
  509. package/.next/server/app/api/bot/task/[id]/route.js +0 -16
  510. package/.next/server/app/api/bot/task/[id]/route.js.nft.json +0 -1
  511. package/.next/server/app/api/bot/task/[id]/route_client-reference-manifest.js +0 -3
  512. package/.next/server/app/api/bot/task/route/app-paths-manifest.json +0 -3
  513. package/.next/server/app/api/bot/task/route.js +0 -16
  514. package/.next/server/app/api/bot/task/route.js.nft.json +0 -1
  515. package/.next/server/app/api/bot/task/route_client-reference-manifest.js +0 -3
  516. package/.next/server/chunks/[root-of-the-server]__026u77e._.js +0 -5
  517. package/.next/server/chunks/[root-of-the-server]__02va5wz._.js +0 -3
  518. package/.next/server/chunks/[root-of-the-server]__050zri4._.js +0 -3
  519. package/.next/server/chunks/[root-of-the-server]__0bg9710._.js +0 -3
  520. package/.next/server/chunks/[root-of-the-server]__0ffbg85._.js +0 -3
  521. package/.next/server/chunks/[root-of-the-server]__0in-d5s._.js +0 -3
  522. package/.next/server/chunks/[root-of-the-server]__0ncs9uu._.js +0 -3
  523. package/.next/server/chunks/[root-of-the-server]__0pa9k-f._.js +0 -8
  524. package/.next/server/chunks/[root-of-the-server]__0qi_m0e._.js +0 -8
  525. package/.next/server/chunks/[root-of-the-server]__0tq0m_k._.js +0 -3
  526. package/.next/server/chunks/[root-of-the-server]__0xep6j_._.js +0 -3
  527. package/.next/server/chunks/[root-of-the-server]__1-ga0kh._.js +0 -5
  528. package/.next/server/chunks/[root-of-the-server]__11n256_._.js +0 -5
  529. package/.next/server/chunks/[root-of-the-server]__144l7qt._.js +0 -8
  530. package/.next/server/chunks/[root-of-the-server]__1_04mcf._.js +0 -5
  531. package/.next/server/chunks/[root-of-the-server]__1anc1p9._.js +0 -3
  532. package/.next/server/chunks/[root-of-the-server]__1fizn31._.js +0 -3
  533. package/.next/server/chunks/[root-of-the-server]__1jg8t86._.js +0 -8
  534. package/.next/server/chunks/[root-of-the-server]__1lldzy9._.js +0 -3
  535. package/.next/server/chunks/[root-of-the-server]__1llj3-b._.js +0 -3
  536. package/.next/server/chunks/[root-of-the-server]__1p8m3f4._.js +0 -3
  537. package/.next/server/chunks/[root-of-the-server]__1peqvsi._.js +0 -3
  538. package/.next/server/chunks/[root-of-the-server]__1w8gm5h._.js +0 -10
  539. package/.next/server/chunks/[root-of-the-server]__1xt39nn._.js +0 -8
  540. package/.next/server/chunks/[root-of-the-server]__1zb8mfo._.js +0 -8
  541. package/.next/server/chunks/_0-73of8._.js +0 -55
  542. package/.next/server/chunks/_0-fjql4._.js +0 -31
  543. package/.next/server/chunks/_00xh_q0._.js +0 -25
  544. package/.next/server/chunks/_051qh8u._.js +0 -3
  545. package/.next/server/chunks/_05sovvj._.js +0 -3
  546. package/.next/server/chunks/_091bi8d._.js +0 -3
  547. package/.next/server/chunks/_0fzqt2y._.js +0 -142
  548. package/.next/server/chunks/_0lr92vd._.js +0 -3
  549. package/.next/server/chunks/_0mecv0f._.js +0 -31
  550. package/.next/server/chunks/_0mh8j4m._.js +0 -3
  551. package/.next/server/chunks/_0sb7tlz._.js +0 -3
  552. package/.next/server/chunks/_0si7xzn._.js +0 -3
  553. package/.next/server/chunks/_0u10wmw._.js +0 -55
  554. package/.next/server/chunks/_0wp4le3._.js +0 -3
  555. package/.next/server/chunks/_0y5fg-x._.js +0 -3
  556. package/.next/server/chunks/_1230wbx._.js +0 -10
  557. package/.next/server/chunks/_12nheja._.js +0 -3
  558. package/.next/server/chunks/_13510xh._.js +0 -3
  559. package/.next/server/chunks/_15hq89o._.js +0 -3
  560. package/.next/server/chunks/_18-1wcc._.js +0 -55
  561. package/.next/server/chunks/_18d7-5p._.js +0 -3
  562. package/.next/server/chunks/_1c78vw5._.js +0 -237
  563. package/.next/server/chunks/_1h5_8sk._.js +0 -3
  564. package/.next/server/chunks/_1hdnxb7._.js +0 -3
  565. package/.next/server/chunks/_1j8k_j5._.js +0 -3
  566. package/.next/server/chunks/_1nula3v._.js +0 -3
  567. package/.next/server/chunks/_1php4eq._.js +0 -3
  568. package/.next/server/chunks/_1yuv4vp._.js +0 -3
  569. package/.next/server/chunks/_1zkfbaa._.js +0 -3
  570. package/.next/server/chunks/_next-internal_server_app_api_bot_task_[id]_route_actions_1_k_8u3.js +0 -3
  571. package/.next/server/chunks/_next-internal_server_app_api_bot_task_route_actions_0xln6ra.js +0 -3
  572. package/.next/server/chunks/features_0rpfxbk._.js +0 -3
  573. package/.next/server/chunks/features_1qf3cei._.js +0 -8
  574. package/.next/server/chunks/features_bot_0zt92oh._.js +0 -3
  575. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1jidylq._.js +0 -3
  576. package/.next/server/chunks/ssr/[externals]__0dizgek._.js +0 -3
  577. package/.next/server/chunks/ssr/[root-of-the-server]__0-nob52._.js +0 -4
  578. package/.next/server/chunks/ssr/[root-of-the-server]__092knlj._.js +0 -3
  579. package/.next/server/chunks/ssr/[root-of-the-server]__0mz33a7._.js +0 -3
  580. package/.next/server/chunks/ssr/[root-of-the-server]__0s4wb56._.js +0 -3
  581. package/.next/server/chunks/ssr/[root-of-the-server]__0s9dq5u._.js +0 -3
  582. package/.next/server/chunks/ssr/[root-of-the-server]__0sd3izg._.js +0 -3
  583. package/.next/server/chunks/ssr/[root-of-the-server]__0vnmv-s._.js +0 -3
  584. package/.next/server/chunks/ssr/[root-of-the-server]__1-13tcn._.js +0 -43
  585. package/.next/server/chunks/ssr/[root-of-the-server]__10-dz5o._.js +0 -61
  586. package/.next/server/chunks/ssr/[root-of-the-server]__1zidpfl._.js +0 -3
  587. package/.next/server/chunks/ssr/_001_ms1._.js +0 -33
  588. package/.next/server/chunks/ssr/_01n3o0m._.js +0 -3
  589. package/.next/server/chunks/ssr/_04z2c7b._.js +0 -3
  590. package/.next/server/chunks/ssr/_06tssji._.js +0 -3
  591. package/.next/server/chunks/ssr/_0fk4c7d._.js +0 -3
  592. package/.next/server/chunks/ssr/_0h42fze._.js +0 -3
  593. package/.next/server/chunks/ssr/_0h7zdni._.js +0 -3
  594. package/.next/server/chunks/ssr/_0mggveb._.js +0 -3
  595. package/.next/server/chunks/ssr/_0nbpc_8._.js +0 -3
  596. package/.next/server/chunks/ssr/_0q0zan6._.js +0 -70
  597. package/.next/server/chunks/ssr/_0zcjrbg._.js +0 -3
  598. package/.next/server/chunks/ssr/_0zhoc3r._.js +0 -3
  599. package/.next/server/chunks/ssr/_11j92tu._.js +0 -3
  600. package/.next/server/chunks/ssr/_13fke0x._.js +0 -3
  601. package/.next/server/chunks/ssr/_18cymkq._.js +0 -3
  602. package/.next/server/chunks/ssr/_1ebxla1._.js +0 -3
  603. package/.next/server/chunks/ssr/_1nj4niv._.js +0 -42
  604. package/.next/server/chunks/ssr/_1ov37if._.js +0 -3
  605. package/.next/server/chunks/ssr/_1ovmc9s._.js +0 -3
  606. package/.next/server/chunks/ssr/_1sji22n._.js +0 -3
  607. package/.next/server/chunks/ssr/_1vi_uo7._.js +0 -3
  608. package/.next/server/chunks/ssr/_1vyd1ji._.js +0 -3
  609. package/.next/server/chunks/ssr/_1xx4zlb._.js +0 -3
  610. package/.next/server/chunks/ssr/components_ui_1y1l7yj._.js +0 -3
  611. package/.next/server/chunks/ssr/features_bot_1s_-2vj._.js +0 -3
  612. package/.next/server/chunks/ssr/lib_realtime_12wg_dd._.js +0 -27
  613. package/.next/server/chunks/ssr/lib_realtime_15h_v1v._.js +0 -27
  614. package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_0npjf8-._.js +0 -3
  615. package/.next/static/chunks/0n3jle9kbqfr2.js +0 -40
  616. package/.next/static/chunks/0r5qcctujhxli.js +0 -1
  617. package/.next/static/chunks/10teww7wt-f6h.js +0 -1
  618. package/.next/static/chunks/126fj4pga_72k.js +0 -1
  619. package/.next/static/chunks/159udiexf6dom.js +0 -2
  620. package/.next/static/chunks/18a96rkhi4_yd.js +0 -1
  621. package/.next/static/chunks/18gi7o2ibrgh8.js +0 -3
  622. package/.next/static/chunks/1dkjg87pxk0n9.js +0 -28
  623. package/.next/static/chunks/1q9z1tohil7xn.js +0 -1
  624. package/.next/static/chunks/1sjgfets224re.js +0 -1
  625. package/.next/static/chunks/1wszsr83t01gh.js +0 -1
  626. package/.next/static/chunks/22_zkas21rpna.js +0 -1
  627. package/.next/static/chunks/2fh4-3z_q8b_w.js +0 -1
  628. package/.next/static/chunks/2h0sd2vumhb15.js +0 -1
  629. package/.next/static/chunks/2szintyibu0s5.js +0 -1
  630. package/.next/static/chunks/2t4vhi3m8ahya.js +0 -1
  631. package/.next/static/chunks/2tigdtz3dpcy8.js +0 -1
  632. package/.next/static/chunks/3-b6b7entqazs.js +0 -1
  633. package/.next/static/chunks/3786h5vayt80-.js +0 -28
  634. package/.next/static/chunks/3cq5g494yuke8.css +0 -1
  635. package/.next/static/chunks/3dmbhsv2u_8kj.js +0 -1
  636. package/.next/static/chunks/3hibbjnt85-k6.js +0 -1
  637. package/.next/static/chunks/3i0a5px-oufgc.js +0 -1
  638. package/.next/static/chunks/3wexk9qo8zuj3.js +0 -18
  639. package/.next/static/chunks/3xk7yqgomx7yq.js +0 -1
  640. package/.next/static/chunks/3xre8bu_pk1p_.js +0 -1
  641. package/.next/static/chunks/3zv6t3i77xhr_.js +0 -1
  642. package/database/migrations/20260911030320_opposite_jack_power/migration.sql +0 -1
  643. package/docs/images/task-thread.png +0 -0
  644. package/features/bot/components/task-badge.tsx +0 -42
  645. package/features/bot/components/task-reply.tsx +0 -328
  646. package/features/bot/components/task-setting.tsx +0 -524
  647. package/features/bot/task.query.ts +0 -1079
  648. package/features/bot/task.store.ts +0 -487
  649. package/lib/realtime/client-secret.ts +0 -86
  650. package/lib/realtime/open-session.ts +0 -31
  651. package/lib/realtime/openai.ts +0 -35
  652. package/lib/realtime/realtime.audio.ts +0 -196
  653. package/lib/realtime/realtime.driver.ts +0 -489
  654. package/lib/realtime/realtime.protocol.ts +0 -393
  655. package/lib/realtime/realtime.schema.ts +0 -37
  656. package/lib/realtime/realtime.session.ts +0 -106
  657. package/lib/realtime/realtime.transport.ts +0 -384
  658. package/lib/realtime/xai.ts +0 -39
  659. /package/.next/server/app/api/bot/{task → thread}/[id]/route/build-manifest.json +0 -0
  660. /package/.next/server/app/api/bot/{task → thread}/[id]/route/server-reference-manifest.json +0 -0
  661. /package/.next/server/app/api/bot/{task → thread}/[id]/route.js.map +0 -0
  662. /package/.next/server/app/api/bot/{task → thread}/route/build-manifest.json +0 -0
  663. /package/.next/server/app/api/bot/{task → thread}/route/server-reference-manifest.json +0 -0
  664. /package/.next/server/app/api/bot/{task → thread}/route.js.map +0 -0
  665. /package/.next/static/{G6-gNoyGDEcgAsq-L4PE1 → yyRVBc0xxYQsh3GpbQX9b}/_buildManifest.js +0 -0
  666. /package/.next/static/{G6-gNoyGDEcgAsq-L4PE1 → yyRVBc0xxYQsh3GpbQX9b}/_clientMiddlewareManifest.js +0 -0
  667. /package/.next/static/{G6-gNoyGDEcgAsq-L4PE1 → yyRVBc0xxYQsh3GpbQX9b}/_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,66 +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
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
- import { createKeyedLock } from "@/lib/queue";
52
40
  import { estimateTokens } from "@/lib/tokens";
53
- import { PromiseChain } from "@/lib/utils";
54
41
  import { findJobBot } from "./bot.query";
55
- import {
56
- findTask,
57
- listBorrowedTails,
58
- listBotThread,
59
- listWrittenPaths,
60
- optionsOf,
61
- writtenPathsIn,
62
- } from "./task.query";
42
+ import { findThread, listWrittenPaths, writtenPathsIn } from "./thread.query";
63
43
 
64
- /**
65
- * The bot loop behind `delegate`: one model with its own prompt, tools and
66
- * context. Only the `answer` reaches Thursday; every event goes through
67
- * `emit` and the runner (bot.runner) writes it to the task's thread.
68
- */
69
-
70
- /** 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. */
71
45
  export type BotEvent =
72
- /** A request enters this participant's own thread before its model runs. */
73
- | {
74
- type: "request";
75
- content: Extract<ModelMessage, { role: "user" }>["content"];
76
- }
77
46
  /** A finished chunk of prose. */
78
47
  | { type: "text"; text: string }
79
48
  /** A tool call. `id` is where the result lands. */
80
- | { 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
+ }
81
57
  | {
82
58
  type: "tool-result";
83
59
  id: string;
@@ -108,97 +84,45 @@ export type BotEvent =
108
84
  messages: number;
109
85
  tokens: number;
110
86
  }
111
- /** Words from the user read before this step (`RunOptions.notes`). Stored as a user row between two steps. */
112
- | { type: "interjection"; text: string }
113
- /** This bot answered a borrowed bot's `ask_back` (answerBack). No row of its own; `usage` is charged to the job. */
114
- | { type: "answered"; question: string; text: string; usage: TokenUsage }
87
+ /** Normal turn end; interruption is a runtime fact, separate from room completion. */
88
+ | { type: "turn-end"; text: string; stopped: boolean }
115
89
  /**
116
- * Stopped for an answer. `id` is the `ask_thursday` call the answer becomes
117
- * the result of; null when asking whether to continue, then the answer is a
118
- * fresh user turn.
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.
119
93
  */
120
- | { type: "waiting"; id: string | null; question: string; options: string[] }
121
- /**
122
- * The only thing that reaches Thursday. `stopped` is the app's word, never the
123
- * model's: the step cap forced this answer, the answer was refused on the last
124
- * step, the output limit cut it off, or the run ended with nothing handed back.
125
- * A stopped job waits to be continued; any other answer ends it.
126
- */
127
- | { type: "answer"; text: string; stopped: boolean }
128
- /**
129
- * The run broke. `failure` is whose it is to fix (model.ts modelFailureOf) — a
130
- * retry's, a compaction's, or a person's — and `budget` where to compact next
131
- * time, when the model refused the context as too long.
132
- */
133
- | {
134
- type: "error";
135
- message: string;
136
- failure: ModelFailure;
137
- budget?: number;
138
- };
94
+ | { type: "error"; message: string; budget?: number; retry: boolean };
139
95
 
140
- /** The event as the thread sees it: which bot, inside which `ask_bot` call. */
141
- 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 };
142
98
 
143
99
  export type RunOptions = {
144
100
  signal?: AbortSignal;
145
- depth?: number;
146
- /** The `ask_bot` call this run answers; null for the job's own bot. */
147
101
  parent?: string | null;
148
- /**
149
- * The job this runs inside, for every seat on it — a borrowed bot's too. Its row
150
- * names the one folder they all work in (workspace.ts jobScratch).
151
- */
152
- taskId?: string | null;
153
- /**
154
- * The browser session this seat's shell drives (workspace.ts jobShellEnv): the
155
- * job's own when unset, a borrowed bot's under its call, so two seats never click
156
- * in one window.
157
- */
102
+ threadId?: string | null;
158
103
  session?: string | null;
159
- /** Shared by every participant in this live segment; history itself lives in the DB. */
160
- desks?: ReturnType<typeof createBotDesks>;
161
- /**
162
- * Drained once per step boundary; each string goes in front of the model as
163
- * a user turn before the next step. Only the job's own bot has one.
164
- */
165
- notes?: () => string[];
166
- /** Answers a borrowed run's `ask_back` from the asking bot's context (answerBack). Without it the tool is not attached. */
167
- answer?: (question: string) => Promise<string>;
168
- 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>;
169
117
  };
170
118
 
171
- /**
172
- * At the step cap the last step is forced to `answer` (lastStep) and the job goes
173
- * to `waiting`. The context budget is the model's own window where that can be
174
- * known (model.ts compactBudget); past it the run compacts and carries on from
175
- * the summary.
176
- */
177
119
  const MAX_STEPS = BOT_RUN.steps;
178
-
179
- /**
180
- * Where a run starts: a job's thread (the opening message first time, past
181
- * stretches plus new turns on resume), or an `ask_bot` brief plus `context`
182
- * from the asking bot (`askedBy`).
183
- */
184
- export type RunInput =
185
- | { bot: string; messages: ModelMessage[] }
186
- | {
187
- bot: string;
188
- request: string;
189
- context?: string | null;
190
- askedBy: string;
191
- /** What the borrowing bot inherited, handed down under the part (bot.prompt buildHandoff). */
192
- chain: string;
193
- /** Every bot above this one on the job, the holder first; `askedBy` is the last. */
194
- above: string[];
195
- };
120
+ export type RunInput = { bot: string; messages: ModelMessage[] };
196
121
 
197
122
  export async function runBot(
198
123
  input: RunInput,
199
124
  options: RunOptions,
200
125
  ): Promise<void> {
201
- const depth = options.depth ?? 0;
202
126
  const parent = options.parent ?? null;
203
127
  const bot = await findJobBot(input.bot.trim());
204
128
  const name = bot?.name ?? input.bot.trim();
@@ -209,125 +133,120 @@ export async function runBot(
209
133
  await emit({
210
134
  type: "error",
211
135
  message: `No bot named "${input.bot}". Use a name from the list.`,
212
- failure: "fatal",
213
- });
214
- return;
215
- }
216
- // Switched off is not a bot to pick — the backstop for every fresh start
217
- // (`delegate`, `ask_bot`, the screen). A resume carries `messages` instead and
218
- // is let through: its thread already exists and the user can still answer it.
219
- const previous =
220
- "askedBy" in input && options.taskId
221
- ? await listBotThread(options.taskId, name)
222
- : [];
223
- if (bot.disabled && "request" in input && !previous.length) {
224
- await emit({
225
- type: "error",
226
- message: `${bot.name} is switched off. Use a name from the list.`,
227
- failure: "fatal",
136
+ retry: false,
228
137
  });
229
138
  return;
230
139
  }
231
-
232
- options = { ...options, desks: options.desks ?? createBotDesks() };
233
- let history: ModelMessage[];
234
- if ("messages" in input) {
235
- history = input.messages;
236
- } else {
237
- const content = previous.length
238
- ? `## ${input.askedBy} → ${name}: the next request\n\n${input.request.trim()}${input.context?.trim() ? `\n\n## What ${input.askedBy} passes on\n\n${input.context.trim()}` : ""}`
239
- : buildHandoff({
240
- chain: input.chain,
241
- bots: [...input.above, name],
242
- did: input.context ?? null,
243
- part: input.request,
244
- });
245
- history = [
246
- ...resumeThread(
247
- previous,
248
- options.taskId ? await listBorrowedTails(options.taskId) : undefined,
249
- ),
250
- { role: "user", content },
251
- ];
252
- options.signal?.throwIfAborted();
253
- await emit({ type: "request", content });
254
- }
140
+ const history = input.messages;
255
141
 
256
142
  // Tools are built on the model: web search runs on this bot's model (load-tools).
257
143
  const model = await resolveModel(bot);
258
- // The job's row: the same one for every seat on the job, a borrowed bot's too
259
- const row = options.taskId ? await findTask(options.taskId) : null;
260
- // One folder per job, not per bot: bots borrowed with `ask_bot` work inside
261
- // the same job and share its material (workspace.ts jobScratch).
262
- const [scratch, own] = await Promise.all([
263
- options.taskId ? openJobScratch(options.taskId, row?.label ?? "job") : null,
264
- 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),
265
151
  ]);
266
152
  const [prompt, tools] = await Promise.all([
267
153
  loadBotPrompt(
268
154
  name,
269
155
  bot.systemPrompt,
270
- "askedBy" in input
271
- ? {
272
- askedBy: input.askedBy,
273
- above: input.above,
274
- canBorrow: depth < BOT_RUN.depth,
275
- }
276
- : null,
277
- { scratch, own },
156
+ { owner: options.owner, caller: options.caller, messageId: parent },
157
+ { scratch, own, artifacts },
278
158
  ),
279
159
  loadTools({
280
160
  target: "bot",
281
161
  bot: name,
282
- // A borrowed bot drives a browser session of its own; everyone else the job's
283
- session: options.session ?? options.taskId,
162
+ session:
163
+ options.session ??
164
+ (options.threadId ? botBrowserSession(options.threadId, name) : null),
284
165
  model,
285
166
  }),
286
167
  ]);
287
168
  // What the owner set, else the model's own window, else the constant (model.ts
288
- // compactBudget), and never above where this job last fit: a context the model
289
- // refused as too long lowers the job's own number (bot.runner parkTask). That
290
- // 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.
291
172
  const budget = Math.min(
292
173
  await compactBudget(model.ref, bot.compactAt),
293
- ("askedBy" in input ? 0 : row?.contextBudget) || Number.POSITIVE_INFINITY,
174
+ options.contextBudget || Number.POSITIVE_INFINITY,
294
175
  );
295
176
 
296
- /** Remaining `ask_back` calls for a borrowed bot; the tool counts it down. */
297
- const asks = { left: BOT_RUN.askBack };
298
- // A borrowed bot asks back instead of asking Thursday. Any seat above the
299
- // depth limit may borrow in turn, handing down the chain it was handed.
300
- const seatTools =
301
- "askedBy" in input ? withAskBack(tools, asks, options) : tools;
302
- const agentTools =
303
- depth < BOT_RUN.depth
304
- ? withAskBot(seatTools, {
305
- name,
306
- model: model.model,
307
- instructions: prompt.text,
308
- peers: prompt.peers,
309
- depth,
310
- chain: chainOf(history[0]),
311
- above: "askedBy" in input ? input.above : [],
312
- options,
313
- emit,
314
- })
315
- : seatTools;
316
-
317
- /**
318
- * A compaction decided in `prepareStep` is emitted from the loop at
319
- * `start-step`, not from the callback: the sdk prepares step N+1 while the
320
- * loop may still be reading step N, and the row must sit between the two
321
- * steps (that position is what a resume restarts from, task.query listThread).
322
- */
323
- const pending: {
324
- compact: Extract<BotEvent, { type: "compact" }> | null;
325
- /** Emitted at the same place, after the compaction. */
326
- interjections: string[];
327
- } = { compact: null, interjections: [] };
328
-
329
- /** 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>>();
330
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 = () => {};
331
250
  /** Context size the latest step went out at; an overflow shrinks the budget from it. */
332
251
  let sent = 0;
333
252
 
@@ -335,26 +254,11 @@ export async function runBot(
335
254
  model: model.model,
336
255
  instructions: prompt.text,
337
256
  tools: agentTools,
338
- // The loop ends on an accepted answer or at the step cap (the last step is
339
- // forced to answer). A refused answer does not end it. `ask_thursday` has
340
- // no execute, but invalid arguments come back as a tool-error and the loop
341
- // would go on, so the stop is on the call itself.
342
- stopWhen: [
343
- stepCountIs(MAX_STEPS),
344
- answerAccepted_,
345
- hasToolCall(TOOL_NAMES.ask_thursday),
346
- ],
257
+ stopWhen: [stepCountIs(MAX_STEPS), () => asked],
347
258
  prepareStep: async ({ stepNumber, steps, messages }) => {
348
- let step = lastStep(stepNumber);
349
- // Remove `ask_back` once spent. The last step is already narrowed to `answer`.
350
- if (!step.activeTools && asks.left <= 0) {
351
- step = {
352
- ...step,
353
- activeTools: Object.keys(agentTools).filter(
354
- (held) => held !== TOOL_NAMES.ask_back,
355
- ),
356
- };
357
- }
259
+ await writtenStep;
260
+ options.signal?.throwIfAborted();
261
+ const step: NonNullable<PrepareStepResult<ToolSet>> = {};
358
262
  // The provider's input count includes instructions and tool schemas the
359
263
  // estimate cannot see; the estimate covers a provider that reports none.
360
264
  const measured = steps.at(-1)?.usage.inputTokens ?? 0;
@@ -375,21 +279,20 @@ export async function runBot(
375
279
  );
376
280
  // Which files the job has is read off its rows and the disk, never asked of the model
377
281
  const text = `${summary.text}${await filesUnder(row?.id ?? null, messages, scratch)}`;
378
- pending.compact = {
282
+ await emit({
379
283
  type: "compact",
380
284
  text,
381
285
  usage: summary.usage,
382
286
  messages: messages.length,
383
287
  tokens: size,
384
- };
288
+ });
385
289
  next = [messages[0], { role: "user", content: text }];
386
290
  }
387
291
 
388
292
  // Notes said during the last step go in as user turns after everything
389
293
  // the model has (and after the summary). Not on the first step.
390
- const notes = stepNumber > 0 ? (options.notes?.() ?? []) : [];
294
+ const notes = stepNumber > 0 ? ((await options.notes?.()) ?? []) : [];
391
295
  if (notes.length) {
392
- pending.interjections.push(...notes);
393
296
  next = [
394
297
  ...next,
395
298
  ...notes.map((text) => ({ role: "user" as const, content: text })),
@@ -411,62 +314,74 @@ export async function runBot(
411
314
  ? AbortSignal.any([options.signal, quiet.signal])
412
315
  : quiet.signal;
413
316
  quiet.touch();
414
- const result = await agent.stream({
415
- messages: history,
416
- abortSignal: stop,
417
- onStepEnd: (step) =>
418
- steps.push({
419
- messages: step.response.messages,
420
- usage: usageOf(step.usage),
421
- }),
422
- });
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
+ }
423
340
 
424
341
  let writing = "";
425
- /** The last chunk of prose; used only if the run ends without an answer. */
426
- let last: string | null = null;
427
- /** The `ask_thursday` call the loop stopped on. */
428
- let asked: { id: string; question: string; options: string[] } | null = null;
429
- /** The answer (bot.tool answerSpec). */
430
- let answered: { text: string; stopped: boolean } | null = null;
431
- /** Index of the step being read; `lastStep` narrows the one at MAX_STEPS - 1. */
432
- let stepAt = -1;
342
+ /** Only prose from the final step is returned to the correspondent. */
343
+ let last = "";
433
344
  /** Why the latest step ended; prose cut off at the output limit is not an answer. */
434
345
  let finish: FinishReason | null = null;
435
- /** Tool calls still running: the model is not expected to send anything meanwhile. */
436
- const working = new Set<string>();
437
346
 
438
- /** 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
+ */
439
352
  const failed = (cause: unknown): Extract<BotEvent, { type: "error" }> => {
440
- const failure = modelFailureOf(cause);
353
+ logger.error(`${name}: the model run broke`, cause);
441
354
  const message = modelErrorToString(cause);
442
- if (failure !== "overflow") return { type: "error", message, failure };
355
+ const retry = !isProviderRefusal(cause);
356
+ if (!isContextOverflow(cause)) return { type: "error", message, retry };
443
357
  const shrunk = Math.floor(
444
358
  Math.min(budget, sent || budget) * BOT_RUN.overflowShrink,
445
359
  );
446
360
  return {
447
361
  type: "error",
448
362
  message,
449
- failure,
450
363
  budget: Math.max(COMPACT_AT_MIN, shrunk),
364
+ retry,
451
365
  };
452
366
  };
453
367
 
454
368
  // An abort ends the stream without a finish; release any waiting take.
455
- stop.addEventListener("abort", () => steps.end(), { once: true });
369
+ stop.addEventListener(
370
+ "abort",
371
+ () => {
372
+ steps.end();
373
+ acknowledgeStep();
374
+ },
375
+ { once: true },
376
+ );
456
377
  try {
457
378
  for await (const part of result.fullStream) {
458
379
  // Anything at all is the model still there
459
380
  quiet.touch();
460
381
  switch (part.type) {
461
382
  case "start-step":
462
- stepAt += 1;
463
- if (pending.compact) {
464
- await emit(pending.compact);
465
- pending.compact = null;
466
- }
467
- for (const text of pending.interjections.splice(0)) {
468
- await emit({ type: "interjection", text });
469
- }
383
+ last = "";
384
+ writing = "";
470
385
  break;
471
386
 
472
387
  case "text-delta":
@@ -477,62 +392,24 @@ export async function runBot(
477
392
  const text = writing.trim();
478
393
  writing = "";
479
394
  if (!text) break;
480
- last = text;
395
+ last = [last, text].filter(Boolean).join("\n\n");
481
396
  await emit({ type: "text", text });
482
397
  break;
483
398
  }
484
399
 
485
400
  case "tool-call": {
486
- if (part.toolName === TOOL_NAMES.ask_thursday) {
487
- const args = part.input as {
488
- question?: unknown;
489
- options?: unknown;
490
- };
491
- asked = {
492
- id: part.toolCallId,
493
- question: String(args.question ?? ""),
494
- options: optionsOf(args.options),
495
- };
496
- } else if (part.toolName === TOOL_NAMES.answer) {
497
- const args = part.input as { result?: unknown };
498
- answered = {
499
- text: String(args.result ?? "").trim(),
500
- // The last step offers nothing but `answer`: an answer there is the cap's doing
501
- stopped: stepAt >= MAX_STEPS - 1,
502
- };
503
- }
504
- // A tool at work sends nothing, and bounds itself (bash, connected
505
- // tools, a borrowed bot's own watch); `ask_thursday` never runs
506
- if (
507
- !part.providerExecuted &&
508
- part.toolName !== TOOL_NAMES.ask_thursday
509
- ) {
510
- working.add(part.toolCallId);
511
- quiet.hold();
512
- }
513
401
  await emit({
514
402
  type: "tool",
515
403
  id: part.toolCallId,
516
404
  name: part.toolName,
517
405
  input: part.input,
406
+ providerExecuted: part.providerExecuted,
407
+ providerOptions: part.providerMetadata,
518
408
  });
519
409
  break;
520
410
  }
521
411
 
522
412
  case "tool-result":
523
- if (working.delete(part.toolCallId)) quiet.release();
524
- // A refused answer is not the ending, unless the step cap ends the run
525
- // on this step; then the text still reaches the user, as a stop.
526
- if (
527
- part.toolName === TOOL_NAMES.answer &&
528
- answered &&
529
- !answerAccepted(part.output)
530
- ) {
531
- answered = {
532
- text: `${answered.text}\n\n${outputText(part.output)}`,
533
- stopped: true,
534
- };
535
- }
536
413
  // A provider-executed tool (web search) lands here too; the step's own
537
414
  // messages decide where it belongs (bot.runner).
538
415
  await emit({
@@ -544,7 +421,6 @@ export async function runBot(
544
421
  break;
545
422
 
546
423
  case "tool-error":
547
- if (working.delete(part.toolCallId)) quiet.release();
548
424
  // The model reads it and carries on; the row shows it now rather than
549
425
  // when the step ends
550
426
  await emit({
@@ -560,18 +436,16 @@ export async function runBot(
560
436
  finish = part.finishReason;
561
437
  const step = await steps.take();
562
438
  if (step) await emit({ type: "step", ...step, budget });
439
+ acknowledgeStep();
563
440
  break;
564
441
  }
565
442
 
566
443
  case "abort":
567
444
  // The job was stopped: whoever stopped it writes the row (bot.runner)
568
445
  if (options.signal?.aborted) return;
569
- // Otherwise the model went quiet (silenceWatch)
570
- await emit({
571
- type: "error",
572
- message: part.reason ?? "The model stopped sending.",
573
- failure: "transient",
574
- });
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."));
575
449
  return;
576
450
 
577
451
  case "error":
@@ -583,8 +457,11 @@ export async function runBot(
583
457
  }
584
458
  }
585
459
  } finally {
460
+ execution.abort();
586
461
  steps.end();
587
462
  quiet.end();
463
+ acknowledgeStep();
464
+ await Promise.allSettled([...inFlight]);
588
465
  }
589
466
 
590
467
  try {
@@ -596,59 +473,26 @@ export async function runBot(
596
473
  return;
597
474
  }
598
475
 
599
- // Asking wins over answering: the loop halts on the ask.
600
- if (asked) {
601
- await emit({ type: "waiting", ...asked });
602
- return;
603
- }
604
-
605
- if (answered) {
606
- await emit({ type: "answer", ...answered });
607
- return;
608
- }
609
-
610
476
  // The provider's filter stopped the answer: there is nothing to continue past
611
477
  if (finish === "content-filter") {
612
478
  await emit({
613
479
  type: "error",
614
480
  message: "The provider's content filter stopped the response.",
615
- failure: "fatal",
481
+ retry: false,
616
482
  });
617
483
  return;
618
484
  }
619
485
 
620
- // The sdk stops on a step with no tool call regardless of `stopWhen`. Prose
621
- // there is an answer given without the tool and ends the job like one;
622
- // nothing at all, prose cut off at the output limit, or prose on the step the
623
- // cap narrowed to `answer`, is a stop — continuing picks up where it broke off.
624
486
  await emit({
625
- type: "answer",
626
- text: last ?? "Stopped without handing anything back.",
627
- 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"),
628
493
  });
629
494
  }
630
495
 
631
- /** The last step ended on an answer the tool accepted. `hasToolCall` would also stop on a refused one. */
632
- const answerAccepted_ = ({ steps }: { steps: StepResult<ToolSet>[] }) =>
633
- steps
634
- .at(-1)
635
- ?.toolResults.some(
636
- (result) =>
637
- result.toolName === TOOL_NAMES.answer && answerAccepted(result.output),
638
- ) ?? false;
639
-
640
- /**
641
- * The step before the cap is narrowed to `answer` and required to call it,
642
- * so a run out of budget ends with something a person can act on.
643
- */
644
- function lastStep(stepNumber: number): NonNullable<PrepareStepResult<ToolSet>> {
645
- if (stepNumber < MAX_STEPS - 1) return {};
646
- return {
647
- activeTools: [TOOL_NAMES.answer],
648
- toolChoice: { type: "tool", toolName: TOOL_NAMES.answer },
649
- };
650
- }
651
-
652
496
  /** Rough token weight of a message list. The provider's own count wins when it reports one (prepareStep). */
653
497
  function sizeOf(messages: ModelMessage[]): number {
654
498
  return messages.reduce(
@@ -667,11 +511,11 @@ function sizeOf(messages: ModelMessage[]): number {
667
511
  /** Instructions for compacting. The summary replaces everything above it, opening included, so it must restate the job. */
668
512
  const compactInstructions = (
669
513
  words: number,
670
- ) => `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.
671
515
 
672
516
  Cover all of it, and prefer a fact over a description of a fact:
673
517
 
674
- - **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.
675
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.
676
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.
677
521
  - **Files.** Every path you wrote or read, and one line on what each holds.
@@ -696,19 +540,19 @@ const FILES_HEAD =
696
540
 
697
541
  /**
698
542
  * The files a job has on disk, as lines under its compaction summary. Read off the
699
- * 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,
700
544
  * never asked of the model: a path a summary leaves out is work the next steps redo.
701
- * 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.
702
546
  * A listing that fails costs the list, never the compaction.
703
547
  */
704
548
  async function filesUnder(
705
- taskId: string | null,
549
+ threadId: string | null,
706
550
  messages: ModelMessage[],
707
551
  folder: string | null,
708
552
  ): Promise<string> {
709
553
  try {
710
- const written = taskId
711
- ? await listWrittenPaths(taskId)
554
+ const written = threadId
555
+ ? await listWrittenPaths(threadId)
712
556
  : writtenPathsIn(messages.map((message) => message.content));
713
557
  const files = await filesOnDisk(written, folder);
714
558
  if (!files.length) return "";
@@ -727,12 +571,11 @@ async function filesUnder(
727
571
  * The model summarizes its own context and continues from the summary. Tools
728
572
  * are passed with calling off: providers refuse a history of tool calls
729
573
  * without the tools that made them. The run's own instructions go too: the
730
- * 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
731
575
  * cached prefix, instructions first, still matches the run's. A context too
732
576
  * long to summarise whole — the very thing a compaction is for — is tried once
733
577
  * more without the tool calls and results but the last few. A failure throws
734
- * with its cause kept: the runner parks what a retry or a smaller budget can
735
- * 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.
736
579
  */
737
580
  async function compact(
738
581
  model: LanguageModel,
@@ -747,8 +590,8 @@ async function compact(
747
590
  if (options.signal?.aborted) throw cause;
748
591
  failure = cause;
749
592
  }
750
- if (modelFailureOf(failure) === "overflow") {
751
- // 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
752
595
  const lighter = pruneMessages({
753
596
  messages,
754
597
  toolCalls: "before-last-4-messages",
@@ -824,7 +667,7 @@ function stepQueue() {
824
667
  /**
825
668
  * No more steps are coming; release waiting takes with `null`. Without
826
669
  * this, a `finish-step` whose `onStepEnd` never fires (abort, provider
827
- * error mid-step) parks the loop forever, and `answerTask` waits on
670
+ * error mid-step) parks the loop forever, and `answerThread` waits on
828
671
  * `run.done` while holding the job's lock (bot.runner).
829
672
  */
830
673
  end() {
@@ -839,7 +682,7 @@ function stepQueue() {
839
682
  * arrived for that long. Held while something other than the model does the
840
683
  * work — a tool, a compaction — since those send nothing and bound themselves;
841
684
  * `touch` starts it over. Not the sdk's `timeout.chunkMs`: that clock keeps
842
- * running while tools execute, so a long command or a borrowed bot's whole run
685
+ * running while tools execute, so a long command
843
686
  * would trip it.
844
687
  */
845
688
  function silenceWatch(ms: number) {
@@ -893,398 +736,107 @@ function resolveModel(bot: JobBot) {
893
736
  return resolveDefaultModel().then(getTextModel);
894
737
  }
895
738
 
896
- /** A whole run as one string; this is all `ask_bot` receives. A stopped run still comes back, marked as such. */
897
- export async function runBotToText(
898
- input: Extract<RunInput, { request: string }>,
899
- options: RunOptions,
900
- ): Promise<string> {
901
- let outcome = "";
902
- let failure: string | null = null;
903
- await runBot(input, {
904
- ...options,
905
- emit: async (event) => {
906
- if (
907
- event.parent === (options.parent ?? null) &&
908
- event.type === "answer"
909
- ) {
910
- outcome = event.stopped
911
- ? `${event.text}\n\n(As far as they got — they stopped before finishing.)`
912
- : event.text;
913
- }
914
- if (event.parent === (options.parent ?? null) && event.type === "error")
915
- failure = event.message;
916
- await options.emit(event);
917
- },
918
- });
919
- options.signal?.throwIfAborted();
920
- if (failure) publicError(failure);
921
- return outcome;
922
- }
923
-
924
- /**
925
- * `ask_bot` gets its execute here, not in loadTools (loadTools importing this
926
- * file would be a cycle). With no peers the tool is absent rather than refusing.
927
- * The borrowed bot's events go to the same thread under this call's id, and
928
- * `answer` serves its `ask_back` from `call.messages`, the context that
929
- * produced this call. Exchanges ride back inside the result so this thread
930
- * keeps them.
931
- */
932
- function withAskBot(
933
- tools: ToolSet,
934
- asker: {
935
- name: string;
936
- model: LanguageModel;
937
- instructions: string;
938
- /** Bots it can call; itself excluded (bot.prompt). */
939
- peers: string[];
940
- depth: number;
941
- /** The chain this bot inherited, handed down under the part it hands over. */
942
- chain: string;
943
- /** Bots above this one on the job, which are blocked waiting on it. */
944
- above: string[];
945
- options: RunOptions;
946
- emit: (event: BotEvent) => Promise<void>;
947
- },
948
- ): ToolSet {
949
- if (asker.peers.length === 0) return tools;
950
- // Calls made in the same step answer from one evolving conversation.
951
- const conversations = new WeakMap<
952
- ModelMessage[],
953
- { messages: ModelMessage[]; answer: ReturnType<typeof PromiseChain> }
954
- >();
955
-
956
- const agentTools: ToolSet = {
957
- ...tools,
958
- [TOOL_NAMES.ask_bot]: tool({
959
- description: askBotSpec.description,
960
- inputSchema: askBotSpec.parameters,
961
- execute: async ({ bot, request, context }, call) => {
962
- const found = await findJobBot(bot);
963
- if (!found) return `No bot named "${bot}". Use a name from the list.`;
964
- bot = found.name;
965
- if (bot === asker.name || asker.above.includes(bot)) {
966
- return `${bot} is already above you and waiting on this part. Use ask_back for a question, or pick another bot.`;
967
- }
968
- const signal = call.abortSignal ?? asker.options.signal;
969
- const desks = asker.options.desks!;
970
- return desks.run(asker.name, bot, signal, async () => {
971
- const session = asker.options.taskId
972
- ? botBrowserSession(asker.options.taskId, bot)
973
- : null;
974
- const exchanges: { question: string; answer: string }[] = [];
975
- let conversation = conversations.get(call.messages);
976
- if (!conversation) {
977
- conversation = {
978
- messages: [...call.messages],
979
- answer: PromiseChain(),
980
- };
981
- conversations.set(call.messages, conversation);
982
- }
983
- const current = conversation;
984
- const answer = (question: string) =>
985
- current.answer(async () => {
986
- signal?.throwIfAborted();
987
- const reply = await answerBack({
988
- model: asker.model,
989
- instructions: asker.instructions,
990
- tools: agentTools,
991
- messages: current.messages,
992
- asked: { bot, request },
993
- question,
994
- signal,
995
- });
996
- current.messages.push(
997
- {
998
- role: "user",
999
- content: `${bot} asks about the part you handed over:\n\n${question}`,
1000
- },
1001
- { role: "assistant", content: reply.text },
1002
- );
1003
- await asker.emit({ type: "answered", question, ...reply });
1004
- exchanges.push({ question, answer: reply.text });
1005
- return reply.text;
1006
- });
1007
- try {
1008
- const outcome = await runBotToText(
1009
- {
1010
- bot,
1011
- request,
1012
- context,
1013
- askedBy: asker.name,
1014
- chain: asker.chain,
1015
- above: [...asker.above, asker.name],
1016
- },
1017
- {
1018
- ...asker.options,
1019
- signal,
1020
- depth: asker.depth + 1,
1021
- parent: call.toolCallId,
1022
- session,
1023
- // Only the task's owner drains words sent by the user.
1024
- notes: undefined,
1025
- answer,
1026
- },
1027
- );
1028
- return exchanges.length
1029
- ? `${outcome}\n\n${exchangeLines(bot, exchanges)}`
1030
- : outcome;
1031
- } catch (cause) {
1032
- signal?.throwIfAborted();
1033
- publicError(
1034
- [modelErrorToString(cause), exchangeLines(bot, exchanges)]
1035
- .filter(Boolean)
1036
- .join("\n\n"),
1037
- );
1038
- }
1039
- });
1040
- },
1041
- }),
1042
- };
1043
- return agentTools;
1044
- }
1045
-
1046
- /** One execution per participant, with wait-cycle detection across concurrent branches. */
1047
- export function createBotDesks() {
1048
- const lock = createKeyedLock();
1049
- const waiting = new Map<string, Map<string, number>>();
1050
- const pending = new Set<Promise<unknown>>();
1051
- const reaches = (
1052
- from: string,
1053
- to: string,
1054
- seen = new Set<string>(),
1055
- ): boolean => {
1056
- if (from === to) return true;
1057
- if (seen.has(from)) return false;
1058
- seen.add(from);
1059
- return [...(waiting.get(from)?.keys() ?? [])].some((next) =>
1060
- reaches(next, to, seen),
1061
- );
1062
- };
1063
- return {
1064
- async run<T>(
1065
- from: string,
1066
- to: string,
1067
- signal: AbortSignal | undefined,
1068
- work: () => Promise<T>,
1069
- ): Promise<T> {
1070
- signal?.throwIfAborted();
1071
- if (reaches(to, from))
1072
- publicError(
1073
- `${to} is waiting on your work. Finish this part before asking them, or choose another bot.`,
1074
- );
1075
- const edges = waiting.get(from) ?? new Map<string, number>();
1076
- waiting.set(from, edges);
1077
- edges.set(to, (edges.get(to) ?? 0) + 1);
1078
- const run = lock(to, async () => {
1079
- signal?.throwIfAborted();
1080
- return work();
1081
- });
1082
- pending.add(run);
1083
- try {
1084
- return await run;
1085
- } finally {
1086
- pending.delete(run);
1087
- const left = (edges.get(to) ?? 1) - 1;
1088
- if (left) edges.set(to, left);
1089
- else edges.delete(to);
1090
- if (!edges.size) waiting.delete(from);
1091
- }
1092
- },
1093
- async settle() {
1094
- while (pending.size) await Promise.allSettled([...pending]);
1095
- },
1096
- };
1097
- }
1098
-
1099
- /** Appended under the answer: what the borrowed bot asked and what this bot answered. */
1100
- function exchangeLines(
1101
- bot: string,
1102
- exchanges: { question: string; answer: string }[],
1103
- ): string {
1104
- return exchanges
1105
- .map(
1106
- ({ question, answer }) =>
1107
- `(On the way ${bot} asked you: "${question}" — you answered: "${answer}")`,
1108
- )
1109
- .join("\n");
1110
- }
1111
-
1112
- /**
1113
- * A borrowed bot's tools: `ask_thursday` removed (the bot above is blocked on
1114
- * this run, so a wait here never resolves), `ask_back` added when there is an
1115
- * `answer`. prepareStep removes it once `asks.left` reaches zero.
1116
- */
1117
- function withAskBack(
1118
- tools: ToolSet,
1119
- asks: { left: number },
1120
- options: RunOptions,
1121
- ): ToolSet {
1122
- const own = { ...tools };
1123
- delete own[TOOL_NAMES.ask_thursday];
1124
- const { answer } = options;
1125
- if (!answer) return own;
1126
- return {
1127
- ...own,
1128
- [TOOL_NAMES.ask_back]: tool({
1129
- description: askBackSpec.description,
1130
- inputSchema: askBackSpec.parameters,
1131
- execute: async ({ question }) => {
1132
- asks.left -= 1;
1133
- return answer(question);
1134
- },
1135
- }),
1136
- };
1137
- }
1138
-
1139
- /** Prompt for answering a borrowed bot's `ask_back`. The blocked call is restated because the sdk's `messages` stop just before it. */
1140
- const answerInstructions = (
1141
- asked: { bot: string; request: string },
1142
- question: string,
1143
- ) => `You handed one part of your job to ${asked.bot}:
1144
-
1145
- > ${asked.request.trim()}
1146
-
1147
- Before going on, they ask you:
1148
-
1149
- > ${question.trim()}
1150
-
1151
- 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.`;
1152
-
1153
- /**
1154
- * One call, no loop: the asking bot's model over its own context plus the
1155
- * question. A provider failure returns a fallback text instead of throwing;
1156
- * only an abort propagates.
1157
- */
1158
- async function answerBack(input: {
1159
- model: LanguageModel;
1160
- instructions: string;
1161
- tools: ToolSet;
1162
- messages: ModelMessage[];
1163
- asked: { bot: string; request: string };
1164
- question: string;
1165
- signal?: AbortSignal;
1166
- }): Promise<{ text: string; usage: TokenUsage }> {
1167
- try {
1168
- const { text, usage } = await generateText({
1169
- model: input.model,
1170
- system: input.instructions,
1171
- tools: input.tools,
1172
- toolChoice: "none",
1173
- messages: [
1174
- ...input.messages,
1175
- {
1176
- role: "user",
1177
- content: answerInstructions(input.asked, input.question),
1178
- },
1179
- ],
1180
- abortSignal: input.signal,
1181
- // One-shot: nothing streams back, so its whole length is one silence
1182
- timeout: BOT_RUN.silenceMs,
1183
- });
1184
- return {
1185
- text:
1186
- text.trim() ||
1187
- "They had nothing to add. Go on with your best reading, and say what you assumed in what you hand back.",
1188
- usage: usageOf(usage),
1189
- };
1190
- } catch (cause) {
1191
- if (input.signal?.aborted) throw cause;
1192
- logger.warn(`ask_back: ${modelErrorToString(cause)}`);
1193
- return {
1194
- text: `No answer came back (${modelErrorToString(cause)}). Go on with your best reading, and say what you assumed in what you hand back.`,
1195
- usage: NO_TOKENS,
1196
- };
1197
- }
1198
- }
1199
-
1200
- /**
1201
- * The stored thread as a resuming run reads it: the rows as they were written,
1202
- * each tool call with its result, so a resumed run carries on the conversation
1203
- * it was having and a provider's prefix cache still matches from its first call.
1204
- * The one repair is a call whose result never arrived — the run was stopped
1205
- * between the two (a closed browser, a restart, a model that went quiet) and a
1206
- * call with no result is refused outright — which gets one saying so, right
1207
- * after it. An `ask_bot` call cut off that way carries what the borrowed bot
1208
- * had written by then, so its part is picked up rather than asked for again
1209
- * from nothing. Nothing else changes: `compact` is the one thing that shortens
1210
- * a bot's own thread.
1211
- */
1212
- export function resumeThread(
1213
- thread: ModelMessage[],
1214
- /** Borrowed bots' last lines, by `ask_bot` call id (task.query listBorrowedTails). */
1215
- 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(),
1216
743
  ): ModelMessage[] {
1217
- const answered = new Set<string>();
1218
- for (const message of thread) {
1219
- // 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) {
1220
751
  if (message.role !== "tool" && message.role !== "assistant") continue;
1221
- if (typeof message.content === "string") continue;
752
+ if (!Array.isArray(message.content)) continue;
1222
753
  for (const part of message.content) {
1223
- 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);
1224
757
  }
1225
758
  }
1226
-
1227
759
  const out: ModelMessage[] = [];
1228
- let owed: Extract<ModelMessage, { role: "tool" }>["content"] = [];
1229
- for (const message of thread) {
1230
- if (owed.length && message.role === "tool") {
1231
- out.push({ ...message, content: [...owed, ...message.content] });
1232
- 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 });
1233
767
  continue;
1234
768
  }
1235
- if (owed.length) {
1236
- out.push({ role: "tool", content: owed });
1237
- owed = [];
1238
- }
1239
- out.push(message);
1240
769
  if (message.role !== "assistant" || typeof message.content === "string") {
770
+ out.push(message);
1241
771
  continue;
1242
772
  }
1243
- owed = message.content.flatMap((part) =>
1244
- part.type === "tool-call" && !answered.has(part.toolCallId)
1245
- ? [
1246
- {
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
+ ? {
1247
804
  type: "tool-result" as const,
1248
805
  toolCallId: part.toolCallId,
1249
806
  toolName: part.toolName,
1250
- output: {
1251
- type: "text" as const,
1252
- value: interrupted(part, borrowed),
1253
- },
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.",
1254
834
  },
1255
- ]
1256
- : [],
1257
- );
835
+ },
836
+ );
837
+ }
838
+ if (content.length) out.push({ ...message, content });
839
+ if (owed.length) out.push({ role: "tool", content: owed });
1258
840
  }
1259
- if (owed.length) out.push({ role: "tool", content: owed });
1260
841
  return out;
1261
842
  }
1262
-
1263
- /** What a resumed run reads where a call's result never arrived (resumeThread). */
1264
- const INTERRUPTED =
1265
- "No result: the run was stopped before this call returned. Check what it did before relying on it.";
1266
-
1267
- /** INTERRUPTED, or for a borrowed bot's part, what it had written when the run stopped. */
1268
- function interrupted(
1269
- call: { toolCallId: string; toolName: string },
1270
- borrowed: ReadonlyMap<string, { bot: string; tail: string }>,
1271
- ): string {
1272
- const cut =
1273
- call.toolName === TOOL_NAMES.ask_bot
1274
- ? borrowed.get(call.toolCallId)
1275
- : undefined;
1276
- if (!cut) return INTERRUPTED;
1277
- 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.`;
1278
- }
1279
-
1280
- /** Just the text out of the shape a tool result reached the model in (ai-sdk ToolResultOutput). */
1281
- function outputText(output: unknown): string {
1282
- if (typeof output === "string") return output;
1283
- const wrapped = output as { type?: string; value?: unknown } | null;
1284
- if (wrapped && typeof wrapped === "object" && "value" in wrapped) {
1285
- return typeof wrapped.value === "string"
1286
- ? wrapped.value
1287
- : JSON.stringify(wrapped.value ?? "");
1288
- }
1289
- return JSON.stringify(output ?? "");
1290
- }