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