thursday-agent 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +6 -5
- 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 +5 -5
- 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 +13 -13
- 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 +10 -11
- 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 -4
- 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 +10 -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]__02846fb._.js +8 -0
- package/.next/server/chunks/[root-of-the-server]__049f521._.js +3 -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]__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]__0c9fgca._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__0g-ueei._.js +8 -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]__0ye3da2._.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/_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_0r28-sm._.js → node_modules_consola_dist_chunks_prompt_mjs_0a7-pbw._.js} +1 -1
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0v_7drq._.js → node_modules_consola_dist_chunks_prompt_mjs_0gzvgfv._.js} +1 -1
- 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_1kipdeo._.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]__09q9med._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0bxsfyn._.js +101 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0lyi8i2._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__16jc196._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__172cq4x._.js +3 -0
- 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]__1fll8dc._.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]__1waa3rc._.js +100 -0
- package/.next/server/chunks/ssr/_00rehl_._.js +3 -0
- package/.next/server/chunks/ssr/{_1lz_cq2._.js → _02oc_h5._.js} +76 -89
- package/.next/server/chunks/ssr/_03_928f._.js +3 -0
- package/.next/server/chunks/ssr/_05yghx6._.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/_0c9e5w6._.js +9 -0
- package/.next/server/chunks/ssr/{_0s99bgb._.js → _0dh2e1j._.js} +9 -3
- package/.next/server/chunks/ssr/_0dr-ddg._.js +3 -0
- package/.next/server/chunks/ssr/_0eaog6_._.js +3 -0
- package/.next/server/chunks/ssr/_0evkpto._.js +3 -0
- package/.next/server/chunks/ssr/_0g_rlog._.js +3 -0
- package/.next/server/chunks/ssr/_0geg212._.js +3 -0
- package/.next/server/chunks/ssr/{_1gd8q8l._.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/{_15j2_8f._.js → _0wzaht-._.js} +2 -2
- package/.next/server/chunks/ssr/_0z-g0uy._.js +3 -0
- package/.next/server/chunks/ssr/_123ttsf._.js +3 -0
- package/.next/server/chunks/ssr/_123y-ah._.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/{_0ap2fzn._.js → _16c6_xm._.js} +2 -2
- package/.next/server/chunks/ssr/_18opah7._.js +3 -0
- package/.next/server/chunks/ssr/_18xqmny._.js +3 -0
- package/.next/server/chunks/ssr/_1_krfph._.js +3 -0
- package/.next/server/chunks/ssr/{_1-_etts._.js → _1anqots._.js} +2 -2
- 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/{_0n3toaw._.js → _1k0spz7._.js} +2 -2
- package/.next/server/chunks/ssr/_1rgtxhr._.js +5 -0
- package/.next/server/chunks/ssr/_1slp25y._.js +3 -0
- package/.next/server/chunks/ssr/{_1fojd41._.js → _1x9pskj._.js} +3 -3
- package/.next/server/chunks/ssr/_1y6b5mr._.js +101 -0
- package/.next/server/chunks/ssr/_1ztk7p6._.js +7 -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/{_1-teee5._.js → lib_logger_ts_0-olsze._.js} +2 -7
- package/.next/server/chunks/ssr/{node_modules_0cxaxk9._.js → node_modules_00_51_v._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_04jf_m8._.js → node_modules_022sipm._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0g7fzk3._.js → node_modules_0332gw4._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1cx_ql6._.js → node_modules_04vxchl._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1vh5ntv._.js → node_modules_0746rzi._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0yugy6m._.js → node_modules_0d5uzof._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_09q35v_._.js → node_modules_0exkwcm._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_08dhkiq._.js → node_modules_0fr-ml8._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_14q6g4n._.js → node_modules_0mxvg8h._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0arus10._.js → node_modules_0qapgjr._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1eg7m6c._.js → node_modules_0qbfyjh._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1cz2hle._.js → node_modules_0t-9nup._.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_1efrqxi._.js → node_modules_0vsxbzp._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0_-_a--._.js → node_modules_0vv0-fg._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1gdh31p._.js → node_modules_0wsvp00._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1hpopmo._.js → node_modules_1-urho9._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1_pc9kq._.js → node_modules_16pi_dd._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_00etblx._.js → node_modules_1d53yiq._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1aaxuef._.js → node_modules_1g1eihe._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_198nmll._.js → node_modules_1gpednh._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0pvd7q-._.js → node_modules_1gu1pph._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0b__sgl._.js → node_modules_1he1w8_._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1qpjvy5._.js → node_modules_1lnjapm._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0940ns5._.js → node_modules_1lynj9m._.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_14om4ar._.js → node_modules_1u6w1pb._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0had7xd._.js → node_modules_1xn6saw._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_1b748ic._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_067_exa._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_1z4hm5e._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0nrrdaq._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@vercel_oidc_dist_06mk8id._.js → node_modules_@vercel_oidc_dist_21a5haa._.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/0dr16v67vj4n7.js +2 -0
- package/.next/static/chunks/0g4b348l0pve7.js +1 -0
- package/.next/static/chunks/0joan-69kbspd.js +1 -0
- package/.next/static/chunks/0vlgzqo9ka90v.js +1 -0
- package/.next/static/chunks/0zrupxgci5gqc.js +1 -0
- package/.next/static/chunks/10k2xvap03z20.css +1 -0
- package/.next/static/chunks/18mkhstgiqcbt.js +1 -0
- package/.next/static/chunks/{15m575979un5f.js → 1i-u2wzub_to1.js} +2 -2
- package/.next/static/chunks/1kp5nfp06nj9h.js +5 -0
- package/.next/static/chunks/1ohslk4568tm3.js +1 -0
- package/.next/static/chunks/1sbmwswc9k2-1.js +1 -0
- package/.next/static/chunks/1t9qms5pwwa3n.js +1 -0
- package/.next/static/chunks/1vj_5bnbxx84e.js +1 -0
- package/.next/static/chunks/1vqezbhjm3goq.js +1 -0
- package/.next/static/chunks/{3akmqm77muj4-.js → 1wlo979mx-y3w.js} +1 -1
- package/.next/static/chunks/26ql4y-43xwhp.js +1 -0
- package/.next/static/chunks/{0fl3gwe4-l6tz.js → 2_pnccjp6j1ca.js} +1 -1
- package/.next/static/chunks/2a51-ywz9b2a3.js +1 -0
- package/.next/static/chunks/2fvdgxih7x7hr.js +1 -0
- package/.next/static/chunks/2jlz8zp4jax_m.js +18 -0
- package/.next/static/chunks/2nvepds1fu2ux.js +1 -0
- package/.next/static/chunks/3addj3i4-izma.js +1 -0
- package/.next/static/chunks/3bfwpww3m3x4u.js +1 -0
- package/.next/static/chunks/3gfh7nxwusbt0.js +1 -0
- package/.next/static/chunks/3gwa44cvz4qoi.js +1 -0
- package/.next/static/chunks/{38gweqdo4jpfj.js → 3izg0a3k87q1-.js} +5 -5
- package/.next/static/chunks/3zk6q3-3f9swq.js +99 -0
- package/.next/static/chunks/3zkg5iup3v4a2.js +12 -0
- package/.next/static/chunks/405m0bqtoihev.js +1 -0
- package/.next/static/chunks/42wcg3wfg_m_p.js +1 -0
- package/README.md +27 -216
- 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 +39 -36
- package/docs/images/keeps-going.png +0 -0
- package/docs/images/two-minds.png +0 -0
- package/docs/live-calls.md +231 -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 +148 -77
- 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 +265 -0
- package/features/ai/prompts/prompt-helper.ts +91 -32
- package/features/ai/prompts/thursday.prompt.ts +125 -172
- package/features/ai/tools/bot.tool.ts +38 -145
- package/features/ai/tools/call.tool.ts +1 -1
- 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 +36 -14
- package/features/ai/tools/studio.tool.ts +26 -21
- package/features/ai/tools/tool-name.ts +2 -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 +299 -636
- package/features/bot/bot.runner.ts +468 -844
- 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/components/ascii-field.tsx +24 -44
- package/features/thursday/components/ascii-orb.tsx +359 -358
- package/features/thursday/components/call-log.tsx +46 -44
- package/features/thursday/components/face.tsx +35 -10
- package/features/thursday/components/tab-state.tsx +3 -3
- package/features/thursday/components/thursday-mark.tsx +1 -0
- package/features/thursday/components/thursday-setting.tsx +631 -332
- package/features/thursday/components/thursday.tsx +106 -41
- package/features/thursday/components/voice-picker.tsx +255 -0
- package/features/thursday/thursday.action.ts +94 -68
- package/features/thursday/thursday.query.ts +49 -30
- package/features/thursday/thursday.schema.ts +27 -30
- package/features/thursday/thursday.store.ts +11 -7
- package/features/thursday/tool-call.ts +3 -3
- package/features/thursday/tool-line.ts +5 -3
- package/features/thursday/use-thursday.ts +725 -450
- package/features/workspace/workspace.query.ts +17 -0
- package/features/workspace/workspace.ts +103 -50
- package/lib/live/live.schema.ts +47 -0
- package/lib/live/live.server.ts +155 -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 +25 -0
- package/scripts/bot-context.test.mts +1408 -0
- package/scripts/live-history.test.mts +115 -0
- package/scripts/live-session.test.mts +948 -0
- package/server.js +1 -1
- package/skills/browser/SKILL.md +17 -6
- 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]__01dkp4a._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__02va5wz._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__02xjoto._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__04q--0f._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__0bl-e63._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0ffbg85._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0jq4ohy._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__0ls6_8y._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__0ncs9uu._.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]__12cabg9._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1339jnv._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__18_teo8._.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]__1ccon1w._.js +0 -10
- package/.next/server/chunks/[root-of-the-server]__1fizn31._.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]__1wa1k7r._.js +0 -8
- package/.next/server/chunks/_0-73of8._.js +0 -55
- package/.next/server/chunks/_0-fjql4._.js +0 -26
- package/.next/server/chunks/_00xh_q0._.js +0 -25
- package/.next/server/chunks/_03a0897._.js +0 -3
- package/.next/server/chunks/_051qh8u._.js +0 -3
- package/.next/server/chunks/_05sovvj._.js +0 -3
- package/.next/server/chunks/_0fzqt2y._.js +0 -134
- package/.next/server/chunks/_0mecv0f._.js +0 -26
- package/.next/server/chunks/_0mh8j4m._.js +0 -3
- package/.next/server/chunks/_0obojvv._.js +0 -3
- package/.next/server/chunks/_0sb7tlz._.js +0 -3
- package/.next/server/chunks/_0sbynis._.js +0 -3
- package/.next/server/chunks/_0u10wmw._.js +0 -55
- package/.next/server/chunks/_0u4aiy2._.js +0 -3
- 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/_1_9mjx-._.js +0 -3
- package/.next/server/chunks/_1c78vw5._.js +0 -229
- 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/_1zjrdag._.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 -3
- package/.next/server/chunks/features_bot_0zt92oh._.js +0 -3
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1xoq2rl._.js +0 -3
- package/.next/server/chunks/ssr/[externals]__0dizgek._.js +0 -3
- 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]__0s9dq5u._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0s9l2sv._.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]__0xsog8n._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__10-dz5o._.js +0 -61
- package/.next/server/chunks/ssr/[root-of-the-server]__1gtlw6v._.js +0 -43
- package/.next/server/chunks/ssr/[root-of-the-server]__1ox7hbk._.js +0 -4
- package/.next/server/chunks/ssr/[root-of-the-server]__202p40z._.js +0 -3
- package/.next/server/chunks/ssr/_001_ms1._.js +0 -28
- package/.next/server/chunks/ssr/_01n3o0m._.js +0 -3
- package/.next/server/chunks/ssr/_0fz4mf_._.js +0 -3
- package/.next/server/chunks/ssr/_0gb4gmf._.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/_0ilna52._.js +0 -42
- 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/_13fke0x._.js +0 -3
- package/.next/server/chunks/ssr/_18cymkq._.js +0 -3
- package/.next/server/chunks/ssr/_1b2mjwr._.js +0 -3
- package/.next/server/chunks/ssr/_1f-dg-6._.js +0 -3
- package/.next/server/chunks/ssr/_1h0rw11._.js +0 -3
- package/.next/server/chunks/ssr/_1jhd09t._.js +0 -3
- package/.next/server/chunks/ssr/_1nnsk7z._.js +0 -3
- package/.next/server/chunks/ssr/_1ov37if._.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/00rd4-v8r7axp.js +0 -1
- package/.next/static/chunks/08qe4as2q428z.js +0 -3
- package/.next/static/chunks/0gnqdjn2lvdad.js +0 -2
- package/.next/static/chunks/0hwap1-p0m5sa.js +0 -1
- package/.next/static/chunks/0r5qcctujhxli.js +0 -1
- package/.next/static/chunks/0w52rrrmuj9r9.js +0 -1
- package/.next/static/chunks/0wcprt5ot5she.js +0 -1
- package/.next/static/chunks/10teww7wt-f6h.js +0 -1
- package/.next/static/chunks/17p6tk6k88dq6.js +0 -1
- package/.next/static/chunks/18a96rkhi4_yd.js +0 -1
- package/.next/static/chunks/1dkjg87pxk0n9.js +0 -28
- package/.next/static/chunks/1jd09sxcjddu1.js +0 -1
- package/.next/static/chunks/1w73ece_6bheu.js +0 -18
- package/.next/static/chunks/214ya3n5gxeih.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/2t4vhi3m8ahya.js +0 -1
- package/.next/static/chunks/2tigdtz3dpcy8.js +0 -1
- package/.next/static/chunks/2u7xt6mxg44lf.js +0 -1
- package/.next/static/chunks/3786h5vayt80-.js +0 -28
- package/.next/static/chunks/39j0fvpybzabl.js +0 -40
- package/.next/static/chunks/3cq5g494yuke8.css +0 -1
- package/.next/static/chunks/3hibbjnt85-k6.js +0 -1
- package/.next/static/chunks/3woj-xd7ki5_i.js +0 -1
- package/.next/static/chunks/3xk7yqgomx7yq.js +0 -1
- package/.next/static/chunks/3zmcsnuo25zvq.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 -976
- package/features/bot/task.store.ts +0 -489
- 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/{kaTO0UHqF1bMnWgCDik2f → 0qzGoKn2wTERVZtblX1tp}/_buildManifest.js +0 -0
- /package/.next/static/{kaTO0UHqF1bMnWgCDik2f → 0qzGoKn2wTERVZtblX1tp}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{kaTO0UHqF1bMnWgCDik2f → 0qzGoKn2wTERVZtblX1tp}/_ssgManifest.js +0 -0
|
@@ -1,976 +0,0 @@
|
|
|
1
|
-
import type { AssistantContent, ModelMessage, ToolContent } from "ai";
|
|
2
|
-
import {
|
|
3
|
-
and,
|
|
4
|
-
desc,
|
|
5
|
-
eq,
|
|
6
|
-
inArray,
|
|
7
|
-
isNotNull,
|
|
8
|
-
isNull,
|
|
9
|
-
lt,
|
|
10
|
-
max,
|
|
11
|
-
sql,
|
|
12
|
-
} from "drizzle-orm";
|
|
13
|
-
import { appEvents } from "@/app/api/events/app-event.server";
|
|
14
|
-
import { INBOX_FINISHED } from "@/config";
|
|
15
|
-
import { database } from "@/database/db";
|
|
16
|
-
import { taskMessageTable, taskTable } from "@/database/tables";
|
|
17
|
-
import { answerAccepted, isAnswerCall } from "@/features/ai/tools/bot.tool";
|
|
18
|
-
import { TOOL_NAMES } from "@/features/ai/tools/tool-name";
|
|
19
|
-
import { clip } from "@/lib/utils";
|
|
20
|
-
import {
|
|
21
|
-
type ResultPart,
|
|
22
|
-
TASK_HISTORY_PAGE,
|
|
23
|
-
type Task,
|
|
24
|
-
type TaskLine,
|
|
25
|
-
type TaskPending,
|
|
26
|
-
type TaskStatus,
|
|
27
|
-
type TokenUsage,
|
|
28
|
-
untagSpeaker,
|
|
29
|
-
} from "./bot.schema";
|
|
30
|
-
|
|
31
|
-
// Tasks and their threads. Bots themselves (roster, pinned tools) are bot.query.
|
|
32
|
-
|
|
33
|
-
/** Every write in this file ends with this; the screen re-reads the list on it. */
|
|
34
|
-
const changed = () => appEvents.emit({ type: "tasks" });
|
|
35
|
-
|
|
36
|
-
/** Task state without the thread. `lines` is filled by `withLines` from task_message; `pending` folds into `ask`. */
|
|
37
|
-
const taskView = {
|
|
38
|
-
id: taskTable.id,
|
|
39
|
-
bot: taskTable.bot,
|
|
40
|
-
label: taskTable.label,
|
|
41
|
-
request: taskTable.request,
|
|
42
|
-
status: taskTable.status,
|
|
43
|
-
outcome: taskTable.outcome,
|
|
44
|
-
pending: taskTable.pending,
|
|
45
|
-
seen: taskTable.seen,
|
|
46
|
-
inputTokens: taskTable.inputTokens,
|
|
47
|
-
outputTokens: taskTable.outputTokens,
|
|
48
|
-
contextTokens: taskTable.contextTokens,
|
|
49
|
-
contextBudget: taskTable.contextBudget,
|
|
50
|
-
createdAt: taskTable.createdAt,
|
|
51
|
-
updatedAt: taskTable.updatedAt,
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
type TaskRow = {
|
|
55
|
-
id: string;
|
|
56
|
-
bot: string;
|
|
57
|
-
label: string;
|
|
58
|
-
request: string;
|
|
59
|
-
status: TaskStatus;
|
|
60
|
-
outcome: string | null;
|
|
61
|
-
pending: TaskPending | null;
|
|
62
|
-
seen: boolean;
|
|
63
|
-
inputTokens: number;
|
|
64
|
-
outputTokens: number;
|
|
65
|
-
contextTokens: number;
|
|
66
|
-
contextBudget: number;
|
|
67
|
-
createdAt: Date;
|
|
68
|
-
updatedAt: Date;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* One row plus the thread's first message. `request` is what the screen shows;
|
|
73
|
-
* `opening` is what the model reads (who is who, the job, the call it came from). The screen
|
|
74
|
-
* skips seq 0 and shows `request` instead (linesOf).
|
|
75
|
-
*/
|
|
76
|
-
export async function insertTask(input: {
|
|
77
|
-
bot: string;
|
|
78
|
-
label: string;
|
|
79
|
-
request: string;
|
|
80
|
-
callId?: string | null;
|
|
81
|
-
opening: Extract<ModelMessage, { role: "user" }>["content"];
|
|
82
|
-
}) {
|
|
83
|
-
const { opening, ...row } = input;
|
|
84
|
-
const [task] = await database
|
|
85
|
-
.insert(taskTable)
|
|
86
|
-
.values({ id: crypto.randomUUID(), status: "running", ...row })
|
|
87
|
-
.returning();
|
|
88
|
-
await upsertMessage(task.id, 0, {
|
|
89
|
-
bot: null,
|
|
90
|
-
parent: null,
|
|
91
|
-
role: "user",
|
|
92
|
-
content: opening,
|
|
93
|
-
});
|
|
94
|
-
changed();
|
|
95
|
-
return task;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export async function updateTask(
|
|
99
|
-
id: string,
|
|
100
|
-
patch: Partial<{
|
|
101
|
-
status: TaskStatus;
|
|
102
|
-
outcome: string | null;
|
|
103
|
-
pending: TaskPending | null;
|
|
104
|
-
seen: boolean;
|
|
105
|
-
endedAt: Date | null;
|
|
106
|
-
/** Where the job compacts from now on; also written at every step (addTaskUsage). */
|
|
107
|
-
contextBudget: number;
|
|
108
|
-
}>,
|
|
109
|
-
) {
|
|
110
|
-
await database
|
|
111
|
-
.update(taskTable)
|
|
112
|
-
.set({ ...patch, updatedAt: new Date() })
|
|
113
|
-
.where(eq(taskTable.id, id));
|
|
114
|
-
changed();
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** Bypasses `updateTask` on purpose: being seen is not movement and must not reorder the inbox by updatedAt. */
|
|
118
|
-
export async function markSeen(ids: string[]) {
|
|
119
|
-
if (ids.length === 0) return;
|
|
120
|
-
await database
|
|
121
|
-
.update(taskTable)
|
|
122
|
-
.set({ seen: true })
|
|
123
|
-
.where(inArray(taskTable.id, ids));
|
|
124
|
-
changed();
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Adds one step's usage (added, not set: borrowed-bot steps land on the same
|
|
129
|
-
* task in parallel) without touching updatedAt. `context` is overwritten: it is
|
|
130
|
-
* the current window fill and threshold, not a sum. null leaves it as is.
|
|
131
|
-
*/
|
|
132
|
-
export async function addTaskUsage(
|
|
133
|
-
id: string,
|
|
134
|
-
usage: TokenUsage,
|
|
135
|
-
context: { tokens: number; budget: number } | null = null,
|
|
136
|
-
) {
|
|
137
|
-
if (!usage.input && !usage.output && context === null) return;
|
|
138
|
-
await database
|
|
139
|
-
.update(taskTable)
|
|
140
|
-
.set({
|
|
141
|
-
inputTokens: sql`${taskTable.inputTokens} + ${usage.input}`,
|
|
142
|
-
outputTokens: sql`${taskTable.outputTokens} + ${usage.output}`,
|
|
143
|
-
...(context === null
|
|
144
|
-
? {}
|
|
145
|
-
: { contextTokens: context.tokens, contextBudget: context.budget }),
|
|
146
|
-
})
|
|
147
|
-
.where(eq(taskTable.id, id));
|
|
148
|
-
changed();
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/** One task as the screen sees it, thread included. null if missing. */
|
|
152
|
-
export async function findTaskView(id: string): Promise<Task | null> {
|
|
153
|
-
const [row] = await database
|
|
154
|
-
.select(taskView)
|
|
155
|
-
.from(taskTable)
|
|
156
|
-
.where(eq(taskTable.id, id));
|
|
157
|
-
if (!row) return null;
|
|
158
|
-
const [task] = await withLines([row]);
|
|
159
|
-
return task ?? null;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export async function findTask(id: string) {
|
|
163
|
-
const [task] = await database
|
|
164
|
-
.select()
|
|
165
|
-
.from(taskTable)
|
|
166
|
-
.where(eq(taskTable.id, id));
|
|
167
|
-
return task ?? null;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/** Inbox: everything running or waiting plus the latest INBOX_FINISHED endings, newest first. `seen` is not a filter here. */
|
|
171
|
-
export async function listInboxTasks(): Promise<Task[]> {
|
|
172
|
-
const [open, finished] = await Promise.all([
|
|
173
|
-
database
|
|
174
|
-
.select(taskView)
|
|
175
|
-
.from(taskTable)
|
|
176
|
-
.where(inArray(taskTable.status, ["running", "waiting"]))
|
|
177
|
-
.orderBy(desc(taskTable.updatedAt)),
|
|
178
|
-
database
|
|
179
|
-
.select(taskView)
|
|
180
|
-
.from(taskTable)
|
|
181
|
-
.where(inArray(taskTable.status, ["done", "failed"]))
|
|
182
|
-
.orderBy(desc(taskTable.updatedAt))
|
|
183
|
-
.limit(INBOX_FINISHED),
|
|
184
|
-
]);
|
|
185
|
-
const rows = [...open, ...finished].sort(
|
|
186
|
-
(a, b) => b.updatedAt.getTime() - a.updatedAt.getTime(),
|
|
187
|
-
);
|
|
188
|
-
return withLines(rows);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/** All tasks, newest first, one page at a time. `before` is a cursor (last page's updatedAt), not an offset, because rows move in between. */
|
|
192
|
-
export async function listTaskHistory(
|
|
193
|
-
options: { before?: Date | null; limit?: number } = {},
|
|
194
|
-
): Promise<Task[]> {
|
|
195
|
-
const rows = await database
|
|
196
|
-
.select(taskView)
|
|
197
|
-
.from(taskTable)
|
|
198
|
-
.where(options.before ? lt(taskTable.updatedAt, options.before) : undefined)
|
|
199
|
-
.orderBy(desc(taskTable.updatedAt))
|
|
200
|
-
.limit(options.limit ?? TASK_HISTORY_PAGE);
|
|
201
|
-
return withLines(rows);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* What each of these calls handed over, for the transcript the next call reads
|
|
206
|
-
* (ai/prompts/thursday.prompt). Not the inbox: a job belongs to the call it was
|
|
207
|
-
* opened from, and this is how it is still there once that call is over.
|
|
208
|
-
*/
|
|
209
|
-
export async function listCallJobs(callIds: string[]) {
|
|
210
|
-
if (!callIds.length) return [];
|
|
211
|
-
return await database
|
|
212
|
-
.select({
|
|
213
|
-
id: taskTable.id,
|
|
214
|
-
callId: taskTable.callId,
|
|
215
|
-
label: taskTable.label,
|
|
216
|
-
status: taskTable.status,
|
|
217
|
-
outcome: taskTable.outcome,
|
|
218
|
-
})
|
|
219
|
-
.from(taskTable)
|
|
220
|
-
.where(inArray(taskTable.callId, callIds))
|
|
221
|
-
.orderBy(taskTable.createdAt);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
export type CallJob = Awaited<ReturnType<typeof listCallJobs>>[number];
|
|
225
|
-
|
|
226
|
-
/** The thread goes with it (cascade). The runner stops a live one first. */
|
|
227
|
-
export async function deleteTask(id: string) {
|
|
228
|
-
const removed = await database
|
|
229
|
-
.delete(taskTable)
|
|
230
|
-
.where(eq(taskTable.id, id))
|
|
231
|
-
.returning({ id: taskTable.id });
|
|
232
|
-
if (removed.length) changed();
|
|
233
|
-
return removed.length > 0;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/** Everything finished, done or failed. Running and waiting rows stay: they are still work. */
|
|
237
|
-
export async function deleteFinishedTasks() {
|
|
238
|
-
const removed = await database
|
|
239
|
-
.delete(taskTable)
|
|
240
|
-
.where(inArray(taskTable.status, ["done", "failed"]))
|
|
241
|
-
// The label comes back too: a job's working folder is named for it and goes
|
|
242
|
-
// with the row (bot.runner removeFinishedTasks).
|
|
243
|
-
.returning({ id: taskTable.id, label: taskTable.label });
|
|
244
|
-
if (removed.length) changed();
|
|
245
|
-
return removed;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/** Every job, newest first. The caller stops each one before deleting it (bot.runner removeTask). */
|
|
249
|
-
export async function listAllTaskIds(): Promise<string[]> {
|
|
250
|
-
const rows = await database
|
|
251
|
-
.select({ id: taskTable.id })
|
|
252
|
-
.from(taskTable)
|
|
253
|
-
.orderBy(desc(taskTable.createdAt));
|
|
254
|
-
return rows.map((row) => row.id);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/** Rows a previous server left as running; the runner decides which still are. */
|
|
258
|
-
export async function listRunningTaskIds() {
|
|
259
|
-
const rows = await database
|
|
260
|
-
.select({ id: taskTable.id })
|
|
261
|
-
.from(taskTable)
|
|
262
|
-
.where(eq(taskTable.status, "running"));
|
|
263
|
-
return rows.map((row) => row.id);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/** Every job with what names its folder and whether it still keeps it (bot.runner sweepJobFiles). */
|
|
267
|
-
export async function listTaskFolders() {
|
|
268
|
-
return await database
|
|
269
|
-
.select({
|
|
270
|
-
id: taskTable.id,
|
|
271
|
-
label: taskTable.label,
|
|
272
|
-
status: taskTable.status,
|
|
273
|
-
endedAt: taskTable.endedAt,
|
|
274
|
-
updatedAt: taskTable.updatedAt,
|
|
275
|
-
})
|
|
276
|
-
.from(taskTable);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/** Jobs the app parked to pick back up by itself (bot.runner parkTask), and when each may go. */
|
|
280
|
-
export async function listAutoStoppedTasks() {
|
|
281
|
-
const rows = await database
|
|
282
|
-
.select({ id: taskTable.id, pending: taskTable.pending })
|
|
283
|
-
.from(taskTable)
|
|
284
|
-
.where(eq(taskTable.status, "waiting"));
|
|
285
|
-
return rows.flatMap((row) =>
|
|
286
|
-
row.pending?.auto
|
|
287
|
-
? [{ id: row.id, retryAt: row.pending.retryAt ?? 0 }]
|
|
288
|
-
: [],
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* How many times the app has stopped this job since a person last said anything
|
|
294
|
-
* to it: the notes it left in the thread — a break, a park — back to the last row
|
|
295
|
-
* a person wrote: the opening, an answer, a word to a running job. Read off the
|
|
296
|
-
* rows rather than kept as a count, so it cannot drift from what the thread shows.
|
|
297
|
-
*/
|
|
298
|
-
export async function countStopsSinceSpoken(taskId: string): Promise<number> {
|
|
299
|
-
const rows = await database
|
|
300
|
-
.select({
|
|
301
|
-
role: taskMessageTable.role,
|
|
302
|
-
bot: taskMessageTable.bot,
|
|
303
|
-
note: taskMessageTable.note,
|
|
304
|
-
compact: taskMessageTable.compact,
|
|
305
|
-
})
|
|
306
|
-
.from(taskMessageTable)
|
|
307
|
-
.where(
|
|
308
|
-
and(eq(taskMessageTable.taskId, taskId), isNull(taskMessageTable.parent)),
|
|
309
|
-
)
|
|
310
|
-
.orderBy(desc(taskMessageTable.seq));
|
|
311
|
-
let stops = 0;
|
|
312
|
-
for (const row of rows) {
|
|
313
|
-
if (row.note) {
|
|
314
|
-
if (!row.compact) stops += 1;
|
|
315
|
-
continue;
|
|
316
|
-
}
|
|
317
|
-
// A person: the opening and every answer are written with no bot, and a
|
|
318
|
-
// word to a running job is a user row in the bot's name
|
|
319
|
-
if (row.bot === null || row.role === "user") break;
|
|
320
|
-
}
|
|
321
|
-
return stops;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/** How much of a cut-off borrowed bot's prose a resume carries: its last few paragraphs. */
|
|
325
|
-
const TAIL = { texts: 3, chars: 600 };
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* What each borrowed bot had written when its `ask_bot` call stopped, by that
|
|
329
|
-
* call's id: its last few paragraphs, and the tool it reached for after them. A
|
|
330
|
-
* resumed run reads it where the call's result never arrived (bot.run
|
|
331
|
-
* resumeThread), so a part cut off half-way is carried on, not asked for afresh.
|
|
332
|
-
*/
|
|
333
|
-
export async function listBorrowedTails(
|
|
334
|
-
taskId: string,
|
|
335
|
-
): Promise<Map<string, { bot: string; tail: string }>> {
|
|
336
|
-
const rows = await database
|
|
337
|
-
.select({
|
|
338
|
-
parent: taskMessageTable.parent,
|
|
339
|
-
bot: taskMessageTable.bot,
|
|
340
|
-
content: taskMessageTable.content,
|
|
341
|
-
})
|
|
342
|
-
.from(taskMessageTable)
|
|
343
|
-
.where(
|
|
344
|
-
and(
|
|
345
|
-
eq(taskMessageTable.taskId, taskId),
|
|
346
|
-
isNotNull(taskMessageTable.parent),
|
|
347
|
-
eq(taskMessageTable.role, "assistant"),
|
|
348
|
-
),
|
|
349
|
-
)
|
|
350
|
-
.orderBy(taskMessageTable.seq);
|
|
351
|
-
|
|
352
|
-
const calls = new Map<
|
|
353
|
-
string,
|
|
354
|
-
{ bot: string; texts: string[]; then: string | null }
|
|
355
|
-
>();
|
|
356
|
-
for (const row of rows) {
|
|
357
|
-
if (!row.parent) continue;
|
|
358
|
-
const call = calls.get(row.parent) ?? {
|
|
359
|
-
bot: row.bot ?? "",
|
|
360
|
-
texts: [],
|
|
361
|
-
then: null,
|
|
362
|
-
};
|
|
363
|
-
const content = row.content as AssistantContent;
|
|
364
|
-
const parts =
|
|
365
|
-
typeof content === "string"
|
|
366
|
-
? [{ type: "text" as const, text: content }]
|
|
367
|
-
: content;
|
|
368
|
-
for (const part of parts) {
|
|
369
|
-
if (part.type === "text" && part.text.trim()) {
|
|
370
|
-
call.texts.push(part.text.trim());
|
|
371
|
-
call.then = null;
|
|
372
|
-
} else if (part.type === "tool-call") {
|
|
373
|
-
call.then =
|
|
374
|
-
`${part.toolName} ${toolLine(part.toolName, part.input)}`.trim();
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
calls.set(row.parent, call);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
const tails = new Map<string, { bot: string; tail: string }>();
|
|
381
|
-
for (const [id, call] of calls) {
|
|
382
|
-
const tail = [
|
|
383
|
-
...call.texts.slice(-TAIL.texts).map((text) => clip(text, TAIL.chars)),
|
|
384
|
-
call.then ? `Then: ${call.then}` : "",
|
|
385
|
-
]
|
|
386
|
-
.filter(Boolean)
|
|
387
|
-
.join("\n\n");
|
|
388
|
-
if (tail) tails.set(id, { bot: call.bot, tail });
|
|
389
|
-
}
|
|
390
|
-
return tails;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Paths a list of message contents gave `write_file`, oldest first and each once; a
|
|
395
|
-
* path written again moves to the end. A refused write is here too, so whoever shows
|
|
396
|
-
* them checks the disk (workspace.ts filesOnDisk).
|
|
397
|
-
*/
|
|
398
|
-
export function writtenPathsIn(contents: unknown[]): string[] {
|
|
399
|
-
const paths = new Set<string>();
|
|
400
|
-
for (const content of contents) {
|
|
401
|
-
if (!Array.isArray(content)) continue;
|
|
402
|
-
for (const part of content as {
|
|
403
|
-
type?: unknown;
|
|
404
|
-
toolName?: unknown;
|
|
405
|
-
input?: unknown;
|
|
406
|
-
}[]) {
|
|
407
|
-
if (
|
|
408
|
-
part.type !== "tool-call" ||
|
|
409
|
-
part.toolName !== TOOL_NAMES.write_file
|
|
410
|
-
) {
|
|
411
|
-
continue;
|
|
412
|
-
}
|
|
413
|
-
const path = (part.input as { path?: unknown } | null)?.path;
|
|
414
|
-
if (typeof path !== "string" || !path.trim()) continue;
|
|
415
|
-
paths.delete(path.trim());
|
|
416
|
-
paths.add(path.trim());
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
return [...paths];
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
/** Every path this job gave `write_file`: its own bot and each bot it borrowed (writtenPathsIn). */
|
|
423
|
-
export async function listWrittenPaths(taskId: string): Promise<string[]> {
|
|
424
|
-
const rows = await database
|
|
425
|
-
.select({ content: taskMessageTable.content })
|
|
426
|
-
.from(taskMessageTable)
|
|
427
|
-
.where(
|
|
428
|
-
and(
|
|
429
|
-
eq(taskMessageTable.taskId, taskId),
|
|
430
|
-
eq(taskMessageTable.role, "assistant"),
|
|
431
|
-
),
|
|
432
|
-
)
|
|
433
|
-
.orderBy(taskMessageTable.seq);
|
|
434
|
-
return writtenPathsIn(rows.map((row) => row.content));
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
/**
|
|
438
|
-
* Finds a task by id or by exact label (case-insensitive). No looser matching:
|
|
439
|
-
* a miss returns null and the caller lists recent tasks instead.
|
|
440
|
-
*/
|
|
441
|
-
export async function resolveTask(ref: string) {
|
|
442
|
-
const term = ref.trim();
|
|
443
|
-
if (!term) return null;
|
|
444
|
-
const whole = await findTask(term);
|
|
445
|
-
if (whole) return whole;
|
|
446
|
-
|
|
447
|
-
const lower = term.toLowerCase();
|
|
448
|
-
const recent = await database
|
|
449
|
-
.select()
|
|
450
|
-
.from(taskTable)
|
|
451
|
-
.orderBy(desc(taskTable.updatedAt))
|
|
452
|
-
.limit(20);
|
|
453
|
-
return recent.find((task) => task.label.toLowerCase() === lower) ?? null;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
export type TaskMessageInput = {
|
|
457
|
-
bot: string | null;
|
|
458
|
-
parent: string | null;
|
|
459
|
-
role: ModelMessage["role"];
|
|
460
|
-
content: ModelMessage["content"];
|
|
461
|
-
/** Compaction summary row; `listThread` re-reads from here. */
|
|
462
|
-
compact?: boolean;
|
|
463
|
-
/** Drawn as the app's own line, not as something a person or a bot said. */
|
|
464
|
-
note?: boolean;
|
|
465
|
-
};
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* Writes by seat, not append: a step's messages are written while streaming and
|
|
469
|
-
* rewritten whole when it ends, and the second write must replace the first.
|
|
470
|
-
*/
|
|
471
|
-
export async function upsertMessage(
|
|
472
|
-
taskId: string,
|
|
473
|
-
seq: number,
|
|
474
|
-
message: TaskMessageInput,
|
|
475
|
-
) {
|
|
476
|
-
await database
|
|
477
|
-
.insert(taskMessageTable)
|
|
478
|
-
.values({
|
|
479
|
-
taskId,
|
|
480
|
-
seq,
|
|
481
|
-
...message,
|
|
482
|
-
compact: message.compact ?? false,
|
|
483
|
-
note: message.note ?? false,
|
|
484
|
-
})
|
|
485
|
-
.onConflictDoUpdate({
|
|
486
|
-
target: [taskMessageTable.taskId, taskMessageTable.seq],
|
|
487
|
-
set: {
|
|
488
|
-
role: message.role,
|
|
489
|
-
content: message.content,
|
|
490
|
-
compact: message.compact ?? false,
|
|
491
|
-
note: message.note ?? false,
|
|
492
|
-
},
|
|
493
|
-
});
|
|
494
|
-
changed();
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
/** Frees seats claimed while streaming but unused when the step ended. Left in, they put unmatched tool results in the thread the model re-reads. */
|
|
498
|
-
export async function deleteMessages(taskId: string, seqs: number[]) {
|
|
499
|
-
if (!seqs.length) return;
|
|
500
|
-
await database
|
|
501
|
-
.delete(taskMessageTable)
|
|
502
|
-
.where(
|
|
503
|
-
and(
|
|
504
|
-
eq(taskMessageTable.taskId, taskId),
|
|
505
|
-
inArray(taskMessageTable.seq, seqs),
|
|
506
|
-
),
|
|
507
|
-
);
|
|
508
|
-
changed();
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
/** Highest seq taken so far; -1 for an empty thread. */
|
|
512
|
-
export async function lastSeq(taskId: string): Promise<number> {
|
|
513
|
-
const [row] = await database
|
|
514
|
-
.select({ seq: max(taskMessageTable.seq) })
|
|
515
|
-
.from(taskMessageTable)
|
|
516
|
-
.where(eq(taskMessageTable.taskId, taskId));
|
|
517
|
-
return row?.seq ?? -1;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
/**
|
|
521
|
-
* The task's own thread as the model sees it: parent-less messages in order.
|
|
522
|
-
* What a borrowed bot said inside an `ask_bot` call is not here; its result is.
|
|
523
|
-
* After a compaction it is the opening, then the last compact row on: a summary
|
|
524
|
-
* replaces what came after the job and the call, never the job and the call.
|
|
525
|
-
*/
|
|
526
|
-
export async function listThread(taskId: string): Promise<ModelMessage[]> {
|
|
527
|
-
const rows = await database
|
|
528
|
-
.select({
|
|
529
|
-
role: taskMessageTable.role,
|
|
530
|
-
content: taskMessageTable.content,
|
|
531
|
-
compact: taskMessageTable.compact,
|
|
532
|
-
})
|
|
533
|
-
.from(taskMessageTable)
|
|
534
|
-
.where(
|
|
535
|
-
and(eq(taskMessageTable.taskId, taskId), isNull(taskMessageTable.parent)),
|
|
536
|
-
)
|
|
537
|
-
.orderBy(taskMessageTable.seq);
|
|
538
|
-
const from = rows.findLastIndex((row) => row.compact);
|
|
539
|
-
const kept = from > 0 ? [rows[0], ...rows.slice(from)] : rows;
|
|
540
|
-
return kept.map(
|
|
541
|
-
(row) => ({ role: row.role, content: row.content }) as ModelMessage,
|
|
542
|
-
);
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
/** One row in the screen's shape; `pending` folds into `ask`. */
|
|
546
|
-
function viewOf(row: TaskRow, lines: TaskLine[]): Task {
|
|
547
|
-
// contextTokens and contextBudget pass through in `rest`
|
|
548
|
-
const { pending, inputTokens, outputTokens, ...rest } = row;
|
|
549
|
-
return {
|
|
550
|
-
...rest,
|
|
551
|
-
ask:
|
|
552
|
-
row.status === "waiting"
|
|
553
|
-
? {
|
|
554
|
-
question: row.outcome ?? "",
|
|
555
|
-
options: pending?.options ?? [],
|
|
556
|
-
auto: pending?.auto === true,
|
|
557
|
-
}
|
|
558
|
-
: null,
|
|
559
|
-
tokens: { input: inputTokens, output: outputTokens },
|
|
560
|
-
lines,
|
|
561
|
-
};
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
/** Reduces every task's thread to lines and attaches them to the rows. */
|
|
565
|
-
async function withLines(rows: TaskRow[]): Promise<Task[]> {
|
|
566
|
-
if (rows.length === 0) return [];
|
|
567
|
-
const messages = await database
|
|
568
|
-
.select()
|
|
569
|
-
.from(taskMessageTable)
|
|
570
|
-
.where(
|
|
571
|
-
inArray(
|
|
572
|
-
taskMessageTable.taskId,
|
|
573
|
-
rows.map((row) => row.id),
|
|
574
|
-
),
|
|
575
|
-
)
|
|
576
|
-
.orderBy(taskMessageTable.seq);
|
|
577
|
-
|
|
578
|
-
// ask_back lines name no addressee; the task's own bot is the addressee
|
|
579
|
-
const owners = new Map(rows.map((row) => [row.id, row.bot]));
|
|
580
|
-
const byTask = new Map<string, TaskLine[]>();
|
|
581
|
-
for (const message of messages) {
|
|
582
|
-
const list = byTask.get(message.taskId) ?? [];
|
|
583
|
-
list.push(...linesOf(message, owners.get(message.taskId) ?? ""));
|
|
584
|
-
byTask.set(message.taskId, list);
|
|
585
|
-
}
|
|
586
|
-
return rows.map((row) => viewOf(row, byTask.get(row.id) ?? []));
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
/** One-line cap: room for a path, small enough for a row. */
|
|
590
|
-
const LINE_MAX = 120;
|
|
591
|
-
|
|
592
|
-
/** Argument names likely to carry the point, in reading order. */
|
|
593
|
-
const TELLING = [
|
|
594
|
-
"query",
|
|
595
|
-
"command",
|
|
596
|
-
"prompt",
|
|
597
|
-
"url",
|
|
598
|
-
"path",
|
|
599
|
-
"pattern",
|
|
600
|
-
"request",
|
|
601
|
-
"question",
|
|
602
|
-
"title",
|
|
603
|
-
"name",
|
|
604
|
-
"task",
|
|
605
|
-
"text",
|
|
606
|
-
] as const;
|
|
607
|
-
|
|
608
|
-
const stringAt = (args: Record<string, unknown>, key: string) => {
|
|
609
|
-
const value = args[key];
|
|
610
|
-
return typeof value === "string" && value.trim() ? value : null;
|
|
611
|
-
};
|
|
612
|
-
|
|
613
|
-
/** Model-written label on bash. null when absent; the command is then the label. */
|
|
614
|
-
function labelOf(args: Record<string, unknown>): string | null {
|
|
615
|
-
const note = stringAt(args, "description");
|
|
616
|
-
return note ? clip(note, LINE_MAX) : null;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
/**
|
|
620
|
-
* One tool call as one line, for the screen. No per-tool summary table: the
|
|
621
|
-
* label is model-written (bash `description`), other tools use the first
|
|
622
|
-
* telling argument, else the argument shape.
|
|
623
|
-
*/
|
|
624
|
-
export function toolLine(name: string, input: unknown): string {
|
|
625
|
-
const args = (input ?? {}) as Record<string, unknown>;
|
|
626
|
-
if (typeof args !== "object" || Array.isArray(args)) {
|
|
627
|
-
return clip(String(input ?? ""), LINE_MAX);
|
|
628
|
-
}
|
|
629
|
-
if (name === TOOL_NAMES.bash) {
|
|
630
|
-
return labelOf(args) ?? clip(String(args.command ?? ""), LINE_MAX);
|
|
631
|
-
}
|
|
632
|
-
if (name === TOOL_NAMES.tool_call || name === TOOL_NAMES.tool_search) {
|
|
633
|
-
// For MCP the call is inside the args. Server name alone would collapse
|
|
634
|
-
// twenty different calls into one line, and a resumed bot would repeat them
|
|
635
|
-
const called = Array.isArray(args.tools)
|
|
636
|
-
? args.tools.map(String).join(", ")
|
|
637
|
-
: String(args.tool ?? "");
|
|
638
|
-
const passed =
|
|
639
|
-
args.args && typeof args.args === "object"
|
|
640
|
-
? JSON.stringify(args.args)
|
|
641
|
-
: "";
|
|
642
|
-
return clip(
|
|
643
|
-
[String(args.server ?? ""), called, passed].filter(Boolean).join(" "),
|
|
644
|
-
LINE_MAX,
|
|
645
|
-
);
|
|
646
|
-
}
|
|
647
|
-
for (const key of TELLING) {
|
|
648
|
-
const value = stringAt(args, key);
|
|
649
|
-
if (value) return clip(value, LINE_MAX);
|
|
650
|
-
}
|
|
651
|
-
for (const key of Object.keys(args)) {
|
|
652
|
-
const value = stringAt(args, key);
|
|
653
|
-
if (value) return clip(value, LINE_MAX);
|
|
654
|
-
}
|
|
655
|
-
const keys = Object.keys(args);
|
|
656
|
-
return keys.length ? clip(JSON.stringify(args), LINE_MAX) : "";
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
/** Result lines carried in the list, enough for a glance. */
|
|
660
|
-
const RESULT_LINES = 4;
|
|
661
|
-
|
|
662
|
-
/** Cap for the full result, so one log file cannot flatten the browser. */
|
|
663
|
-
const FULL_RESULT_LINES = 400;
|
|
664
|
-
|
|
665
|
-
/** Everything one tool returned; only when the screen asks for more. The list carries a few lines (linesOf). */
|
|
666
|
-
export async function readToolResult(
|
|
667
|
-
taskId: string,
|
|
668
|
-
toolCallId: string,
|
|
669
|
-
): Promise<ResultPart[] | null> {
|
|
670
|
-
// Usually a tool row, but results of provider-run tools (search) sit inside
|
|
671
|
-
// an assistant row, so both are scanned
|
|
672
|
-
const rows = await database
|
|
673
|
-
.select({ role: taskMessageTable.role, content: taskMessageTable.content })
|
|
674
|
-
.from(taskMessageTable)
|
|
675
|
-
.where(
|
|
676
|
-
and(
|
|
677
|
-
eq(taskMessageTable.taskId, taskId),
|
|
678
|
-
inArray(taskMessageTable.role, ["tool", "assistant"]),
|
|
679
|
-
),
|
|
680
|
-
);
|
|
681
|
-
for (const row of rows) {
|
|
682
|
-
if (typeof row.content === "string") continue;
|
|
683
|
-
for (const part of row.content as ToolContent) {
|
|
684
|
-
if (part.type === "tool-result" && part.toolCallId === toolCallId) {
|
|
685
|
-
return resultParts(part.output, FULL_RESULT_LINES);
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
return null;
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
/** Said to a resumed run after why it stopped. The screen shows only the why (linesOf). */
|
|
693
|
-
const IN_FLIGHT =
|
|
694
|
-
"Anything that was under way — a command, a page loading, a download — may not have finished: check before relying on it, then carry on.";
|
|
695
|
-
|
|
696
|
-
/** The thread line a stop the app made leaves (bot.runner parkTask). */
|
|
697
|
-
export const stopNote = (why: string) => `${why} ${IN_FLIGHT}`;
|
|
698
|
-
|
|
699
|
-
type StoredMessage = typeof taskMessageTable.$inferSelect;
|
|
700
|
-
|
|
701
|
-
/**
|
|
702
|
-
* One stored message as screen lines. seq 0 is the request and is drawn from the
|
|
703
|
-
* row instead, so it yields nothing. Tool calls aimed at the user or another bot
|
|
704
|
-
* get their own kinds because the screen draws them as speech. `owner` is the
|
|
705
|
-
* task's bot, the addressee of an ask_back line.
|
|
706
|
-
*/
|
|
707
|
-
function linesOf(message: StoredMessage, owner: string): TaskLine[] {
|
|
708
|
-
const base = {
|
|
709
|
-
seq: message.seq,
|
|
710
|
-
bot: message.bot,
|
|
711
|
-
parent: message.parent,
|
|
712
|
-
at: message.createdAt,
|
|
713
|
-
};
|
|
714
|
-
const id = (index: number) => `${message.id}-${index}`;
|
|
715
|
-
const content = message.content;
|
|
716
|
-
|
|
717
|
-
if (message.role === "user") {
|
|
718
|
-
if (message.seq === 0) return [];
|
|
719
|
-
const text = typeof content === "string" ? content : textOf(content);
|
|
720
|
-
if (!text) return [];
|
|
721
|
-
// The app's own lines, never the user's words. `compact` alone marks a
|
|
722
|
-
// compaction: rows written before `note` existed carry only that.
|
|
723
|
-
if (message.compact) return [{ ...base, id: id(0), kind: "note", text }];
|
|
724
|
-
if (message.note) {
|
|
725
|
-
const why = text.endsWith(IN_FLIGHT)
|
|
726
|
-
? text.slice(0, -IN_FLIGHT.length).trimEnd()
|
|
727
|
-
: text;
|
|
728
|
-
return [{ ...base, id: id(0), kind: "stop", text: why }];
|
|
729
|
-
}
|
|
730
|
-
// Tagged with who said it for the bot (bot.schema tagSpeaker); the screen draws the words
|
|
731
|
-
return [{ ...base, id: id(0), kind: "user", text: untagSpeaker(text) }];
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
if (message.role === "assistant") {
|
|
735
|
-
const content = message.content as AssistantContent;
|
|
736
|
-
if (typeof content === "string") {
|
|
737
|
-
return content.trim()
|
|
738
|
-
? [{ ...base, id: id(0), kind: "text", text: content.trim() }]
|
|
739
|
-
: [];
|
|
740
|
-
}
|
|
741
|
-
const lines: TaskLine[] = [];
|
|
742
|
-
content.forEach((part, index) => {
|
|
743
|
-
if (part.type === "text") {
|
|
744
|
-
const text = part.text.trim();
|
|
745
|
-
if (text) lines.push({ ...base, id: id(index), kind: "text", text });
|
|
746
|
-
return;
|
|
747
|
-
}
|
|
748
|
-
// Result of a provider-run tool (search) arrives inside the same message
|
|
749
|
-
if (part.type === "tool-result") {
|
|
750
|
-
lines.push(resultLine(base, id(index), part));
|
|
751
|
-
return;
|
|
752
|
-
}
|
|
753
|
-
if (part.type !== "tool-call") return;
|
|
754
|
-
const args = (part.input ?? {}) as Record<string, unknown>;
|
|
755
|
-
if (part.toolName === TOOL_NAMES.ask_thursday) {
|
|
756
|
-
lines.push({
|
|
757
|
-
...base,
|
|
758
|
-
id: id(index),
|
|
759
|
-
kind: "waiting",
|
|
760
|
-
callId: part.toolCallId,
|
|
761
|
-
question: String(args.question ?? ""),
|
|
762
|
-
options: optionsOf(args.options),
|
|
763
|
-
});
|
|
764
|
-
} else if (isAnswerCall(part.toolName)) {
|
|
765
|
-
// The answer is a tool call but draws as text: it is the last thing said
|
|
766
|
-
// in the thread, and taskFromRow promotes it to the result line
|
|
767
|
-
const text = String(args.result ?? "").trim();
|
|
768
|
-
if (text) lines.push({ ...base, id: id(index), kind: "text", text });
|
|
769
|
-
} else if (part.toolName === TOOL_NAMES.ask_bot) {
|
|
770
|
-
lines.push({
|
|
771
|
-
...base,
|
|
772
|
-
id: id(index),
|
|
773
|
-
kind: "ask",
|
|
774
|
-
callId: part.toolCallId,
|
|
775
|
-
to: String(args.bot ?? ""),
|
|
776
|
-
text: String(args.request ?? ""),
|
|
777
|
-
});
|
|
778
|
-
} else if (part.toolName === TOOL_NAMES.ask_back) {
|
|
779
|
-
// Borrowed bot asking its borrower; the addressee is not in the args
|
|
780
|
-
lines.push({
|
|
781
|
-
...base,
|
|
782
|
-
id: id(index),
|
|
783
|
-
kind: "ask",
|
|
784
|
-
callId: part.toolCallId,
|
|
785
|
-
to: owner,
|
|
786
|
-
text: String(args.question ?? ""),
|
|
787
|
-
});
|
|
788
|
-
} else if (part.toolName === TOOL_NAMES.bash) {
|
|
789
|
-
// Shell line: the command is the body, the model-written label sits above it
|
|
790
|
-
lines.push({
|
|
791
|
-
...base,
|
|
792
|
-
id: id(index),
|
|
793
|
-
kind: "tool",
|
|
794
|
-
callId: part.toolCallId,
|
|
795
|
-
name: part.toolName,
|
|
796
|
-
input: clip(String(args.command ?? ""), LINE_MAX),
|
|
797
|
-
note: labelOf(args),
|
|
798
|
-
});
|
|
799
|
-
} else {
|
|
800
|
-
lines.push({
|
|
801
|
-
...base,
|
|
802
|
-
id: id(index),
|
|
803
|
-
kind: "tool",
|
|
804
|
-
callId: part.toolCallId,
|
|
805
|
-
name: part.toolName,
|
|
806
|
-
input: toolLine(part.toolName, args),
|
|
807
|
-
// Any tool's model-written `description` is its label
|
|
808
|
-
note: labelOf(args),
|
|
809
|
-
// File tools open from the screen, so the path also travels unclipped
|
|
810
|
-
path:
|
|
811
|
-
part.toolName === TOOL_NAMES.write_file
|
|
812
|
-
? (stringAt(args, "path") ?? null)
|
|
813
|
-
: null,
|
|
814
|
-
});
|
|
815
|
-
}
|
|
816
|
-
});
|
|
817
|
-
return lines;
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
if (message.role === "tool") {
|
|
821
|
-
const lines: TaskLine[] = [];
|
|
822
|
-
(message.content as ToolContent).forEach((part, index) => {
|
|
823
|
-
if (part.type !== "tool-result") return;
|
|
824
|
-
if (isAnswerCall(part.toolName) && answerAccepted(part.output)) {
|
|
825
|
-
// An accepted answer's result is only a confirmation; the answer itself
|
|
826
|
-
// was drawn from the call. A rejected one is drawn: it explains the next step
|
|
827
|
-
return;
|
|
828
|
-
}
|
|
829
|
-
if (part.toolName === TOOL_NAMES.ask_thursday) {
|
|
830
|
-
// The user's answer, returned as the question's result
|
|
831
|
-
const text = resultParts(part.output, RESULT_LINES)
|
|
832
|
-
.flatMap((result) => (result.type === "text" ? [result.text] : []))
|
|
833
|
-
.join("\n");
|
|
834
|
-
lines.push({
|
|
835
|
-
...base,
|
|
836
|
-
id: id(index),
|
|
837
|
-
kind: "user",
|
|
838
|
-
text: untagSpeaker(text),
|
|
839
|
-
});
|
|
840
|
-
} else if (
|
|
841
|
-
part.toolName === TOOL_NAMES.ask_bot ||
|
|
842
|
-
part.toolName === TOOL_NAMES.ask_back
|
|
843
|
-
) {
|
|
844
|
-
// Another bot's answer: result of an ask, or of an ask_back
|
|
845
|
-
const text = resultParts(part.output, RESULT_LINES)
|
|
846
|
-
.flatMap((result) => (result.type === "text" ? [result.text] : []))
|
|
847
|
-
.join("\n");
|
|
848
|
-
lines.push({
|
|
849
|
-
...base,
|
|
850
|
-
id: id(index),
|
|
851
|
-
kind: "ask-result",
|
|
852
|
-
callId: part.toolCallId,
|
|
853
|
-
text,
|
|
854
|
-
});
|
|
855
|
-
} else {
|
|
856
|
-
lines.push(resultLine(base, id(index), part));
|
|
857
|
-
}
|
|
858
|
-
});
|
|
859
|
-
return lines;
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
return [];
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
/** List line for one tool result. Asks for one line extra to know whether it was clipped. Called for tool rows and, for provider-run tools, assistant rows. */
|
|
866
|
-
function resultLine(
|
|
867
|
-
base: Omit<TaskLine, "id" | "kind">,
|
|
868
|
-
id: string,
|
|
869
|
-
part: { toolCallId: string; toolName: string; output: unknown },
|
|
870
|
-
): TaskLine {
|
|
871
|
-
const peek = resultParts(part.output, RESULT_LINES + 1);
|
|
872
|
-
return {
|
|
873
|
-
...base,
|
|
874
|
-
id,
|
|
875
|
-
kind: "tool-result",
|
|
876
|
-
callId: part.toolCallId,
|
|
877
|
-
name: part.toolName,
|
|
878
|
-
results: peek.slice(0, RESULT_LINES),
|
|
879
|
-
more: peek.length > RESULT_LINES,
|
|
880
|
-
};
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
/**
|
|
884
|
-
* Options the model gave. Only arrays are read. Some providers serialise the
|
|
885
|
-
* array as one string with no separator; that cannot be split back, so the
|
|
886
|
-
* schema accepts a string (keeping the call valid) and it becomes an open
|
|
887
|
-
* question here rather than a guess.
|
|
888
|
-
*/
|
|
889
|
-
export function optionsOf(value: unknown): string[] {
|
|
890
|
-
return Array.isArray(value)
|
|
891
|
-
? value.map((option) => String(option).trim()).filter(Boolean)
|
|
892
|
-
: [];
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
function textOf(parts: Array<{ type: string; text?: string }>): string {
|
|
896
|
-
return parts
|
|
897
|
-
.flatMap((part) => (part.type === "text" && part.text ? [part.text] : []))
|
|
898
|
-
.join("\n")
|
|
899
|
-
.trim();
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
/** Tool output as a few glanceable parts: text becomes lines, images stay images. ai-sdk output shapes first, then our tools' shapes, then raw JSON. */
|
|
903
|
-
function resultParts(output: unknown, limit: number): ResultPart[] {
|
|
904
|
-
if (output == null) return [];
|
|
905
|
-
if (typeof output === "string") return textParts(output, limit);
|
|
906
|
-
if (typeof output !== "object") return textParts(String(output), limit);
|
|
907
|
-
|
|
908
|
-
const wrapped = output as { type?: unknown; value?: unknown };
|
|
909
|
-
switch (wrapped.type) {
|
|
910
|
-
case "text":
|
|
911
|
-
case "error-text":
|
|
912
|
-
return textParts(String(wrapped.value ?? ""), limit);
|
|
913
|
-
case "json":
|
|
914
|
-
case "error-json":
|
|
915
|
-
return resultParts(wrapped.value, limit);
|
|
916
|
-
case "execution-denied":
|
|
917
|
-
return textParts("Denied.", limit);
|
|
918
|
-
case "content": {
|
|
919
|
-
if (!Array.isArray(wrapped.value)) return [];
|
|
920
|
-
const parts: ResultPart[] = [];
|
|
921
|
-
for (const block of wrapped.value as Array<Record<string, unknown>>) {
|
|
922
|
-
if (block.type === "text" && typeof block.text === "string") {
|
|
923
|
-
parts.push(...textParts(block.text, limit));
|
|
924
|
-
continue;
|
|
925
|
-
}
|
|
926
|
-
const image = imageSrc(block);
|
|
927
|
-
if (image) parts.push({ type: "image", src: image });
|
|
928
|
-
}
|
|
929
|
-
return parts;
|
|
930
|
-
}
|
|
931
|
-
default:
|
|
932
|
-
break;
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
const record = output as Record<string, unknown>;
|
|
936
|
-
if (Array.isArray(record.sources)) {
|
|
937
|
-
return (
|
|
938
|
-
record.sources
|
|
939
|
-
.map((source) => String((source as { title?: string })?.title ?? ""))
|
|
940
|
-
// Untitled results would be blank rows; filter after map so slice still fills
|
|
941
|
-
.filter(Boolean)
|
|
942
|
-
.slice(0, limit)
|
|
943
|
-
.map((text) => ({ type: "text" as const, text }))
|
|
944
|
-
);
|
|
945
|
-
}
|
|
946
|
-
if (typeof record.stdout === "string") {
|
|
947
|
-
return textParts(record.stdout || String(record.stderr ?? ""), limit);
|
|
948
|
-
}
|
|
949
|
-
if (typeof record.answer === "string") return textParts(record.answer, limit);
|
|
950
|
-
return textParts(JSON.stringify(output), limit);
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
/** A file block holding an image, as something <img> can show. base64 becomes a data url, urls pass through, anything else is dropped. */
|
|
954
|
-
function imageSrc(block: Record<string, unknown>): string | null {
|
|
955
|
-
const mediaType = String(block.mediaType ?? "");
|
|
956
|
-
if (!mediaType.startsWith("image/")) return null;
|
|
957
|
-
const data = block.data as
|
|
958
|
-
| string
|
|
959
|
-
| { type?: string; data?: unknown; url?: string }
|
|
960
|
-
| undefined;
|
|
961
|
-
if (typeof data === "string") return `data:${mediaType};base64,${data}`;
|
|
962
|
-
if (data?.type === "url" && data.url) return data.url;
|
|
963
|
-
if (data?.type === "data" && typeof data.data === "string") {
|
|
964
|
-
return `data:${mediaType};base64,${data.data}`;
|
|
965
|
-
}
|
|
966
|
-
return null;
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
function textParts(text: string, limit: number): ResultPart[] {
|
|
970
|
-
return text
|
|
971
|
-
.split("\n")
|
|
972
|
-
.map((line) => line.trim())
|
|
973
|
-
.filter(Boolean)
|
|
974
|
-
.slice(0, limit)
|
|
975
|
-
.map((line) => ({ type: "text" as const, text: line }));
|
|
976
|
-
}
|