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
|
@@ -0,0 +1,971 @@
|
|
|
1
|
+
import type { AssistantContent, ModelMessage, ToolContent } from "ai";
|
|
2
|
+
import { and, desc, eq, inArray, lt, max, sql } from "drizzle-orm";
|
|
3
|
+
import { appEvents } from "@/app/api/events/app-event.server";
|
|
4
|
+
import { INBOX_FINISHED, PAGE_SIZE, THREAD_STATUS_LIMIT } from "@/config";
|
|
5
|
+
import { database } from "@/database/db";
|
|
6
|
+
import {
|
|
7
|
+
threadDeliveryTable,
|
|
8
|
+
threadMessageTable,
|
|
9
|
+
threadRelayTable,
|
|
10
|
+
threadTable,
|
|
11
|
+
threadWorkTable,
|
|
12
|
+
} from "@/database/tables";
|
|
13
|
+
import { TOOL_NAMES } from "@/features/ai/tools/tool-name";
|
|
14
|
+
import { clip } from "@/lib/utils";
|
|
15
|
+
import {
|
|
16
|
+
type ResultPart,
|
|
17
|
+
type Thread,
|
|
18
|
+
type ThreadLine,
|
|
19
|
+
type ThreadPending,
|
|
20
|
+
type ThreadStatus,
|
|
21
|
+
type TokenUsage,
|
|
22
|
+
untagSpeaker,
|
|
23
|
+
} from "./bot.schema";
|
|
24
|
+
import { RESUME_CHECK, ROOM_THURSDAY } from "./room.schema";
|
|
25
|
+
|
|
26
|
+
// Threads and their messages. Bots themselves (roster, pinned tools) are bot.query.
|
|
27
|
+
|
|
28
|
+
/** Every write in this file ends with this; the screen re-reads the list on it. */
|
|
29
|
+
const changed = () => appEvents.emit({ type: "threads" });
|
|
30
|
+
|
|
31
|
+
/** Thread state without its messages. `lines` is filled by `withLines` from thread_message; `pending` folds into `ask`. */
|
|
32
|
+
const threadView = {
|
|
33
|
+
id: threadTable.id,
|
|
34
|
+
bot: threadTable.bot,
|
|
35
|
+
label: threadTable.label,
|
|
36
|
+
request: threadTable.request,
|
|
37
|
+
status: threadTable.status,
|
|
38
|
+
outcome: threadTable.outcome,
|
|
39
|
+
pending: threadTable.pending,
|
|
40
|
+
seen: threadTable.seen,
|
|
41
|
+
inputTokens: threadTable.inputTokens,
|
|
42
|
+
outputTokens: threadTable.outputTokens,
|
|
43
|
+
contextTokens: threadTable.contextTokens,
|
|
44
|
+
contextBudget: threadTable.contextBudget,
|
|
45
|
+
createdAt: threadTable.createdAt,
|
|
46
|
+
updatedAt: threadTable.updatedAt,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
type ThreadRow = {
|
|
50
|
+
id: string;
|
|
51
|
+
bot: string;
|
|
52
|
+
label: string;
|
|
53
|
+
request: string;
|
|
54
|
+
status: ThreadStatus;
|
|
55
|
+
outcome: string | null;
|
|
56
|
+
pending: ThreadPending | null;
|
|
57
|
+
seen: boolean;
|
|
58
|
+
inputTokens: number;
|
|
59
|
+
outputTokens: number;
|
|
60
|
+
contextTokens: number;
|
|
61
|
+
contextBudget: number;
|
|
62
|
+
createdAt: Date;
|
|
63
|
+
updatedAt: Date;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* One row, the thread's first message and the coordinator's first exchange, in one
|
|
68
|
+
* transaction: a thread is a room from its first row. `request` is what the screen
|
|
69
|
+
* shows; `opening` is what the model reads (who handed it over, and the job). The
|
|
70
|
+
* screen skips seq 0 and shows `request` instead (linesOf).
|
|
71
|
+
*/
|
|
72
|
+
export async function insertThread(input: {
|
|
73
|
+
bot: string;
|
|
74
|
+
label: string;
|
|
75
|
+
request: string;
|
|
76
|
+
callId?: string | null;
|
|
77
|
+
opening: Extract<ModelMessage, { role: "user" }>["content"];
|
|
78
|
+
}) {
|
|
79
|
+
const { opening, ...row } = input;
|
|
80
|
+
const thread = await database.transaction(async (tx) => {
|
|
81
|
+
const [created] = await tx
|
|
82
|
+
.insert(threadTable)
|
|
83
|
+
.values({ id: crypto.randomUUID(), status: "running", ...row })
|
|
84
|
+
.returning();
|
|
85
|
+
await tx.insert(threadMessageTable).values({
|
|
86
|
+
threadId: created.id,
|
|
87
|
+
seq: 0,
|
|
88
|
+
bot: null,
|
|
89
|
+
parent: null,
|
|
90
|
+
role: "user",
|
|
91
|
+
content: opening,
|
|
92
|
+
compact: false,
|
|
93
|
+
note: false,
|
|
94
|
+
});
|
|
95
|
+
await tx.insert(threadWorkTable).values({
|
|
96
|
+
id: crypto.randomUUID(),
|
|
97
|
+
threadId: created.id,
|
|
98
|
+
bot: created.bot,
|
|
99
|
+
caller: ROOM_THURSDAY,
|
|
100
|
+
state: "queued",
|
|
101
|
+
});
|
|
102
|
+
return created;
|
|
103
|
+
});
|
|
104
|
+
changed();
|
|
105
|
+
return thread;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function updateThread(
|
|
109
|
+
id: string,
|
|
110
|
+
patch: Partial<{
|
|
111
|
+
status: ThreadStatus;
|
|
112
|
+
outcome: string | null;
|
|
113
|
+
pending: ThreadPending | null;
|
|
114
|
+
seen: boolean;
|
|
115
|
+
endedAt: Date | null;
|
|
116
|
+
}>,
|
|
117
|
+
) {
|
|
118
|
+
await database
|
|
119
|
+
.update(threadTable)
|
|
120
|
+
.set({ ...patch, updatedAt: new Date() })
|
|
121
|
+
.where(eq(threadTable.id, id));
|
|
122
|
+
changed();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Bypasses `updateThread` on purpose: being seen is not movement and must not
|
|
127
|
+
* reorder the inbox by updatedAt. What was read on screen, or told enough on a
|
|
128
|
+
* call, has nothing left to relay, so its relays are settled with it.
|
|
129
|
+
*/
|
|
130
|
+
export async function markSeen(ids: string[]) {
|
|
131
|
+
if (ids.length === 0) return;
|
|
132
|
+
await database.transaction(async (tx) => {
|
|
133
|
+
await tx
|
|
134
|
+
.update(threadTable)
|
|
135
|
+
.set({ seen: true })
|
|
136
|
+
.where(inArray(threadTable.id, ids));
|
|
137
|
+
await tx
|
|
138
|
+
.update(threadRelayTable)
|
|
139
|
+
.set({ accepted: true })
|
|
140
|
+
.where(inArray(threadRelayTable.threadId, ids));
|
|
141
|
+
});
|
|
142
|
+
changed();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Adds one step's usage (added, not set: participants' steps land on the same
|
|
147
|
+
* thread in parallel) without touching updatedAt. `context` is overwritten: it is
|
|
148
|
+
* the current window fill and threshold, not a sum. null leaves it as is.
|
|
149
|
+
*/
|
|
150
|
+
export async function addThreadUsage(
|
|
151
|
+
id: string,
|
|
152
|
+
usage: TokenUsage,
|
|
153
|
+
context: { tokens: number; budget: number } | null = null,
|
|
154
|
+
) {
|
|
155
|
+
if (!usage.input && !usage.output && context === null) return;
|
|
156
|
+
await database
|
|
157
|
+
.update(threadTable)
|
|
158
|
+
.set({
|
|
159
|
+
inputTokens: sql`${threadTable.inputTokens} + ${usage.input}`,
|
|
160
|
+
outputTokens: sql`${threadTable.outputTokens} + ${usage.output}`,
|
|
161
|
+
...(context === null
|
|
162
|
+
? {}
|
|
163
|
+
: { contextTokens: context.tokens, contextBudget: context.budget }),
|
|
164
|
+
})
|
|
165
|
+
.where(eq(threadTable.id, id));
|
|
166
|
+
changed();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** One thread as the screen sees it, messages included. null if missing. */
|
|
170
|
+
export async function findThreadView(id: string): Promise<Thread | null> {
|
|
171
|
+
const [row] = await database
|
|
172
|
+
.select(threadView)
|
|
173
|
+
.from(threadTable)
|
|
174
|
+
.where(eq(threadTable.id, id));
|
|
175
|
+
if (!row) return null;
|
|
176
|
+
const [thread] = await withLines([row]);
|
|
177
|
+
return thread ?? null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export async function findThread(id: string) {
|
|
181
|
+
const [thread] = await database
|
|
182
|
+
.select()
|
|
183
|
+
.from(threadTable)
|
|
184
|
+
.where(eq(threadTable.id, id));
|
|
185
|
+
return thread ?? null;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Keep open work, unread endings and unrelayed messages alongside recent read endings. */
|
|
189
|
+
export async function listInboxThreads(): Promise<Thread[]> {
|
|
190
|
+
const [open, finished, unread, unrelayed] = await Promise.all([
|
|
191
|
+
database
|
|
192
|
+
.select(threadView)
|
|
193
|
+
.from(threadTable)
|
|
194
|
+
.where(inArray(threadTable.status, ["running", "waiting"]))
|
|
195
|
+
.orderBy(desc(threadTable.updatedAt)),
|
|
196
|
+
database
|
|
197
|
+
.select(threadView)
|
|
198
|
+
.from(threadTable)
|
|
199
|
+
.where(inArray(threadTable.status, ["done", "cancelled"]))
|
|
200
|
+
.orderBy(desc(threadTable.updatedAt))
|
|
201
|
+
.limit(INBOX_FINISHED),
|
|
202
|
+
database
|
|
203
|
+
.select(threadView)
|
|
204
|
+
.from(threadTable)
|
|
205
|
+
.where(
|
|
206
|
+
and(
|
|
207
|
+
inArray(threadTable.status, ["done", "cancelled"]),
|
|
208
|
+
eq(threadTable.seen, false),
|
|
209
|
+
),
|
|
210
|
+
),
|
|
211
|
+
database
|
|
212
|
+
.select(threadView)
|
|
213
|
+
.from(threadTable)
|
|
214
|
+
.where(
|
|
215
|
+
inArray(
|
|
216
|
+
threadTable.id,
|
|
217
|
+
database
|
|
218
|
+
.select({ id: threadRelayTable.threadId })
|
|
219
|
+
.from(threadRelayTable)
|
|
220
|
+
.where(eq(threadRelayTable.accepted, false)),
|
|
221
|
+
),
|
|
222
|
+
),
|
|
223
|
+
]);
|
|
224
|
+
const rows = [
|
|
225
|
+
...new Map(
|
|
226
|
+
[...open, ...finished, ...unread, ...unrelayed].map((row) => [
|
|
227
|
+
row.id,
|
|
228
|
+
row,
|
|
229
|
+
]),
|
|
230
|
+
).values(),
|
|
231
|
+
].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
|
|
232
|
+
return withLines(rows);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** The call's bounded overview includes older open work before recent endings. */
|
|
236
|
+
export async function listThreadOverview(): Promise<Thread[]> {
|
|
237
|
+
const rows = await database
|
|
238
|
+
.select(threadView)
|
|
239
|
+
.from(threadTable)
|
|
240
|
+
.orderBy(
|
|
241
|
+
sql`case when ${threadTable.status} in ('running', 'waiting') then 0 else 1 end`,
|
|
242
|
+
desc(threadTable.updatedAt),
|
|
243
|
+
desc(threadTable.id),
|
|
244
|
+
)
|
|
245
|
+
.limit(THREAD_STATUS_LIMIT);
|
|
246
|
+
return withLines(rows);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** All threads, newest first, one page at a time. `before` is a cursor (last page's updatedAt), not an offset, because rows move in between. */
|
|
250
|
+
export async function listThreadHistory(
|
|
251
|
+
options: { before?: Date | null; limit?: number } = {},
|
|
252
|
+
): Promise<Thread[]> {
|
|
253
|
+
const rows = await database
|
|
254
|
+
.select(threadView)
|
|
255
|
+
.from(threadTable)
|
|
256
|
+
.where(
|
|
257
|
+
options.before ? lt(threadTable.updatedAt, options.before) : undefined,
|
|
258
|
+
)
|
|
259
|
+
.orderBy(desc(threadTable.updatedAt))
|
|
260
|
+
.limit(options.limit ?? PAGE_SIZE);
|
|
261
|
+
return withLines(rows);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* What each of these calls handed over, for the transcript the next call reads
|
|
266
|
+
* (ai/prompts/thursday.prompt). Not the inbox: a job belongs to the call it was
|
|
267
|
+
* opened from, and this is how it is still there once that call is over.
|
|
268
|
+
*/
|
|
269
|
+
export async function listCallJobs(callIds: string[]) {
|
|
270
|
+
if (!callIds.length) return [];
|
|
271
|
+
return await database
|
|
272
|
+
.select({
|
|
273
|
+
id: threadTable.id,
|
|
274
|
+
callId: threadTable.callId,
|
|
275
|
+
label: threadTable.label,
|
|
276
|
+
status: threadTable.status,
|
|
277
|
+
outcome: threadTable.outcome,
|
|
278
|
+
})
|
|
279
|
+
.from(threadTable)
|
|
280
|
+
.where(inArray(threadTable.callId, callIds))
|
|
281
|
+
.orderBy(threadTable.createdAt);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export type CallJob = Awaited<ReturnType<typeof listCallJobs>>[number];
|
|
285
|
+
|
|
286
|
+
/** Its messages go with it (cascade). The runner stops a live one first. */
|
|
287
|
+
export async function deleteThread(id: string) {
|
|
288
|
+
const removed = await database
|
|
289
|
+
.delete(threadTable)
|
|
290
|
+
.where(eq(threadTable.id, id))
|
|
291
|
+
.returning({ id: threadTable.id });
|
|
292
|
+
if (removed.length) changed();
|
|
293
|
+
return removed.length > 0;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** Every job, newest first. The caller stops each one before deleting it (bot.runner removeThread). */
|
|
297
|
+
export async function listAllThreadIds(): Promise<string[]> {
|
|
298
|
+
const rows = await database
|
|
299
|
+
.select({ id: threadTable.id })
|
|
300
|
+
.from(threadTable)
|
|
301
|
+
.orderBy(desc(threadTable.createdAt));
|
|
302
|
+
return rows.map((row) => row.id);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** Rows a previous server left as running; the runner decides which still are. */
|
|
306
|
+
export async function listRunningThreadIds() {
|
|
307
|
+
const rows = await database
|
|
308
|
+
.select({ id: threadTable.id })
|
|
309
|
+
.from(threadTable)
|
|
310
|
+
.where(eq(threadTable.status, "running"));
|
|
311
|
+
return rows.map((row) => row.id);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** Every job with what names its folder and whether it still keeps it (bot.runner sweepJobFiles). */
|
|
315
|
+
export async function listThreadFolders() {
|
|
316
|
+
return await database
|
|
317
|
+
.select({
|
|
318
|
+
id: threadTable.id,
|
|
319
|
+
label: threadTable.label,
|
|
320
|
+
status: threadTable.status,
|
|
321
|
+
endedAt: threadTable.endedAt,
|
|
322
|
+
updatedAt: threadTable.updatedAt,
|
|
323
|
+
})
|
|
324
|
+
.from(threadTable);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** Jobs paused for browser absence, which pick themselves back up. */
|
|
328
|
+
export async function listAutoStoppedThreads(): Promise<string[]> {
|
|
329
|
+
const rows = await database
|
|
330
|
+
.select({ id: threadTable.id, pending: threadTable.pending })
|
|
331
|
+
.from(threadTable)
|
|
332
|
+
.where(eq(threadTable.status, "waiting"));
|
|
333
|
+
return rows.flatMap((row) => (row.pending?.auto ? [row.id] : []));
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Paths a list of message contents gave `write_file`, oldest first and each once; a
|
|
338
|
+
* path written again moves to the end. A refused write is here too, so whoever shows
|
|
339
|
+
* them checks the disk (workspace.ts filesOnDisk).
|
|
340
|
+
*/
|
|
341
|
+
export function writtenPathsIn(contents: unknown[]): string[] {
|
|
342
|
+
const paths = new Set<string>();
|
|
343
|
+
for (const content of contents) {
|
|
344
|
+
if (!Array.isArray(content)) continue;
|
|
345
|
+
for (const part of content as {
|
|
346
|
+
type?: unknown;
|
|
347
|
+
toolName?: unknown;
|
|
348
|
+
input?: unknown;
|
|
349
|
+
}[]) {
|
|
350
|
+
if (
|
|
351
|
+
part.type !== "tool-call" ||
|
|
352
|
+
part.toolName !== TOOL_NAMES.write_file
|
|
353
|
+
) {
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
const path = (part.input as { path?: unknown } | null)?.path;
|
|
357
|
+
if (typeof path !== "string" || !path.trim()) continue;
|
|
358
|
+
paths.delete(path.trim());
|
|
359
|
+
paths.add(path.trim());
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return [...paths];
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/** Every path this job gave `write_file`, from every participant (writtenPathsIn). */
|
|
366
|
+
export async function listWrittenPaths(threadId: string): Promise<string[]> {
|
|
367
|
+
const rows = await database
|
|
368
|
+
.select({ content: threadMessageTable.content })
|
|
369
|
+
.from(threadMessageTable)
|
|
370
|
+
.where(
|
|
371
|
+
and(
|
|
372
|
+
eq(threadMessageTable.threadId, threadId),
|
|
373
|
+
eq(threadMessageTable.role, "assistant"),
|
|
374
|
+
),
|
|
375
|
+
)
|
|
376
|
+
.orderBy(threadMessageTable.seq);
|
|
377
|
+
return writtenPathsIn(rows.map((row) => row.content));
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Finds a thread by id or by exact label (case-insensitive). No looser matching:
|
|
382
|
+
* a miss returns null and the caller lists recent threads instead.
|
|
383
|
+
*/
|
|
384
|
+
export async function resolveThread(ref: string) {
|
|
385
|
+
const term = ref.trim();
|
|
386
|
+
if (!term) return null;
|
|
387
|
+
const whole = await findThread(term);
|
|
388
|
+
if (whole) return whole;
|
|
389
|
+
|
|
390
|
+
const lower = term.toLowerCase();
|
|
391
|
+
const recent = await database
|
|
392
|
+
.select()
|
|
393
|
+
.from(threadTable)
|
|
394
|
+
.orderBy(desc(threadTable.updatedAt))
|
|
395
|
+
.limit(20);
|
|
396
|
+
return recent.find((thread) => thread.label.toLowerCase() === lower) ?? null;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
export type ThreadMessageInput = {
|
|
400
|
+
bot: string | null;
|
|
401
|
+
parent: string | null;
|
|
402
|
+
role: ModelMessage["role"];
|
|
403
|
+
content: ModelMessage["content"];
|
|
404
|
+
/** Compaction summary row; `listParticipantTranscript` re-reads from here. */
|
|
405
|
+
compact?: boolean;
|
|
406
|
+
/** Drawn as the app's own line, not as something a person or a bot said. */
|
|
407
|
+
note?: boolean;
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Writes by seat, not append: a step's messages are written while streaming and
|
|
412
|
+
* rewritten whole when it ends, and the second write must replace the first.
|
|
413
|
+
*/
|
|
414
|
+
export async function upsertMessage(
|
|
415
|
+
threadId: string,
|
|
416
|
+
seq: number,
|
|
417
|
+
message: ThreadMessageInput,
|
|
418
|
+
) {
|
|
419
|
+
await database
|
|
420
|
+
.insert(threadMessageTable)
|
|
421
|
+
.values({
|
|
422
|
+
threadId,
|
|
423
|
+
seq,
|
|
424
|
+
...message,
|
|
425
|
+
compact: message.compact ?? false,
|
|
426
|
+
note: message.note ?? false,
|
|
427
|
+
})
|
|
428
|
+
.onConflictDoUpdate({
|
|
429
|
+
target: [threadMessageTable.threadId, threadMessageTable.seq],
|
|
430
|
+
set: {
|
|
431
|
+
role: message.role,
|
|
432
|
+
content: message.content,
|
|
433
|
+
compact: message.compact ?? false,
|
|
434
|
+
note: message.note ?? false,
|
|
435
|
+
},
|
|
436
|
+
});
|
|
437
|
+
changed();
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/** Frees seats claimed while streaming but unused when the step ended. Left in, they put unmatched tool results in the transcript the model re-reads. */
|
|
441
|
+
export async function deleteMessages(threadId: string, seqs: number[]) {
|
|
442
|
+
if (!seqs.length) return;
|
|
443
|
+
await database
|
|
444
|
+
.delete(threadMessageTable)
|
|
445
|
+
.where(
|
|
446
|
+
and(
|
|
447
|
+
eq(threadMessageTable.threadId, threadId),
|
|
448
|
+
inArray(threadMessageTable.seq, seqs),
|
|
449
|
+
),
|
|
450
|
+
);
|
|
451
|
+
changed();
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/** Highest seq taken so far; -1 for an empty thread. */
|
|
455
|
+
export async function lastSeq(threadId: string): Promise<number> {
|
|
456
|
+
const [row] = await database
|
|
457
|
+
.select({ seq: max(threadMessageTable.seq) })
|
|
458
|
+
.from(threadMessageTable)
|
|
459
|
+
.where(eq(threadMessageTable.threadId, threadId));
|
|
460
|
+
return row?.seq ?? -1;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/** One row in the screen's shape; `pending` folds into `ask`. */
|
|
464
|
+
function viewOf(row: ThreadRow, lines: ThreadLine[]): Omit<Thread, "room"> {
|
|
465
|
+
// contextTokens and contextBudget pass through in `rest`
|
|
466
|
+
const { pending, inputTokens, outputTokens, ...rest } = row;
|
|
467
|
+
return {
|
|
468
|
+
...rest,
|
|
469
|
+
ask:
|
|
470
|
+
row.status === "waiting"
|
|
471
|
+
? {
|
|
472
|
+
question: row.outcome ?? "",
|
|
473
|
+
options: pending?.options ?? [],
|
|
474
|
+
auto: pending?.auto === true,
|
|
475
|
+
messageId: pending?.messageId,
|
|
476
|
+
bot: pending?.bot,
|
|
477
|
+
}
|
|
478
|
+
: null,
|
|
479
|
+
tokens: { input: inputTokens, output: outputTokens },
|
|
480
|
+
lines,
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** Reduces every thread's messages to lines and attaches them to the rows. */
|
|
485
|
+
async function withLines(rows: ThreadRow[]): Promise<Thread[]> {
|
|
486
|
+
if (rows.length === 0) return [];
|
|
487
|
+
const messages = await database
|
|
488
|
+
.select()
|
|
489
|
+
.from(threadMessageTable)
|
|
490
|
+
.where(
|
|
491
|
+
inArray(
|
|
492
|
+
threadMessageTable.threadId,
|
|
493
|
+
rows.map((row) => row.id),
|
|
494
|
+
),
|
|
495
|
+
)
|
|
496
|
+
.orderBy(threadMessageTable.seq);
|
|
497
|
+
|
|
498
|
+
const threadIds = rows.map((row) => row.id);
|
|
499
|
+
const [works, deliveries, relays] = await Promise.all([
|
|
500
|
+
database
|
|
501
|
+
.select()
|
|
502
|
+
.from(threadWorkTable)
|
|
503
|
+
.where(inArray(threadWorkTable.threadId, threadIds)),
|
|
504
|
+
database
|
|
505
|
+
.select()
|
|
506
|
+
.from(threadDeliveryTable)
|
|
507
|
+
.where(
|
|
508
|
+
and(
|
|
509
|
+
inArray(threadDeliveryTable.threadId, threadIds),
|
|
510
|
+
eq(threadDeliveryTable.visible, true),
|
|
511
|
+
),
|
|
512
|
+
),
|
|
513
|
+
database
|
|
514
|
+
.select()
|
|
515
|
+
.from(threadRelayTable)
|
|
516
|
+
.where(
|
|
517
|
+
and(
|
|
518
|
+
inArray(threadRelayTable.threadId, threadIds),
|
|
519
|
+
eq(threadRelayTable.accepted, false),
|
|
520
|
+
),
|
|
521
|
+
),
|
|
522
|
+
]);
|
|
523
|
+
// A line is addressed to whoever opened the exchange it was written under
|
|
524
|
+
const owners = new Map(rows.map((row) => [row.id, row.bot]));
|
|
525
|
+
const callers = new Map(works.map((item) => [item.id, item.caller]));
|
|
526
|
+
const byThread = new Map<string, ThreadLine[]>();
|
|
527
|
+
for (const message of messages) {
|
|
528
|
+
const list = byThread.get(message.threadId) ?? [];
|
|
529
|
+
list.push(
|
|
530
|
+
...linesOf(
|
|
531
|
+
message,
|
|
532
|
+
(message.parent ? callers.get(message.parent) : null) ??
|
|
533
|
+
owners.get(message.threadId) ??
|
|
534
|
+
"",
|
|
535
|
+
),
|
|
536
|
+
);
|
|
537
|
+
byThread.set(message.threadId, list);
|
|
538
|
+
}
|
|
539
|
+
return rows.map((row) => {
|
|
540
|
+
const own = works.filter((item) => item.threadId === row.id);
|
|
541
|
+
const rank = [
|
|
542
|
+
"running",
|
|
543
|
+
"queued",
|
|
544
|
+
"paused",
|
|
545
|
+
"waiting",
|
|
546
|
+
"done",
|
|
547
|
+
"cancelled",
|
|
548
|
+
];
|
|
549
|
+
const participants = [...new Set(own.map((item) => item.bot))]
|
|
550
|
+
.filter((bot) => bot !== ROOM_THURSDAY)
|
|
551
|
+
.map((bot) => ({
|
|
552
|
+
bot,
|
|
553
|
+
state: own
|
|
554
|
+
.filter((item) => item.bot === bot)
|
|
555
|
+
.sort((a, b) => rank.indexOf(a.state) - rank.indexOf(b.state))[0]
|
|
556
|
+
.state,
|
|
557
|
+
}));
|
|
558
|
+
return {
|
|
559
|
+
...viewOf(row, byThread.get(row.id) ?? []),
|
|
560
|
+
room: {
|
|
561
|
+
participants,
|
|
562
|
+
questions: own
|
|
563
|
+
.filter((item) => item.state === "external")
|
|
564
|
+
.map((item) => ({
|
|
565
|
+
id: item.id,
|
|
566
|
+
bot: item.caller,
|
|
567
|
+
text: item.result ?? "",
|
|
568
|
+
options: item.options,
|
|
569
|
+
})),
|
|
570
|
+
deliveries: deliveries
|
|
571
|
+
.filter((item) => item.threadId === row.id)
|
|
572
|
+
.map((item) => ({
|
|
573
|
+
id: item.key,
|
|
574
|
+
bot: own.find((w) => w.id === item.workId)?.bot ?? row.bot,
|
|
575
|
+
text: item.text,
|
|
576
|
+
delivered: item.consumed,
|
|
577
|
+
})),
|
|
578
|
+
relays: relays.filter((item) => item.threadId === row.id),
|
|
579
|
+
},
|
|
580
|
+
};
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/** One-line cap: room for a path, small enough for a row. */
|
|
585
|
+
const LINE_MAX = 120;
|
|
586
|
+
|
|
587
|
+
/** Argument names likely to carry the point, in reading order. */
|
|
588
|
+
const TELLING = [
|
|
589
|
+
"query",
|
|
590
|
+
"command",
|
|
591
|
+
"prompt",
|
|
592
|
+
"url",
|
|
593
|
+
"path",
|
|
594
|
+
"pattern",
|
|
595
|
+
"request",
|
|
596
|
+
"question",
|
|
597
|
+
"title",
|
|
598
|
+
"name",
|
|
599
|
+
"thread",
|
|
600
|
+
"text",
|
|
601
|
+
] as const;
|
|
602
|
+
|
|
603
|
+
const stringAt = (args: Record<string, unknown>, key: string) => {
|
|
604
|
+
const value = args[key];
|
|
605
|
+
return typeof value === "string" && value.trim() ? value : null;
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
/** Model-written label on bash. null when absent; the command is then the label. */
|
|
609
|
+
function labelOf(args: Record<string, unknown>): string | null {
|
|
610
|
+
const note = stringAt(args, "description");
|
|
611
|
+
return note ? clip(note, LINE_MAX) : null;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* One tool call as one line, for the screen. No per-tool summary table: the
|
|
616
|
+
* label is model-written (bash `description`), other tools use the first
|
|
617
|
+
* telling argument, else the argument shape.
|
|
618
|
+
*/
|
|
619
|
+
function argumentLine(name: string, input: unknown): string {
|
|
620
|
+
const args = (input ?? {}) as Record<string, unknown>;
|
|
621
|
+
if (typeof args !== "object" || Array.isArray(args)) {
|
|
622
|
+
return clip(String(input ?? ""), LINE_MAX);
|
|
623
|
+
}
|
|
624
|
+
if (name === TOOL_NAMES.bash) {
|
|
625
|
+
return labelOf(args) ?? clip(String(args.command ?? ""), LINE_MAX);
|
|
626
|
+
}
|
|
627
|
+
if (name === TOOL_NAMES.tool_call || name === TOOL_NAMES.tool_search) {
|
|
628
|
+
// For MCP the call is inside the args. Server name alone would collapse
|
|
629
|
+
// twenty different calls into one line, and a resumed bot would repeat them
|
|
630
|
+
const called = Array.isArray(args.tools)
|
|
631
|
+
? args.tools.map(String).join(", ")
|
|
632
|
+
: String(args.tool ?? "");
|
|
633
|
+
const passed =
|
|
634
|
+
args.args && typeof args.args === "object"
|
|
635
|
+
? JSON.stringify(args.args)
|
|
636
|
+
: "";
|
|
637
|
+
return clip(
|
|
638
|
+
[String(args.server ?? ""), called, passed].filter(Boolean).join(" "),
|
|
639
|
+
LINE_MAX,
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
for (const key of TELLING) {
|
|
643
|
+
const value = stringAt(args, key);
|
|
644
|
+
if (value) return clip(value, LINE_MAX);
|
|
645
|
+
}
|
|
646
|
+
for (const key of Object.keys(args)) {
|
|
647
|
+
const value = stringAt(args, key);
|
|
648
|
+
if (value) return clip(value, LINE_MAX);
|
|
649
|
+
}
|
|
650
|
+
const keys = Object.keys(args);
|
|
651
|
+
return keys.length ? clip(JSON.stringify(args), LINE_MAX) : "";
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/** Result lines carried in the list, enough for a glance. */
|
|
655
|
+
const RESULT_LINES = 4;
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* Characters kept of each glance line. The list carries every thread's results
|
|
659
|
+
* on every read, so a long line (a skill's body, a JSON blob) would ride along
|
|
660
|
+
* whole; the output opens in full on demand. Long enough for a file path.
|
|
661
|
+
*/
|
|
662
|
+
const RESULT_LINE_MAX = 200;
|
|
663
|
+
|
|
664
|
+
/** Cap for the full result, so one log file cannot flatten the browser. */
|
|
665
|
+
const FULL_RESULT_LINES = 400;
|
|
666
|
+
|
|
667
|
+
/** Everything one tool returned, as written; only when the screen asks for it. The list carries a clipped glance (resultLine). */
|
|
668
|
+
export async function readToolResult(
|
|
669
|
+
threadId: string,
|
|
670
|
+
toolCallId: string,
|
|
671
|
+
): Promise<ResultPart[] | null> {
|
|
672
|
+
// Usually a tool row, but results of provider-run tools (search) sit inside
|
|
673
|
+
// an assistant row, so both are scanned
|
|
674
|
+
const rows = await database
|
|
675
|
+
.select({
|
|
676
|
+
role: threadMessageTable.role,
|
|
677
|
+
content: threadMessageTable.content,
|
|
678
|
+
})
|
|
679
|
+
.from(threadMessageTable)
|
|
680
|
+
.where(
|
|
681
|
+
and(
|
|
682
|
+
eq(threadMessageTable.threadId, threadId),
|
|
683
|
+
inArray(threadMessageTable.role, ["tool", "assistant"]),
|
|
684
|
+
),
|
|
685
|
+
);
|
|
686
|
+
for (const row of rows) {
|
|
687
|
+
if (typeof row.content === "string") continue;
|
|
688
|
+
for (const part of row.content as ToolContent) {
|
|
689
|
+
if (part.type === "tool-result" && part.toolCallId === toolCallId) {
|
|
690
|
+
return resultParts(part.output, FULL_RESULT_LINES, true);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
return null;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/** Who a message call names, with Thursday spelled the room's way whatever case the model wrote. */
|
|
698
|
+
function addresseeOf(args: Record<string, unknown>): string {
|
|
699
|
+
const to = String(args.to ?? "").trim();
|
|
700
|
+
return to.toLowerCase() === ROOM_THURSDAY.toLowerCase() ? ROOM_THURSDAY : to;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
type StoredMessage = typeof threadMessageTable.$inferSelect;
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* One stored message as screen lines. seq 0 is the request and is drawn from the
|
|
707
|
+
* row instead, so it yields nothing. A message call draws as speech. `addressee`
|
|
708
|
+
* is who opened the exchange the message was written under.
|
|
709
|
+
*/
|
|
710
|
+
function linesOf(message: StoredMessage, addressee: string): ThreadLine[] {
|
|
711
|
+
if (message.hidden) return [];
|
|
712
|
+
const base = {
|
|
713
|
+
seq: message.seq,
|
|
714
|
+
bot: message.bot,
|
|
715
|
+
parent: message.parent,
|
|
716
|
+
to: message.role === "user" ? message.bot : addressee,
|
|
717
|
+
at: message.createdAt,
|
|
718
|
+
};
|
|
719
|
+
const id = (index: number) => `${message.id}-${index}`;
|
|
720
|
+
const content = message.content;
|
|
721
|
+
|
|
722
|
+
if (message.role === "user") {
|
|
723
|
+
if (message.seq === 0) return [];
|
|
724
|
+
const text = typeof content === "string" ? content : textOf(content);
|
|
725
|
+
if (!text) return [];
|
|
726
|
+
// The app's own lines, never the user's words
|
|
727
|
+
if (message.compact) return [{ ...base, id: id(0), kind: "note", text }];
|
|
728
|
+
if (message.note) {
|
|
729
|
+
const why = text.endsWith(RESUME_CHECK)
|
|
730
|
+
? text.slice(0, -RESUME_CHECK.length).trimEnd()
|
|
731
|
+
: text;
|
|
732
|
+
return [{ ...base, id: id(0), kind: "stop", text: why }];
|
|
733
|
+
}
|
|
734
|
+
// Tagged with who said it for the bot (bot.schema tagSpeaker); the screen draws the words
|
|
735
|
+
return [{ ...base, id: id(0), kind: "user", text: untagSpeaker(text) }];
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
if (message.role === "assistant") {
|
|
739
|
+
const content = message.content as AssistantContent;
|
|
740
|
+
if (typeof content === "string") {
|
|
741
|
+
return content.trim()
|
|
742
|
+
? [{ ...base, id: id(0), kind: "text", text: content.trim() }]
|
|
743
|
+
: [];
|
|
744
|
+
}
|
|
745
|
+
const lines: ThreadLine[] = [];
|
|
746
|
+
content.forEach((part, index) => {
|
|
747
|
+
if (part.type === "text") {
|
|
748
|
+
const text = part.text.trim();
|
|
749
|
+
if (text) lines.push({ ...base, id: id(index), kind: "text", text });
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
// Result of a provider-run tool (search) arrives inside the same message
|
|
753
|
+
if (part.type === "tool-result") {
|
|
754
|
+
lines.push(resultLine(base, id(index), part));
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
if (part.type !== "tool-call") return;
|
|
758
|
+
const args = (part.input ?? {}) as Record<string, unknown>;
|
|
759
|
+
if (part.toolName === TOOL_NAMES.send_message) {
|
|
760
|
+
lines.push({
|
|
761
|
+
...base,
|
|
762
|
+
id: id(index),
|
|
763
|
+
kind: "ask",
|
|
764
|
+
callId: part.toolCallId,
|
|
765
|
+
to: addresseeOf(args),
|
|
766
|
+
text: String(args.text ?? ""),
|
|
767
|
+
// No kind is a message (room.schema RoomMessageSchema), never a question
|
|
768
|
+
question: args.kind === "question",
|
|
769
|
+
});
|
|
770
|
+
} else if (part.toolName === TOOL_NAMES.bash) {
|
|
771
|
+
// Shell line: the command is the body, the model-written label sits above it
|
|
772
|
+
lines.push({
|
|
773
|
+
...base,
|
|
774
|
+
id: id(index),
|
|
775
|
+
kind: "tool",
|
|
776
|
+
callId: part.toolCallId,
|
|
777
|
+
name: part.toolName,
|
|
778
|
+
input: clip(String(args.command ?? ""), LINE_MAX),
|
|
779
|
+
note: labelOf(args),
|
|
780
|
+
});
|
|
781
|
+
} else {
|
|
782
|
+
lines.push({
|
|
783
|
+
...base,
|
|
784
|
+
id: id(index),
|
|
785
|
+
kind: "tool",
|
|
786
|
+
callId: part.toolCallId,
|
|
787
|
+
name: part.toolName,
|
|
788
|
+
input: argumentLine(part.toolName, args),
|
|
789
|
+
// Any tool's model-written `description` is its label
|
|
790
|
+
note: labelOf(args),
|
|
791
|
+
// File tools open from the screen, so the path also travels unclipped
|
|
792
|
+
path:
|
|
793
|
+
part.toolName === TOOL_NAMES.write_file
|
|
794
|
+
? (stringAt(args, "path") ?? null)
|
|
795
|
+
: null,
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
});
|
|
799
|
+
return lines;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
if (message.role === "tool") {
|
|
803
|
+
const lines: ThreadLine[] = [];
|
|
804
|
+
(message.content as ToolContent).forEach((part, index) => {
|
|
805
|
+
if (part.type !== "tool-result") return;
|
|
806
|
+
// A delivered message's receipt is not a step; a refused one is (thread.store)
|
|
807
|
+
if (
|
|
808
|
+
part.toolName === TOOL_NAMES.send_message &&
|
|
809
|
+
part.output.type !== "error-text"
|
|
810
|
+
)
|
|
811
|
+
return;
|
|
812
|
+
lines.push(resultLine(base, id(index), part));
|
|
813
|
+
});
|
|
814
|
+
return lines;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
return [];
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* List line for one tool result: a glance of text lines, each clipped, and whether
|
|
822
|
+
* the output holds more. Asks for one line past the glance to know. Called for tool
|
|
823
|
+
* rows and, for provider-run tools, assistant rows.
|
|
824
|
+
*/
|
|
825
|
+
function resultLine(
|
|
826
|
+
base: Omit<ThreadLine, "id" | "kind">,
|
|
827
|
+
id: string,
|
|
828
|
+
part: { toolCallId: string; toolName: string; output: unknown },
|
|
829
|
+
): ThreadLine {
|
|
830
|
+
const peek = resultParts(part.output, RESULT_LINES + 1);
|
|
831
|
+
const lines = peek.flatMap((result) =>
|
|
832
|
+
result.type === "text" ? [result.text] : [],
|
|
833
|
+
);
|
|
834
|
+
const glance = lines.slice(0, RESULT_LINES);
|
|
835
|
+
return {
|
|
836
|
+
...base,
|
|
837
|
+
id,
|
|
838
|
+
kind: "tool-result",
|
|
839
|
+
callId: part.toolCallId,
|
|
840
|
+
name: part.toolName,
|
|
841
|
+
// Images stay out of the list: a screenshot is fetched with the full output
|
|
842
|
+
results: glance.map((text) => ({
|
|
843
|
+
type: "text" as const,
|
|
844
|
+
text: clip(text, RESULT_LINE_MAX),
|
|
845
|
+
})),
|
|
846
|
+
more:
|
|
847
|
+
lines.length > glance.length ||
|
|
848
|
+
peek.length > lines.length ||
|
|
849
|
+
glance.some((text) => text.length > RESULT_LINE_MAX),
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
function textOf(parts: Array<{ type: string; text?: string }>): string {
|
|
854
|
+
return parts
|
|
855
|
+
.flatMap((part) => (part.type === "text" && part.text ? [part.text] : []))
|
|
856
|
+
.join("\n")
|
|
857
|
+
.trim();
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* Tool output as parts: text and images. A glance trims lines and drops blank ones;
|
|
862
|
+
* `full` keeps the text as written and lays objects out to be read. ai-sdk output
|
|
863
|
+
* shapes first, then our tools' shapes, then raw JSON.
|
|
864
|
+
*/
|
|
865
|
+
function resultParts(
|
|
866
|
+
output: unknown,
|
|
867
|
+
limit: number,
|
|
868
|
+
full = false,
|
|
869
|
+
): ResultPart[] {
|
|
870
|
+
if (output == null) return [];
|
|
871
|
+
if (typeof output === "string") return textParts(output, limit, full);
|
|
872
|
+
if (typeof output !== "object") return textParts(String(output), limit, full);
|
|
873
|
+
|
|
874
|
+
const wrapped = output as { type?: unknown; value?: unknown };
|
|
875
|
+
switch (wrapped.type) {
|
|
876
|
+
case "text":
|
|
877
|
+
case "error-text":
|
|
878
|
+
return textParts(String(wrapped.value ?? ""), limit, full);
|
|
879
|
+
case "json":
|
|
880
|
+
case "error-json":
|
|
881
|
+
return resultParts(wrapped.value, limit, full);
|
|
882
|
+
case "execution-denied":
|
|
883
|
+
return textParts("Denied.", limit, full);
|
|
884
|
+
case "content": {
|
|
885
|
+
if (!Array.isArray(wrapped.value)) return [];
|
|
886
|
+
const parts: ResultPart[] = [];
|
|
887
|
+
for (const block of wrapped.value as Array<Record<string, unknown>>) {
|
|
888
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
889
|
+
parts.push(...textParts(block.text, limit, full));
|
|
890
|
+
continue;
|
|
891
|
+
}
|
|
892
|
+
const image = imageSrc(block);
|
|
893
|
+
if (image) parts.push({ type: "image", src: image });
|
|
894
|
+
}
|
|
895
|
+
return parts;
|
|
896
|
+
}
|
|
897
|
+
default:
|
|
898
|
+
break;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
const record = output as Record<string, unknown>;
|
|
902
|
+
if (Array.isArray(record.sources)) {
|
|
903
|
+
return (
|
|
904
|
+
record.sources
|
|
905
|
+
.map((source) => String((source as { title?: string })?.title ?? ""))
|
|
906
|
+
// Untitled results would be blank rows; filter after map so slice still fills
|
|
907
|
+
.filter(Boolean)
|
|
908
|
+
.slice(0, limit)
|
|
909
|
+
.map((text) => ({ type: "text" as const, text }))
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
if (typeof record.stdout === "string") {
|
|
913
|
+
return textParts(record.stdout || String(record.stderr ?? ""), limit, full);
|
|
914
|
+
}
|
|
915
|
+
if (typeof record.answer === "string")
|
|
916
|
+
return textParts(record.answer, limit, full);
|
|
917
|
+
return textParts(
|
|
918
|
+
full ? readable(output) : JSON.stringify(output),
|
|
919
|
+
limit,
|
|
920
|
+
full,
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/** An object read whole: a multi-line string (a skill's body) as the text it is, anything else as indented JSON. */
|
|
925
|
+
function readable(value: object): string {
|
|
926
|
+
if (Array.isArray(value)) return JSON.stringify(value, null, 2);
|
|
927
|
+
return Object.entries(value)
|
|
928
|
+
.map(([key, field]) =>
|
|
929
|
+
typeof field === "string" && field.includes("\n")
|
|
930
|
+
? `${key}:\n${field}`
|
|
931
|
+
: `${key}: ${JSON.stringify(field, null, 2)}`,
|
|
932
|
+
)
|
|
933
|
+
.join("\n");
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/** A file block holding an image, as something <img> can show. base64 becomes a data url, urls pass through, anything else is dropped. */
|
|
937
|
+
function imageSrc(block: Record<string, unknown>): string | null {
|
|
938
|
+
const mediaType = String(block.mediaType ?? "");
|
|
939
|
+
if (!mediaType.startsWith("image/")) return null;
|
|
940
|
+
const data = block.data as
|
|
941
|
+
| string
|
|
942
|
+
| { type?: string; data?: unknown; url?: string }
|
|
943
|
+
| undefined;
|
|
944
|
+
if (typeof data === "string") return `data:${mediaType};base64,${data}`;
|
|
945
|
+
if (data?.type === "url" && data.url) return data.url;
|
|
946
|
+
if (data?.type === "data" && typeof data.data === "string") {
|
|
947
|
+
return `data:${mediaType};base64,${data.data}`;
|
|
948
|
+
}
|
|
949
|
+
return null;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/**
|
|
953
|
+
* Text as parts. A glance is one part per non-blank trimmed line, up to `limit`;
|
|
954
|
+
* `full` is the text as written in one part, cut after `limit` lines with a count
|
|
955
|
+
* of what was left.
|
|
956
|
+
*/
|
|
957
|
+
function textParts(text: string, limit: number, full = false): ResultPart[] {
|
|
958
|
+
if (full) {
|
|
959
|
+
const lines = text.split("\n");
|
|
960
|
+
const kept = lines.slice(0, limit);
|
|
961
|
+
if (lines.length > limit) kept.push(`… ${lines.length - limit} more lines`);
|
|
962
|
+
const whole = kept.join("\n").trimEnd();
|
|
963
|
+
return whole ? [{ type: "text", text: whole }] : [];
|
|
964
|
+
}
|
|
965
|
+
return text
|
|
966
|
+
.split("\n")
|
|
967
|
+
.map((line) => line.trim())
|
|
968
|
+
.filter(Boolean)
|
|
969
|
+
.slice(0, limit)
|
|
970
|
+
.map((line) => ({ type: "text" as const, text: line }));
|
|
971
|
+
}
|