thursday-agent 0.2.0 → 0.3.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 +5 -2
- package/.next/build-manifest.json +3 -3
- package/.next/prerender-manifest.json +5 -5
- package/.next/routes-manifest.json +24 -6
- package/.next/server/app/_global-error/page.js +2 -2
- 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 +2 -2
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page.js +2 -2
- 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 +14 -14
- package/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
- 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 +3 -3
- package/.next/server/app/api/artifact/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/memory/files/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/bot/memory/files/route.js +10 -0
- package/.next/server/app/api/bot/memory/files/route.js.nft.json +1 -0
- package/.next/server/app/api/bot/memory/files/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/bot/memory/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/bot/memory/route.js +14 -0
- package/.next/server/app/api/bot/{notes → memory}/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/memory/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/bot/route.js +6 -6
- package/.next/server/app/api/bot/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/task/[id]/route.js +8 -8
- package/.next/server/app/api/bot/task/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/task/route.js +8 -8
- package/.next/server/app/api/bot/task/route.js.nft.json +1 -1
- package/.next/server/app/api/config/route.js +5 -5
- package/.next/server/app/api/config/route.js.nft.json +1 -1
- package/.next/server/app/api/events/route.js +4 -5
- package/.next/server/app/api/events/route.js.nft.json +1 -1
- package/.next/server/app/api/file/[...path]/route.js +3 -3
- package/.next/server/app/api/file/[...path]/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/catalog/route.js +9 -8
- package/.next/server/app/api/llm-model/catalog/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/credits/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/llm-model/credits/route/build-manifest.json +12 -0
- package/.next/server/app/api/llm-model/credits/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/llm-model/credits/route.js +16 -0
- package/.next/server/app/api/llm-model/credits/route.js.map +5 -0
- package/.next/server/app/api/llm-model/credits/route.js.nft.json +1 -0
- package/.next/server/app/api/llm-model/credits/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/llm-model/route.js +10 -8
- package/.next/server/app/api/llm-model/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/subscription/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/llm-model/subscription/route/build-manifest.json +12 -0
- package/.next/server/app/api/llm-model/subscription/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/llm-model/subscription/route.js +15 -0
- package/.next/server/app/api/llm-model/subscription/route.js.map +5 -0
- package/.next/server/app/api/llm-model/subscription/route.js.nft.json +1 -0
- package/.next/server/app/api/llm-model/subscription/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/mcp/[name]/route.js +4 -4
- 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 +4 -4
- package/.next/server/app/api/mcp/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/tools/route.js +4 -4
- package/.next/server/app/api/mcp/tools/route.js.nft.json +1 -1
- package/.next/server/app/api/memory/edit/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/memory/edit/route/build-manifest.json +12 -0
- package/.next/server/app/api/memory/edit/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/memory/edit/route.js +24 -0
- package/.next/server/app/api/memory/edit/route.js.map +5 -0
- package/.next/server/app/api/memory/edit/route.js.nft.json +1 -0
- package/.next/server/app/api/memory/{tidy → edit}/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/memory/route.js +6 -7
- package/.next/server/app/api/memory/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/[source]/[name]/route.js +3 -3
- package/.next/server/app/api/skills/[source]/[name]/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/route.js +3 -3
- package/.next/server/app/api/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/call/route.js +9 -7
- package/.next/server/app/api/thursday/call/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/skills/route.js +9 -7
- package/.next/server/app/api/thursday/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/tool-call/route.js +17 -15
- package/.next/server/app/api/thursday/tool-call/route.js.nft.json +1 -1
- package/.next/server/app/api/workspace/route.js +4 -5
- package/.next/server/app/api/workspace/route.js.nft.json +1 -1
- package/.next/server/app/artifact/[...path]/page/server-reference-manifest.json +1 -1
- package/.next/server/app/artifact/[...path]/page.js +5 -6
- 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 +3 -4
- package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
- package/.next/server/app/favicon.ico.body +0 -0
- package/.next/server/app/page/server-reference-manifest.json +57 -90
- package/.next/server/app/page.js +10 -10
- 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 +5 -2
- package/.next/server/chunks/[externals]__0dzuk6_._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__01dkp4a._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__02va5wz._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__02xjoto._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__04q--0f._.js +8 -0
- package/.next/server/chunks/[root-of-the-server]__0bl-e63._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0ce8nf8._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__0ffbg85._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0j7z0r3._.js +12 -0
- package/.next/server/chunks/[root-of-the-server]__0jq4ohy._.js +8 -0
- package/.next/server/chunks/[root-of-the-server]__0ls6_8y._.js +8 -0
- package/.next/server/chunks/[root-of-the-server]__0ncs9uu._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0pa2n68._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0q0vvt2._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__1-ga0kh._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__11n256_._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__12cabg9._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__1339jnv._.js +8 -0
- package/.next/server/chunks/[root-of-the-server]__15bjnu5._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__18_teo8._.js +8 -0
- package/.next/server/chunks/[root-of-the-server]__1_04mcf._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__1anc1p9._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__1ccon1w._.js +5 -5
- package/.next/server/chunks/[root-of-the-server]__1fizn31._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__1llj3-b._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__1orn-5b._.js +11 -0
- package/.next/server/chunks/[root-of-the-server]__1p8m3f4._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__1wa1k7r._.js +8 -0
- package/.next/server/chunks/_0-73of8._.js +5 -5
- package/.next/server/chunks/_0-fjql4._.js +26 -0
- package/.next/server/chunks/_00xh_q0._.js +27 -0
- package/.next/server/chunks/{_0pppbb5._.js → _03kp0jp._.js} +163 -36
- package/.next/server/chunks/_051qh8u._.js +3 -0
- package/.next/server/chunks/{node_modules_next_04q-o58._.js → _05dla15._.js} +2 -2
- package/.next/server/chunks/_05h2cii._.js +1 -1
- package/.next/server/chunks/_0c9j6rb._.js +49 -0
- package/.next/server/chunks/_0fzqt2y._.js +130 -0
- package/.next/server/chunks/{_0vnutv9._.js → _0ioz3nd._.js} +1 -1
- package/.next/server/chunks/_0iwfby5._.js +3 -0
- package/.next/server/chunks/_0mecv0f._.js +26 -0
- package/.next/server/chunks/_0mh8j4m._.js +3 -0
- package/.next/server/chunks/_0n74zsf._.js +4 -4
- package/.next/server/chunks/_0obojvv._.js +3 -0
- package/.next/server/chunks/_0recufk._.js +49 -0
- package/.next/server/chunks/_0rnf-6n._.js +4 -4
- package/.next/server/chunks/_0sb7tlz._.js +3 -0
- package/.next/server/chunks/_0u10wmw._.js +5 -5
- package/.next/server/chunks/_0u4aiy2._.js +3 -0
- package/.next/server/chunks/_0wp4le3._.js +1 -1
- package/.next/server/chunks/_0y5fg-x._.js +1 -1
- package/.next/server/chunks/_1230wbx._.js +10 -0
- package/.next/server/chunks/_12nheja._.js +3 -0
- package/.next/server/chunks/_13510xh._.js +3 -0
- package/.next/server/chunks/_15hq89o._.js +1 -1
- package/.next/server/chunks/_17l5ygt._.js +49 -0
- package/.next/server/chunks/_18-1wcc._.js +5 -5
- package/.next/server/chunks/_1_5mnq4._.js +4 -4
- package/.next/server/chunks/_1_9mjx-._.js +3 -0
- package/.next/server/chunks/_1d0j961._.js +6 -0
- package/.next/server/chunks/_1d5zmy8._.js +1 -1
- package/.next/server/chunks/_1drgfx4._.js +4 -4
- package/.next/server/chunks/_1f46eie._.js +3 -0
- package/.next/server/chunks/_1h5_8sk._.js +3 -0
- package/.next/server/chunks/_1hdnxb7._.js +3 -0
- package/.next/server/chunks/_1j8k_j5._.js +3 -0
- package/.next/server/chunks/_1krd9rf._.js +3 -0
- package/.next/server/chunks/_1nc0eui._.js +11 -0
- package/.next/server/chunks/_1nula3v._.js +3 -0
- package/.next/server/chunks/_1pf4qe-._.js +3 -0
- package/.next/server/chunks/_1php4eq._.js +1 -1
- package/.next/server/chunks/_1qxheoa._.js +3 -0
- package/.next/server/chunks/_1x6kd1r._.js +3 -0
- package/.next/server/chunks/_1yuv4vp._.js +1 -1
- package/.next/server/chunks/_1zjrdag._.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_bot_memory_files_route_actions_1_xd_8f.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_bot_memory_route_actions_1a8xfr7.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_llm-model_credits_route_actions_0v424pb.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_llm-model_subscription_route_actions_0-udcm5.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_memory_edit_route_actions_02sfnmr.js +3 -0
- package/.next/server/chunks/config_ts_1g4sz5o._.js +1 -1
- package/.next/server/chunks/features_0rpfxbk._.js +3 -0
- package/.next/server/chunks/features_1qf3cei._.js +3 -0
- package/.next/server/chunks/features_bot_0zt92oh._.js +3 -0
- package/.next/server/chunks/features_bot_bot_query_ts_12mgkl2._.js +3 -0
- package/.next/server/chunks/features_memory_1uqvksn._.js +6 -0
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0a1rd09._.js → node_modules_consola_dist_chunks_prompt_mjs_0i7cvml._.js} +1 -1
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_06067w6._.js → node_modules_consola_dist_chunks_prompt_mjs_0icqaqw._.js} +1 -1
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_096ais_._.js → node_modules_consola_dist_chunks_prompt_mjs_0r28-sm._.js} +1 -1
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_02s3hjt._.js → node_modules_consola_dist_chunks_prompt_mjs_0v_7drq._.js} +1 -1
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1xoq2rl._.js +3 -0
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0tvkfgh._.js → node_modules_consola_dist_chunks_prompt_mjs_1y49572._.js} +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__09q9med._.js +1 -1
- package/.next/server/chunks/ssr/{[root-of-the-server]__1qfqvmg._.js → [root-of-the-server]__0_69wqr._.js} +2 -2
- package/.next/server/chunks/ssr/[root-of-the-server]__0hg5gib._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0i8ie7f._.js +3 -0
- package/.next/server/chunks/ssr/{[root-of-the-server]__0jsmmb-._.js → [root-of-the-server]__0mz33a7._.js} +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0tbkdlx._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0vnmv-s._.js +3 -0
- package/.next/server/chunks/ssr/{[root-of-the-server]__0f6s48x._.js → [root-of-the-server]__0xpcv9w._.js} +3 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0xtssj3._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0zkv7z_._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__10-dz5o._.js +57 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__15_uhip._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__16jc196._.js +1 -1
- package/.next/server/chunks/ssr/{[root-of-the-server]__0m1b5ky._.js → [root-of-the-server]__1cyjywu._.js} +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__1eh7hid._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1gtlw6v._.js +43 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1mzi-ju._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__1ox7hbk._.js +4 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1pduf5n._.js +3 -0
- package/.next/server/chunks/ssr/{_1mznrxk._.js → _001_ms1._.js} +3 -3
- package/.next/server/chunks/ssr/_01_6m32._.js +3 -0
- package/.next/server/chunks/ssr/_01n3o0m._.js +3 -0
- package/.next/server/chunks/ssr/_07uchnc._.js +3 -0
- package/.next/server/chunks/ssr/_0ap2fzn._.js +3 -0
- package/.next/server/chunks/ssr/_0c-ql2m._.js +3 -0
- package/.next/server/chunks/ssr/_0h42fze._.js +3 -0
- package/.next/server/chunks/ssr/_0h7zdni._.js +1 -1
- package/.next/server/chunks/ssr/_0i_6q1w._.js +41 -0
- package/.next/server/chunks/ssr/_0kbkmar._.js +3 -0
- package/.next/server/chunks/ssr/{_077-g6s._.js → _0mb8zc8._.js} +3 -3
- package/.next/server/chunks/ssr/_0mo2w06._.js +3 -0
- package/.next/server/chunks/ssr/{_13punpm._.js → _0q0zan6._.js} +2 -2
- package/.next/server/chunks/ssr/{_040sk32._.js → _0u2jv_x._.js} +76 -82
- package/.next/server/chunks/ssr/_0y0tiht._.js +4 -0
- package/.next/server/chunks/ssr/_0zhoc3r._.js +3 -0
- package/.next/server/chunks/ssr/_1-teee5._.js +3 -3
- package/.next/server/chunks/ssr/_13fke0x._.js +3 -0
- package/.next/server/chunks/ssr/{_1lip4th._.js → _13z3f85._.js} +2 -2
- package/.next/server/chunks/ssr/_14_lw10._.js +3 -0
- package/.next/server/chunks/ssr/_15j2_8f._.js +3 -0
- package/.next/server/chunks/ssr/{_09ja_w1._.js → _1eoqg_g._.js} +2 -2
- package/.next/server/chunks/ssr/_1sji22n._.js +3 -0
- package/.next/server/chunks/ssr/_1spzm8z._.js +3 -0
- package/.next/server/chunks/ssr/{_0x_nu5e._.js → _1vi_uo7._.js} +2 -2
- package/.next/server/chunks/ssr/_1vyd1ji._.js +1 -1
- package/.next/server/chunks/ssr/{_1m3bsfr._.js → _1xvgn77._.js} +2 -2
- package/.next/server/chunks/ssr/_20kd_n0._.js +42 -0
- package/.next/server/chunks/ssr/features_bot_1s_-2vj._.js +3 -0
- package/.next/server/chunks/ssr/features_config_components_config-setting_tsx_0xfwqdg._.js +1 -1
- package/.next/server/chunks/ssr/features_connectors_components_mcp-setting_tsx_0jmbgvp._.js +1 -1
- package/.next/server/chunks/ssr/lib_date-like_ts_02is0pw._.js +3 -0
- package/.next/server/chunks/ssr/{node_modules_18_dk7_._.js → node_modules_063xwpp._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_01qnv-a._.js → node_modules_0bvraul._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0n1kj4p._.js → node_modules_0eu1va6._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0kl7-tn._.js → node_modules_0h50b8n._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0_n241r._.js → node_modules_0h5hnr1._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0lrjk5t._.js → node_modules_0h7u60n._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0ndvxub._.js → node_modules_0lm7_vm._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_13r44jq._.js → node_modules_0mwym0g._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_17lrt3y._.js → node_modules_0rjg6t1._.js} +1 -1
- package/.next/server/chunks/ssr/node_modules_0x3-ply._.js +3 -0
- package/.next/server/chunks/ssr/{node_modules_004qlr4._.js → node_modules_0y_cded._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0kxz4gt._.js → node_modules_0z5tsu8._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0iz1rzd._.js → node_modules_0zp9r92._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_19dp7ko._.js → node_modules_1-hc6uv._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1q0p85z._.js → node_modules_113tz0l._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1toafc-._.js → node_modules_124bue5._.js} +1 -1
- package/.next/server/chunks/ssr/node_modules_17bwo43._.js +55 -0
- package/.next/server/chunks/ssr/{node_modules_08d8bke._.js → node_modules_1_n3ztw._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1dwguw_._.js → node_modules_1bc7gu8._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_20yebtj._.js → node_modules_1bgb6-f._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1n5ygqe._.js → node_modules_1h4a4pp._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_04n7mjs._.js → node_modules_1i7u1ia._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1hn8z1f._.js → node_modules_1juq3hs._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1xyd7ov._.js → node_modules_1l0qtdu._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0m93zeg._.js → node_modules_1t9cyee._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_09xhur7._.js → node_modules_1uuj9x1._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1hd-5tv._.js → node_modules_1vqb6po._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1zu988w._.js → node_modules_1xx28h1._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0m9a4y0._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0d3skpl._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_13w_wy9._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0i44kk5._.js} +1 -1
- package/.next/server/chunks/ssr/node_modules_@vercel_oidc_dist_0qe30a_._.js +3 -0
- package/.next/server/chunks/ssr/{node_modules_next_dist_0w6mzq5._.js → node_modules_next_dist_0drixxt._.js} +4 -4
- package/.next/server/chunks/ssr/node_modules_next_dist_1n3w9lb._.js +2 -2
- package/.next/server/instrumentation.js +1 -1
- 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 +58 -91
- package/.next/static/chunks/{31ttnjt6g35a0.js → 0ev4hyghq0mbb.js} +1 -1
- package/.next/static/chunks/0r5qcctujhxli.js +1 -0
- package/.next/static/chunks/0tmgrnlje14-s.js +3 -0
- package/.next/static/chunks/0x79tx04dxbpp.js +1 -0
- package/.next/static/chunks/0xzgvc0fkdb6t.js +1 -0
- package/.next/static/chunks/10teww7wt-f6h.js +1 -0
- package/.next/static/chunks/14hgtsjg-kael.css +1 -0
- package/.next/static/chunks/17donp69y4do7.js +40 -0
- package/.next/static/chunks/{3_h9txt3tq8t8.js → 1dkjg87pxk0n9.js} +1 -1
- package/.next/static/chunks/1ekc7ul556kbl.js +1 -0
- package/.next/static/chunks/{3ard_noqapnw1.js → 1grhjo7a5a0_-.js} +1 -1
- package/.next/static/chunks/1kq8h8wi_e0bc.js +53 -0
- package/.next/static/chunks/1okqiww3pnwka.js +1 -0
- package/.next/static/chunks/26-pr2c9ro222.js +1 -0
- package/.next/static/chunks/{18vhba44l1fc9.js → 280t0_e99i276.js} +1 -1
- package/.next/static/chunks/28cizcdaabd4h.js +1 -0
- package/.next/static/chunks/2a0k6jlq0_v1u.js +2 -0
- package/.next/static/chunks/2fzs9fm8yarvq.js +1 -0
- package/.next/static/chunks/2h0sd2vumhb15.js +1 -0
- package/.next/static/chunks/2hw2f6zmt7fmt.js +1 -0
- package/.next/static/chunks/2t4vhi3m8ahya.js +1 -0
- package/.next/static/chunks/2tigdtz3dpcy8.js +1 -0
- package/.next/static/chunks/3111pj5wnjgos.js +1 -0
- package/.next/static/chunks/{07kl352c-lvcz.js → 3agpgkwaa321p.js} +1 -1
- package/.next/static/chunks/3cfi_q6d_wtci.js +1 -0
- package/.next/static/chunks/3ok9_x74rip3v.js +1 -0
- package/.next/static/chunks/3xk7yqgomx7yq.js +1 -0
- package/.next/static/chunks/3xpbkl66ssib5.js +38 -0
- package/.next/static/media/favicon.37o1pjldwi2uc.ico +0 -0
- package/README.md +6 -4
- package/app/api/bot/memory/files/route.ts +14 -0
- package/app/api/bot/memory/route.ts +5 -0
- package/app/api/events/app-event.ts +3 -5
- package/app/api/llm-model/credits/route.ts +11 -0
- package/app/api/llm-model/route.ts +6 -1
- package/app/api/llm-model/subscription/route.ts +11 -0
- package/app/api/memory/edit/route.ts +7 -0
- package/app/api/memory/route.ts +21 -0
- package/app/api/query-key.ts +19 -4
- package/bin/thursday.mjs +3 -0
- package/components/ui/shiny-text.tsx +48 -134
- package/database/migrations/{20260909060813_optimal_sabretooth → 20260910173049_numerous_spirit}/migration.sql +24 -34
- package/database/migrations/{20260909060813_optimal_sabretooth → 20260910173049_numerous_spirit}/snapshot.json +71 -145
- package/database/migrations/20260911030320_opposite_jack_power/migration.sql +1 -0
- package/database/migrations/{20260909063821_bitter_tenebrous → 20260911030320_opposite_jack_power}/snapshot.json +57 -184
- package/features/ai/chatgpt.ts +617 -0
- package/features/ai/components/chatgpt-sign-in.tsx +66 -0
- package/features/ai/components/model-picker.tsx +17 -1
- package/features/ai/components/provider-icon.tsx +1 -0
- package/features/ai/load-tools.ts +58 -42
- package/features/ai/model.schema.ts +232 -34
- package/features/ai/model.ts +169 -24
- package/features/ai/prompts/bot.prompt.ts +156 -65
- package/features/ai/prompts/memory-edit.prompt.ts +33 -0
- package/features/ai/prompts/prompt-helper.ts +45 -26
- package/features/ai/prompts/thursday.prompt.ts +40 -17
- package/features/ai/tools/bot.tool.ts +35 -52
- package/features/ai/tools/connected.ts +17 -4
- package/features/ai/tools/memory.tool.ts +133 -72
- package/features/ai/tools/search.tool.ts +136 -47
- package/features/ai/tools/tool-name.ts +2 -5
- package/features/ai/tools/workspace.tool.ts +2 -1
- package/features/bot/bot.action.ts +8 -18
- package/features/bot/bot.memory.ts +89 -0
- package/features/bot/bot.query.ts +37 -57
- package/features/bot/bot.run.ts +507 -241
- package/features/bot/bot.runner.ts +410 -114
- package/features/bot/bot.schema.ts +79 -19
- package/features/bot/bot.seed.ts +89 -16
- package/features/bot/components/bot-mark.tsx +122 -20
- package/features/bot/components/bot-room.tsx +159 -130
- package/features/bot/components/bot-setting.tsx +790 -402
- package/features/bot/components/bot-tool.tsx +2 -6
- package/features/bot/components/mark-palette.tsx +54 -0
- package/features/bot/components/path-chips.tsx +1 -1
- package/features/bot/components/task-badge.tsx +26 -6
- package/features/bot/components/task-reply.tsx +22 -33
- package/features/bot/components/task-setting.tsx +20 -17
- package/features/bot/task.query.ts +215 -28
- package/features/bot/task.store.ts +53 -9
- package/features/config/components/config-setting.tsx +265 -34
- package/features/config/components/models-badge.tsx +42 -0
- package/features/config/config.action.ts +12 -0
- package/features/config/config.const.ts +38 -2
- package/features/connectors/mcp.manager.ts +10 -2
- package/features/intro/components/intro.tsx +47 -46
- package/features/memory/components/memory-edit.tsx +338 -0
- package/features/memory/components/memory-setting.tsx +75 -77
- package/features/memory/memory.action.ts +3 -43
- package/features/memory/memory.edit.ts +87 -0
- package/features/memory/memory.query.ts +24 -42
- package/features/memory/memory.schema.ts +29 -106
- package/features/settings/components/settings.tsx +2 -0
- package/features/settings/settings.alert.ts +3 -1
- package/features/thursday/components/ascii-orb.tsx +106 -90
- package/features/thursday/components/boot.tsx +7 -0
- package/features/thursday/components/call-log.tsx +219 -92
- package/features/thursday/components/face.tsx +6 -4
- package/features/thursday/components/tab-state.tsx +97 -0
- package/features/thursday/components/thursday-mark.tsx +4 -1
- package/features/thursday/components/thursday-setting.tsx +17 -47
- package/features/thursday/components/thursday.tsx +208 -95
- package/features/thursday/thursday.action.ts +12 -17
- package/features/thursday/thursday.query.ts +61 -70
- package/features/thursday/thursday.schema.ts +20 -7
- package/features/thursday/tool-line.ts +85 -11
- package/features/thursday/use-thursday.ts +86 -59
- package/features/workspace/components/file-view.tsx +4 -2
- package/features/workspace/workspace.ts +110 -26
- package/lib/limits.ts +1 -1
- package/lib/oauth-page.ts +27 -0
- package/lib/realtime/openai.ts +2 -2
- package/lib/realtime/realtime.protocol.ts +1 -1
- package/lib/realtime/realtime.tap.ts +122 -0
- package/lib/sandbox.ts +59 -36
- package/package.json +1 -1
- package/skills/browser/SKILL.md +55 -34
- package/.next/server/app/api/bot/notes/route/app-paths-manifest.json +0 -3
- package/.next/server/app/api/bot/notes/route.js +0 -14
- package/.next/server/app/api/bot/notes/route_client-reference-manifest.js +0 -3
- package/.next/server/app/api/memory/tidy/route/app-paths-manifest.json +0 -3
- package/.next/server/app/api/memory/tidy/route.js +0 -22
- package/.next/server/app/api/memory/tidy/route.js.nft.json +0 -1
- package/.next/server/chunks/0d-b_next_dist_server_app-render_after-task-async-storage_external_0-exn7p.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0-npkh_._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__005var5._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__019dx9f._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__02_2sm-._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__04_8spk._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__05luceg._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__06t77t4._.js +0 -11
- package/.next/server/chunks/[root-of-the-server]__0bed6hu._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__0cro2dz._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__0dupqt7._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__0emr6go._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0le4d-p._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0m8ywyo._.js +0 -10
- package/.next/server/chunks/[root-of-the-server]__0nfuugi._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__0p32jfz._.js +0 -12
- package/.next/server/chunks/[root-of-the-server]__0phdgco._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__0wvgrsl._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__10qshki._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__11j52t0._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__1692zrv._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__16thabu._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__173nhwf._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__1_aoyr9._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1bn73ih._.js +0 -10
- package/.next/server/chunks/[root-of-the-server]__1bqhuh9._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__1cqizfq._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__1e-ci13._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1fboj2j._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1lr7j9t._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__1oli73c._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1txedx6._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__1untqv8._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__1zgpz1w._.js +0 -3
- package/.next/server/chunks/_00_584r._.js +0 -293
- package/.next/server/chunks/_012yhfm._.js +0 -39
- package/.next/server/chunks/_06mxlgn._.js +0 -3
- package/.next/server/chunks/_07rtlxv._.js +0 -3
- package/.next/server/chunks/_0a351g7._.js +0 -6
- package/.next/server/chunks/_0kjjmag._.js +0 -3
- package/.next/server/chunks/_0nn99o2._.js +0 -3
- package/.next/server/chunks/_0zuzmt_._.js +0 -39
- package/.next/server/chunks/_106co2o._.js +0 -3
- package/.next/server/chunks/_12bbv5i._.js +0 -100
- package/.next/server/chunks/_15q1pig._.js +0 -3
- package/.next/server/chunks/_19wg5yn._.js +0 -138
- package/.next/server/chunks/_1bt95p9._.js +0 -55
- package/.next/server/chunks/_1cbk7ms._.js +0 -26
- package/.next/server/chunks/_1drv2et._.js +0 -40
- package/.next/server/chunks/_1ic3bew._.js +0 -5
- package/.next/server/chunks/_1mxn_lb._.js +0 -3
- package/.next/server/chunks/_1rysr-r._.js +0 -10
- package/.next/server/chunks/_1u3rwbr._.js +0 -3
- package/.next/server/chunks/_1x7p319._.js +0 -6
- package/.next/server/chunks/_next-internal_server_app_api_bot_notes_route_actions_0qcsyu7.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_memory_tidy_route_actions_0_cfuom.js +0 -3
- package/.next/server/chunks/features_bot_task_query_ts_11xuzl3._.js +0 -3
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_0gzvgfv._.js +0 -3
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_0vorg6w._.js +0 -3
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1tpvamf._.js +0 -3
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1vmaq9f._.js +0 -3
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1y9-wcr._.js +0 -3
- package/.next/server/chunks/ssr/[externals]_node_crypto_1kn79-7._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0iuoa-r._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0un-erv._.js +0 -4
- package/.next/server/chunks/ssr/[root-of-the-server]__1-mnhyd._.js +0 -92
- package/.next/server/chunks/ssr/[root-of-the-server]__1bww500._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__1vcdast._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__1xwnu-h._.js +0 -3
- package/.next/server/chunks/ssr/_0-mc7ro._.js +0 -3
- package/.next/server/chunks/ssr/_01p5r6-._.js +0 -3
- package/.next/server/chunks/ssr/_03webdr._.js +0 -3
- package/.next/server/chunks/ssr/_09-0k3z._.js +0 -3
- package/.next/server/chunks/ssr/_0f_b_9s._.js +0 -3
- package/.next/server/chunks/ssr/_0fafgvl._.js +0 -3
- package/.next/server/chunks/ssr/_0sf1isi._.js +0 -3
- package/.next/server/chunks/ssr/_0su6_08._.js +0 -3
- package/.next/server/chunks/ssr/_0ti51xc._.js +0 -3
- package/.next/server/chunks/ssr/_0w5bgak._.js +0 -3
- package/.next/server/chunks/ssr/_0y84qo6._.js +0 -3
- package/.next/server/chunks/ssr/_1ciyoo3._.js +0 -3
- package/.next/server/chunks/ssr/_1e-dgz0._.js +0 -4
- package/.next/server/chunks/ssr/_1fq7zgb._.js +0 -3
- package/.next/server/chunks/ssr/_1g6l4uj._.js +0 -3
- package/.next/server/chunks/ssr/_1ruzehw._.js +0 -3
- package/.next/server/chunks/ssr/features_bot_components_bot-setting_tsx_01ajft6._.js +0 -4
- package/.next/server/chunks/ssr/features_config_components_14jqgy4._.js +0 -3
- package/.next/server/chunks/ssr/features_memory_components_memory-setting_tsx_0oa43sw._.js +0 -4
- package/.next/server/chunks/ssr/node_modules_zod_v4_classic_external_15fmmz3.js +0 -55
- package/.next/static/chunks/05b6nx_o3_f_n.js +0 -1
- package/.next/static/chunks/0dmns-gi0raa4.js +0 -1
- package/.next/static/chunks/0f9sq-7q_yxyl.js +0 -1
- package/.next/static/chunks/0oc4_xoia77dg.css +0 -1
- package/.next/static/chunks/0pheo8m3a-rzi.js +0 -1
- package/.next/static/chunks/19i8h2t-5evjx.js +0 -1
- package/.next/static/chunks/1c5fcj473025q.js +0 -1
- package/.next/static/chunks/1pi_hbmjw-f-z.js +0 -1
- package/.next/static/chunks/1twt6k09vg4np.js +0 -1
- package/.next/static/chunks/1ytnip8wdng93.js +0 -1
- package/.next/static/chunks/28gotuisldmom.js +0 -1
- package/.next/static/chunks/2998p0y7k1c8z.js +0 -3
- package/.next/static/chunks/29gyb3nubj51j.js +0 -53
- package/.next/static/chunks/2l233xfycjpd8.js +0 -1
- package/.next/static/chunks/2p5n7pa3c40t-.js +0 -2
- package/.next/static/chunks/2ru1q132af7h7.js +0 -1
- package/.next/static/chunks/2t8wqycwg3y04.js +0 -2
- package/.next/static/chunks/2uxum7qbyeyxd.js +0 -1
- package/.next/static/chunks/32z_ypgtchw48.js +0 -1
- package/.next/static/chunks/33pyjb3xd1iez.js +0 -1
- package/.next/static/chunks/3omud5xunrk1f.js +0 -1
- package/.next/static/chunks/42dnrkbph9e-1.js +0 -1
- package/.next/static/media/favicon.2vob68tjqpejf.ico +0 -0
- package/database/migrations/20260909063821_bitter_tenebrous/migration.sql +0 -1
- package/features/ai/prompts/memory-tidy.prompt.ts +0 -129
- package/features/ai/tools/tidy.tool.ts +0 -22
- package/features/bot/bot.notes.ts +0 -120
- package/features/memory/components/memory-tidy.tsx +0 -395
- package/features/memory/memory.tidy.ts +0 -332
- package/features/memory/tidy.query.ts +0 -135
- package/features/thursday/use-call-title.ts +0 -31
- /package/.next/server/app/api/bot/{notes → memory/files}/route/build-manifest.json +0 -0
- /package/.next/server/app/api/bot/{notes → memory/files}/route/server-reference-manifest.json +0 -0
- /package/.next/server/app/api/bot/{notes → memory/files}/route.js.map +0 -0
- /package/.next/server/app/api/{memory/tidy → bot/memory}/route/build-manifest.json +0 -0
- /package/.next/server/app/api/{memory/tidy → bot/memory}/route/server-reference-manifest.json +0 -0
- /package/.next/server/app/api/{memory/tidy → bot/memory}/route.js.map +0 -0
- /package/.next/static/{YR4NWcDDuzwLbTTU7U7Vr → x1cCqY0kWpR_VcYx_lWO5}/_buildManifest.js +0 -0
- /package/.next/static/{YR4NWcDDuzwLbTTU7U7Vr → x1cCqY0kWpR_VcYx_lWO5}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{YR4NWcDDuzwLbTTU7U7Vr → x1cCqY0kWpR_VcYx_lWO5}/_ssgManifest.js +0 -0
|
@@ -64,6 +64,9 @@ export type TokenUsage = z.infer<typeof TokenUsageSchema>;
|
|
|
64
64
|
|
|
65
65
|
export const NO_TOKENS: TokenUsage = { input: 0, output: 0 };
|
|
66
66
|
|
|
67
|
+
/** A budget under this leaves no room for the opening message, so it is refused rather than stored. */
|
|
68
|
+
export const COMPACT_AT_MIN = 8_000;
|
|
69
|
+
|
|
67
70
|
export const BotSchema = z.object({
|
|
68
71
|
name: z.string(),
|
|
69
72
|
description: z.string(),
|
|
@@ -72,15 +75,16 @@ export const BotSchema = z.object({
|
|
|
72
75
|
/** Set only when chosen; empty runs on the app default model. */
|
|
73
76
|
provider: textModelProviderSchema.nullish(),
|
|
74
77
|
model: z.string().nullish(),
|
|
78
|
+
/** Where this bot's runs compact; empty derives it from the model's own window. */
|
|
79
|
+
compactAt: z.number().nullish(),
|
|
80
|
+
/** Switched off by the user: kept whole, shown to no model. */
|
|
81
|
+
disabled: z.boolean(),
|
|
75
82
|
createdAt: DateLikeSchema,
|
|
76
83
|
tools: PinnedToolSchema.array().default([]),
|
|
77
84
|
/** Sum over every job this bot ran; deleted jobs drop out. */
|
|
78
85
|
tokens: TokenUsageSchema,
|
|
79
86
|
/** When this bot's latest job last moved; null if it never ran. Carried here so the roster need not page through history. */
|
|
80
87
|
lastJobAt: DateLikeSchema.nullish(),
|
|
81
|
-
/** The bot's own notes, written by itself at report time. Shown, never edited; the user's only move is to clear them. */
|
|
82
|
-
note: z.string().nullish(),
|
|
83
|
-
noteAt: DateLikeSchema.nullish(),
|
|
84
88
|
});
|
|
85
89
|
|
|
86
90
|
export const BotFormSchema = z.object({
|
|
@@ -95,6 +99,13 @@ export const BotFormSchema = z.object({
|
|
|
95
99
|
/** Unset by default (runs on the app default). A half pick is emptied, see `pickedModel`. */
|
|
96
100
|
provider: textModelProviderSchema.nullish(),
|
|
97
101
|
model: z.string().trim().max(80).nullish(),
|
|
102
|
+
/** Empty derives it from the model; a number overrides. Below the floor a run cannot start. */
|
|
103
|
+
compactAt: z.coerce
|
|
104
|
+
.number()
|
|
105
|
+
.int()
|
|
106
|
+
.min(COMPACT_AT_MIN, `At least ${COMPACT_AT_MIN / 1000}k`)
|
|
107
|
+
.nullish(),
|
|
108
|
+
disabled: z.boolean().optional(),
|
|
98
109
|
toolIds: z.number().int().array().max(MAX_PINNED_TOOLS).default([]),
|
|
99
110
|
});
|
|
100
111
|
|
|
@@ -127,6 +138,10 @@ export type JobBot = {
|
|
|
127
138
|
/** null means whatever key is configured; only the default bot. */
|
|
128
139
|
provider: TextModelProviderId | null;
|
|
129
140
|
model: string | null;
|
|
141
|
+
/** Carried so a run that already has this bot can say so; `listJobBots` never returns a disabled one. */
|
|
142
|
+
disabled: boolean;
|
|
143
|
+
/** Where its runs compact; null derives it from the model (ai/model compactBudget). */
|
|
144
|
+
compactAt: number | null;
|
|
130
145
|
};
|
|
131
146
|
|
|
132
147
|
/**
|
|
@@ -141,22 +156,41 @@ export const DEFAULT_BOT: JobBot = {
|
|
|
141
156
|
icon: { color: "#14B8A6", shape: "squircle" },
|
|
142
157
|
provider: null,
|
|
143
158
|
model: null,
|
|
159
|
+
disabled: false,
|
|
160
|
+
compactAt: null,
|
|
144
161
|
};
|
|
145
162
|
|
|
146
|
-
// A bot's own
|
|
147
|
-
//
|
|
163
|
+
// A bot's own memory (features/bot/bot.memory): the setting behind it. The files
|
|
164
|
+
// are per bot; whether any bot is shown its own is not.
|
|
148
165
|
|
|
149
166
|
/** Config key (features/config config.query) the switch lives under. */
|
|
150
|
-
export const
|
|
167
|
+
export const BOT_MEMORY_KEY = "BOT_MEMORY";
|
|
151
168
|
|
|
152
|
-
/**
|
|
153
|
-
|
|
154
|
-
* a pass runs only when a job actually turned something up, and costs one small
|
|
155
|
-
* call when it does — so a bot that never learns anything never spends.
|
|
156
|
-
*/
|
|
157
|
-
export const isBotNotesOn = (value: string | undefined) =>
|
|
169
|
+
/** On unless it was switched off. Off, no prompt lists a bot's memory; the files stay. */
|
|
170
|
+
export const isBotMemoryOn = (value: string | undefined) =>
|
|
158
171
|
value?.trim() !== "off";
|
|
159
172
|
|
|
173
|
+
/** One file of a bot's own memory (bot.memory), as its prompt lists it and its page draws it. */
|
|
174
|
+
export const BotMemoryFileSchema = z.object({
|
|
175
|
+
file: z.string(),
|
|
176
|
+
/** Workspace-relative, for reading or deleting it the way Workspace does. */
|
|
177
|
+
path: z.string(),
|
|
178
|
+
/** The line it is listed by; empty for a file with nothing in it. */
|
|
179
|
+
line: z.string(),
|
|
180
|
+
at: DateLikeSchema,
|
|
181
|
+
bytes: z.number(),
|
|
182
|
+
});
|
|
183
|
+
export type BotMemoryFile = z.infer<typeof BotMemoryFileSchema>;
|
|
184
|
+
|
|
185
|
+
export type BotMemory = {
|
|
186
|
+
/** Workspace-relative: `bots/<name>/memory`. */
|
|
187
|
+
folder: string;
|
|
188
|
+
/** Newest first, as many as the reader asked for. */
|
|
189
|
+
entries: BotMemoryFile[];
|
|
190
|
+
/** Every file in the folder, listed or not. */
|
|
191
|
+
total: number;
|
|
192
|
+
};
|
|
193
|
+
|
|
160
194
|
/**
|
|
161
195
|
* `waiting`: the bot stopped on a question only the user can answer; the answer
|
|
162
196
|
* resumes the same thread. `done` and `failed` can be resumed as well.
|
|
@@ -168,11 +202,28 @@ export type TaskStatus = (typeof TASK_STATUSES)[number];
|
|
|
168
202
|
/** One page of history (config PAGE_SIZE). */
|
|
169
203
|
export const TASK_HISTORY_PAGE = PAGE_SIZE;
|
|
170
204
|
|
|
171
|
-
/** The single option offered when a job
|
|
205
|
+
/** The single option offered when the app stopped a job and asks whether to go on. Button text and spoken word alike. */
|
|
172
206
|
export const TASK_CONTINUE = "Continue";
|
|
173
207
|
|
|
174
|
-
/**
|
|
175
|
-
|
|
208
|
+
/**
|
|
209
|
+
* What a `waiting` job waits on (database task.pending). `toolCallId` is the
|
|
210
|
+
* `ask_thursday` call the answer resolves, null when the app stopped the run and
|
|
211
|
+
* offers to continue. `auto` marks a stop the app picks back up by itself once a
|
|
212
|
+
* browser is on the app, not before `retryAt` (epoch ms) (bot.runner parkTask).
|
|
213
|
+
*/
|
|
214
|
+
export type TaskPending = {
|
|
215
|
+
toolCallId: string | null;
|
|
216
|
+
options: string[];
|
|
217
|
+
auto?: boolean;
|
|
218
|
+
retryAt?: number;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* True when the app stopped the job (step cap, closed browser, restart) rather
|
|
223
|
+
* than the bot asking something. Why it stopped is in the outcome text; every
|
|
224
|
+
* list calls it waiting on you, because the answer is the same click.
|
|
225
|
+
*/
|
|
226
|
+
export const isAppStop = (ask: { options: string[] } | null | undefined) => {
|
|
176
227
|
const options = ask?.options ?? [];
|
|
177
228
|
return options.length === 1 && options[0] === TASK_CONTINUE;
|
|
178
229
|
};
|
|
@@ -199,7 +250,7 @@ const LineBase = z.object({
|
|
|
199
250
|
export const TaskLineSchema = z.discriminatedUnion("kind", [
|
|
200
251
|
/** What the user said (via Thursday): request, answer. */
|
|
201
252
|
LineBase.extend({ kind: z.literal("user"), text: z.string() }),
|
|
202
|
-
/** Bot text; the last one is the
|
|
253
|
+
/** Bot text; the last one is the answer. */
|
|
203
254
|
LineBase.extend({ kind: z.literal("text"), text: z.string() }),
|
|
204
255
|
/** Compaction summary (bot.run compact). The model resumes from here; the screen draws it as a divider. */
|
|
205
256
|
LineBase.extend({ kind: z.literal("note"), text: z.string() }),
|
|
@@ -272,10 +323,15 @@ export function taskActivity(lines: TaskLine[], max = 120): string | null {
|
|
|
272
323
|
return doing ? clip(doing, max) : null;
|
|
273
324
|
}
|
|
274
325
|
|
|
275
|
-
/**
|
|
326
|
+
/**
|
|
327
|
+
* What a `waiting` task asks: question from the row's `outcome`, options from
|
|
328
|
+
* `pending`. `auto` is a stop the app picks back up by itself (TaskPending), so
|
|
329
|
+
* nobody is told about it and nobody is rung for it.
|
|
330
|
+
*/
|
|
276
331
|
export const TaskAskSchema = z.object({
|
|
277
332
|
question: z.string(),
|
|
278
333
|
options: z.string().array(),
|
|
334
|
+
auto: z.boolean(),
|
|
279
335
|
});
|
|
280
336
|
|
|
281
337
|
export type TaskAsk = z.infer<typeof TaskAskSchema>;
|
|
@@ -290,8 +346,12 @@ export const TaskSchema = z.object({
|
|
|
290
346
|
outcome: z.string().nullable(),
|
|
291
347
|
/** Set only while `waiting`. */
|
|
292
348
|
ask: TaskAskSchema.nullable(),
|
|
293
|
-
/**
|
|
294
|
-
|
|
349
|
+
/**
|
|
350
|
+
* Whether the user has had the ending: Thursday said it on a call, or a task
|
|
351
|
+
* list was on screen while it sat there. A cancel is seen by whoever cancelled.
|
|
352
|
+
* A highlight and a count, never a filter.
|
|
353
|
+
*/
|
|
354
|
+
seen: z.boolean(),
|
|
295
355
|
/** Burned so far, across every segment including borrowed bots. */
|
|
296
356
|
tokens: TokenUsageSchema,
|
|
297
357
|
/** Context size the model read on the last step (not a sum) and the compaction threshold (BOT_RUN.compactAt). 0 means no step ran yet. */
|
package/features/bot/bot.seed.ts
CHANGED
|
@@ -1,36 +1,50 @@
|
|
|
1
|
-
import { BROWSER_SKILL } from "@/config";
|
|
1
|
+
import { BROWSER_SKILL, STUDIO_SERVER } from "@/config";
|
|
2
|
+
import type { MediaKind } from "@/features/ai/model.schema";
|
|
3
|
+
import { STUDIO_TOOLS, TOOL_NAMES } from "@/features/ai/tools/tool-name";
|
|
2
4
|
import type { BotIcon } from "./bot.schema";
|
|
3
5
|
import { randomMarkColors } from "./mark.const";
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
|
-
* Bots offered to a fresh install
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
8
|
+
* Bots offered to a fresh install. Jarvis shares its name with the row-less
|
|
9
|
+
* fallback (bot.schema DEFAULT_BOT) on purpose: seeding turns that worker into an
|
|
10
|
+
* editable row. Names are one word with no punctuation because they travel through
|
|
11
|
+
* a voice transcript into `delegate`. A seed fixes its silhouette but not its
|
|
12
|
+
* colour: that is rolled per install (`rollSeedColors`), so no two rosters look
|
|
13
|
+
* alike. No seed names a model; the run resolves the app default. Prompts hold only
|
|
14
|
+
* what is true of that bot; the base persona (ai/prompts/bot.prompt) and the
|
|
15
|
+
* browser skill cover the rest.
|
|
16
|
+
*
|
|
17
|
+
* The list is flat and grows. `recommended` is the only split: the intro offers
|
|
18
|
+
* those three and says the rest are in Settings, because which subject bot a
|
|
19
|
+
* person wants is not answerable on install day.
|
|
14
20
|
*/
|
|
15
21
|
export type BotSeed = {
|
|
16
22
|
name: string;
|
|
23
|
+
/** What the model reads when it picks who a job belongs to. */
|
|
17
24
|
description: string;
|
|
25
|
+
/** What the screen shows under the name: one line, never wrapped. */
|
|
26
|
+
hint: string;
|
|
18
27
|
systemPrompt: string;
|
|
19
28
|
icon: BotIcon;
|
|
20
|
-
/** Ticked by default. */
|
|
29
|
+
/** Ticked by default, and the intro's whole offer. */
|
|
21
30
|
recommended?: boolean;
|
|
22
|
-
/**
|
|
23
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Studio models this bot cannot work without (config MEDIA_MODEL_KEYS). Unset
|
|
33
|
+
* ones are named on its row, because an unset media model is not a fallback —
|
|
34
|
+
* the tool is simply absent (ai/model resolveMediaRef) and the bot would find
|
|
35
|
+
* out mid-job. A login is not listed here: the bot asks for one itself.
|
|
36
|
+
*/
|
|
37
|
+
requires?: MediaKind[];
|
|
24
38
|
};
|
|
25
39
|
|
|
26
40
|
export const BOT_SEEDS: BotSeed[] = [
|
|
27
41
|
{
|
|
28
42
|
name: "Jarvis",
|
|
29
43
|
description:
|
|
30
|
-
"Any job — turns it into a plan, hands each part to whoever it belongs to, does the rest,
|
|
44
|
+
"Any job — turns it into a plan, hands each part to whoever it belongs to, does the rest, answers with one result",
|
|
45
|
+
hint: "Plans a job and hands out the parts",
|
|
31
46
|
icon: { shape: "squircle" },
|
|
32
47
|
recommended: true,
|
|
33
|
-
example: "Set up a Google account just for my bots to use",
|
|
34
48
|
systemPrompt:
|
|
35
49
|
"You are Jarvis. A job reaches you as a sentence and leaves as a plan: what it actually needs, in what order, and who does each part. Settle what is still open in one round rather than guessing at it, then send for what the job is made of before you build — a part another bot exists for is theirs, and everything nobody else is for is yours. You hold the whole while they hold pieces: the order the parts run in, the seams where what comes back has to fit together, and the single thing that is handed over at the end. The plan is how the job gets done, not what gets handed back.",
|
|
36
50
|
},
|
|
@@ -38,26 +52,85 @@ export const BOT_SEEDS: BotSeed[] = [
|
|
|
38
52
|
name: "Navigator",
|
|
39
53
|
description:
|
|
40
54
|
"The web through a real browser — opens the page itself, signs in, brings back prices, images and links as they stand now",
|
|
55
|
+
hint: "Opens the page itself and reads what is on it",
|
|
41
56
|
icon: { shape: "poly" },
|
|
42
57
|
recommended: true,
|
|
43
|
-
example: "Find thirty wedding pose photos and put them on one page for me",
|
|
44
58
|
systemPrompt: `You are Navigator. Every browser job is yours: load the \`${BROWSER_SKILL}\` skill first and work through it. A search result says a page exists; you say what is on it right now — so open it, sign in where it asks you to, and keep going past the first screen until you have the thing itself: exact figures, the links that open them, the images off the page. Nothing you did not see goes in what you hand back, and a page that refused you is a fact with a url on it, not a gap to fill in from memory. Whoever asked should never need to open the site after you.`,
|
|
45
59
|
},
|
|
46
60
|
{
|
|
47
61
|
name: "Scribe",
|
|
48
62
|
description:
|
|
49
63
|
"Writing that gets read — finds out what it is for, researches it, then picks the form: prose, table, or a page",
|
|
64
|
+
hint: "Writes what gets read, in the form that fits",
|
|
50
65
|
icon: { shape: "blob" },
|
|
51
66
|
recommended: true,
|
|
52
|
-
example: "Write up what we decided today as a one-page brief",
|
|
53
67
|
systemPrompt:
|
|
54
68
|
"You are Scribe. Who reads it and what they do next decides everything else, so learn that first, then research before you claim: every number and name you write traces back to something you actually read. Pick the form by the reading — a `.md` for something read once, a `.csv` for what gets sorted, a page (the `interactive-page` skill) for something used or something whose pictures are the point. When the request does not settle which it is — something read once or something used — ask before you write rather than committing a whole draft to a guess. Cut what the reader would skip, and never spend a paragraph where a table or a picture answers in one glance. One page they finish beats five they abandon.",
|
|
55
69
|
},
|
|
70
|
+
{
|
|
71
|
+
name: "Insta",
|
|
72
|
+
description:
|
|
73
|
+
"Instagram end to end — settles what the post is, draws it, writes it, and puts it up once approved",
|
|
74
|
+
hint: "Draws the pictures, writes the caption, posts it",
|
|
75
|
+
icon: { shape: "poly" },
|
|
76
|
+
requires: ["image"],
|
|
77
|
+
systemPrompt: `You are Insta. One job takes a post from nothing to live on instagram.com — settle what it is, sign in, draw it, write it, put it up. Load the \`${BROWSER_SKILL}\` skill before any browser step.
|
|
78
|
+
|
|
79
|
+
Settle it in one question. Ask only for what the request leaves open, in a single \`${TOOL_NAMES.ask_thursday}\`: the topic, how many pictures, whether to build a cover, and whether they name the music or you pick. A request that already says its topic is not asked for it again. Told to decide yourself: \`${TOOL_NAMES.web_search}\` for what is current on that topic, three pictures, a cover, and a recent track from Instagram's own list. Never ask twice for one round.
|
|
80
|
+
|
|
81
|
+
Then check the tools are there. \`${TOOL_NAMES.tool_search}\` with \`server: "${STUDIO_SERVER}"\` and \`tools: ["${STUDIO_TOOLS.generate_image}", "${STUDIO_TOOLS.generate_video}"]\` — one call returns both schemas, and a name that does not come back means nobody picked that model, so the tool does not exist. Missing what the job needs: \`${TOOL_NAMES.ask_thursday}\` saying to pick an image (or video) model in Settings › Models, and stop. Never work around a tool that is not there.
|
|
82
|
+
|
|
83
|
+
Sign in once, and into an account made for this rather than their own — the session is kept and every later job posts as whoever it is. Your file is \`bots/Insta/.auth/instagram.json\`, the login page is \`https://www.instagram.com/accounts/login/\`, and the skill carries the rest.
|
|
84
|
+
|
|
85
|
+
Anything covering the screen is closed before you read what is under it. It is in the way, not a sign-in problem.
|
|
86
|
+
|
|
87
|
+
Pictures come from two places. Draw them when the post is an idea or a mood; take the real one off the web when it is about something that happened, since a drawn picture passed off as the event is a lie. To take one: open the page and \`curl -o\` its image url into your folder. What is on someone's page is theirs — name the source, or draw one instead.
|
|
88
|
+
Draw. \`${TOOL_NAMES.tool_call}\` with \`server: "${STUDIO_SERVER}"\`, the tool, its \`args\`, and a \`description\` line saying what the call is for. \`aspectRatio\` is \`"1:1"\` for feed, \`"3:4"\` portrait, \`"9:16"\` story or reel. Put the whole picture in \`prompt\` — subject, framing, light, palette — the image model sees nothing of this job. Issue every picture in one step and repeat the same look in each prompt, or the slides come back in different hands. Keep what you use under \`artifacts/\`.
|
|
89
|
+
|
|
90
|
+
The cover is built, not drawn: image models mangle text. Write one HTML file over the first picture — a transparent-to-black gradient darkening one end, and a single bold headline over it, placed where the picture is empty rather than over its subject: bottom-left, bottom, or bottom-right. Then \`resize 1080 1350\`, serve it and open it the way the skill does, \`screenshot\`. That is slide one.
|
|
91
|
+
|
|
92
|
+
Write the caption to be read: what this is, in the user's voice, no invented facts, no wall of tags.
|
|
93
|
+
|
|
94
|
+
The composer is the one part not to memorise. Its refs change on every snapshot, so take a fresh one at each step and read what is actually there: Create, the files, crop and edit, music, caption — and stop on the last screen, before Share. Music: search the name they gave, or take a current one that fits the topic. No music step on this format means there is none.
|
|
95
|
+
|
|
96
|
+
Ask before it goes up. \`${TOOL_NAMES.ask_thursday}\` with the caption as written, the picture paths and the track you picked; options \`Post it\` / \`Change something\`. Share only on the first. Nothing is published on your own judgement, whatever the request said.
|
|
97
|
+
|
|
98
|
+
Answer with the post's url once it is up, and the file paths either way.`,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: "Voyage",
|
|
102
|
+
description:
|
|
103
|
+
"Plans a trip — the days in walking order, a map for each one, and a page with the pictures",
|
|
104
|
+
hint: "Plans the days, maps them, builds the page",
|
|
105
|
+
icon: { shape: "blob" },
|
|
106
|
+
systemPrompt: `You are Voyage. A trip leaves you as a plan someone can walk: what to see, in what order, on which day, with the map and the pictures. Load the \`${BROWSER_SKILL}\` skill before any browser step.
|
|
107
|
+
|
|
108
|
+
Settle it in one question. Ask only what the request leaves open, in a single \`${TOOL_NAMES.ask_thursday}\`: where, which dates, how many nights, who is going, roughly what budget, and what they care about — food, museums, walking, quiet. Never ask twice for one round.
|
|
109
|
+
|
|
110
|
+
Research in one step, not six. Send every \`${TOOL_NAMES.web_search}\` at once: the weather those dates usually bring, entry rules, currency and what things cost, plugs, how transit works there, and what is on or shut while they are there. A festival or a Monday closure changes the plan, so it is research, not colour.
|
|
111
|
+
|
|
112
|
+
Then open the pages for the places you shortlist — hours, closing days and prices are what search results get wrong. While you are on a page, take its pictures with \`--raw eval "JSON.stringify([...document.images].filter(i => i.naturalWidth > 200).map(i => i.currentSrc))"\` and keep whose page they came from.
|
|
113
|
+
|
|
114
|
+
A day is a walkable cluster, not a wishlist. Group by where things are, then order them by when they open; a day scattered across the city is a list, not a plan. Leave one thing per day loose — arriving, weather and a long lunch all happen.
|
|
115
|
+
|
|
116
|
+
The map is written, not clicked. One directions url per day, built by hand: \`https://www.google.com/maps/dir/?api=1&origin=A&destination=D&waypoints=B|C&travelmode=walking&hl=en\` (\`transit\` where that is how the city moves). Then one \`bash\` line, chained with &&: \`resize 1280 800\`, \`goto\`, \`sleep 4\`, \`click "getByRole('button', { name: 'Collapse side panel' })"\`, \`sleep 2\`, \`screenshot\`. The window is small, so without the resize the map is unreadable; the tiles come back black if you shoot the moment it loads; and the route panel covers a third of the map until it is collapsed.
|
|
117
|
+
|
|
118
|
+
Nobody has to sign in to anything: the day urls open in their own Maps app on a tap, and the trip is finished without them. Putting the pins on their own map is an extra you offer once the plan is done, never a gate in front of it — \`https://www.google.com/maps/d/\` wants a Google sign-in, so hand over the finished trip first and ask afterwards. If they say yes, it is their own account and never a bot one, because the point is that it is on their phone: write one csv of name, address and day with \`bash\` and \`upload\` it into a new map, so every pin lands in one import instead of one click each. Read the import screens rather than remembering them.
|
|
119
|
+
|
|
120
|
+
Photos in one step too: every \`curl -o\` in a single \`bash\` call, into the trip folder.
|
|
121
|
+
|
|
122
|
+
The page is the deliverable: the days in order, the map under each, the photos with whose page they came from, and the facts that decide what to pack. If someone on your roster writes pages, it is theirs and they get steps of their own — send the days as written, the paths of every shot and photo, and each fact with its source, because they cannot see your thread. If nobody does, write it yourself with the \`interactive-page\` skill.
|
|
123
|
+
|
|
124
|
+
Answer with the folder under \`artifacts/\` and what is in it: the page, a map per day, the pictures.`,
|
|
125
|
+
},
|
|
56
126
|
];
|
|
57
127
|
|
|
58
128
|
export const findBotSeed = (name: string) =>
|
|
59
129
|
BOT_SEEDS.find((one) => one.name === name) ?? null;
|
|
60
130
|
|
|
131
|
+
/** What the intro offers; the rest wait in Settings › Bots. */
|
|
132
|
+
export const RECOMMENDED_SEEDS = BOT_SEEDS.filter((seed) => seed.recommended);
|
|
133
|
+
|
|
61
134
|
/**
|
|
62
135
|
* One colour per seed, in `BOT_SEEDS` order. Rolled by the caller and carried
|
|
63
136
|
* from there — the intro shows the face it is about to create, and the roll
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useEffect, useId, useMemo, useRef } from "react";
|
|
4
|
+
import { createVoiceFollower } from "@/lib/realtime/realtime.tap";
|
|
4
5
|
import { MARK_BANDS, type MarkShape } from "../mark.const";
|
|
5
6
|
|
|
6
7
|
// Procedural bot avatar: a generated silhouette with two eyes, animated per frame from refs.
|
|
@@ -273,7 +274,7 @@ export type MarkOptions = {
|
|
|
273
274
|
|
|
274
275
|
/** Peak opacity of the soft shadow that swells under the mark while thinking, percent. 0 disables. */
|
|
275
276
|
shadow: number;
|
|
276
|
-
/** Soft
|
|
277
|
+
/** Soft halo around the silhouette, cut out of the head; blur radius in view-box units. 0 disables. */
|
|
277
278
|
glow: number;
|
|
278
279
|
/** Aura color. null follows the mark color. */
|
|
279
280
|
glowColor: string | null;
|
|
@@ -391,20 +392,46 @@ function fitRadii(radii: number[]): number[] {
|
|
|
391
392
|
return radii.map((r) => r * k);
|
|
392
393
|
}
|
|
393
394
|
|
|
395
|
+
/**
|
|
396
|
+
* How far a poly or a squircle leans off its true outline, as a fraction of the
|
|
397
|
+
* radius. Ruled edges read as an icon rather than a face; this is a hand's worth
|
|
398
|
+
* of wobble, and the seed gives each face its own.
|
|
399
|
+
*/
|
|
400
|
+
const WARP = 0.035;
|
|
401
|
+
|
|
402
|
+
/** Bends a ruled outline by a few slow waves. Blob outlines already wobble and are left alone. */
|
|
403
|
+
function warpRadii(radii: number[], seed: number): number[] {
|
|
404
|
+
const rnd = mulberry32(seed ^ 0x2f6b);
|
|
405
|
+
const waves = [2, 3, 5].map((k) => ({
|
|
406
|
+
k,
|
|
407
|
+
a: (rnd() * 2 - 1) / k,
|
|
408
|
+
p: rnd() * TAU,
|
|
409
|
+
}));
|
|
410
|
+
const offsets = radii.map((_, i) => {
|
|
411
|
+
const a = (i / RES) * TAU;
|
|
412
|
+
let d = 0;
|
|
413
|
+
for (const wave of waves) d += wave.a * Math.sin(wave.k * a + wave.p);
|
|
414
|
+
return d;
|
|
415
|
+
});
|
|
416
|
+
const peak = Math.max(...offsets.map(Math.abs)) || 1;
|
|
417
|
+
return radii.map((r, i) => r * (1 + (WARP * offsets[i]) / peak));
|
|
418
|
+
}
|
|
419
|
+
|
|
394
420
|
const radiiCache = new Map<string, number[]>();
|
|
395
421
|
|
|
396
422
|
/** Ray-scanning the outline is the expensive step; cache by shape key. */
|
|
397
423
|
function radiiFor(cfg: MarkOptions, shape: MarkShape, seed: number): number[] {
|
|
398
424
|
const key = `${
|
|
399
425
|
shape === "poly"
|
|
400
|
-
? `poly|${cfg.sides}|${cfg.corner}|${cfg.rotation}`
|
|
426
|
+
? `poly|${cfg.sides}|${cfg.corner}|${cfg.rotation}|${seed}`
|
|
401
427
|
: shape === "squircle"
|
|
402
|
-
? `squircle|${cfg.squircle}`
|
|
428
|
+
? `squircle|${cfg.squircle}|${seed}`
|
|
403
429
|
: `blob|${cfg.wobble}|${seed}`
|
|
404
430
|
}|${cfg.autofit}`;
|
|
405
431
|
const hit = radiiCache.get(key);
|
|
406
432
|
if (hit) return hit;
|
|
407
|
-
const
|
|
433
|
+
const traced = toRadii(outlineFor(cfg, shape, seed));
|
|
434
|
+
const raw = shape === "blob" ? traced : warpRadii(traced, seed);
|
|
408
435
|
const radii = cfg.autofit ? fitRadii(raw) : raw;
|
|
409
436
|
if (radiiCache.size > 400) radiiCache.clear();
|
|
410
437
|
radiiCache.set(key, radii);
|
|
@@ -835,6 +862,21 @@ const STATE_SHAPE: Record<MarkState, StateShape> = {
|
|
|
835
862
|
},
|
|
836
863
|
};
|
|
837
864
|
|
|
865
|
+
/** What the follower reads while the mark is not speaking. */
|
|
866
|
+
const SILENT: number[] = new Array<number>(MARK_BANDS).fill(0);
|
|
867
|
+
|
|
868
|
+
/**
|
|
869
|
+
* Milliseconds to the next blink, irregular the way a person's are: mostly a
|
|
870
|
+
* couple of seconds apart, now and then a long look, and sometimes a second
|
|
871
|
+
* blink right on the heels of the first.
|
|
872
|
+
*/
|
|
873
|
+
function blinkGap(): number {
|
|
874
|
+
const roll = Math.random();
|
|
875
|
+
if (roll < 0.15) return 180 + Math.random() * 320;
|
|
876
|
+
if (roll < 0.8) return 1200 + Math.random() * 2300;
|
|
877
|
+
return 3500 + Math.random() * 2800;
|
|
878
|
+
}
|
|
879
|
+
|
|
838
880
|
const pointer = { x: 0, y: 0, live: false };
|
|
839
881
|
|
|
840
882
|
/** One passive listener for the whole app, bound when the first mark mounts. */
|
|
@@ -924,6 +966,7 @@ export function BotMark({
|
|
|
924
966
|
[options, outline, varySeed],
|
|
925
967
|
);
|
|
926
968
|
const glowId = `${clipId}-glow`;
|
|
969
|
+
const haloId = `${clipId}-halo`;
|
|
927
970
|
const maskId = `${clipId}-mask`;
|
|
928
971
|
const shadowId = `${clipId}-shadow`;
|
|
929
972
|
const lifeRef = useRef<SVGGElement>(null);
|
|
@@ -933,6 +976,7 @@ export function BotMark({
|
|
|
933
976
|
const eyeLRef = useRef<SVGGElement>(null);
|
|
934
977
|
const eyeRRef = useRef<SVGGElement>(null);
|
|
935
978
|
const glowRef = useRef<SVGPathElement>(null);
|
|
979
|
+
const haloHeadRef = useRef<SVGPathElement>(null);
|
|
936
980
|
const shadowRef = useRef<SVGPathElement>(null);
|
|
937
981
|
const maskHeadRef = useRef<SVGPathElement>(null);
|
|
938
982
|
const rimRef = useRef<SVGPathElement>(null);
|
|
@@ -997,9 +1041,11 @@ export function BotMark({
|
|
|
997
1041
|
const phase = (live.current.shapeSeed % 17) * 0.91;
|
|
998
1042
|
let gx = 0;
|
|
999
1043
|
let gy = 0;
|
|
1000
|
-
let nextBlink = performance.now() +
|
|
1044
|
+
let nextBlink = performance.now() + 600 + blinkGap();
|
|
1001
1045
|
let blinkStart = 0;
|
|
1046
|
+
let blinkLength = 130;
|
|
1002
1047
|
let queuedBlink = 0;
|
|
1048
|
+
const voice = createVoiceFollower();
|
|
1003
1049
|
|
|
1004
1050
|
// STATE_SHAPE values are targets, eased over about 0.6 s.
|
|
1005
1051
|
const shape = { ...STATE_SHAPE.idle };
|
|
@@ -1047,33 +1093,33 @@ export function BotMark({
|
|
|
1047
1093
|
// Audio at 60 fps has little frame-to-frame correlation, so nothing below reads
|
|
1048
1094
|
// level as position: onsets become spring impulses, only the phrase average is tracked.
|
|
1049
1095
|
const spec = st === "speaking" ? live.current.getSpectrum?.() : undefined;
|
|
1096
|
+
// A voice is loud and narrow, so its raw bands hold the mark swollen and
|
|
1097
|
+
// still; each band inside its own recent range moves with the words.
|
|
1098
|
+
const heard = voice.read(spec ?? SILENT, dt);
|
|
1050
1099
|
let specSum = 0;
|
|
1051
1100
|
for (let k = 0; k < MARK_BANDS; k++) {
|
|
1052
|
-
const target =
|
|
1101
|
+
const target = heard.bands[k];
|
|
1053
1102
|
// Bands set the shape, so smooth hard; faster and small marks shimmer.
|
|
1054
1103
|
band[k] += (target - band[k]) * c.bandEase;
|
|
1055
1104
|
bandPhase[k] += dt * (0.05 + k * 0.018) * (k % 2 ? -1 : 1);
|
|
1056
|
-
// Summed before smoothing: the onset detector needs the rise over the moving
|
|
1057
|
-
// average, and smoothed bands erase it.
|
|
1058
1105
|
specSum += target;
|
|
1059
1106
|
}
|
|
1060
1107
|
|
|
1061
|
-
const
|
|
1062
|
-
|
|
1063
|
-
(spec ? Math.min(1, specSum / (MARK_BANDS * 0.45)) : 0);
|
|
1064
|
-
const want = st === "speaking" ? clamp(raw, 0, 1) : 0;
|
|
1108
|
+
const given = live.current.getLevel?.();
|
|
1109
|
+
const want = st === "speaking" ? clamp(given ?? heard.level, 0, 1) : 0;
|
|
1065
1110
|
|
|
1066
1111
|
fastLvl += (want - fastLvl) * 0.3;
|
|
1067
1112
|
midLvl += (want - midLvl) * 0.055;
|
|
1068
1113
|
lvl += (want - lvl) * 0.016;
|
|
1069
1114
|
|
|
1070
|
-
// A syllable is a rise over the recent average
|
|
1115
|
+
// A syllable is a rise over the recent average: the follower's for a
|
|
1116
|
+
// spectrum, this one's for a bare level. The gap must exceed the spring
|
|
1071
1117
|
// period or kicks pile up into a tremble.
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
) {
|
|
1118
|
+
const syllable =
|
|
1119
|
+
given === undefined
|
|
1120
|
+
? heard.onset > 0
|
|
1121
|
+
: fastLvl > midLvl * 1.25 + 0.03 && fastLvl > 0.09;
|
|
1122
|
+
if (syllable && now - lastOnset > 340) {
|
|
1077
1123
|
lastOnset = now;
|
|
1078
1124
|
// Slower springs integrate each impulse longer, so scale the kick with stiffness.
|
|
1079
1125
|
bobVel -= c.punch * Math.min(1, fastLvl * 1.6) * c.bobRate;
|
|
@@ -1196,6 +1242,7 @@ export function BotMark({
|
|
|
1196
1242
|
headRef.current.setAttribute("d", d);
|
|
1197
1243
|
clipRef.current?.setAttribute("d", d);
|
|
1198
1244
|
glowRef.current?.setAttribute("d", d);
|
|
1245
|
+
haloHeadRef.current?.setAttribute("d", d);
|
|
1199
1246
|
shadowRef.current?.setAttribute("d", d);
|
|
1200
1247
|
rimRef.current?.setAttribute("d", d);
|
|
1201
1248
|
maskHeadRef.current?.setAttribute("d", d);
|
|
@@ -1238,12 +1285,14 @@ export function BotMark({
|
|
|
1238
1285
|
} else if (c.blink && now >= nextBlink) {
|
|
1239
1286
|
blinkStart = now;
|
|
1240
1287
|
}
|
|
1288
|
+
// No two blinks quite the same length either.
|
|
1289
|
+
if (blinkStart > 0) blinkLength = 110 + Math.random() * 60;
|
|
1241
1290
|
}
|
|
1242
1291
|
if (blinkStart > 0) {
|
|
1243
|
-
const p = (now - blinkStart) /
|
|
1292
|
+
const p = (now - blinkStart) / blinkLength;
|
|
1244
1293
|
if (p >= 1) {
|
|
1245
1294
|
blinkStart = 0;
|
|
1246
|
-
nextBlink = now + (
|
|
1295
|
+
nextBlink = now + blinkGap() * shape.blink;
|
|
1247
1296
|
} else {
|
|
1248
1297
|
sy = 1 - Math.sin(Math.PI * p) * 0.94;
|
|
1249
1298
|
}
|
|
@@ -1332,6 +1381,29 @@ export function BotMark({
|
|
|
1332
1381
|
<feGaussianBlur stdDeviation={9} />
|
|
1333
1382
|
</filter>
|
|
1334
1383
|
)}
|
|
1384
|
+
{/* The aura is a halo around the silhouette, never a light behind it: the
|
|
1385
|
+
eyes are holes cut out of the head, so anything drawn under it shines
|
|
1386
|
+
straight through them and the face reads as hollow. */}
|
|
1387
|
+
{cfg.glow > 0 && (
|
|
1388
|
+
<mask
|
|
1389
|
+
id={haloId}
|
|
1390
|
+
maskUnits="userSpaceOnUse"
|
|
1391
|
+
// The default region is the view box; the blur reaches past it.
|
|
1392
|
+
x={-PAD * 4}
|
|
1393
|
+
y={-PAD * 4}
|
|
1394
|
+
width={BOX + PAD * 8}
|
|
1395
|
+
height={BOX + PAD * 8}
|
|
1396
|
+
>
|
|
1397
|
+
<rect
|
|
1398
|
+
x={-PAD * 4}
|
|
1399
|
+
y={-PAD * 4}
|
|
1400
|
+
width={BOX + PAD * 8}
|
|
1401
|
+
height={BOX + PAD * 8}
|
|
1402
|
+
fill="#fff"
|
|
1403
|
+
/>
|
|
1404
|
+
<path ref={haloHeadRef} d={headPath} fill="#000" />
|
|
1405
|
+
</mask>
|
|
1406
|
+
)}
|
|
1335
1407
|
{cfg.glow > 0 && (
|
|
1336
1408
|
<filter
|
|
1337
1409
|
id={glowId}
|
|
@@ -1362,6 +1434,7 @@ export function BotMark({
|
|
|
1362
1434
|
d={headPath}
|
|
1363
1435
|
fill={cfg.glowColor ?? "var(--fg)"}
|
|
1364
1436
|
filter={`url(#${glowId})`}
|
|
1437
|
+
mask={`url(#${haloId})`}
|
|
1365
1438
|
opacity={cfg.glowStrength / 100}
|
|
1366
1439
|
/>
|
|
1367
1440
|
)}
|
|
@@ -1411,6 +1484,35 @@ export function BotMark({
|
|
|
1411
1484
|
);
|
|
1412
1485
|
}
|
|
1413
1486
|
|
|
1487
|
+
/**
|
|
1488
|
+
* A mark at rest as bare path data, for a surface React does not draw — the tab's
|
|
1489
|
+
* icon. The silhouette and eyes `<BotMark seed>` draws with no other props, in the
|
|
1490
|
+
* 240-unit box (VIEW_BOX adds the headroom around it).
|
|
1491
|
+
*/
|
|
1492
|
+
export function markAtRest(seed: number | string): {
|
|
1493
|
+
head: string;
|
|
1494
|
+
eyes: [string, string];
|
|
1495
|
+
} {
|
|
1496
|
+
const cfg = MARK_DEFAULTS;
|
|
1497
|
+
const eye = (cx: number, tilt: number) =>
|
|
1498
|
+
eyePath({
|
|
1499
|
+
len: cfg.eyeLen,
|
|
1500
|
+
width: cfg.eyeWidth,
|
|
1501
|
+
bend: cfg.eyeBend,
|
|
1502
|
+
taper: cfg.eyeTaper,
|
|
1503
|
+
tilt,
|
|
1504
|
+
cx,
|
|
1505
|
+
cy: cfg.eyeY,
|
|
1506
|
+
});
|
|
1507
|
+
return {
|
|
1508
|
+
head: radiiToPath(radiiFor(cfg, cfg.shape, hashSeed(seed))),
|
|
1509
|
+
eyes: [
|
|
1510
|
+
eye(CENTER - cfg.eyeGap / 2, cfg.eyeTilt),
|
|
1511
|
+
eye(CENTER + cfg.eyeGap / 2, cfg.eyeTilt + cfg.eyeSkew),
|
|
1512
|
+
],
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1414
1516
|
/** Mark for bots as a group (the settings section icon). No color or shape of its own:
|
|
1415
1517
|
* `currentColor` and the default silhouette, since it names the room, not a bot. */
|
|
1416
1518
|
export function BotsMark({
|