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.
- package/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +3 -2
- package/.next/build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/required-server-files.json +8 -1
- package/.next/routes-manifest.json +18 -6
- package/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- 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 +13 -13
- package/.next/server/app/_not-found.segments/_full.segment.rsc +13 -13
- 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 +10 -6
- package/.next/server/app/api/artifact/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/memory/files/route.js +5 -4
- package/.next/server/app/api/bot/memory/files/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/memory/route.js +8 -8
- package/.next/server/app/api/bot/memory/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/route.js +8 -8
- package/.next/server/app/api/bot/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/thread/[id]/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/bot/thread/[id]/route.js +15 -0
- package/.next/server/app/api/bot/thread/[id]/route.js.nft.json +1 -0
- package/.next/server/app/api/bot/thread/[id]/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/bot/thread/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/bot/thread/route.js +15 -0
- package/.next/server/app/api/bot/thread/route.js.nft.json +1 -0
- package/.next/server/app/api/bot/thread/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/config/route.js +7 -7
- package/.next/server/app/api/config/route.js.nft.json +1 -1
- package/.next/server/app/api/events/route.js +3 -3
- package/.next/server/app/api/events/route.js.nft.json +1 -1
- package/.next/server/app/api/file/[...path]/route.js +4 -4
- package/.next/server/app/api/file/[...path]/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/catalog/route.js +8 -8
- package/.next/server/app/api/llm-model/catalog/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/credits/route.js +8 -8
- package/.next/server/app/api/llm-model/credits/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/route.js +8 -8
- package/.next/server/app/api/llm-model/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/subscription/route.js +8 -8
- package/.next/server/app/api/llm-model/subscription/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/[name]/route.js +6 -6
- package/.next/server/app/api/mcp/[name]/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/oauth/callback/route.js +4 -4
- package/.next/server/app/api/mcp/oauth/callback/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/route.js +6 -6
- package/.next/server/app/api/mcp/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/tools/route.js +6 -6
- package/.next/server/app/api/mcp/tools/route.js.nft.json +1 -1
- package/.next/server/app/api/memory/edit/route.js +15 -15
- package/.next/server/app/api/memory/edit/route.js.nft.json +1 -1
- package/.next/server/app/api/memory/route.js +7 -6
- package/.next/server/app/api/memory/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/[source]/[name]/route.js +6 -6
- package/.next/server/app/api/skills/[source]/[name]/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/route.js +6 -6
- package/.next/server/app/api/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/call/route.js +10 -11
- package/.next/server/app/api/thursday/call/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/skills/route.js +9 -10
- package/.next/server/app/api/thursday/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/tool-call/route.js +15 -15
- package/.next/server/app/api/thursday/tool-call/route.js.nft.json +1 -1
- package/.next/server/app/api/workspace/missing/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/workspace/missing/route/build-manifest.json +12 -0
- package/.next/server/app/api/workspace/missing/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/workspace/missing/route.js +12 -0
- package/.next/server/app/api/workspace/missing/route.js.map +5 -0
- package/.next/server/app/api/workspace/missing/route.js.nft.json +1 -0
- package/.next/server/app/api/workspace/missing/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/workspace/route.js +5 -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 +4 -3
- 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.nft.json +1 -1
- package/.next/server/app/page/server-reference-manifest.json +67 -45
- package/.next/server/app/page.js +11 -9
- 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 +3 -2
- package/.next/server/chunks/[root-of-the-server]__0-06t3f._.js +8 -0
- package/.next/server/chunks/[root-of-the-server]__02846fb._.js +8 -0
- package/.next/server/chunks/[root-of-the-server]__05luceg._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__06--3q3._.js +8 -0
- package/.next/server/chunks/[root-of-the-server]__06l05n_._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__07bnah_._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__08eae20._.js +8 -0
- package/.next/server/chunks/[root-of-the-server]__0_ue8oe._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0btqrct._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0c9fgca._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__0h_x6q6._.js +5 -0
- package/.next/server/chunks/{[root-of-the-server]__0pa2n68._.js → [root-of-the-server]__0kxrvxf._.js} +2 -2
- package/.next/server/chunks/{[root-of-the-server]__0q0vvt2._.js → [root-of-the-server]__0ov3iir._.js} +2 -2
- package/.next/server/chunks/[root-of-the-server]__0qwtlmx._.js +8 -0
- package/.next/server/chunks/[root-of-the-server]__0sm5xvm._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0zp35ul._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__144u9d_._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__18-2tsq._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__1e-ci13._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__1e2365e._.js +8 -0
- package/.next/server/chunks/[root-of-the-server]__1fix932._.js +8 -0
- package/.next/server/chunks/{[root-of-the-server]__0ce8nf8._.js → [root-of-the-server]__1g3t7vn._.js} +3 -3
- package/.next/server/chunks/[root-of-the-server]__1kp-83u._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__1kv8p3e._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__1orn-5b._.js +2 -3
- package/.next/server/chunks/_02szwk8._.js +5 -0
- package/.next/server/chunks/_05h2cii._.js +1 -1
- package/.next/server/chunks/_06c9kie._.js +216 -0
- package/.next/server/chunks/{_1x6kd1r._.js → _07y2gj-._.js} +1 -1
- package/.next/server/chunks/_0a43a-0._.js +3 -0
- package/.next/server/chunks/_0bujjey._.js +5 -0
- package/.next/server/chunks/_0c9j6rb._.js +1 -1
- package/.next/server/chunks/_0dkd549._.js +3 -0
- package/.next/server/chunks/_0ej1kvv._.js +3 -0
- package/.next/server/chunks/_0f6ts7z._.js +3 -0
- package/.next/server/chunks/_0gm9hyv._.js +55 -0
- package/.next/server/chunks/_0i55en5._.js +25 -0
- package/.next/server/chunks/_0k50n27._.js +18 -0
- package/.next/server/chunks/_0khihjk._.js +3 -3
- package/.next/server/chunks/_0n74zsf._.js +5 -5
- package/.next/server/chunks/_0nvm9ef._.js +3 -0
- package/.next/server/chunks/_0p56fn0._.js +3 -0
- package/.next/server/chunks/_0recufk._.js +1 -1
- package/.next/server/chunks/_0xbkz10._.js +3 -0
- package/.next/server/chunks/_10tkr26._.js +3 -0
- package/.next/server/chunks/{_0ioz3nd._.js → _11u2kp7._.js} +1 -1
- package/.next/server/chunks/_11ujpya._.js +3 -0
- package/.next/server/chunks/_127nx_p._.js +5 -0
- package/.next/server/chunks/_17l5ygt._.js +1 -1
- package/.next/server/chunks/_19uauj6._.js +3 -0
- package/.next/server/chunks/_1_5mnq4._.js +5 -5
- package/.next/server/chunks/_1abl6t9._.js +55 -0
- package/.next/server/chunks/_1d0j961._.js +3 -3
- package/.next/server/chunks/_1dbixxz._.js +3 -0
- package/.next/server/chunks/_1dc_d1q._.js +98 -0
- package/.next/server/chunks/_1drgfx4._.js +5 -5
- package/.next/server/chunks/_1e48czp._.js +3 -3
- package/.next/server/chunks/_1f31u5x._.js +5 -0
- package/.next/server/chunks/_1gnjfli._.js +3 -0
- package/.next/server/chunks/_1kg70wc._.js +3 -3
- package/.next/server/chunks/_1klbcft._.js +5 -0
- package/.next/server/chunks/_1krd9rf._.js +4 -1
- package/.next/server/chunks/_1kwmzab._.js +3 -0
- package/.next/server/chunks/{_1pf4qe-._.js → _1oxj9fa._.js} +1 -1
- package/.next/server/chunks/{_1f46eie._.js → _1pcmdbb._.js} +1 -1
- package/.next/server/chunks/_1qgix-j._.js +3 -0
- package/.next/server/chunks/{lib_logger_ts_1exwjut._.js → _1rpchta._.js} +5 -5
- package/.next/server/chunks/_1vsej-s._.js +5 -0
- package/.next/server/chunks/_1yx0p_s._.js +5 -0
- package/.next/server/chunks/{_0rnf-6n._.js → _20fsr_z._.js} +6 -6
- package/.next/server/chunks/_2128hkj._.js +5 -0
- package/.next/server/chunks/_next-internal_server_app_api_bot_thread_[id]_route_actions_1fstjn2.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_bot_thread_route_actions_003ccso.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_workspace_missing_route_actions_1fidi6p.js +3 -0
- package/.next/server/chunks/config_ts_1g4sz5o._.js +1 -1
- package/.next/server/chunks/features_0-cnt0l._.js +4 -0
- package/.next/server/chunks/features_0hdaci0._.js +4 -0
- package/.next/server/chunks/features_1e39j7-._.js +8 -0
- package/.next/server/chunks/features_bot_bot_query_ts_12mgkl2._.js +1 -1
- package/.next/server/chunks/features_memory_1uqvksn._.js +1 -1
- 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
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_17k8irj._.js → node_modules_consola_dist_chunks_prompt_mjs_0gzvgfv._.js} +1 -1
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0i7cvml._.js → node_modules_consola_dist_chunks_prompt_mjs_107pxfn._.js} +1 -1
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0icqaqw._.js → node_modules_consola_dist_chunks_prompt_mjs_15hbel8._.js} +1 -1
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_18i-23e._.js +3 -0
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1bewgf3._.js +3 -0
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1nlww1m._.js +3 -0
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1q77nfx._.js +3 -0
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1xqdzx_._.js +3 -0
- package/.next/server/chunks/{node_modules_zod_v4_1801ax7._.js → node_modules_zod_v4_0lkux5-._.js} +2 -2
- package/.next/server/chunks/{node_modules_zod_v4_1cha_bw._.js → node_modules_zod_v4_1jmr9cq._.js} +2 -2
- package/.next/server/chunks/{node_modules_zod_v4_0ubdflj._.js → node_modules_zod_v4_1yrxmek._.js} +2 -2
- package/.next/server/chunks/node_modules_zod_v4_classic_errors_0tp0y-q.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0-e011f._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__09q9med._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0lyi8i2._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0pd8yr7._.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]__18dthbp._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__18e68w8._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1mzi-ju._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__1nygfik._.js +5 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1sdphb0._.js +8 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1vdmfbe._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1waa3rc._.js +85 -0
- package/.next/server/chunks/ssr/_0-2qsxo._.js +3 -0
- package/.next/server/chunks/ssr/_00rehl_._.js +3 -0
- package/.next/server/chunks/ssr/{_1lz_cq2._.js → _02oc_h5._.js} +76 -97
- package/.next/server/chunks/ssr/_05yghx6._.js +3 -0
- package/.next/server/chunks/ssr/_07nbwap._.js +3 -0
- package/.next/server/chunks/ssr/_08c4z-9._.js +3 -0
- package/.next/server/chunks/ssr/_0_qfdlw._.js +4 -0
- package/.next/server/chunks/ssr/{_0m2lsd1._.js → _0b6_jv-._.js} +2 -2
- package/.next/server/chunks/ssr/_0evkpto._.js +3 -0
- package/.next/server/chunks/ssr/_0geg212._.js +3 -0
- package/.next/server/chunks/ssr/{_1jdgjv9._.js → _0hxt2ul._.js} +2 -2
- package/.next/server/chunks/ssr/_0i_6q1w._.js +2 -2
- package/.next/server/chunks/ssr/_0lu1n_x._.js +1 -1
- package/.next/server/chunks/ssr/_0no7i1o._.js +3 -0
- package/.next/server/chunks/ssr/_0tzg--b._.js +3 -0
- package/.next/server/chunks/ssr/{_15j2_8f._.js → _0wzaht-._.js} +2 -2
- package/.next/server/chunks/ssr/_0z-g0uy._.js +3 -0
- package/.next/server/chunks/ssr/_114ylv7._.js +7 -0
- package/.next/server/chunks/ssr/_123y-ah._.js +3 -0
- package/.next/server/chunks/ssr/_12c-iwh._.js +3 -0
- package/.next/server/chunks/ssr/_12w42gi._.js +3 -0
- package/.next/server/chunks/ssr/_161kqav._.js +3 -0
- package/.next/server/chunks/ssr/_167hchk._.js +3 -0
- package/.next/server/chunks/ssr/{_0ap2fzn._.js → _16c6_xm._.js} +2 -2
- package/.next/server/chunks/ssr/_18opah7._.js +3 -0
- package/.next/server/chunks/ssr/_1_krfph._.js +3 -0
- package/.next/server/chunks/ssr/{_0rzucul._.js → _1_x5spz._.js} +3 -3
- package/.next/server/chunks/ssr/_1a61fc9._.js +3 -0
- package/.next/server/chunks/ssr/_1ey7qn-._.js +70 -0
- package/.next/server/chunks/ssr/_1g9vs7q._.js +3 -0
- package/.next/server/chunks/ssr/_1jmz33w._.js +3 -0
- package/.next/server/chunks/ssr/_1rgtxhr._.js +5 -0
- package/.next/server/chunks/ssr/_1slp25y._.js +3 -0
- package/.next/server/chunks/ssr/_1w_t7h7._.js +101 -0
- package/.next/server/chunks/ssr/{_0s99bgb._.js → _1wyfaq_._.js} +9 -3
- package/.next/server/chunks/ssr/_200gt-m._.js +102 -0
- package/.next/server/chunks/ssr/components_ui_tabs_tsx_0ej40b4._.js +3 -0
- package/.next/server/chunks/ssr/features_bot_thread_query_ts_023eklt._.js +4 -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/features_workspace_00jz2hu._.js +8 -0
- package/.next/server/chunks/ssr/{_1-teee5._.js → lib_logger_ts_0-olsze._.js} +2 -7
- package/.next/server/chunks/ssr/{node_modules_05-n6dz._.js → node_modules_02ai7br._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1chvq5f._.js → node_modules_02deexj._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0m383r2._.js → node_modules_07za262._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_07dsgkx._.js → node_modules_0_s9get._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1a0kmjl._.js → node_modules_0n3c7h0._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0zeg3x_._.js → node_modules_0od6jwr._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1y79i83._.js → node_modules_0rdt7pk._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_17bwo43._.js → node_modules_0tusx4d._.js} +3 -3
- package/.next/server/chunks/ssr/{node_modules_04sksn9._.js → node_modules_0w1z7kz._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1uyk91w._.js → node_modules_0wdupfz._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0ifp389._.js → node_modules_1720w0a._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0u9da1h._.js → node_modules_18lsg7d._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1k6n600._.js → node_modules_1b94fxf._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_20c28js._.js → node_modules_1ecdo1s._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0fkeku3._.js → node_modules_1fa9ag7._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1j8j4pt._.js → node_modules_1fv-_si._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0-dr_o7._.js → node_modules_1hf461g._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1lz0oxc._.js → node_modules_1m71ta-._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1t77e4n._.js → node_modules_1n93n-g._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_16mkxuo._.js → node_modules_1n9lqu9._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1k7z2j6._.js → node_modules_1npnsvd._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_056qgf-._.js → node_modules_1rn2r0n._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0x3-ply._.js → node_modules_1seuurl._.js} +2 -2
- package/.next/server/chunks/ssr/{node_modules_0bvopy2._.js → node_modules_1vq-mfk._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0iur59s._.js → node_modules_1vzyw1t._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1k8j8n0._.js → node_modules_1w359w4._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1x0ztf4._.js → node_modules_1yajbl6._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1-y7nni._.js → node_modules_20eqhy8._.js} +1 -1
- 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
- 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
- package/.next/server/chunks/ssr/{node_modules_@vercel_oidc_dist_1o553h-._.js → node_modules_@vercel_oidc_dist_05itbb1._.js} +1 -1
- package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_trash_mjs_0px2gzr._.js +3 -0
- 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 +68 -46
- package/.next/static/chunks/07vqbk_9t4em4.js +2 -0
- package/.next/static/chunks/08krja1zx7nqg.js +1 -0
- package/.next/static/chunks/0g4b348l0pve7.js +1 -0
- package/.next/static/chunks/0qcuwdk132jjt.js +1 -0
- package/.next/static/chunks/0vlgzqo9ka90v.js +1 -0
- package/.next/static/chunks/{11r2yp_yh9_qn.js → 0z69gvysmave7.js} +1 -1
- package/.next/static/chunks/0zrupxgci5gqc.js +1 -0
- package/.next/static/chunks/12fvz1w5ows75.js +1 -0
- package/.next/static/chunks/14t03ned286iu.js +1 -0
- package/.next/static/chunks/156xxbkrz5lzu.js +18 -0
- package/.next/static/chunks/1fz14reod4lft.js +1 -0
- package/.next/static/chunks/1g23dbabl-vkr.js +1 -0
- package/.next/static/chunks/1ohslk4568tm3.js +1 -0
- package/.next/static/chunks/1qy_rc3oxfxok.js +1 -0
- package/.next/static/chunks/1sbmwswc9k2-1.js +1 -0
- package/.next/static/chunks/1vqezbhjm3goq.js +1 -0
- package/.next/static/chunks/1zea7qdl-7365.js +1 -0
- package/.next/static/chunks/1zvqumkg7kmo0.css +1 -0
- package/.next/static/chunks/26hpqt3y6bogl.js +12 -0
- package/.next/static/chunks/2_j5mqm6d-z18.js +99 -0
- package/.next/static/chunks/2dt6tu7a22svs.js +1 -0
- package/.next/static/chunks/2h5ls3d86kya6.js +1 -0
- package/.next/static/chunks/2wc6y1ht6dfuy.js +1 -0
- package/.next/static/chunks/{435atsnhs7ksu.js → 2xr0zpll7thyi.js} +2 -2
- package/.next/static/chunks/31jztc-yq30m7.js +1 -0
- package/.next/static/chunks/391af0i8eljzk.js +1 -0
- package/.next/static/chunks/3bfwpww3m3x4u.js +1 -0
- package/.next/static/chunks/{38gweqdo4jpfj.js → 3izg0a3k87q1-.js} +5 -5
- package/.next/static/chunks/3p2s2cjokx1aa.js +5 -0
- package/.next/static/chunks/{408ueu4hfvcg7.js → 41s3fml5oivsa.js} +1 -1
- package/README.md +45 -193
- package/app/api/events/app-event.ts +6 -4
- package/app/api/query-key.ts +25 -16
- package/bin/database.mjs +37 -0
- package/bin/thursday.mjs +44 -28
- package/components/ui/custom-icon.tsx +0 -60
- package/components/ui/radio-group.tsx +37 -0
- package/components/ui/shiny-text.tsx +33 -6
- package/database/migrations/{20260910173049_numerous_spirit → 20260916145701_clever_multiple_man}/migration.sql +80 -24
- package/database/migrations/{20260910173049_numerous_spirit → 20260916145701_clever_multiple_man}/snapshot.json +689 -110
- package/database/migrations/20260916160713_thread_cancelled/migration.sql +2 -0
- package/database/migrations/{20260911030320_opposite_jack_power → 20260916160713_thread_cancelled}/snapshot.json +706 -84
- package/docs/how-it-works.md +40 -35
- package/docs/images/keeps-going.png +0 -0
- package/docs/images/two-minds.png +0 -0
- package/docs/live-calls.md +236 -0
- package/docs/thread-rooms.md +71 -0
- package/features/ai/chatgpt.ts +1 -2
- package/features/ai/live.schema.ts +128 -0
- package/features/ai/load-tools.ts +152 -79
- package/features/ai/model.schema.ts +2 -120
- package/features/ai/model.ts +52 -88
- package/features/ai/prompts/bot.prompt.ts +91 -214
- package/features/ai/prompts/live.prompt.ts +169 -0
- package/features/ai/prompts/prompt-helper.ts +92 -32
- package/features/ai/prompts/thursday.prompt.ts +164 -190
- package/features/ai/tools/bot.tool.ts +38 -145
- package/features/ai/tools/call.tool.ts +33 -5
- package/features/ai/tools/connected.ts +5 -3
- package/features/ai/tools/mcp.tool.ts +7 -4
- package/features/ai/tools/memory.tool.ts +35 -31
- package/features/ai/tools/skills.tool.ts +11 -6
- package/features/ai/tools/studio.tool.ts +26 -21
- package/features/ai/tools/tool-name.ts +3 -5
- package/features/ai/tools/workspace.tool.ts +33 -10
- package/features/artifact/artifact.action.ts +2 -2
- package/features/artifact/artifact.query.ts +112 -61
- package/features/artifact/artifact.schema.ts +8 -6
- package/features/artifact/components/artifact-setting.tsx +83 -33
- package/features/bot/bot.action.ts +43 -36
- package/features/bot/bot.memory.ts +137 -20
- package/features/bot/bot.query.ts +28 -11
- package/features/bot/bot.run.ts +296 -744
- package/features/bot/bot.runner.ts +463 -864
- package/features/bot/bot.schema.ts +93 -82
- package/features/bot/bot.seed.ts +141 -64
- package/features/bot/components/bot-mark.tsx +324 -232
- package/features/bot/components/bot-room.tsx +1613 -697
- package/features/bot/components/bot-roster.tsx +8 -13
- package/features/bot/components/bot-setting.tsx +58 -42
- package/features/bot/components/bot-tool.tsx +55 -87
- package/features/bot/components/mark-palette.tsx +35 -32
- package/features/bot/components/path-chips.tsx +33 -3
- package/features/bot/components/thread-badge.tsx +34 -0
- package/features/bot/components/thread-reply.tsx +639 -0
- package/features/bot/components/thread-setting.tsx +674 -0
- package/features/bot/mark.const.ts +79 -31
- package/features/bot/room.query.ts +889 -0
- package/features/bot/room.schema.ts +64 -0
- package/features/bot/seed-bots.ts +2 -1
- package/features/bot/thread.query.ts +971 -0
- package/features/bot/thread.store.ts +587 -0
- package/features/config/components/voice-key.tsx +113 -88
- package/features/config/config.const.ts +5 -9
- package/features/connectors/mcp.schema.ts +0 -2
- package/features/intro/components/intro.tsx +169 -139
- package/features/memory/components/memory-setting.tsx +2 -7
- package/features/memory/memory.schema.ts +14 -23
- package/features/settings/components/setting-ui.tsx +0 -44
- package/features/settings/components/settings.tsx +12 -12
- package/features/settings/settings.alert.ts +3 -3
- package/features/settings/settings.store.ts +1 -1
- package/features/skills/skills.schema.ts +0 -2
- package/features/thursday/ascii.const.ts +74 -0
- package/features/thursday/components/ascii-field.tsx +24 -44
- package/features/thursday/components/ascii-orb.tsx +525 -381
- package/features/thursday/components/call-log.tsx +46 -44
- package/features/thursday/components/face.tsx +40 -10
- package/features/thursday/components/side-captions.tsx +273 -0
- package/features/thursday/components/tab-state.tsx +14 -6
- package/features/thursday/components/thursday-mark.tsx +1 -0
- package/features/thursday/components/thursday-setting.tsx +633 -333
- package/features/thursday/components/thursday.tsx +315 -274
- package/features/thursday/components/voice-picker.tsx +255 -0
- package/features/thursday/face.store.ts +5 -0
- package/features/thursday/thursday.action.ts +96 -72
- package/features/thursday/thursday.query.ts +49 -30
- package/features/thursday/thursday.schema.ts +37 -35
- package/features/thursday/thursday.store.ts +14 -7
- package/features/thursday/tool-call.ts +3 -3
- package/features/thursday/tool-line.ts +5 -3
- package/features/thursday/use-thursday.ts +801 -457
- package/features/workspace/file-kind.ts +9 -0
- package/features/workspace/workspace.query.ts +17 -0
- package/features/workspace/workspace.ts +82 -36
- package/hooks/use-hotkey.ts +3 -2
- package/lib/live/live.schema.ts +26 -0
- package/lib/live/live.server.ts +153 -0
- package/lib/live/live.session.ts +574 -0
- package/lib/{realtime/realtime.tap.ts → live/live.tap.ts} +41 -12
- package/lib/live/live.transport.ts +152 -0
- package/lib/protocol/use-server-pages.ts +9 -5
- package/lib/utils.ts +21 -20
- package/package.json +2 -2
- package/public/voices/alloy.ogg +0 -0
- package/public/voices/ash.ogg +0 -0
- package/public/voices/ballad.ogg +0 -0
- package/public/voices/beacon.ogg +0 -0
- package/public/voices/bossa.ogg +0 -0
- package/public/voices/cedar.ogg +0 -0
- package/public/voices/cinder.ogg +0 -0
- package/public/voices/coral.ogg +0 -0
- package/public/voices/delta.ogg +0 -0
- package/public/voices/echo.ogg +0 -0
- package/public/voices/gleam.ogg +0 -0
- package/public/voices/marin.ogg +0 -0
- package/public/voices/meridian.ogg +0 -0
- package/public/voices/quartz.ogg +0 -0
- package/public/voices/ripple.ogg +0 -0
- package/public/voices/sage.ogg +0 -0
- package/public/voices/shimmer.ogg +0 -0
- package/public/voices/stone.ogg +0 -0
- package/public/voices/tempo.ogg +0 -0
- package/public/voices/verse.ogg +0 -0
- package/public/voices/vesper.ogg +0 -0
- package/public/voices/willow.ogg +0 -0
- package/scripts/artifact-paths.test.mts +38 -0
- package/scripts/bot-context.test.mts +1408 -0
- package/scripts/live-history.test.mts +115 -0
- package/scripts/live-session.test.mts +938 -0
- package/server.js +1 -1
- package/skills/browser/SKILL.md +11 -1
- package/skills/computer/SKILL.md +1 -1
- package/skills/find-skills/SKILL.md +3 -8
- package/skills/interactive-page/SKILL.md +12 -7
- package/skills/interactive-page/references/diagram.md +89 -0
- package/skills/interactive-page/scripts/archify/LICENSE +22 -0
- package/skills/interactive-page/scripts/archify/README.md +25 -0
- package/skills/interactive-page/scripts/archify/assets/JetBrainsMono-OFL.txt +93 -0
- package/skills/interactive-page/scripts/archify/assets/template.html +14934 -0
- package/skills/interactive-page/scripts/archify/bin/archify.mjs +2126 -0
- package/skills/interactive-page/scripts/archify/bin/open-artifact.mjs +86 -0
- package/skills/interactive-page/scripts/archify/bin/preview.mjs +653 -0
- package/skills/interactive-page/scripts/archify/bin/visual-check.mjs +829 -0
- package/skills/interactive-page/scripts/archify/delta/architecture-delta.mjs +1223 -0
- package/skills/interactive-page/scripts/archify/examples/agent-run.lifecycle.json +60 -0
- package/skills/interactive-page/scripts/archify/examples/agent-tool-call.workflow.json +94 -0
- package/skills/interactive-page/scripts/archify/examples/async-job-roundtrip.sequence.json +61 -0
- package/skills/interactive-page/scripts/archify/examples/cache-miss-request.sequence.json +82 -0
- package/skills/interactive-page/scripts/archify/examples/checkout-platform.base.architecture.json +31 -0
- package/skills/interactive-page/scripts/archify/examples/checkout-platform.head.architecture.json +31 -0
- package/skills/interactive-page/scripts/archify/examples/deployment-release.lifecycle.json +49 -0
- package/skills/interactive-page/scripts/archify/examples/event-stream.dataflow.json +57 -0
- package/skills/interactive-page/scripts/archify/examples/incident-response.workflow.json +64 -0
- package/skills/interactive-page/scripts/archify/examples/product-analytics.dataflow.json +76 -0
- package/skills/interactive-page/scripts/archify/examples/production-deployment.architecture.json +71 -0
- package/skills/interactive-page/scripts/archify/examples/release-delivery.workflow.json +62 -0
- package/skills/interactive-page/scripts/archify/examples/web-app.architecture.json +46 -0
- package/skills/interactive-page/scripts/archify/migrations/workflow-v2.mjs +279 -0
- package/skills/interactive-page/scripts/archify/recipes/scenarios.mjs +284 -0
- package/skills/interactive-page/scripts/archify/references/authoring-contract.md +243 -0
- package/skills/interactive-page/scripts/archify/references/delivery-contract.md +120 -0
- package/skills/interactive-page/scripts/archify/references/viewer-runtime.md +45 -0
- package/skills/interactive-page/scripts/archify/renderers/architecture/grid.mjs +62 -0
- package/skills/interactive-page/scripts/archify/renderers/architecture/render-architecture.mjs +1078 -0
- package/skills/interactive-page/scripts/archify/renderers/dataflow/README.md +104 -0
- package/skills/interactive-page/scripts/archify/renderers/dataflow/render-dataflow.mjs +483 -0
- package/skills/interactive-page/scripts/archify/renderers/lifecycle/README.md +115 -0
- package/skills/interactive-page/scripts/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
- package/skills/interactive-page/scripts/archify/renderers/sequence/README.md +114 -0
- package/skills/interactive-page/scripts/archify/renderers/sequence/render-sequence.mjs +464 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/brand-marks.mjs +563 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/cli.mjs +218 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/desktop-readability.mjs +26 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/diagnostics.mjs +127 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/engineering-profiles.mjs +157 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/generated-brand-marks.mjs +3 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/generated-validators.mjs +13 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/geometry.mjs +1423 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/i18n.mjs +595 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/layout-report.mjs +40 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/legend.mjs +217 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/output-path.mjs +340 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/repository-evidence.mjs +238 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/repository-location.mjs +58 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/text-fit.mjs +49 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/utils.mjs +232 -0
- package/skills/interactive-page/scripts/archify/renderers/shared/validator.mjs +86 -0
- package/skills/interactive-page/scripts/archify/renderers/workflow/README.md +223 -0
- package/skills/interactive-page/scripts/archify/renderers/workflow/render-workflow.mjs +35 -0
- package/skills/interactive-page/scripts/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
- package/skills/interactive-page/scripts/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
- package/skills/interactive-page/scripts/archify/schemas/README.md +211 -0
- package/skills/interactive-page/scripts/archify/schemas/architecture.schema.json +178 -0
- package/skills/interactive-page/scripts/archify/schemas/common.schema.json +115 -0
- package/skills/interactive-page/scripts/archify/schemas/dataflow.schema.json +243 -0
- package/skills/interactive-page/scripts/archify/schemas/lifecycle.schema.json +266 -0
- package/skills/interactive-page/scripts/archify/schemas/sequence.schema.json +223 -0
- package/skills/interactive-page/scripts/archify/schemas/workflow.schema.json +428 -0
- package/skills/interactive-page/scripts/archify/scripts/check-render-output.mjs +916 -0
- package/skills/interactive-page/scripts/archify/scripts/render-examples.mjs +26 -0
- package/skills/interactive-page/scripts/bundle-artifact.sh +9 -8
- package/skills/skill-creator/SKILL.md +4 -5
- package/.next/server/app/api/bot/task/[id]/route/app-paths-manifest.json +0 -3
- package/.next/server/app/api/bot/task/[id]/route.js +0 -16
- package/.next/server/app/api/bot/task/[id]/route.js.nft.json +0 -1
- package/.next/server/app/api/bot/task/[id]/route_client-reference-manifest.js +0 -3
- package/.next/server/app/api/bot/task/route/app-paths-manifest.json +0 -3
- package/.next/server/app/api/bot/task/route.js +0 -16
- package/.next/server/app/api/bot/task/route.js.nft.json +0 -1
- package/.next/server/app/api/bot/task/route_client-reference-manifest.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__026u77e._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__02va5wz._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__050zri4._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0bg9710._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0ffbg85._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0in-d5s._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0ncs9uu._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0pa9k-f._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__0qi_m0e._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__0tq0m_k._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0xep6j_._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1-ga0kh._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__11n256_._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__144l7qt._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__1_04mcf._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__1anc1p9._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1fizn31._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1jg8t86._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__1lldzy9._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1llj3-b._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1p8m3f4._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1peqvsi._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1w8gm5h._.js +0 -10
- package/.next/server/chunks/[root-of-the-server]__1xt39nn._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__1zb8mfo._.js +0 -8
- package/.next/server/chunks/_0-73of8._.js +0 -55
- package/.next/server/chunks/_0-fjql4._.js +0 -31
- package/.next/server/chunks/_00xh_q0._.js +0 -25
- package/.next/server/chunks/_051qh8u._.js +0 -3
- package/.next/server/chunks/_05sovvj._.js +0 -3
- package/.next/server/chunks/_091bi8d._.js +0 -3
- package/.next/server/chunks/_0fzqt2y._.js +0 -142
- package/.next/server/chunks/_0lr92vd._.js +0 -3
- package/.next/server/chunks/_0mecv0f._.js +0 -31
- package/.next/server/chunks/_0mh8j4m._.js +0 -3
- package/.next/server/chunks/_0sb7tlz._.js +0 -3
- package/.next/server/chunks/_0si7xzn._.js +0 -3
- package/.next/server/chunks/_0u10wmw._.js +0 -55
- package/.next/server/chunks/_0wp4le3._.js +0 -3
- package/.next/server/chunks/_0y5fg-x._.js +0 -3
- package/.next/server/chunks/_1230wbx._.js +0 -10
- package/.next/server/chunks/_12nheja._.js +0 -3
- package/.next/server/chunks/_13510xh._.js +0 -3
- package/.next/server/chunks/_15hq89o._.js +0 -3
- package/.next/server/chunks/_18-1wcc._.js +0 -55
- package/.next/server/chunks/_18d7-5p._.js +0 -3
- package/.next/server/chunks/_1c78vw5._.js +0 -237
- package/.next/server/chunks/_1h5_8sk._.js +0 -3
- package/.next/server/chunks/_1hdnxb7._.js +0 -3
- package/.next/server/chunks/_1j8k_j5._.js +0 -3
- package/.next/server/chunks/_1nula3v._.js +0 -3
- package/.next/server/chunks/_1php4eq._.js +0 -3
- package/.next/server/chunks/_1yuv4vp._.js +0 -3
- package/.next/server/chunks/_1zkfbaa._.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_bot_task_[id]_route_actions_1_k_8u3.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_bot_task_route_actions_0xln6ra.js +0 -3
- package/.next/server/chunks/features_0rpfxbk._.js +0 -3
- package/.next/server/chunks/features_1qf3cei._.js +0 -8
- package/.next/server/chunks/features_bot_0zt92oh._.js +0 -3
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1jidylq._.js +0 -3
- package/.next/server/chunks/ssr/[externals]__0dizgek._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0-nob52._.js +0 -4
- package/.next/server/chunks/ssr/[root-of-the-server]__092knlj._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0mz33a7._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0s4wb56._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0s9dq5u._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0sd3izg._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0vnmv-s._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__1-13tcn._.js +0 -43
- package/.next/server/chunks/ssr/[root-of-the-server]__10-dz5o._.js +0 -61
- package/.next/server/chunks/ssr/[root-of-the-server]__1zidpfl._.js +0 -3
- package/.next/server/chunks/ssr/_001_ms1._.js +0 -33
- package/.next/server/chunks/ssr/_01n3o0m._.js +0 -3
- package/.next/server/chunks/ssr/_04z2c7b._.js +0 -3
- package/.next/server/chunks/ssr/_06tssji._.js +0 -3
- package/.next/server/chunks/ssr/_0fk4c7d._.js +0 -3
- package/.next/server/chunks/ssr/_0h42fze._.js +0 -3
- package/.next/server/chunks/ssr/_0h7zdni._.js +0 -3
- package/.next/server/chunks/ssr/_0mggveb._.js +0 -3
- package/.next/server/chunks/ssr/_0nbpc_8._.js +0 -3
- package/.next/server/chunks/ssr/_0q0zan6._.js +0 -70
- package/.next/server/chunks/ssr/_0zcjrbg._.js +0 -3
- package/.next/server/chunks/ssr/_0zhoc3r._.js +0 -3
- package/.next/server/chunks/ssr/_11j92tu._.js +0 -3
- package/.next/server/chunks/ssr/_13fke0x._.js +0 -3
- package/.next/server/chunks/ssr/_18cymkq._.js +0 -3
- package/.next/server/chunks/ssr/_1ebxla1._.js +0 -3
- package/.next/server/chunks/ssr/_1nj4niv._.js +0 -42
- package/.next/server/chunks/ssr/_1ov37if._.js +0 -3
- package/.next/server/chunks/ssr/_1ovmc9s._.js +0 -3
- package/.next/server/chunks/ssr/_1sji22n._.js +0 -3
- package/.next/server/chunks/ssr/_1vi_uo7._.js +0 -3
- package/.next/server/chunks/ssr/_1vyd1ji._.js +0 -3
- package/.next/server/chunks/ssr/_1xx4zlb._.js +0 -3
- package/.next/server/chunks/ssr/components_ui_1y1l7yj._.js +0 -3
- package/.next/server/chunks/ssr/features_bot_1s_-2vj._.js +0 -3
- package/.next/server/chunks/ssr/lib_realtime_12wg_dd._.js +0 -27
- package/.next/server/chunks/ssr/lib_realtime_15h_v1v._.js +0 -27
- package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_0npjf8-._.js +0 -3
- package/.next/static/chunks/0n3jle9kbqfr2.js +0 -40
- package/.next/static/chunks/0r5qcctujhxli.js +0 -1
- package/.next/static/chunks/10teww7wt-f6h.js +0 -1
- package/.next/static/chunks/126fj4pga_72k.js +0 -1
- package/.next/static/chunks/159udiexf6dom.js +0 -2
- package/.next/static/chunks/18a96rkhi4_yd.js +0 -1
- package/.next/static/chunks/18gi7o2ibrgh8.js +0 -3
- package/.next/static/chunks/1dkjg87pxk0n9.js +0 -28
- package/.next/static/chunks/1q9z1tohil7xn.js +0 -1
- package/.next/static/chunks/1sjgfets224re.js +0 -1
- package/.next/static/chunks/1wszsr83t01gh.js +0 -1
- package/.next/static/chunks/22_zkas21rpna.js +0 -1
- package/.next/static/chunks/2fh4-3z_q8b_w.js +0 -1
- package/.next/static/chunks/2h0sd2vumhb15.js +0 -1
- package/.next/static/chunks/2szintyibu0s5.js +0 -1
- package/.next/static/chunks/2t4vhi3m8ahya.js +0 -1
- package/.next/static/chunks/2tigdtz3dpcy8.js +0 -1
- package/.next/static/chunks/3-b6b7entqazs.js +0 -1
- package/.next/static/chunks/3786h5vayt80-.js +0 -28
- package/.next/static/chunks/3cq5g494yuke8.css +0 -1
- package/.next/static/chunks/3dmbhsv2u_8kj.js +0 -1
- package/.next/static/chunks/3hibbjnt85-k6.js +0 -1
- package/.next/static/chunks/3i0a5px-oufgc.js +0 -1
- package/.next/static/chunks/3wexk9qo8zuj3.js +0 -18
- package/.next/static/chunks/3xk7yqgomx7yq.js +0 -1
- package/.next/static/chunks/3xre8bu_pk1p_.js +0 -1
- package/.next/static/chunks/3zv6t3i77xhr_.js +0 -1
- package/database/migrations/20260911030320_opposite_jack_power/migration.sql +0 -1
- package/docs/images/task-thread.png +0 -0
- package/features/bot/components/task-badge.tsx +0 -42
- package/features/bot/components/task-reply.tsx +0 -328
- package/features/bot/components/task-setting.tsx +0 -524
- package/features/bot/task.query.ts +0 -1079
- package/features/bot/task.store.ts +0 -487
- package/lib/realtime/client-secret.ts +0 -86
- package/lib/realtime/open-session.ts +0 -31
- package/lib/realtime/openai.ts +0 -35
- package/lib/realtime/realtime.audio.ts +0 -196
- package/lib/realtime/realtime.driver.ts +0 -489
- package/lib/realtime/realtime.protocol.ts +0 -393
- package/lib/realtime/realtime.schema.ts +0 -37
- package/lib/realtime/realtime.session.ts +0 -106
- package/lib/realtime/realtime.transport.ts +0 -384
- package/lib/realtime/xai.ts +0 -39
- /package/.next/server/app/api/bot/{task → thread}/[id]/route/build-manifest.json +0 -0
- /package/.next/server/app/api/bot/{task → thread}/[id]/route/server-reference-manifest.json +0 -0
- /package/.next/server/app/api/bot/{task → thread}/[id]/route.js.map +0 -0
- /package/.next/server/app/api/bot/{task → thread}/route/build-manifest.json +0 -0
- /package/.next/server/app/api/bot/{task → thread}/route/server-reference-manifest.json +0 -0
- /package/.next/server/app/api/bot/{task → thread}/route.js.map +0 -0
- /package/.next/static/{G6-gNoyGDEcgAsq-L4PE1 → yyRVBc0xxYQsh3GpbQX9b}/_buildManifest.js +0 -0
- /package/.next/static/{G6-gNoyGDEcgAsq-L4PE1 → yyRVBc0xxYQsh3GpbQX9b}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{G6-gNoyGDEcgAsq-L4PE1 → yyRVBc0xxYQsh3GpbQX9b}/_ssgManifest.js +0 -0
package/features/bot/bot.run.ts
CHANGED
|
@@ -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
|
-
|
|
20
|
+
isContextOverflow,
|
|
21
|
+
isProviderRefusal,
|
|
22
22
|
modelErrorToString,
|
|
23
|
-
modelFailureOf,
|
|
24
23
|
resolveDefaultModel,
|
|
25
24
|
} from "@/features/ai/model";
|
|
26
|
-
import {
|
|
27
|
-
|
|
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
|
-
|
|
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
|
-
| {
|
|
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
|
-
/**
|
|
112
|
-
| { type: "
|
|
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
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
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: "
|
|
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
|
|
141
|
-
export type
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
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
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
-
|
|
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
|
-
|
|
283
|
-
|
|
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
|
|
289
|
-
// refused as too long lowers
|
|
290
|
-
//
|
|
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
|
-
|
|
174
|
+
options.contextBudget || Number.POSITIVE_INFINITY,
|
|
294
175
|
);
|
|
295
176
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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
|
-
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
-
|
|
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
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
/**
|
|
426
|
-
let last
|
|
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
|
-
/**
|
|
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
|
-
|
|
353
|
+
logger.error(`${name}: the model run broke`, cause);
|
|
441
354
|
const message = modelErrorToString(cause);
|
|
442
|
-
|
|
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(
|
|
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
|
-
|
|
463
|
-
|
|
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
|
-
|
|
571
|
-
|
|
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
|
-
|
|
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: "
|
|
626
|
-
text: last
|
|
627
|
-
|
|
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 —
|
|
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
|
|
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
|
|
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
|
-
*
|
|
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
|
-
|
|
549
|
+
threadId: string | null,
|
|
706
550
|
messages: ModelMessage[],
|
|
707
551
|
folder: string | null,
|
|
708
552
|
): Promise<string> {
|
|
709
553
|
try {
|
|
710
|
-
const written =
|
|
711
|
-
? await listWrittenPaths(
|
|
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
|
|
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
|
|
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 (
|
|
751
|
-
// pruneMessages drops a call together with its result, so the
|
|
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 `
|
|
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
|
|
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
|
-
/**
|
|
897
|
-
export
|
|
898
|
-
|
|
899
|
-
|
|
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
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
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 (
|
|
752
|
+
if (!Array.isArray(message.content)) continue;
|
|
1222
753
|
for (const part of message.content) {
|
|
1223
|
-
if (part.type
|
|
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
|
-
|
|
1229
|
-
for (const message of
|
|
1230
|
-
if (
|
|
1231
|
-
|
|
1232
|
-
|
|
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 =
|
|
1244
|
-
|
|
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
|
-
|
|
1252
|
-
|
|
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
|
-
}
|