thursday-agent 0.1.2 → 0.2.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 +1 -0
- package/.next/build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/routes-manifest.json +6 -0
- package/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +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 +1 -1
- package/.next/server/app/api/artifact/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/notes/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/bot/notes/route/build-manifest.json +12 -0
- package/.next/server/app/api/bot/notes/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/bot/notes/route.js +14 -0
- package/.next/server/app/api/bot/notes/route.js.map +5 -0
- package/.next/server/app/api/bot/notes/route.js.nft.json +1 -0
- package/.next/server/app/api/bot/notes/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/bot/route.js +4 -4
- package/.next/server/app/api/bot/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/task/[id]/route.js +6 -6
- package/.next/server/app/api/bot/task/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/task/route.js +6 -6
- package/.next/server/app/api/bot/task/route.js.nft.json +1 -1
- package/.next/server/app/api/config/route.js +3 -3
- package/.next/server/app/api/config/route.js.nft.json +1 -1
- package/.next/server/app/api/events/route.js +1 -1
- package/.next/server/app/api/events/route.js.nft.json +1 -1
- package/.next/server/app/api/file/[...path]/route.js +1 -1
- package/.next/server/app/api/file/[...path]/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/catalog/route.js +6 -6
- package/.next/server/app/api/llm-model/catalog/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/route.js +3 -3
- package/.next/server/app/api/llm-model/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/[name]/route.js +3 -3
- package/.next/server/app/api/mcp/[name]/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/oauth/callback/route.js +5 -5
- package/.next/server/app/api/mcp/oauth/callback/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/route.js +3 -3
- package/.next/server/app/api/mcp/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/tools/route.js +3 -3
- package/.next/server/app/api/mcp/tools/route.js.nft.json +1 -1
- package/.next/server/app/api/memory/route.js +7 -7
- package/.next/server/app/api/memory/route.js.nft.json +1 -1
- package/.next/server/app/api/memory/tidy/route.js +10 -11
- package/.next/server/app/api/memory/tidy/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/[source]/[name]/route.js +1 -1
- package/.next/server/app/api/skills/[source]/[name]/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/route.js +1 -1
- package/.next/server/app/api/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/call/route.js +8 -8
- package/.next/server/app/api/thursday/call/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/skills/route.js +8 -8
- package/.next/server/app/api/thursday/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/tool-call/route.js +11 -12
- package/.next/server/app/api/thursday/tool-call/route.js.nft.json +1 -1
- package/.next/server/app/api/workspace/route.js +1 -1
- 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 +3 -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 +63 -41
- package/.next/server/app/page.js +7 -7
- 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 +1 -0
- package/.next/server/chunks/0d-b_next_dist_server_app-render_after-task-async-storage_external_0-exn7p.js +3 -0
- package/.next/server/chunks/{[root-of-the-server]__0t_dqaj._.js → [root-of-the-server]__0-npkh_._.js} +1 -1
- package/.next/server/chunks/[root-of-the-server]__005var5._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__019dx9f._.js +2 -2
- package/.next/server/chunks/[root-of-the-server]__02va5wz._.js +3 -0
- package/.next/server/chunks/{[root-of-the-server]__1mzp_el._.js → [root-of-the-server]__04_8spk._.js} +2 -2
- package/.next/server/chunks/[root-of-the-server]__06t77t4._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0bed6hu._.js +4 -4
- package/.next/server/chunks/[root-of-the-server]__0cro2dz._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__0emr6go._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0le4d-p._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0m8ywyo._.js +5 -5
- package/.next/server/chunks/{[root-of-the-server]__113fqg5._.js → [root-of-the-server]__0p32jfz._.js} +3 -3
- package/.next/server/chunks/[root-of-the-server]__0phdgco._.js +4 -4
- package/.next/server/chunks/[root-of-the-server]__0wvgrsl._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__11j52t0._.js +5 -0
- package/.next/server/chunks/{[root-of-the-server]__0j_n5l3._.js → [root-of-the-server]__1692zrv._.js} +2 -2
- package/.next/server/chunks/[root-of-the-server]__16thabu._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__173nhwf._.js +3 -3
- package/.next/server/chunks/[root-of-the-server]__1bn73ih._.js +10 -0
- package/.next/server/chunks/[root-of-the-server]__1bqhuh9._.js +4 -4
- package/.next/server/chunks/[root-of-the-server]__1ccon1w._.js +10 -0
- package/.next/server/chunks/[root-of-the-server]__1cqizfq._.js +1 -1
- package/.next/server/chunks/{[root-of-the-server]__1u_qvc7._.js → [root-of-the-server]__1fboj2j._.js} +2 -2
- package/.next/server/chunks/{[root-of-the-server]__1dow0bz._.js → [root-of-the-server]__1oli73c._.js} +2 -2
- package/.next/server/chunks/[root-of-the-server]__1txedx6._.js +4 -4
- package/.next/server/chunks/[root-of-the-server]__1untqv8._.js +5 -0
- package/.next/server/chunks/_0-73of8._.js +55 -0
- package/.next/server/chunks/{_2050gwn._.js → _00_584r._.js} +66 -28
- package/.next/server/chunks/{_0y8is-s._.js → _03a0897._.js} +1 -1
- package/.next/server/chunks/_05h2cii._.js +1 -1
- package/.next/server/chunks/_06mxlgn._.js +1 -1
- package/.next/server/chunks/_0a351g7._.js +1 -1
- package/.next/server/chunks/{_0hin38_._.js → _0khihjk._.js} +5 -5
- package/.next/server/chunks/_0kjjmag._.js +1 -1
- package/.next/server/chunks/{_1t8fv7b._.js → _0n74zsf._.js} +5 -5
- package/.next/server/chunks/_0nn99o2._.js +3 -0
- package/.next/server/chunks/_0pppbb5._.js +10 -6
- package/.next/server/chunks/{_1icgbi8._.js → _0rnf-6n._.js} +5 -5
- package/.next/server/chunks/_0u10wmw._.js +55 -0
- package/.next/server/chunks/{_0kl37zl._.js → _0vnutv9._.js} +1 -1
- package/.next/server/chunks/_0wp4le3._.js +1 -1
- package/.next/server/chunks/_0y5fg-x._.js +1 -1
- package/.next/server/chunks/_106co2o._.js +3 -0
- package/.next/server/chunks/_12bbv5i._.js +10 -6
- package/.next/server/chunks/_15hq89o._.js +1 -1
- package/.next/server/chunks/_15q1pig._.js +1 -1
- package/.next/server/chunks/_18-1wcc._.js +55 -0
- package/.next/server/chunks/_19wg5yn._.js +138 -0
- package/.next/server/chunks/_1_5mnq4._.js +18 -0
- package/.next/server/chunks/_1bt95p9._.js +55 -0
- package/.next/server/chunks/_1cbk7ms._.js +9 -9
- package/.next/server/chunks/_1drgfx4._.js +18 -0
- package/.next/server/chunks/_1drv2et._.js +10 -6
- package/.next/server/chunks/{_10qam6o._.js → _1e48czp._.js} +5 -5
- package/.next/server/chunks/{_1skgy7b._.js → _1kg70wc._.js} +5 -5
- package/.next/server/chunks/_1php4eq._.js +1 -1
- package/.next/server/chunks/_1rysr-r._.js +10 -0
- package/.next/server/chunks/_1u3rwbr._.js +3 -0
- package/.next/server/chunks/_1x7p319._.js +1 -1
- package/.next/server/chunks/_1yuv4vp._.js +1 -1
- package/.next/server/chunks/_next-internal_server_app_api_bot_notes_route_actions_0qcsyu7.js +3 -0
- package/.next/server/chunks/config_ts_1g4sz5o._.js +1 -1
- package/.next/server/chunks/features_bot_task_query_ts_11xuzl3._.js +1 -1
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0228f43._.js → node_modules_consola_dist_chunks_prompt_mjs_02s3hjt._.js} +1 -1
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0ir-vo2._.js → node_modules_consola_dist_chunks_prompt_mjs_06067w6._.js} +1 -1
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0otw8ef._.js → node_modules_consola_dist_chunks_prompt_mjs_096ais_._.js} +1 -1
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0tf290y._.js → node_modules_consola_dist_chunks_prompt_mjs_0a1rd09._.js} +1 -1
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_0gzvgfv._.js +3 -0
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_0j61-ma._.js +3 -0
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_0tvkfgh._.js +3 -0
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_11zjo-1._.js +3 -0
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1pycqsq._.js +3 -0
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1tpvamf._.js +3 -0
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1y9-wcr._.js +3 -0
- package/.next/server/chunks/ssr/[externals]__0dizgek._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0f6s48x._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0m1b5ky._.js +3 -0
- package/.next/server/chunks/ssr/{[root-of-the-server]__0dfwvr4._.js → [root-of-the-server]__0un-erv._.js} +2 -2
- package/.next/server/chunks/ssr/[root-of-the-server]__1-mnhyd._.js +5 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__1bww500._.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]__1qfqvmg._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1vcdast._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1xwnu-h._.js +3 -0
- package/.next/server/chunks/ssr/_03webdr._.js +3 -0
- package/.next/server/chunks/ssr/{_0gd4vaa._.js → _040sk32._.js} +71 -29
- package/.next/server/chunks/ssr/_077-g6s._.js +1 -1
- package/.next/server/chunks/ssr/{_0lc_7c8._.js → _09ja_w1._.js} +2 -2
- package/.next/server/chunks/ssr/{_1dcxzqe._.js → _0f_b_9s._.js} +2 -2
- package/.next/server/chunks/ssr/_0h7zdni._.js +1 -1
- package/.next/server/chunks/ssr/_0lu1n_x._.js +3 -0
- package/.next/server/chunks/ssr/_0sf1isi._.js +1 -1
- package/.next/server/chunks/ssr/_0su6_08._.js +3 -0
- package/.next/server/chunks/ssr/{_0nso8tp._.js → _0w5bgak._.js} +2 -2
- package/.next/server/chunks/ssr/{_19_o9d-._.js → _0x_nu5e._.js} +2 -2
- package/.next/server/chunks/ssr/_1-teee5._.js +3 -3
- package/.next/server/chunks/ssr/_13punpm._.js +1 -1
- package/.next/server/chunks/ssr/{_0ay6b01._.js → _1ciyoo3._.js} +2 -2
- package/.next/server/chunks/ssr/_1e-dgz0._.js +1 -1
- package/.next/server/chunks/ssr/_1fq7zgb._.js +1 -1
- package/.next/server/chunks/ssr/{_0cvjbuz._.js → _1g6l4uj._.js} +2 -2
- package/.next/server/chunks/ssr/{_0q_2f2x._.js → _1lip4th._.js} +2 -2
- package/.next/server/chunks/ssr/_1m3bsfr._.js +3 -0
- package/.next/server/chunks/ssr/_1mznrxk._.js +1 -1
- package/.next/server/chunks/ssr/_1ruzehw._.js +3 -0
- package/.next/server/chunks/ssr/_1vyd1ji._.js +3 -0
- package/.next/server/chunks/ssr/features_bot_components_bot-setting_tsx_01ajft6._.js +2 -2
- package/.next/server/chunks/ssr/features_config_components_14jqgy4._.js +1 -1
- 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_memory_components_memory-setting_tsx_0oa43sw._.js +1 -1
- package/.next/server/chunks/ssr/lib_realtime_12wg_dd._.js +27 -0
- package/.next/server/chunks/ssr/lib_realtime_15h_v1v._.js +27 -0
- package/.next/server/chunks/ssr/{node_modules_108wd2o._.js → node_modules_004qlr4._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1ub40gk._.js → node_modules_01qnv-a._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1y8_3vg._.js → node_modules_04n7mjs._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0_j7-g4._.js → node_modules_08d8bke._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0y2-av4._.js → node_modules_09xhur7._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_043-236._.js → node_modules_0_n241r._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0ls96je._.js → node_modules_0iz1rzd._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0xwx4u-._.js → node_modules_0kl7-tn._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1jatz7w._.js → node_modules_0kxz4gt._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1t0y5jt._.js → node_modules_0lrjk5t._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_13f2md6._.js → node_modules_0m93zeg._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1nhw9qg._.js → node_modules_0n1kj4p._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1k29j1j._.js → node_modules_0ndvxub._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_16i4o_3._.js → node_modules_13r44jq._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0fbw5-u._.js → node_modules_17lrt3y._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1d-is1o._.js → node_modules_18_dk7_._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1hn_sd5._.js → node_modules_19dp7ko._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_20ynonm._.js → node_modules_1dwguw_._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_00d6675._.js → node_modules_1hd-5tv._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1ee8gav._.js → node_modules_1hn8z1f._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0bue6gy._.js → node_modules_1n5ygqe._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1_588_4._.js → node_modules_1q0p85z._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0ipn1ho._.js → node_modules_1toafc-._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0_fvxnt._.js → node_modules_1xyd7ov._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1v0077l._.js → node_modules_1zu988w._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_06kvvuo._.js → node_modules_20yebtj._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_092s8nx._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0m9a4y0._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_1nvexgu._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_13w_wy9._.js} +1 -1
- package/.next/server/chunks/ssr/node_modules_consola_dist_chunks_prompt_mjs_05cwqv2._.js +47 -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 +64 -42
- package/.next/static/chunks/05b6nx_o3_f_n.js +1 -0
- package/.next/static/chunks/{3aquirz5h71da.js → 07kl352c-lvcz.js} +1 -1
- package/.next/static/chunks/0dmns-gi0raa4.js +1 -0
- package/.next/static/chunks/0oc4_xoia77dg.css +1 -0
- package/.next/static/chunks/0pheo8m3a-rzi.js +1 -0
- package/.next/static/chunks/{2ihkf6l2jowo9.js → 18vhba44l1fc9.js} +3 -3
- package/.next/static/chunks/19i8h2t-5evjx.js +1 -0
- package/.next/static/chunks/{1gy3bbv7usnjo.js → 1c5fcj473025q.js} +1 -1
- package/.next/static/chunks/1pi_hbmjw-f-z.js +1 -0
- package/.next/static/chunks/{0tydnn2kybibk.js → 1twt6k09vg4np.js} +1 -1
- package/.next/static/chunks/{21rk8w7bda-su.js → 28gotuisldmom.js} +1 -1
- package/.next/static/chunks/{3xxa2j56-f3nm.js → 2998p0y7k1c8z.js} +1 -1
- package/.next/static/chunks/2l233xfycjpd8.js +1 -0
- package/.next/static/chunks/2p5n7pa3c40t-.js +2 -0
- package/.next/static/chunks/2ru1q132af7h7.js +1 -0
- package/.next/static/chunks/{2fq_86jvq9ymq.js → 2t8wqycwg3y04.js} +2 -2
- package/.next/static/chunks/32z_ypgtchw48.js +1 -0
- package/.next/static/chunks/{247d-r95j8xs0.js → 33pyjb3xd1iez.js} +1 -1
- package/.next/static/chunks/3786h5vayt80-.js +28 -0
- package/.next/static/chunks/3_h9txt3tq8t8.js +28 -0
- package/.next/static/chunks/{1khakdu_iuedc.js → 3ard_noqapnw1.js} +1 -1
- package/.next/static/chunks/{1fr5m75pwtyic.js → 3omud5xunrk1f.js} +1 -1
- package/.next/static/chunks/42dnrkbph9e-1.js +1 -0
- package/app/api/events/app-event.client.ts +24 -0
- package/app/api/events/app-event.server.ts +45 -0
- package/app/api/events/app-event.ts +44 -0
- package/app/api/llm-model/catalog/route.ts +12 -0
- package/app/api/llm-model/route.ts +21 -0
- package/app/api/query-key.ts +144 -0
- package/app/api/thursday/tool-call/route.ts +67 -0
- package/components/ui/button-group.tsx +45 -0
- package/components/ui/custom-icon.tsx +259 -0
- package/components/ui/dropdown-menu.tsx +271 -0
- package/components/ui/folded-text.tsx +68 -0
- package/components/ui/input-group.tsx +157 -0
- package/components/ui/json-view.tsx +240 -0
- package/components/ui/shiny-text.tsx +141 -0
- package/components/ui/text-type.tsx +213 -0
- package/components/ui/theme-sync.tsx +9 -0
- package/database/migrations/{20260908030742_rainy_marvel_zombies → 20260909060813_optimal_sabretooth}/migration.sql +7 -0
- package/database/migrations/{20260908030742_rainy_marvel_zombies → 20260909060813_optimal_sabretooth}/snapshot.json +54 -1
- package/database/migrations/20260909063821_bitter_tenebrous/migration.sql +1 -0
- package/database/migrations/20260909063821_bitter_tenebrous/snapshot.json +1285 -0
- package/docs/how-it-works.md +59 -0
- package/docs/images/keeps-going.png +0 -0
- package/docs/images/task-thread.png +0 -0
- package/docs/images/two-minds.png +0 -0
- package/features/ai/load-tools.ts +55 -5
- package/features/ai/prompts/bot.prompt.ts +121 -26
- package/features/ai/prompts/memory-tidy.prompt.ts +7 -1
- package/features/ai/prompts/prompt-helper.ts +31 -5
- package/features/ai/prompts/thursday.prompt.ts +2 -0
- package/features/ai/tools/bot.tool.ts +37 -18
- package/features/ai/tools/mcp.tool.ts +5 -5
- package/features/ai/tools/memory.tool.ts +17 -10
- package/features/ai/tools/studio.tool.ts +5 -5
- package/features/ai/tools/tool-name.ts +2 -0
- package/features/ai/tools/workspace.tool.ts +33 -5
- package/features/bot/bot.action.ts +31 -4
- package/features/bot/bot.notes.ts +120 -0
- package/features/bot/bot.query.ts +56 -1
- package/features/bot/bot.run.ts +43 -3
- package/features/bot/bot.runner.ts +94 -20
- package/features/bot/bot.schema.ts +17 -0
- package/features/bot/components/bot-room.tsx +405 -306
- package/features/bot/components/bot-setting.tsx +123 -20
- package/features/bot/task.query.ts +18 -5
- package/features/memory/components/memory-setting.tsx +11 -2
- package/features/memory/memory.query.ts +9 -2
- package/features/memory/memory.schema.ts +30 -0
- package/features/skills/components/skills-setting.tsx +9 -1
- package/features/workspace/workspace.ts +136 -3
- package/hooks/use-draft.ts +37 -0
- package/hooks/use-hotkey.ts +105 -0
- package/hooks/use-object-state.ts +51 -0
- package/hooks/use-on-visible.ts +72 -0
- package/hooks/use-theme.ts +106 -0
- package/hooks/use-wake-word.ts +262 -0
- package/lib/realtime/realtime.driver.ts +34 -1
- package/lib/realtime/realtime.protocol.ts +6 -0
- package/lib/sandbox.ts +9 -1
- package/lib/tokens.ts +19 -0
- package/package.json +1 -1
- package/.next/server/chunks/[root-of-the-server]__02kt8yn._.js +0 -10
- package/.next/server/chunks/[root-of-the-server]__0h59di-._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__0qjqtwo._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__0yk6s3w._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__0z3gelv._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__11w_7_3._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1_0q_9w._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1gmn1cs._.js +0 -10
- package/.next/server/chunks/_0fzqt2y._.js +0 -100
- package/.next/server/chunks/_0jgm3b8._.js +0 -55
- package/.next/server/chunks/_0ks6_sg._.js +0 -55
- package/.next/server/chunks/_0tzfesa._.js +0 -3
- package/.next/server/chunks/_0zmestz._.js +0 -55
- package/.next/server/chunks/_12nr4xc._.js +0 -3
- package/.next/server/chunks/_1hzrsvg._.js +0 -55
- package/.next/server/chunks/_1uj4_io._.js +0 -10
- package/.next/server/chunks/_1yldgo4._.js +0 -55
- package/.next/server/chunks/features_0scmr4i._.js +0 -3
- package/.next/server/chunks/features_bot_bot_query_ts_12mgkl2._.js +0 -3
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_0ykklj0._.js +0 -3
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1ryg3wh._.js +0 -3
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1vwbnq_._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__01id-v3._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0b-0i32._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__185grso._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__1minz8_._.js +0 -3
- package/.next/server/chunks/ssr/_0j4v8of._.js +0 -3
- package/.next/server/chunks/ssr/_0l81bzy._.js +0 -3
- package/.next/server/chunks/ssr/_10uvw3l._.js +0 -3
- package/.next/server/chunks/ssr/_1jbv7m1._.js +0 -3
- package/.next/server/chunks/ssr/lib_realtime_0aetuyp._.js +0 -24
- package/.next/server/chunks/ssr/lib_realtime_18k_aj-._.js +0 -24
- package/.next/server/chunks/ssr/node_modules_zod_v4_classic_errors_05xv7vp.js +0 -3
- package/.next/static/chunks/0yysv1gzin17t.js +0 -1
- package/.next/static/chunks/1keib_gt066gc.js +0 -1
- package/.next/static/chunks/28g42tw8tsvhd.js +0 -1
- package/.next/static/chunks/2cwjgkt_hoyhj.js +0 -22
- package/.next/static/chunks/2rbf1fpd8n80-.js +0 -22
- package/.next/static/chunks/2ukj8k69y89um.js +0 -1
- package/.next/static/chunks/2wngeetfnzxkp.js +0 -2
- package/.next/static/chunks/388-t2v72jh39.css +0 -1
- package/.next/static/chunks/3fqvc0cg-atq_.js +0 -1
- package/.next/static/chunks/3p4egvfjxpatb.js +0 -1
- package/.next/static/chunks/3qe1w1ei-huhl.js +0 -1
- package/.next/static/chunks/3y40riycdip-i.js +0 -1
- package/.next/static/chunks/45a6zbtkelph9.js +0 -1
- /package/.next/static/{X34BUd9bfdH27ripuwwBm → YR4NWcDDuzwLbTTU7U7Vr}/_buildManifest.js +0 -0
- /package/.next/static/{X34BUd9bfdH27ripuwwBm → YR4NWcDDuzwLbTTU7U7Vr}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{X34BUd9bfdH27ripuwwBm → YR4NWcDDuzwLbTTU7U7Vr}/_ssgManifest.js +0 -0
|
@@ -22,9 +22,9 @@ const MAX_SCHEMAS = 8;
|
|
|
22
22
|
export const mcpToolSpec = {
|
|
23
23
|
[TOOL_NAMES.tool_search]: {
|
|
24
24
|
name: TOOL_NAMES.tool_search,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
// What it is, not when to reach for it: the Connected tools chapter says
|
|
26
|
+
// that the listing carries names alone and that this is how a schema is got
|
|
27
|
+
description: `Return the description and exact inputSchema of tools on a connected server, so they can be called with \`${TOOL_NAMES.tool_call}\`.`,
|
|
28
28
|
parameters: z.object({
|
|
29
29
|
server: z
|
|
30
30
|
.string()
|
|
@@ -48,13 +48,13 @@ The Connected tools section names every server and every tool on it, and that is
|
|
|
48
48
|
tool: z.string().describe("Its name, as search returned it."),
|
|
49
49
|
args: z
|
|
50
50
|
.record(z.string(), z.unknown())
|
|
51
|
-
.
|
|
51
|
+
.nullish()
|
|
52
52
|
.describe(
|
|
53
53
|
"Arguments matching the inputSchema search returned — do not guess. Null when it takes none.",
|
|
54
54
|
),
|
|
55
55
|
description: z
|
|
56
56
|
.string()
|
|
57
|
-
.
|
|
57
|
+
.nullish()
|
|
58
58
|
.describe(
|
|
59
59
|
"One short line for the user: what this call is for. Shown on their screen while it runs.",
|
|
60
60
|
),
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
MANY_FACTS,
|
|
16
16
|
MEMORY_ALWAYS_LISTED,
|
|
17
17
|
MEMORY_INBOX,
|
|
18
|
+
type MemorySource,
|
|
18
19
|
} from "@/features/memory/memory.schema";
|
|
19
20
|
|
|
20
21
|
const GONE =
|
|
@@ -27,7 +28,12 @@ const WROTE =
|
|
|
27
28
|
const tooMany = (path: string, count: number) =>
|
|
28
29
|
` ${path} carries ${count} facts — enough that it is worth tidying. Put it on screen with \`${TOOL_NAMES.memory_show}\` and ask which of it they no longer need.`;
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
/**
|
|
32
|
+
* @param source Which hand these writes are recorded under. The runtime knows
|
|
33
|
+
* it without being told — the call, the read-back pass — so no model ever
|
|
34
|
+
* chooses it (load-tools, memory.schema MemorySource).
|
|
35
|
+
*/
|
|
36
|
+
export const createMemoryTools = (source: MemorySource) => ({
|
|
31
37
|
[TOOL_NAMES.memory_recall]: tool({
|
|
32
38
|
description: "Open one note from the listing, whole.",
|
|
33
39
|
inputSchema: z.object({
|
|
@@ -65,32 +71,32 @@ export const createMemoryTools = () => ({
|
|
|
65
71
|
replaces: z
|
|
66
72
|
.number()
|
|
67
73
|
.int()
|
|
68
|
-
.
|
|
74
|
+
.nullish()
|
|
69
75
|
.describe(
|
|
70
76
|
"The id of the fact this replaces — from this note as handed back here or by `memory_recall`. Null if this is new. A fact that stopped being true is replaced rather than deleted, and so is a note saying the same thing twice: replacing keeps what changed on the record, deleting does not.",
|
|
71
77
|
),
|
|
72
78
|
alwaysLoad: z
|
|
73
79
|
.boolean()
|
|
74
|
-
.
|
|
80
|
+
.nullish()
|
|
75
81
|
.describe(
|
|
76
82
|
`True carries this line into every call's instructions without opening its note — for what to call them, their language and register, a standing rule. At most ${ALWAYS_LOADED_MAX}. Null leaves it; false makes a carried line an ordinary fact.`,
|
|
77
83
|
),
|
|
78
84
|
})
|
|
79
85
|
.array()
|
|
80
|
-
.
|
|
86
|
+
.nullish()
|
|
81
87
|
.describe(
|
|
82
88
|
"What to write under this note. Send everything that came up at once: one call is one pause in the conversation, three calls are three. Null when only naming it.",
|
|
83
89
|
),
|
|
84
90
|
description: z
|
|
85
91
|
.string()
|
|
86
|
-
.
|
|
92
|
+
.nullish()
|
|
87
93
|
.describe(
|
|
88
94
|
`One line saying what this note is about, not what it currently says. Give it for a new note, or when the line no longer fits. Null leaves it; ${MEMORY_ALWAYS_LISTED.join(" and ")} keep their own line.`,
|
|
89
95
|
),
|
|
90
96
|
aliases: z
|
|
91
97
|
.string()
|
|
92
98
|
.array()
|
|
93
|
-
.
|
|
99
|
+
.nullish()
|
|
94
100
|
.describe(
|
|
95
101
|
'What the user calls this note out loud, in the language they say it — the words they would use to ask for it, not the facts inside. A note at people/yuri might be "Yuri" and "my wife". Replaces the set it has, so send the whole set. Null leaves it.',
|
|
96
102
|
),
|
|
@@ -127,7 +133,7 @@ export const createMemoryTools = () => ({
|
|
|
127
133
|
facts,
|
|
128
134
|
};
|
|
129
135
|
|
|
130
|
-
const write = await writeNotes([filed]);
|
|
136
|
+
const write = await writeNotes([filed], source);
|
|
131
137
|
|
|
132
138
|
// The write already succeeded; what follows are requests, not failures.
|
|
133
139
|
// A path filed elsewhere must be said, or the model reports it saved where it asked.
|
|
@@ -240,9 +246,10 @@ export const botRememberTool = tool({
|
|
|
240
246
|
// A path outside the convention goes to inbox, as it does for the call
|
|
241
247
|
const filed = known || isMemoryPath(said) ? (known ?? said) : MEMORY_INBOX;
|
|
242
248
|
|
|
243
|
-
await writeNotes(
|
|
244
|
-
{ path: filed, facts: written.map((text) => ({ text })) },
|
|
245
|
-
|
|
249
|
+
await writeNotes(
|
|
250
|
+
[{ path: filed, facts: written.map((text) => ({ text })) }],
|
|
251
|
+
"bot",
|
|
252
|
+
);
|
|
246
253
|
|
|
247
254
|
// A write filed elsewhere must be said, or the report claims the wrong place
|
|
248
255
|
return {
|
|
@@ -118,7 +118,7 @@ const imageTool = async (): Promise<StudioTool | null> => {
|
|
|
118
118
|
),
|
|
119
119
|
aspectRatio: z
|
|
120
120
|
.enum(RATIOS)
|
|
121
|
-
.
|
|
121
|
+
.nullish()
|
|
122
122
|
.describe("Aspect ratio. Null for square."),
|
|
123
123
|
}),
|
|
124
124
|
execute: async ({ prompt, aspectRatio }, { sandbox, abortSignal }) => {
|
|
@@ -161,11 +161,11 @@ const speechTool = async (): Promise<StudioTool | null> => {
|
|
|
161
161
|
),
|
|
162
162
|
voice: z
|
|
163
163
|
.string()
|
|
164
|
-
.
|
|
164
|
+
.nullish()
|
|
165
165
|
.describe("A voice name the provider knows. Null for its default."),
|
|
166
166
|
instructions: z
|
|
167
167
|
.string()
|
|
168
|
-
.
|
|
168
|
+
.nullish()
|
|
169
169
|
.describe(
|
|
170
170
|
'How to read it — "slowly", "warm", "like a news anchor". Null for plain.',
|
|
171
171
|
),
|
|
@@ -259,14 +259,14 @@ const videoTool = async (): Promise<StudioTool | null> => {
|
|
|
259
259
|
),
|
|
260
260
|
aspectRatio: z
|
|
261
261
|
.enum(["16:9", "9:16"])
|
|
262
|
-
.
|
|
262
|
+
.nullish()
|
|
263
263
|
.describe("Aspect ratio. Null for 16:9."),
|
|
264
264
|
seconds: z
|
|
265
265
|
.number()
|
|
266
266
|
.int()
|
|
267
267
|
.min(2)
|
|
268
268
|
.max(10)
|
|
269
|
-
.
|
|
269
|
+
.nullish()
|
|
270
270
|
.describe(
|
|
271
271
|
"Length in seconds, 2 to 10. Null for the provider's default.",
|
|
272
272
|
),
|
|
@@ -3,7 +3,7 @@ import { type ToolSet, tool } from "ai";
|
|
|
3
3
|
import z from "zod";
|
|
4
4
|
import { TOOL_NAMES } from "@/features/ai/tools/tool-name";
|
|
5
5
|
import { writeRefusal } from "@/features/workspace/workspace";
|
|
6
|
-
import type
|
|
6
|
+
import { EXEC_TIMEOUT_MS, type Sandbox } from "@/lib/sandbox";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Shell access, plus `write_file` for bots: multi-line files through a
|
|
@@ -15,6 +15,8 @@ export const createWorkspaceTools = (
|
|
|
15
15
|
write: boolean;
|
|
16
16
|
/** Per-run environment for `bash` (a job's browser session). */
|
|
17
17
|
env?: Record<string, string>;
|
|
18
|
+
/** Attach the shell guide to this run's first `bash` result (bots only). */
|
|
19
|
+
guide?: boolean;
|
|
18
20
|
},
|
|
19
21
|
): ToolSet => {
|
|
20
22
|
/** Fold absolute sandbox paths against cwd; they still resolve when handed back. */
|
|
@@ -23,20 +25,30 @@ export const createWorkspaceTools = (
|
|
|
23
25
|
return r && !r.startsWith("..") ? r : p;
|
|
24
26
|
};
|
|
25
27
|
|
|
28
|
+
// The tool set is built per run, so one guide per run lives in this closure.
|
|
29
|
+
let owed = options.guide === true;
|
|
30
|
+
|
|
26
31
|
const bash = tool({
|
|
27
32
|
description: "Run a bash command.",
|
|
28
33
|
inputSchema: z.object({
|
|
29
34
|
command: z.string(),
|
|
30
35
|
description: z
|
|
31
36
|
.string()
|
|
32
|
-
.
|
|
37
|
+
.nullish()
|
|
33
38
|
.describe(
|
|
34
39
|
"One short line for the user: what this command does and why. Shown on their screen while it runs — not a restatement of the command.",
|
|
35
40
|
),
|
|
36
41
|
}),
|
|
37
42
|
// The only tool here long-running enough to be cancelled.
|
|
38
|
-
execute: ({ command }, { abortSignal }) =>
|
|
39
|
-
sandbox.exec(command, {
|
|
43
|
+
execute: async ({ command }, { abortSignal }) => {
|
|
44
|
+
const result = await sandbox.exec(command, {
|
|
45
|
+
signal: abortSignal,
|
|
46
|
+
env: options.env,
|
|
47
|
+
});
|
|
48
|
+
if (!owed) return result;
|
|
49
|
+
owed = false;
|
|
50
|
+
return { ...result, guide: SHELL_GUIDE };
|
|
51
|
+
},
|
|
40
52
|
});
|
|
41
53
|
|
|
42
54
|
if (!options.write) return { [TOOL_NAMES.bash]: bash };
|
|
@@ -53,7 +65,7 @@ export const createWorkspaceTools = (
|
|
|
53
65
|
content: z.string(),
|
|
54
66
|
description: z
|
|
55
67
|
.string()
|
|
56
|
-
.
|
|
68
|
+
.nullish()
|
|
57
69
|
.describe(
|
|
58
70
|
"One short line for the user: what this file is. Shown on their screen next to the path.",
|
|
59
71
|
),
|
|
@@ -69,3 +81,19 @@ export const createWorkspaceTools = (
|
|
|
69
81
|
|
|
70
82
|
return { [TOOL_NAMES.bash]: bash, [TOOL_NAMES.write_file]: writeFile };
|
|
71
83
|
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* How this shell differs from a terminal, attached to a run's first `bash`
|
|
87
|
+
* result and never again. Each line is a way a job is lost for minutes and
|
|
88
|
+
* none can be found out from a command's output: state that silently does not
|
|
89
|
+
* carry, a question nobody will answer, a variable that was removed rather
|
|
90
|
+
* than never set. What this machine *has* is not here — that decides the first
|
|
91
|
+
* command, so it is in the prompt (prompts/bot.prompt environment).
|
|
92
|
+
*/
|
|
93
|
+
const SHELL_GUIDE = `## The shell here
|
|
94
|
+
|
|
95
|
+
Every command is a new shell: \`cd\`, exported variables and an activated venv are gone by the next one — chain what depends on the last step into a single command.
|
|
96
|
+
|
|
97
|
+
Nothing is watching it. A command that stops to ask never gets an answer and is killed after ${EXEC_TIMEOUT_MS / 1000}s, so pass the flag that skips the question (\`-y\`, \`--yes\`, \`--no-input\`), and send anything genuinely long to the background with its output redirected to a file.
|
|
98
|
+
|
|
99
|
+
Keys are not in the environment: every variable named like a key, a token or a secret is removed before the shell starts. An empty one is not an unset one — say which it was, and ask for what a command needs.`;
|
|
@@ -4,11 +4,24 @@ import { z } from "zod";
|
|
|
4
4
|
import { textModelProviderSchema } from "@/features/ai/model.schema";
|
|
5
5
|
import { serverAction } from "@/lib/protocol/server-action";
|
|
6
6
|
import { publicError } from "@/lib/public-error";
|
|
7
|
-
import {
|
|
8
|
-
|
|
7
|
+
import {
|
|
8
|
+
clearBotNote,
|
|
9
|
+
createBot,
|
|
10
|
+
deleteBot,
|
|
11
|
+
findBot,
|
|
12
|
+
updateBot,
|
|
13
|
+
writeBotNotesOn,
|
|
14
|
+
} from "./bot.query";
|
|
15
|
+
import {
|
|
16
|
+
answerTask,
|
|
17
|
+
cancelTask,
|
|
18
|
+
removeFinishedTasks,
|
|
19
|
+
removeTask,
|
|
20
|
+
startTask,
|
|
21
|
+
} from "./bot.runner";
|
|
9
22
|
import { BotFormSchema, botIconSchema } from "./bot.schema";
|
|
10
23
|
import { findBotSeed, rollSeedColors } from "./bot.seed";
|
|
11
|
-
import {
|
|
24
|
+
import { markReported, resolveTask } from "./task.query";
|
|
12
25
|
|
|
13
26
|
export const createBotAction = serverAction(async (input: unknown) => {
|
|
14
27
|
const form = BotFormSchema.parse(input);
|
|
@@ -67,6 +80,20 @@ export const deleteBotAction = serverAction(async (name: string) => {
|
|
|
67
80
|
if (!(await deleteBot(name))) publicError("Bot not found");
|
|
68
81
|
});
|
|
69
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Throws away what a bot wrote about itself. The only human touch on notes:
|
|
85
|
+
* they are never edited here, because a line the user typed would come back
|
|
86
|
+
* rewritten by the bot's next report.
|
|
87
|
+
*/
|
|
88
|
+
export const clearBotNoteAction = serverAction(async (name: string) => {
|
|
89
|
+
await clearBotNote(name);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
/** Switched off, no bot writes to its own instructions and none is shown one. What is already written stays. */
|
|
93
|
+
export const setBotNotesOnAction = serverAction(async (on: unknown) => {
|
|
94
|
+
await writeBotNotesOn(on === true);
|
|
95
|
+
});
|
|
96
|
+
|
|
70
97
|
// Tasks are opened by the `delegate` tool during a call, or here when the user
|
|
71
98
|
// hands one over from the screen. Each returns at once, the run itself continues
|
|
72
99
|
// in bot.runner behind `after()`.
|
|
@@ -126,5 +153,5 @@ export const deleteTaskAction = serverAction(async (id: string) => {
|
|
|
126
153
|
|
|
127
154
|
/** Empties the log of what is over. Running and waiting jobs are not touched. */
|
|
128
155
|
export const clearFinishedTasksAction = serverAction(async () => {
|
|
129
|
-
return { removed: await
|
|
156
|
+
return { removed: await removeFinishedTasks() };
|
|
130
157
|
});
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generateText,
|
|
3
|
+
hasToolCall,
|
|
4
|
+
type LanguageModel,
|
|
5
|
+
stepCountIs,
|
|
6
|
+
tool,
|
|
7
|
+
} from "ai";
|
|
8
|
+
import * as z from "zod";
|
|
9
|
+
import { BOT_NOTES } from "@/config";
|
|
10
|
+
import { TOOL_NAMES } from "@/features/ai/tools/tool-name";
|
|
11
|
+
import { logger } from "@/lib/logger";
|
|
12
|
+
import { createKeyedLock } from "@/lib/queue";
|
|
13
|
+
import { readBotNote, writeBotNote } from "./bot.query";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Keeping a bot's own notes — the block of instructions it writes to itself, carried into
|
|
17
|
+
* every job of its own (ai/prompts/bot.prompt).
|
|
18
|
+
*
|
|
19
|
+
* The bot never writes the block. It says what it wants changed, in its own words, on
|
|
20
|
+
* `report` (ai/tools/bot.tool `notes`), and this rewrites the block from that. Two models
|
|
21
|
+
* because they are two jobs: a bot that has just finished work edits around the work, while
|
|
22
|
+
* a model handed one document and three change requests does what it was handed. Same model
|
|
23
|
+
* the job ran on, no system prompt, one tool, forced.
|
|
24
|
+
*
|
|
25
|
+
* Serialized per bot, which is the whole of the concurrency story: two jobs of one bot
|
|
26
|
+
* finishing together queue, and the second reads the block the first wrote.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/** One bot at a time. Without it two passes read the same block and one of them is lost. */
|
|
30
|
+
const lane = createKeyedLock();
|
|
31
|
+
|
|
32
|
+
/** One call is the whole job; the rest is headroom for a rejected argument list. */
|
|
33
|
+
const MAX_STEPS = 3;
|
|
34
|
+
|
|
35
|
+
/** No request, no pass: a job that taught nothing must not cost a model call. */
|
|
36
|
+
export const hasNotesRequest = (want: string | null): boolean =>
|
|
37
|
+
Boolean(want?.trim());
|
|
38
|
+
|
|
39
|
+
export type KeepNotesInput = {
|
|
40
|
+
/** The bot whose notes these are; DEFAULT_BOT keeps them like any other. */
|
|
41
|
+
bot: string;
|
|
42
|
+
/** The model the job itself ran on (bot.run resolveModel). */
|
|
43
|
+
model: LanguageModel;
|
|
44
|
+
/** What the bot asked for, in its own words (ai/tools/bot.tool `notes`). */
|
|
45
|
+
want: string;
|
|
46
|
+
signal?: AbortSignal;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Applies one job's request. Resolves to the block as it now stands — and to what it was
|
|
51
|
+
* when the pass could not run: notes are never worth failing a job that already reported.
|
|
52
|
+
*/
|
|
53
|
+
export async function keepNotes(input: KeepNotesInput): Promise<string | null> {
|
|
54
|
+
return lane(input.bot, async () => {
|
|
55
|
+
const current = await readBotNote(input.bot);
|
|
56
|
+
let kept = current;
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
await generateText({
|
|
60
|
+
model: input.model,
|
|
61
|
+
messages: [{ role: "user", content: briefing(current, input.want) }],
|
|
62
|
+
abortSignal: input.signal,
|
|
63
|
+
tools: {
|
|
64
|
+
[TOOL_NAMES.update_notes]: tool({
|
|
65
|
+
description: "Hand back the updated prompt.",
|
|
66
|
+
inputSchema: z.object({
|
|
67
|
+
prompt: z
|
|
68
|
+
.string()
|
|
69
|
+
.max(BOT_NOTES.chars)
|
|
70
|
+
.describe(
|
|
71
|
+
`The whole prompt as it should now read, at most ${BOT_NOTES.chars} characters.`,
|
|
72
|
+
),
|
|
73
|
+
}),
|
|
74
|
+
execute: async ({ prompt }) => {
|
|
75
|
+
await writeBotNote(input.bot, prompt);
|
|
76
|
+
kept = prompt.trim() || null;
|
|
77
|
+
return "Saved.";
|
|
78
|
+
},
|
|
79
|
+
}),
|
|
80
|
+
},
|
|
81
|
+
toolChoice: "required",
|
|
82
|
+
stopWhen: [
|
|
83
|
+
stepCountIs(MAX_STEPS),
|
|
84
|
+
hasToolCall(TOOL_NAMES.update_notes),
|
|
85
|
+
],
|
|
86
|
+
});
|
|
87
|
+
} catch (cause) {
|
|
88
|
+
// Never the job's problem: its report is already handed back.
|
|
89
|
+
logger.warn(`notes pass for ${input.bot} failed: ${cause}`);
|
|
90
|
+
}
|
|
91
|
+
return kept;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The whole instruction, as one user turn. No system prompt: there is one document, three
|
|
97
|
+
* change requests, and one thing to do with them. A persona above that only invites the
|
|
98
|
+
* model to have views about the worker instead of editing the text it was handed.
|
|
99
|
+
*/
|
|
100
|
+
function briefing(current: string | null, want: string): string {
|
|
101
|
+
return `You keep one worker's notes. It is an agent with a shell on this machine; the notes are its own, read at the start of every job it runs and by nobody else. They hold what this machine has taught it — the command that works here, the flag without which something fails, the tool this platform does not have. Write what the worker found, never what it proposed.
|
|
102
|
+
|
|
103
|
+
Four things do not belong, however true today: what is in a folder right now, what a job produced, anything about the person the work is for, and how to satisfy the worker's own tools (\`report\`, \`write_file\`, \`bash\` — those belong to the app it runs inside and change with it). A program *on the machine* is the opposite and does belong.
|
|
104
|
+
|
|
105
|
+
Nor does how the worker should behave. Whoever owns it writes that separately and it outranks these, so a note about manner or language is either already said or about to be overruled.
|
|
106
|
+
|
|
107
|
+
The change is applied to what is already there, not written over it. Say the notes read \`Tests here run with npm.\` and then \`Chromium needs --no-sandbox or it exits.\` The worker asks for the npm line to be corrected to pnpm: both lines come back, the first now reading \`Tests here run with pnpm.\` and the second word for word as it was. Added, corrected or dropped is one line moving; everything the request did not name is untouched.
|
|
108
|
+
|
|
109
|
+
${current ? "The notes as they stand:" : "There are no notes yet — the fence below is empty because nothing has been written, not because the notes say so."}
|
|
110
|
+
|
|
111
|
+
\`\`\`
|
|
112
|
+
${current ?? ""}
|
|
113
|
+
\`\`\`
|
|
114
|
+
|
|
115
|
+
The worker asks for this change:
|
|
116
|
+
|
|
117
|
+
${want.trim()}
|
|
118
|
+
|
|
119
|
+
Hand back the whole thing as it should now read, keeping what the request does not touch. Only what belongs inside the fence, never a word about it, and nothing at all when the request holds nothing that belongs. At most ${BOT_NOTES.chars} characters, which is a ceiling and not a target.`;
|
|
120
|
+
}
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import { eq, inArray, sql } from "drizzle-orm";
|
|
2
|
+
import { BOT_NOTES } from "@/config";
|
|
2
3
|
import { database } from "@/database/db";
|
|
3
4
|
import {
|
|
4
5
|
botMcpToolTable,
|
|
6
|
+
botNoteTable,
|
|
5
7
|
botTable,
|
|
6
8
|
mcpToolTable,
|
|
7
9
|
taskTable,
|
|
8
10
|
} from "@/database/tables";
|
|
9
11
|
import type { TextModelProviderId } from "@/features/ai/model.schema";
|
|
12
|
+
import { readConfig, writeConfig } from "@/features/config/config.query";
|
|
10
13
|
import {
|
|
14
|
+
BOT_NOTES_KEY,
|
|
11
15
|
type BotForm,
|
|
12
16
|
type BotIcon,
|
|
13
17
|
DEFAULT_BOT,
|
|
18
|
+
isBotNotesOn,
|
|
14
19
|
type JobBot,
|
|
15
20
|
type PinnedTool,
|
|
16
21
|
pickedModel,
|
|
@@ -61,7 +66,7 @@ export async function findJobBot(name: string): Promise<JobBot | null> {
|
|
|
61
66
|
|
|
62
67
|
/** Bots with their pinned tools and token totals: a set of queries plus grouping, not a join. */
|
|
63
68
|
export async function findAllBots() {
|
|
64
|
-
const [bots, pins, spent] = await Promise.all([
|
|
69
|
+
const [bots, pins, spent, notes] = await Promise.all([
|
|
65
70
|
database.select().from(botTable).orderBy(botTable.createdAt),
|
|
66
71
|
database
|
|
67
72
|
.select({
|
|
@@ -83,6 +88,7 @@ export async function findAllBots() {
|
|
|
83
88
|
})
|
|
84
89
|
.from(taskTable)
|
|
85
90
|
.groupBy(taskTable.bot),
|
|
91
|
+
database.select().from(botNoteTable),
|
|
86
92
|
]);
|
|
87
93
|
|
|
88
94
|
const byBot = new Map<string, PinnedTool[]>();
|
|
@@ -104,14 +110,61 @@ export async function findAllBots() {
|
|
|
104
110
|
: [[row.bot, new Date(Number(row.lastJobAt) * 1000)] as const],
|
|
105
111
|
),
|
|
106
112
|
);
|
|
113
|
+
const noteOf = new Map(notes.map((row) => [row.bot, row]));
|
|
107
114
|
return bots.map((bot) => ({
|
|
108
115
|
...bot,
|
|
109
116
|
tools: byBot.get(bot.name) ?? [],
|
|
110
117
|
tokens: tokensOf.get(bot.name) ?? { input: 0, output: 0 },
|
|
111
118
|
lastJobAt: lastJobOf.get(bot.name) ?? null,
|
|
119
|
+
note: noteOf.get(bot.name)?.text ?? null,
|
|
120
|
+
noteAt: noteOf.get(bot.name)?.updatedAt ?? null,
|
|
112
121
|
}));
|
|
113
122
|
}
|
|
114
123
|
|
|
124
|
+
// A bot's own notes. Nothing here belongs to a job: the row survives every job
|
|
125
|
+
// the bot runs and is read back into its next prompt (ai/prompts/bot.prompt).
|
|
126
|
+
|
|
127
|
+
/** What this bot has written to itself. Keyed by name, so DEFAULT_BOT has one too. */
|
|
128
|
+
export async function readBotNote(bot: string): Promise<string | null> {
|
|
129
|
+
const [row] = await database
|
|
130
|
+
.select({ text: botNoteTable.text })
|
|
131
|
+
.from(botNoteTable)
|
|
132
|
+
.where(eq(botNoteTable.bot, bot));
|
|
133
|
+
return row?.text ?? null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* The whole block, as the pass rewrote it (bot.notes). Cut rather than refused: the schema
|
|
138
|
+
* already caps it, and a run that gets this far has handed its report back. Empty clears it.
|
|
139
|
+
*/
|
|
140
|
+
export async function writeBotNote(bot: string, text: string): Promise<void> {
|
|
141
|
+
const next = text.trim().slice(0, BOT_NOTES.chars).trim();
|
|
142
|
+
if (!next) return clearBotNote(bot);
|
|
143
|
+
|
|
144
|
+
const updatedAt = new Date();
|
|
145
|
+
await database
|
|
146
|
+
.insert(botNoteTable)
|
|
147
|
+
.values({ bot, text: next, updatedAt })
|
|
148
|
+
.onConflictDoUpdate({
|
|
149
|
+
target: botNoteTable.bot,
|
|
150
|
+
set: { text: next, updatedAt },
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Whether any bot keeps its own instructions at all (bot.schema BOT_NOTES_KEY). */
|
|
155
|
+
export async function readBotNotesOn(): Promise<boolean> {
|
|
156
|
+
return isBotNotesOn(await readConfig(BOT_NOTES_KEY));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export async function writeBotNotesOn(on: boolean): Promise<void> {
|
|
160
|
+
await writeConfig(BOT_NOTES_KEY, on ? "on" : "off");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** The one thing the user does to notes: throw them away when a bot has learned something wrong. */
|
|
164
|
+
export async function clearBotNote(bot: string): Promise<void> {
|
|
165
|
+
await database.delete(botNoteTable).where(eq(botNoteTable.bot, bot));
|
|
166
|
+
}
|
|
167
|
+
|
|
115
168
|
export async function findBot(name: string) {
|
|
116
169
|
const [bot] = await database
|
|
117
170
|
.select()
|
|
@@ -175,5 +228,7 @@ export async function deleteBot(name: string) {
|
|
|
175
228
|
.delete(botTable)
|
|
176
229
|
.where(eq(botTable.name, name))
|
|
177
230
|
.returning({ name: botTable.name });
|
|
231
|
+
// Notes are keyed by name with no foreign key, so nothing cascades for them.
|
|
232
|
+
await clearBotNote(name);
|
|
178
233
|
return removed.length > 0;
|
|
179
234
|
}
|
package/features/bot/bot.run.ts
CHANGED
|
@@ -30,13 +30,18 @@ import {
|
|
|
30
30
|
NO_TOKENS,
|
|
31
31
|
type TokenUsage,
|
|
32
32
|
} from "@/features/bot/bot.schema";
|
|
33
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
closeJobShell,
|
|
35
|
+
openBotFolder,
|
|
36
|
+
openJobScratch,
|
|
37
|
+
} from "@/features/workspace/workspace";
|
|
34
38
|
import { logger } from "@/lib/logger";
|
|
35
39
|
import { publicError } from "@/lib/public-error";
|
|
36
40
|
import { estimateTokens } from "@/lib/tokens";
|
|
37
41
|
import { clip } from "@/lib/utils";
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
42
|
+
import { hasNotesRequest, keepNotes } from "./bot.notes";
|
|
43
|
+
import { findJobBot, readBotNotesOn } from "./bot.query";
|
|
44
|
+
import { findTask, optionsOf, toolLine } from "./task.query";
|
|
40
45
|
|
|
41
46
|
/**
|
|
42
47
|
* The bot loop behind `delegate`: one model with its own prompt, tools and
|
|
@@ -154,11 +159,23 @@ export async function runBot(
|
|
|
154
159
|
|
|
155
160
|
// Tools are built on the model: web search runs on this bot's model (load-tools).
|
|
156
161
|
const model = await resolveModel(bot);
|
|
162
|
+
// One folder per job, not per bot: bots borrowed with `ask_bot` work inside
|
|
163
|
+
// the same job and share its material (workspace.ts jobScratch).
|
|
164
|
+
const [scratch, own] = await Promise.all([
|
|
165
|
+
options.taskId
|
|
166
|
+
? openJobScratch(
|
|
167
|
+
options.taskId,
|
|
168
|
+
(await findTask(options.taskId))?.label ?? "job",
|
|
169
|
+
)
|
|
170
|
+
: null,
|
|
171
|
+
openBotFolder(name),
|
|
172
|
+
]);
|
|
157
173
|
const [prompt, tools] = await Promise.all([
|
|
158
174
|
loadBotPrompt(
|
|
159
175
|
name,
|
|
160
176
|
bot.systemPrompt,
|
|
161
177
|
"askedBy" in input ? input.askedBy : null,
|
|
178
|
+
{ scratch, own },
|
|
162
179
|
),
|
|
163
180
|
loadTools({
|
|
164
181
|
target: "bot",
|
|
@@ -286,6 +303,8 @@ export async function runBot(
|
|
|
286
303
|
let asked: { id: string; question: string; options: string[] } | null = null;
|
|
287
304
|
/** The report (bot.tool reportSpec). */
|
|
288
305
|
let reported: { text: string; complete: boolean } | null = null;
|
|
306
|
+
/** What the bot wants changed about its own instructions (bot.tool `notes`); applied after the report. */
|
|
307
|
+
let wants: string | null = null;
|
|
289
308
|
|
|
290
309
|
// An abort ends the stream without a finish; release any waiting take.
|
|
291
310
|
options.signal?.addEventListener("abort", () => steps.end(), { once: true });
|
|
@@ -332,6 +351,7 @@ export async function runBot(
|
|
|
332
351
|
text: String(args.result ?? "").trim(),
|
|
333
352
|
complete: args.complete !== false,
|
|
334
353
|
};
|
|
354
|
+
wants = notesRequestOf(part.input);
|
|
335
355
|
}
|
|
336
356
|
await emit({
|
|
337
357
|
type: "tool",
|
|
@@ -401,6 +421,16 @@ export async function runBot(
|
|
|
401
421
|
|
|
402
422
|
if (reported) {
|
|
403
423
|
await emit({ type: "report", ...reported });
|
|
424
|
+
// After the report, never before, and only when the bot asked for something:
|
|
425
|
+
// a job that changed nothing about this machine must not cost a model call.
|
|
426
|
+
if (hasNotesRequest(wants) && (await readBotNotesOn())) {
|
|
427
|
+
await keepNotes({
|
|
428
|
+
bot: name,
|
|
429
|
+
model: model.model,
|
|
430
|
+
want: wants as string,
|
|
431
|
+
signal: options.signal,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
404
434
|
return;
|
|
405
435
|
}
|
|
406
436
|
|
|
@@ -422,6 +452,16 @@ const reportAccepted_ = ({ steps }: { steps: StepResult<ToolSet>[] }) =>
|
|
|
422
452
|
result.toolName === TOOL_NAMES.report && reportAccepted(result.output),
|
|
423
453
|
) ?? false;
|
|
424
454
|
|
|
455
|
+
/** A provider that cannot send null in a string field writes the word instead. */
|
|
456
|
+
const NO_NOTES = new Set(["null", "none", "n/a", "-", "없음"]);
|
|
457
|
+
|
|
458
|
+
/** `report`'s `notes` as the pass takes it. */
|
|
459
|
+
function notesRequestOf(input: unknown): string | null {
|
|
460
|
+
const said = (input as { notes?: unknown })?.notes;
|
|
461
|
+
const text = typeof said === "string" ? said.trim() : "";
|
|
462
|
+
return text && !NO_NOTES.has(text.toLowerCase()) ? text : null;
|
|
463
|
+
}
|
|
464
|
+
|
|
425
465
|
/**
|
|
426
466
|
* The step before the cap is narrowed to `report` and required to call it,
|
|
427
467
|
* so a run out of budget ends with something a person can act on.
|