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
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
Check,
|
|
5
|
+
History,
|
|
6
|
+
PencilLine,
|
|
7
|
+
Plus,
|
|
8
|
+
Trash2,
|
|
9
|
+
Wrench,
|
|
10
|
+
X,
|
|
11
|
+
} from "lucide-react";
|
|
4
12
|
import { type ReactNode, useState } from "react";
|
|
5
13
|
import { queryKey } from "@/app/api/query-key";
|
|
6
14
|
import { Button } from "@/components/ui/button";
|
|
@@ -16,13 +24,17 @@ import { Input } from "@/components/ui/input";
|
|
|
16
24
|
import { notify } from "@/components/ui/notify";
|
|
17
25
|
import ShinyText from "@/components/ui/shiny-text";
|
|
18
26
|
import { Swatch } from "@/components/ui/swatch";
|
|
27
|
+
import { Switch } from "@/components/ui/switch";
|
|
19
28
|
import { Textarea } from "@/components/ui/textarea";
|
|
29
|
+
import { BOT_NOTES, PROMPT_CROWDED } from "@/config";
|
|
20
30
|
import { ModelPicker } from "@/features/ai/components/model-picker";
|
|
21
31
|
import type { TextModelProviderId } from "@/features/ai/model.schema";
|
|
22
32
|
import {
|
|
33
|
+
clearBotNoteAction,
|
|
23
34
|
createBotAction,
|
|
24
35
|
createSeedBotsAction,
|
|
25
36
|
deleteBotAction,
|
|
37
|
+
setBotNotesOnAction,
|
|
26
38
|
updateBotAction,
|
|
27
39
|
} from "@/features/bot/bot.action";
|
|
28
40
|
import {
|
|
@@ -38,6 +50,7 @@ import { BOT_SEEDS, type BotSeed } from "@/features/bot/bot.seed";
|
|
|
38
50
|
import { BotMark } from "@/features/bot/components/bot-mark";
|
|
39
51
|
import {
|
|
40
52
|
SettingError,
|
|
53
|
+
SettingNote,
|
|
41
54
|
SettingPanes,
|
|
42
55
|
SettingPanesSkeleton,
|
|
43
56
|
SettingRailNote,
|
|
@@ -111,6 +124,13 @@ export function BotSetting() {
|
|
|
111
124
|
onPick={() => setPicked(SEEDS)}
|
|
112
125
|
/>
|
|
113
126
|
)}
|
|
127
|
+
|
|
128
|
+
{bots.length > PROMPT_CROWDED.bots && (
|
|
129
|
+
<SettingNote className="mx-3 mt-2 leading-relaxed">
|
|
130
|
+
{bots.length} bots. Each is a line in every prompt, and one more
|
|
131
|
+
for Thursday to choose between.
|
|
132
|
+
</SettingNote>
|
|
133
|
+
)}
|
|
114
134
|
</div>
|
|
115
135
|
}
|
|
116
136
|
right={
|
|
@@ -514,6 +534,12 @@ function BotPage({
|
|
|
514
534
|
onDone(null);
|
|
515
535
|
},
|
|
516
536
|
});
|
|
537
|
+
// Shared with the rail's switch; SWR dedups the read
|
|
538
|
+
const { data: notesOn } = useServerRoute<boolean>(queryKey.botNotes);
|
|
539
|
+
const [clearNote, clearingNote] = useServerAction(clearBotNoteAction, {
|
|
540
|
+
okMessage: "Notes cleared",
|
|
541
|
+
onOk: () => revalidate(queryKey.bot),
|
|
542
|
+
});
|
|
517
543
|
|
|
518
544
|
const ready =
|
|
519
545
|
name.trim() && description.trim() && provider && model.trim() && !creating;
|
|
@@ -531,6 +557,19 @@ function BotPage({
|
|
|
531
557
|
});
|
|
532
558
|
};
|
|
533
559
|
|
|
560
|
+
/** The bot writes its own notes; the only thing to do to them from here is throw them away. */
|
|
561
|
+
const confirmClearNote = async () => {
|
|
562
|
+
if (!bot) return;
|
|
563
|
+
const confirmed = await notify.confirm({
|
|
564
|
+
title: `Clear ${bot.name}'s own prompt?`,
|
|
565
|
+
description:
|
|
566
|
+
"What it worked out about doing jobs here is gone, and it starts the next one without it.",
|
|
567
|
+
okText: "Clear",
|
|
568
|
+
destructive: true,
|
|
569
|
+
});
|
|
570
|
+
if (confirmed) clearNote(bot.name);
|
|
571
|
+
};
|
|
572
|
+
|
|
534
573
|
const confirmRemove = async () => {
|
|
535
574
|
if (!bot) return;
|
|
536
575
|
const confirmed = await notify.confirm({
|
|
@@ -595,6 +634,50 @@ function BotPage({
|
|
|
595
634
|
</div>
|
|
596
635
|
</div>
|
|
597
636
|
|
|
637
|
+
{bot?.note && (
|
|
638
|
+
/* Not a Row like the fields above it: those are settings the owner
|
|
639
|
+
typed, this is what the bot has worked out, and a block under its
|
|
640
|
+
face reads as part of who it is rather than one more control. */
|
|
641
|
+
<div className="overflow-hidden rounded-xl border border-border/60 bg-muted/25">
|
|
642
|
+
<div className="flex items-center gap-2.5 border-b border-border/60 px-4 py-3">
|
|
643
|
+
<PencilLine className="size-3.5 shrink-0 text-muted-foreground" />
|
|
644
|
+
<span className="min-w-0 flex-1 truncate text-[13px] font-medium">
|
|
645
|
+
What {bot.name} has worked out
|
|
646
|
+
</span>
|
|
647
|
+
<span className="shrink-0 font-mono text-[10px] text-muted-foreground">
|
|
648
|
+
{bot.note.length}/{BOT_NOTES.chars}
|
|
649
|
+
</span>
|
|
650
|
+
</div>
|
|
651
|
+
<p className="px-4 py-3.5 text-[13px] leading-relaxed whitespace-pre-wrap">
|
|
652
|
+
{bot.note}
|
|
653
|
+
</p>
|
|
654
|
+
<div className="flex items-center gap-2 px-4 pb-3">
|
|
655
|
+
{/* No amber: switched off is the user's own choice, not something
|
|
656
|
+
waiting on them. The word carries it. */}
|
|
657
|
+
<span className="min-w-0 flex-1 truncate font-mono text-[10px] text-muted-foreground">
|
|
658
|
+
{notesOn === false
|
|
659
|
+
? "Paused — bots are not keeping their own prompt"
|
|
660
|
+
: "It writes this itself when a job ends"}
|
|
661
|
+
{bot.noteAt && (
|
|
662
|
+
<>
|
|
663
|
+
<span className="px-1.5 opacity-50">·</span>
|
|
664
|
+
{shortAgo(bot.noteAt)}
|
|
665
|
+
</>
|
|
666
|
+
)}
|
|
667
|
+
</span>
|
|
668
|
+
<Button
|
|
669
|
+
size="sm"
|
|
670
|
+
variant="ghost"
|
|
671
|
+
loading={clearingNote}
|
|
672
|
+
onClick={confirmClearNote}
|
|
673
|
+
className="shrink-0 text-muted-foreground hover:text-destructive"
|
|
674
|
+
>
|
|
675
|
+
Clear
|
|
676
|
+
</Button>
|
|
677
|
+
</div>
|
|
678
|
+
</div>
|
|
679
|
+
)}
|
|
680
|
+
|
|
598
681
|
<MarkPicker
|
|
599
682
|
icon={icon}
|
|
600
683
|
onChange={(next) => {
|
|
@@ -668,29 +751,49 @@ function BotPage({
|
|
|
668
751
|
);
|
|
669
752
|
}
|
|
670
753
|
|
|
671
|
-
/**
|
|
754
|
+
/**
|
|
755
|
+
* What the roster's pick is, and the one setting that is the whole set's rather
|
|
756
|
+
* than any bot's: whether bots keep their own instructions at all.
|
|
757
|
+
*/
|
|
672
758
|
function BotRail({ bot }: { bot: Bot | null }) {
|
|
673
|
-
|
|
674
|
-
|
|
759
|
+
const { data: notesOn, mutate } = useServerRoute<boolean>(queryKey.botNotes);
|
|
760
|
+
const [setNotesOn] = useServerAction(setBotNotesOnAction, {
|
|
761
|
+
onOk: () => revalidate(queryKey.botNotes),
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
const tokens = bot ? bot.tokens.input + bot.tokens.output : 0;
|
|
765
|
+
return (
|
|
766
|
+
<>
|
|
675
767
|
<SettingRailNote>
|
|
676
|
-
|
|
768
|
+
{bot ? (
|
|
769
|
+
<span className="font-mono text-[11px]">
|
|
770
|
+
{bot.name}
|
|
771
|
+
<span className="px-1.5 opacity-50">·</span>
|
|
772
|
+
<span
|
|
773
|
+
title={`in ${formatCount(bot.tokens.input)} · out ${formatCount(bot.tokens.output)}`}
|
|
774
|
+
>
|
|
775
|
+
{tokens > 0 ? `${formatCount(tokens)} tokens` : "no tokens yet"}
|
|
776
|
+
</span>
|
|
777
|
+
<span className="px-1.5 opacity-50">·</span>
|
|
778
|
+
since {whenOf(bot.createdAt)}
|
|
779
|
+
</span>
|
|
780
|
+
) : (
|
|
781
|
+
"Becomes a bot once it has a name and a model"
|
|
782
|
+
)}
|
|
677
783
|
</SettingRailNote>
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
return (
|
|
681
|
-
<SettingRailNote>
|
|
682
|
-
<span className="font-mono text-[11px]">
|
|
683
|
-
{bot.name}
|
|
684
|
-
<span className="px-1.5 opacity-50">·</span>
|
|
685
|
-
<span
|
|
686
|
-
title={`in ${formatCount(bot.tokens.input)} · out ${formatCount(bot.tokens.output)}`}
|
|
687
|
-
>
|
|
688
|
-
{tokens > 0 ? `${formatCount(tokens)} tokens` : "no tokens yet"}
|
|
689
|
-
</span>
|
|
690
|
-
<span className="px-1.5 opacity-50">·</span>
|
|
691
|
-
since {whenOf(bot.createdAt)}
|
|
784
|
+
<span className="shrink-0 text-xs text-muted-foreground">
|
|
785
|
+
Bots keep their own prompt
|
|
692
786
|
</span>
|
|
693
|
-
|
|
787
|
+
<Switch
|
|
788
|
+
checked={notesOn ?? true}
|
|
789
|
+
disabled={notesOn === undefined}
|
|
790
|
+
onCheckedChange={(on) => {
|
|
791
|
+
void mutate(on, false);
|
|
792
|
+
setNotesOn(on);
|
|
793
|
+
}}
|
|
794
|
+
aria-label="Bots keep their own prompt"
|
|
795
|
+
/>
|
|
796
|
+
</>
|
|
694
797
|
);
|
|
695
798
|
}
|
|
696
799
|
|
|
@@ -226,9 +226,11 @@ export async function deleteFinishedTasks() {
|
|
|
226
226
|
const removed = await database
|
|
227
227
|
.delete(taskTable)
|
|
228
228
|
.where(inArray(taskTable.status, ["done", "failed"]))
|
|
229
|
-
|
|
229
|
+
// The label comes back too: a job's working folder is named for it and goes
|
|
230
|
+
// with the row (bot.runner removeFinishedTasks).
|
|
231
|
+
.returning({ id: taskTable.id, label: taskTable.label });
|
|
230
232
|
if (removed.length) changed();
|
|
231
|
-
return removed
|
|
233
|
+
return removed;
|
|
232
234
|
}
|
|
233
235
|
|
|
234
236
|
/** Every job, newest first. The caller stops each one before deleting it (bot.runner removeTask). */
|
|
@@ -275,6 +277,8 @@ export type TaskMessageInput = {
|
|
|
275
277
|
content: ModelMessage["content"];
|
|
276
278
|
/** Compaction summary row; `listThread` re-reads from here. */
|
|
277
279
|
compact?: boolean;
|
|
280
|
+
/** Drawn as the app's own line, not as something a person or a bot said. */
|
|
281
|
+
note?: boolean;
|
|
278
282
|
};
|
|
279
283
|
|
|
280
284
|
/**
|
|
@@ -288,13 +292,20 @@ export async function upsertMessage(
|
|
|
288
292
|
) {
|
|
289
293
|
await database
|
|
290
294
|
.insert(taskMessageTable)
|
|
291
|
-
.values({
|
|
295
|
+
.values({
|
|
296
|
+
taskId,
|
|
297
|
+
seq,
|
|
298
|
+
...message,
|
|
299
|
+
compact: message.compact ?? false,
|
|
300
|
+
note: message.note ?? false,
|
|
301
|
+
})
|
|
292
302
|
.onConflictDoUpdate({
|
|
293
303
|
target: [taskMessageTable.taskId, taskMessageTable.seq],
|
|
294
304
|
set: {
|
|
295
305
|
role: message.role,
|
|
296
306
|
content: message.content,
|
|
297
307
|
compact: message.compact ?? false,
|
|
308
|
+
note: message.note ?? false,
|
|
298
309
|
},
|
|
299
310
|
});
|
|
300
311
|
changed();
|
|
@@ -512,8 +523,10 @@ function linesOf(message: StoredMessage, owner: string): TaskLine[] {
|
|
|
512
523
|
if (message.seq === 0) return [];
|
|
513
524
|
const text = typeof content === "string" ? content : textOf(content);
|
|
514
525
|
if (!text) return [];
|
|
515
|
-
//
|
|
516
|
-
|
|
526
|
+
// The app's own line — a compaction marker, or why a run stopped — never
|
|
527
|
+
// the user's words. `compact` is read too: rows written before `note` was.
|
|
528
|
+
if (message.note || message.compact)
|
|
529
|
+
return [{ ...base, id: id(0), kind: "note", text }];
|
|
517
530
|
return [{ ...base, id: id(0), kind: "user", text }];
|
|
518
531
|
}
|
|
519
532
|
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
isFading,
|
|
26
26
|
type MemoryNote,
|
|
27
27
|
type MemorySection,
|
|
28
|
+
memorySourceLabel,
|
|
28
29
|
noteTitle,
|
|
29
30
|
sectionOf,
|
|
30
31
|
} from "@/features/memory/memory.schema";
|
|
@@ -348,8 +349,16 @@ function NotePage({ note }: { note: MemoryNote }) {
|
|
|
348
349
|
<span className="flex-1 text-sm leading-relaxed text-foreground/90">
|
|
349
350
|
{fact.text}
|
|
350
351
|
</span>
|
|
351
|
-
|
|
352
|
-
|
|
352
|
+
{/* When, and whose hand — memory is kept by four of them */}
|
|
353
|
+
<span className="shrink-0 pt-0.5 text-right text-[11px] text-muted-foreground/50">
|
|
354
|
+
<span className="block font-mono">
|
|
355
|
+
{format(fact.createdAt, "yyyy.MM.dd")}
|
|
356
|
+
</span>
|
|
357
|
+
{memorySourceLabel(fact.source) && (
|
|
358
|
+
<span className="block">
|
|
359
|
+
{memorySourceLabel(fact.source)}
|
|
360
|
+
</span>
|
|
361
|
+
)}
|
|
353
362
|
</span>
|
|
354
363
|
</li>
|
|
355
364
|
))}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type MemoryAlwaysLoaded,
|
|
13
13
|
type MemoryNoteView,
|
|
14
14
|
type MemoryNoteWrite,
|
|
15
|
+
type MemorySource,
|
|
15
16
|
type MemoryWrite,
|
|
16
17
|
recallScore,
|
|
17
18
|
} from "./memory.schema";
|
|
@@ -68,6 +69,7 @@ function latestFacts(noteIds: number[]) {
|
|
|
68
69
|
noteId: memoryFactTable.noteId,
|
|
69
70
|
text: memoryFactTable.text,
|
|
70
71
|
alwaysLoad: memoryFactTable.alwaysLoad,
|
|
72
|
+
source: memoryFactTable.source,
|
|
71
73
|
createdAt: memoryFactTable.createdAt,
|
|
72
74
|
})
|
|
73
75
|
.from(memoryFactTable)
|
|
@@ -129,7 +131,8 @@ export async function deleteNote(id: number) {
|
|
|
129
131
|
export async function addFact(noteId: number, text: string) {
|
|
130
132
|
const [fact] = await database
|
|
131
133
|
.insert(memoryFactTable)
|
|
132
|
-
|
|
134
|
+
// Only the screen calls this, so the hand is known here (memory.action)
|
|
135
|
+
.values({ noteId, text, source: "user" })
|
|
133
136
|
.returning();
|
|
134
137
|
await touchNote(noteId);
|
|
135
138
|
changed();
|
|
@@ -155,7 +158,7 @@ export async function reviseFact(noteId: number, factId: number, text: string) {
|
|
|
155
158
|
|
|
156
159
|
const [fact] = await tx
|
|
157
160
|
.insert(memoryFactTable)
|
|
158
|
-
.values({ noteId, text })
|
|
161
|
+
.values({ noteId, text, source: "user" })
|
|
159
162
|
.returning();
|
|
160
163
|
await tx
|
|
161
164
|
.update(memoryNoteTable)
|
|
@@ -313,6 +316,8 @@ export const sameFact = (text: string) =>
|
|
|
313
316
|
*/
|
|
314
317
|
export async function writeNotes(
|
|
315
318
|
input: MemoryNoteWrite[],
|
|
319
|
+
/** Which hand is writing; recorded on every fact (memory.schema MemorySource). */
|
|
320
|
+
source: MemorySource,
|
|
316
321
|
): Promise<MemoryWrite> {
|
|
317
322
|
const unnamed: string[] = [];
|
|
318
323
|
const paths: string[] = [];
|
|
@@ -466,6 +471,7 @@ export async function writeNotes(
|
|
|
466
471
|
.values({
|
|
467
472
|
noteId,
|
|
468
473
|
text: fact.text,
|
|
474
|
+
source,
|
|
469
475
|
alwaysLoad: claim(
|
|
470
476
|
fact.alwaysLoad ?? target.alwaysLoad,
|
|
471
477
|
fact.text,
|
|
@@ -494,6 +500,7 @@ export async function writeNotes(
|
|
|
494
500
|
.values({
|
|
495
501
|
noteId,
|
|
496
502
|
text: fact.text,
|
|
503
|
+
source,
|
|
497
504
|
alwaysLoad: claim(fact.alwaysLoad === true, fact.text),
|
|
498
505
|
})
|
|
499
506
|
.returning({
|
|
@@ -1,12 +1,42 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { type DateLike, DateLikeSchema, toDate } from "@/lib/date-like";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Who put a fact here. Memory is one note kept by four hands — the user typing
|
|
6
|
+
* on the screen, the call as they talk, a bot that turned something up mid-job,
|
|
7
|
+
* and the pass that reads calls back afterwards — and they are not equally
|
|
8
|
+
* close to the user. A reader that cannot tell them apart reads what a bot
|
|
9
|
+
* inferred as something the user said.
|
|
10
|
+
*/
|
|
11
|
+
export const MemorySourceSchema = z.enum(["user", "call", "bot", "tidy"]);
|
|
12
|
+
|
|
13
|
+
export type MemorySource = z.infer<typeof MemorySourceSchema>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* How a source reads on screen. Recorded for the person looking at their own
|
|
17
|
+
* memory, not for a model: a bare `bot` in a prompt says nothing a reader can
|
|
18
|
+
* act on — it cannot tell whether that was itself — and costs a sentence to
|
|
19
|
+
* explain. Empty where it was never recorded.
|
|
20
|
+
*/
|
|
21
|
+
export const memorySourceLabel = (source: MemorySource | null | undefined) =>
|
|
22
|
+
source === "user"
|
|
23
|
+
? "you"
|
|
24
|
+
: source === "call"
|
|
25
|
+
? "on a call"
|
|
26
|
+
: source === "bot"
|
|
27
|
+
? "a bot"
|
|
28
|
+
: source === "tidy"
|
|
29
|
+
? "read back"
|
|
30
|
+
: "";
|
|
31
|
+
|
|
4
32
|
// Storage is fact-based: one row per fact, history via isLatest.
|
|
5
33
|
export const MemoryFactSchema = z.object({
|
|
6
34
|
id: z.number(),
|
|
7
35
|
text: z.string(),
|
|
8
36
|
/** Loaded into every prompt without opening the note (ALWAYS_LOADED_MAX). */
|
|
9
37
|
alwaysLoad: z.boolean(),
|
|
38
|
+
/** Null on rows written before the hand was recorded. */
|
|
39
|
+
source: MemorySourceSchema.nullish(),
|
|
10
40
|
createdAt: DateLikeSchema,
|
|
11
41
|
});
|
|
12
42
|
|
|
@@ -28,13 +28,14 @@ import { Skeleton } from "@/components/ui/skeleton";
|
|
|
28
28
|
import { Switch } from "@/components/ui/switch";
|
|
29
29
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
30
30
|
import { Textarea } from "@/components/ui/textarea";
|
|
31
|
-
import { PATHS } from "@/config";
|
|
31
|
+
import { PATHS, PROMPT_CROWDED } from "@/config";
|
|
32
32
|
import {
|
|
33
33
|
SettingDialogContent,
|
|
34
34
|
SettingError,
|
|
35
35
|
SettingFilter,
|
|
36
36
|
SettingGroup,
|
|
37
37
|
SettingItems,
|
|
38
|
+
SettingNote,
|
|
38
39
|
SettingRailNote,
|
|
39
40
|
SettingScreen,
|
|
40
41
|
SettingSkeleton,
|
|
@@ -128,6 +129,13 @@ export function SkillsSetting() {
|
|
|
128
129
|
))}
|
|
129
130
|
</SettingItems>
|
|
130
131
|
</SettingGroup>
|
|
132
|
+
|
|
133
|
+
{on > PROMPT_CROWDED.skills && (
|
|
134
|
+
<SettingNote>
|
|
135
|
+
{on} skills are on. Each is a line in every prompt a bot reads, and
|
|
136
|
+
one more to look past when it picks.
|
|
137
|
+
</SettingNote>
|
|
138
|
+
)}
|
|
131
139
|
</SettingScreen>
|
|
132
140
|
);
|
|
133
141
|
}
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
mkdir,
|
|
3
3
|
readdir,
|
|
4
4
|
realpath,
|
|
5
|
+
rm,
|
|
5
6
|
stat,
|
|
6
7
|
unlink,
|
|
7
8
|
writeFile,
|
|
@@ -42,7 +43,12 @@ export const ARTIFACTS = join(WORKSPACE, PATHS.artifacts);
|
|
|
42
43
|
* Created on open, and the only places inside the workspace `write_file`
|
|
43
44
|
* accepts. `.agents` is the skills CLI's install folder.
|
|
44
45
|
*/
|
|
45
|
-
const BOT_FOLDERS = [
|
|
46
|
+
const BOT_FOLDERS = [
|
|
47
|
+
PATHS.artifacts,
|
|
48
|
+
PATHS.projects,
|
|
49
|
+
PATHS.scratch,
|
|
50
|
+
PATHS.bots,
|
|
51
|
+
];
|
|
46
52
|
const WRITABLE = new Set([...BOT_FOLDERS, ".agents"]);
|
|
47
53
|
|
|
48
54
|
/** Where playwright-cli drops a snapshot after every command. Its name, not ours. */
|
|
@@ -61,7 +67,7 @@ export function writeRefusal(full: string): string | null {
|
|
|
61
67
|
const rel = relative(WORKSPACE, full);
|
|
62
68
|
const [top] = rel.split(sep);
|
|
63
69
|
if (top && WRITABLE.has(top)) return null;
|
|
64
|
-
return `Not written: ${rel || "the workspace root"} is outside the workspace's folders. Finished work goes under ${PATHS.artifacts}/, code under ${PATHS.projects}/,
|
|
70
|
+
return `Not written: ${rel || "the workspace root"} is outside the workspace's folders. Finished work goes under ${PATHS.artifacts}/, code under ${PATHS.projects}/, your own kit under ${PATHS.bots}/, and this job's working material under ${PATHS.scratch}/.`;
|
|
65
71
|
}
|
|
66
72
|
if (under(APP_ROOT, full)) {
|
|
67
73
|
return `Not written: ${full} is inside the app that is running you, not the user's files. Work in ${WORKSPACE}; outside it, write only where the user pointed you.`;
|
|
@@ -89,6 +95,50 @@ export async function insideWorkspace(rel: string): Promise<string | null> {
|
|
|
89
95
|
return under(root, target) ? target : null;
|
|
90
96
|
}
|
|
91
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Where one job keeps what it is still working on. `scratch/` used to be shared
|
|
100
|
+
* by every job at once, so nobody could tell whose a file was or when it stopped
|
|
101
|
+
* mattering, and it only grew. A job has a beginning and an end, which is what
|
|
102
|
+
* makes its working material safe to clear later — and it is the right unit
|
|
103
|
+
* rather than the bot, because several bots work inside one job (`ask_bot`) and
|
|
104
|
+
* one bot runs many jobs.
|
|
105
|
+
*
|
|
106
|
+
* Named for the label so the folder is readable on the Workspace screen, with
|
|
107
|
+
* the head of the id after it so two jobs called the same thing stay apart.
|
|
108
|
+
* Finished work never lands here: that is `artifacts/`, which stays flat and
|
|
109
|
+
* unattributed, and code is `projects/`, which outlives the job that started it.
|
|
110
|
+
*/
|
|
111
|
+
export function jobScratch(taskId: string, label: string): string {
|
|
112
|
+
const slug =
|
|
113
|
+
label
|
|
114
|
+
.trim()
|
|
115
|
+
.toLowerCase()
|
|
116
|
+
.replace(/[^\p{L}\p{N}]+/gu, "-")
|
|
117
|
+
.replace(/^-+|-+$/g, "")
|
|
118
|
+
.slice(0, 40) || "job";
|
|
119
|
+
return `${PATHS.scratch}/${slug}-${taskId.slice(0, 6)}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Where one bot keeps what it wants on its next job (config PATHS.bots). */
|
|
123
|
+
export const botFolder = (bot: string): string =>
|
|
124
|
+
`${PATHS.bots}/${bot.replace(/[^\p{L}\p{N}_-]+/gu, "-")}`;
|
|
125
|
+
|
|
126
|
+
export async function openBotFolder(bot: string): Promise<string> {
|
|
127
|
+
const path = botFolder(bot);
|
|
128
|
+
await mkdir(join(WORKSPACE, path), { recursive: true });
|
|
129
|
+
return path;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Creates it, so a job never has to and never writes to the shared root by mistake. */
|
|
133
|
+
export async function openJobScratch(
|
|
134
|
+
taskId: string,
|
|
135
|
+
label: string,
|
|
136
|
+
): Promise<string> {
|
|
137
|
+
const path = jobScratch(taskId, label);
|
|
138
|
+
await mkdir(join(WORKSPACE, path), { recursive: true });
|
|
139
|
+
return path;
|
|
140
|
+
}
|
|
141
|
+
|
|
92
142
|
/**
|
|
93
143
|
* A job's own browser session. playwright-cli reads the session name from
|
|
94
144
|
* this variable when `-s=` is not given, so the model never picks one and the
|
|
@@ -105,6 +155,52 @@ export const jobShellEnv = (
|
|
|
105
155
|
}
|
|
106
156
|
: {};
|
|
107
157
|
|
|
158
|
+
/** Set by `ensureBrowser`, read by `readMachineTools`; null until it has finished. */
|
|
159
|
+
let browserReady: boolean | null = null;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* What the shell can actually reach. A bot writes a job around what is here —
|
|
163
|
+
* node or python3, which package manager — and the platform does not say: two
|
|
164
|
+
* macs differ. Probed in one `command -v` sweep at the moment it is asked, per
|
|
165
|
+
* job rather than cached, so a job that installs something is not told
|
|
166
|
+
* otherwise on its next run.
|
|
167
|
+
*/
|
|
168
|
+
export const PROBED = {
|
|
169
|
+
runtimes: ["node", "python3", "uv", "bun", "deno"],
|
|
170
|
+
managers: ["pnpm", "npm", "yarn"],
|
|
171
|
+
} as const;
|
|
172
|
+
|
|
173
|
+
export type MachineTools = {
|
|
174
|
+
[K in keyof typeof PROBED]: { found: string[]; missing: string[] };
|
|
175
|
+
} & {
|
|
176
|
+
/** Null while `ensureBrowser` is still running; it starts at boot and is not awaited. */
|
|
177
|
+
browser: boolean | null;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export async function readMachineTools(
|
|
181
|
+
sandbox: Sandbox,
|
|
182
|
+
): Promise<MachineTools> {
|
|
183
|
+
const names = Object.values(PROBED).flat();
|
|
184
|
+
const { stdout } = await sandbox.exec(
|
|
185
|
+
names
|
|
186
|
+
.map(
|
|
187
|
+
(name) => `command -v ${name} >/dev/null 2>&1 && printf '%s ' ${name}`,
|
|
188
|
+
)
|
|
189
|
+
.join("; "),
|
|
190
|
+
{ timeoutMs: 10_000 },
|
|
191
|
+
);
|
|
192
|
+
const found = new Set(stdout.trim().split(/\s+/).filter(Boolean));
|
|
193
|
+
const split = (list: readonly string[]) => ({
|
|
194
|
+
found: list.filter((name) => found.has(name)),
|
|
195
|
+
missing: list.filter((name) => !found.has(name)),
|
|
196
|
+
});
|
|
197
|
+
return {
|
|
198
|
+
runtimes: split(PROBED.runtimes),
|
|
199
|
+
managers: split(PROBED.managers),
|
|
200
|
+
browser: browserReady,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
108
204
|
/**
|
|
109
205
|
* The browser bots drive. It is not in the package — ~280 MB, and its build
|
|
110
206
|
* moves with playwright-core — so it is fetched here instead of from an install
|
|
@@ -122,7 +218,8 @@ export async function ensureBrowser(): Promise<void> {
|
|
|
122
218
|
.exec("playwright-cli install-browser chromium")
|
|
123
219
|
.catch((cause: unknown) => ({ exitCode: 1, stderr: String(cause) }));
|
|
124
220
|
|
|
125
|
-
|
|
221
|
+
browserReady = done.exitCode === 0;
|
|
222
|
+
if (browserReady) return;
|
|
126
223
|
logger.warn(
|
|
127
224
|
`no browser — bots cannot open a page until \`playwright-cli install-browser chromium\` succeeds: ${done.stderr.trim().split("\n").at(-1) ?? ""}`,
|
|
128
225
|
);
|
|
@@ -137,6 +234,7 @@ export async function closeJobShell(taskId: string): Promise<void> {
|
|
|
137
234
|
})
|
|
138
235
|
.catch(() => {});
|
|
139
236
|
await pruneBrowserFiles();
|
|
237
|
+
await pruneOutputFiles();
|
|
140
238
|
}
|
|
141
239
|
|
|
142
240
|
/**
|
|
@@ -145,6 +243,26 @@ export async function closeJobShell(taskId: string): Promise<void> {
|
|
|
145
243
|
*/
|
|
146
244
|
const BROWSER_FILE_TTL_MS = 60 * 60 * 1000;
|
|
147
245
|
|
|
246
|
+
async function pruneOutputFiles(): Promise<void> {
|
|
247
|
+
const dir = join(WORKSPACE, PATHS.output);
|
|
248
|
+
const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
249
|
+
const cutoff = Date.now() - OUTPUT_FILE_TTL_MS;
|
|
250
|
+
for (const entry of entries) {
|
|
251
|
+
if (!entry.isFile()) continue;
|
|
252
|
+
const file = join(dir, entry.name);
|
|
253
|
+
const info = await stat(file).catch(() => null);
|
|
254
|
+
if (info && info.mtimeMs < cutoff) await unlink(file).catch(() => {});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Tool output over `TOOL_OUTPUT.max` is written here in full and the model is
|
|
260
|
+
* told where. Nothing read it after the run that made it, and nothing deleted
|
|
261
|
+
* it either — a day is long enough to still be looking, short enough that the
|
|
262
|
+
* folder does not become the biggest thing in the workspace.
|
|
263
|
+
*/
|
|
264
|
+
const OUTPUT_FILE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
265
|
+
|
|
148
266
|
async function pruneBrowserFiles(): Promise<void> {
|
|
149
267
|
const dir = join(WORKSPACE, BROWSER_DIR);
|
|
150
268
|
const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
@@ -185,3 +303,18 @@ export async function openWorkspace(): Promise<Sandbox> {
|
|
|
185
303
|
toolPath: TOOL_PATH,
|
|
186
304
|
});
|
|
187
305
|
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Removes a job's working folder. Its lifetime is the task's: when the row goes,
|
|
309
|
+
* so does what it was working with (features/bot/bot.runner). Never touches
|
|
310
|
+
* `artifacts` or `projects` — those are the user's and outlive every job.
|
|
311
|
+
*/
|
|
312
|
+
export async function removeJobScratch(
|
|
313
|
+
taskId: string,
|
|
314
|
+
label: string,
|
|
315
|
+
): Promise<void> {
|
|
316
|
+
await rm(join(WORKSPACE, jobScratch(taskId, label)), {
|
|
317
|
+
recursive: true,
|
|
318
|
+
force: true,
|
|
319
|
+
}).catch(() => {});
|
|
320
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { KeyboardEvent } from "react";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Draft for a one-line input, committed on blur. Enter blurs (one commit path);
|
|
6
|
+
* a draft shorter than `min` reverts to `value`; an outside change to `value`
|
|
7
|
+
* resets the draft.
|
|
8
|
+
*/
|
|
9
|
+
export function useDraft(
|
|
10
|
+
value: string,
|
|
11
|
+
onCommit: (next: string) => void | Promise<void>,
|
|
12
|
+
{ min = 1 }: { min?: number } = {},
|
|
13
|
+
) {
|
|
14
|
+
const [draft, setDraft] = useState(value);
|
|
15
|
+
// Sync during render; an effect would show the stale draft for a frame.
|
|
16
|
+
const [seen, setSeen] = useState(value);
|
|
17
|
+
if (value !== seen) {
|
|
18
|
+
setSeen(value);
|
|
19
|
+
setDraft(value);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const commit = () => {
|
|
23
|
+
const next = draft.trim();
|
|
24
|
+
if (next.length < min) return setDraft(value);
|
|
25
|
+
setDraft(next);
|
|
26
|
+
if (next !== value) void onCommit(next);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const onKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
|
|
30
|
+
// Enter during IME composition confirms the character, not the draft.
|
|
31
|
+
if (event.key === "Enter" && !event.nativeEvent.isComposing) {
|
|
32
|
+
event.currentTarget.blur();
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return { value: draft, set: setDraft, commit, onKeyDown };
|
|
37
|
+
}
|