thursday-agent 0.1.2 → 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 -1
- package/.next/build-manifest.json +3 -3
- package/.next/prerender-manifest.json +5 -5
- package/.next/routes-manifest.json +27 -3
- 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.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/build-manifest.json +12 -0
- package/.next/server/app/api/bot/memory/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/bot/memory/route.js +14 -0
- package/.next/server/app/api/bot/memory/route.js.map +5 -0
- package/.next/server/app/api/bot/memory/route.js.nft.json +1 -0
- package/.next/server/app/api/bot/memory/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/bot/route.js +7 -7
- package/.next/server/app/api/bot/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/task/[id]/route.js +9 -9
- 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 +6 -6
- 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 +10 -9
- 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 +5 -5
- package/.next/server/app/api/mcp/[name]/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/oauth/callback/route.js +6 -6
- package/.next/server/app/api/mcp/oauth/callback/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/route.js +5 -5
- package/.next/server/app/api/mcp/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/tools/route.js +5 -5
- 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 +7 -8
- 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 +16 -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 +66 -77
- package/.next/server/app/page.js +11 -11
- 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 -1
- 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 +3 -0
- 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 +10 -0
- 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/{_1hzrsvg._.js → _0-73of8._.js} +6 -6
- package/.next/server/chunks/{_1cbk7ms._.js → _0-fjql4._.js} +10 -10
- package/.next/server/chunks/_00xh_q0._.js +27 -0
- package/.next/server/chunks/{_0y8is-s._.js → _03a0897._.js} +1 -1
- package/.next/server/chunks/{_0pppbb5._.js → _03kp0jp._.js} +166 -35
- 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 +74 -44
- package/.next/server/chunks/{_0kl37zl._.js → _0ioz3nd._.js} +1 -1
- package/.next/server/chunks/_0iwfby5._.js +3 -0
- package/.next/server/chunks/{_0hin38_._.js → _0khihjk._.js} +5 -5
- package/.next/server/chunks/_0mecv0f._.js +26 -0
- package/.next/server/chunks/_0mh8j4m._.js +3 -0
- package/.next/server/chunks/{_1icgbi8._.js → _0n74zsf._.js} +2 -2
- package/.next/server/chunks/_0obojvv._.js +3 -0
- package/.next/server/chunks/_0recufk._.js +49 -0
- package/.next/server/chunks/{_1t8fv7b._.js → _0rnf-6n._.js} +2 -2
- package/.next/server/chunks/_0sb7tlz._.js +3 -0
- package/.next/server/chunks/{_0jgm3b8._.js → _0u10wmw._.js} +6 -6
- package/.next/server/chunks/{_12nr4xc._.js → _0u4aiy2._.js} +2 -2
- 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/{_0ks6_sg._.js → _18-1wcc._.js} +6 -6
- package/.next/server/chunks/_1_5mnq4._.js +18 -0
- package/.next/server/chunks/{_0tzfesa._.js → _1_9mjx-._.js} +1 -1
- package/.next/server/chunks/_1d0j961._.js +6 -0
- package/.next/server/chunks/_1d5zmy8._.js +1 -1
- package/.next/server/chunks/_1drgfx4._.js +18 -0
- package/.next/server/chunks/{_1skgy7b._.js → _1e48czp._.js} +5 -5
- 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/{_10qam6o._.js → _1kg70wc._.js} +5 -5
- 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 +1 -1
- package/.next/server/chunks/features_memory_1uqvksn._.js +6 -0
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0tf290y._.js → node_modules_consola_dist_chunks_prompt_mjs_0i7cvml._.js} +1 -1
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0228f43._.js → node_modules_consola_dist_chunks_prompt_mjs_0icqaqw._.js} +1 -1
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0ir-vo2._.js → node_modules_consola_dist_chunks_prompt_mjs_0j61-ma._.js} +1 -1
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0otw8ef._.js → node_modules_consola_dist_chunks_prompt_mjs_0r28-sm._.js} +1 -1
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_0v_7drq._.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_1xoq2rl._.js +3 -0
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1y49572._.js +3 -0
- package/.next/server/chunks/ssr/[externals]__0dizgek._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__09q9med._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0_69wqr._.js +3 -0
- 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]__1cyjywu._.js +3 -0
- 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/_0lu1n_x._.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/{_0gd4vaa._.js → _0u2jv_x._.js} +94 -58
- 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/{_0q_2f2x._.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/{_0lc_7c8._.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/{_19_o9d-._.js → _1vi_uo7._.js} +2 -2
- package/.next/server/chunks/ssr/_1vyd1ji._.js +3 -0
- package/.next/server/chunks/ssr/_1xvgn77._.js +3 -0
- 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/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_1d-is1o._.js → node_modules_063xwpp._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1ub40gk._.js → node_modules_0bvraul._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1nhw9qg._.js → node_modules_0eu1va6._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0xwx4u-._.js → node_modules_0h50b8n._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_043-236._.js → node_modules_0h5hnr1._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1t0y5jt._.js → node_modules_0h7u60n._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1k29j1j._.js → node_modules_0lm7_vm._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_16i4o_3._.js → node_modules_0mwym0g._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1y8_3vg._.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_108wd2o._.js → node_modules_0y_cded._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1jatz7w._.js → node_modules_0z5tsu8._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0ls96je._.js → node_modules_0zp9r92._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_06kvvuo._.js → node_modules_1-hc6uv._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1_588_4._.js → node_modules_113tz0l._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0ipn1ho._.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_0_j7-g4._.js → node_modules_1_n3ztw._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_20ynonm._.js → node_modules_1bc7gu8._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1hn_sd5._.js → node_modules_1bgb6-f._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0bue6gy._.js → node_modules_1h4a4pp._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0fbw5-u._.js → node_modules_1i7u1ia._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1ee8gav._.js → node_modules_1juq3hs._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0_fvxnt._.js → node_modules_1l0qtdu._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_13f2md6._.js → node_modules_1t9cyee._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0y2-av4._.js → node_modules_1uuj9x1._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_00d6675._.js → node_modules_1vqb6po._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1v0077l._.js → node_modules_1xx28h1._.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_0d3skpl._.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_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_consola_dist_chunks_prompt_mjs_05cwqv2._.js +47 -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 +67 -78
- 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/1dkjg87pxk0n9.js +28 -0
- package/.next/static/chunks/1ekc7ul556kbl.js +1 -0
- package/.next/static/chunks/{1khakdu_iuedc.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/{2ihkf6l2jowo9.js → 280t0_e99i276.js} +3 -3
- 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/3786h5vayt80-.js +28 -0
- package/.next/static/chunks/{3aquirz5h71da.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.client.ts +24 -0
- package/app/api/events/app-event.server.ts +45 -0
- package/app/api/events/app-event.ts +42 -0
- package/app/api/llm-model/catalog/route.ts +12 -0
- package/app/api/llm-model/credits/route.ts +11 -0
- package/app/api/llm-model/route.ts +26 -0
- 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 +159 -0
- package/app/api/thursday/tool-call/route.ts +67 -0
- package/bin/thursday.mjs +3 -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 +55 -0
- package/components/ui/text-type.tsx +213 -0
- package/components/ui/theme-sync.tsx +9 -0
- package/database/migrations/{20260908030742_rainy_marvel_zombies → 20260910173049_numerous_spirit}/migration.sql +30 -33
- package/database/migrations/{20260908030742_rainy_marvel_zombies → 20260910173049_numerous_spirit}/snapshot.json +124 -145
- package/database/migrations/20260911030320_opposite_jack_power/migration.sql +1 -0
- package/database/migrations/20260911030320_opposite_jack_power/snapshot.json +1158 -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/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 +105 -39
- package/features/ai/model.schema.ts +232 -34
- package/features/ai/model.ts +169 -24
- package/features/ai/prompts/bot.prompt.ts +245 -59
- package/features/ai/prompts/memory-edit.prompt.ts +33 -0
- package/features/ai/prompts/prompt-helper.ts +75 -30
- package/features/ai/prompts/thursday.prompt.ts +42 -17
- package/features/ai/tools/bot.tool.ts +32 -30
- package/features/ai/tools/connected.ts +17 -4
- package/features/ai/tools/mcp.tool.ts +5 -5
- package/features/ai/tools/memory.tool.ts +142 -74
- package/features/ai/tools/search.tool.ts +136 -47
- package/features/ai/tools/studio.tool.ts +5 -5
- package/features/ai/tools/tool-name.ts +2 -3
- package/features/ai/tools/workspace.tool.ts +33 -4
- package/features/bot/bot.action.ts +24 -7
- package/features/bot/bot.memory.ts +89 -0
- package/features/bot/bot.query.ts +41 -6
- package/features/bot/bot.run.ts +516 -210
- package/features/bot/bot.runner.ts +463 -93
- package/features/bot/bot.schema.ts +84 -7
- package/features/bot/bot.seed.ts +89 -16
- package/features/bot/components/bot-mark.tsx +122 -20
- package/features/bot/components/bot-room.tsx +519 -391
- package/features/bot/components/bot-setting.tsx +832 -341
- 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 +233 -33
- 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 +86 -79
- package/features/memory/memory.action.ts +3 -43
- package/features/memory/memory.edit.ts +87 -0
- package/features/memory/memory.query.ts +33 -44
- package/features/memory/memory.schema.ts +50 -97
- package/features/settings/components/settings.tsx +2 -0
- package/features/settings/settings.alert.ts +3 -1
- package/features/skills/components/skills-setting.tsx +9 -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 +230 -13
- 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/limits.ts +1 -1
- package/lib/oauth-page.ts +27 -0
- package/lib/realtime/openai.ts +2 -2
- package/lib/realtime/realtime.driver.ts +34 -1
- package/lib/realtime/realtime.protocol.ts +7 -1
- package/lib/realtime/realtime.tap.ts +122 -0
- package/lib/sandbox.ts +60 -29
- package/lib/tokens.ts +19 -0
- package/package.json +1 -1
- package/skills/browser/SKILL.md +55 -34
- package/.next/server/app/api/memory/tidy/route/app-paths-manifest.json +0 -3
- package/.next/server/app/api/memory/tidy/route.js +0 -23
- package/.next/server/app/api/memory/tidy/route.js.nft.json +0 -1
- 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]__02kt8yn._.js +0 -10
- 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]__0dupqt7._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__0h59di-._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__0j_n5l3._.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]__0phdgco._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__0qjqtwo._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__0t_dqaj._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0wvgrsl._.js +0 -3
- 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]__10qshki._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__113fqg5._.js +0 -12
- package/.next/server/chunks/[root-of-the-server]__11w_7_3._.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_0q_9w._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1_aoyr9._.js +0 -3
- 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]__1dow0bz._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1e-ci13._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1gmn1cs._.js +0 -10
- package/.next/server/chunks/[root-of-the-server]__1lr7j9t._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__1mzp_el._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1txedx6._.js +0 -8
- package/.next/server/chunks/[root-of-the-server]__1u_qvc7._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1zgpz1w._.js +0 -3
- 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/_0zmestz._.js +0 -55
- package/.next/server/chunks/_0zuzmt_._.js +0 -39
- package/.next/server/chunks/_12bbv5i._.js +0 -96
- package/.next/server/chunks/_15q1pig._.js +0 -3
- package/.next/server/chunks/_1drv2et._.js +0 -36
- package/.next/server/chunks/_1ic3bew._.js +0 -5
- package/.next/server/chunks/_1mxn_lb._.js +0 -3
- package/.next/server/chunks/_1uj4_io._.js +0 -10
- package/.next/server/chunks/_1x7p319._.js +0 -6
- package/.next/server/chunks/_1yldgo4._.js +0 -55
- package/.next/server/chunks/_2050gwn._.js +0 -255
- package/.next/server/chunks/_next-internal_server_app_api_memory_tidy_route_actions_0_cfuom.js +0 -3
- package/.next/server/chunks/features_0scmr4i._.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_0vorg6w._.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_1vmaq9f._.js +0 -3
- package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1vwbnq_._.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]__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]__0dfwvr4._.js +0 -4
- package/.next/server/chunks/ssr/[root-of-the-server]__0iuoa-r._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__1-mnhyd._.js +0 -90
- 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/_0-mc7ro._.js +0 -3
- package/.next/server/chunks/ssr/_01p5r6-._.js +0 -3
- package/.next/server/chunks/ssr/_09-0k3z._.js +0 -3
- package/.next/server/chunks/ssr/_0ay6b01._.js +0 -3
- package/.next/server/chunks/ssr/_0cvjbuz._.js +0 -3
- package/.next/server/chunks/ssr/_0fafgvl._.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/_0nso8tp._.js +0 -3
- package/.next/server/chunks/ssr/_0sf1isi._.js +0 -3
- package/.next/server/chunks/ssr/_0ti51xc._.js +0 -3
- package/.next/server/chunks/ssr/_0y84qo6._.js +0 -3
- package/.next/server/chunks/ssr/_10uvw3l._.js +0 -3
- package/.next/server/chunks/ssr/_1dcxzqe._.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/_1jbv7m1._.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/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/server/chunks/ssr/node_modules_zod_v4_classic_external_15fmmz3.js +0 -55
- package/.next/static/chunks/0f9sq-7q_yxyl.js +0 -1
- package/.next/static/chunks/0tydnn2kybibk.js +0 -1
- package/.next/static/chunks/0yysv1gzin17t.js +0 -1
- package/.next/static/chunks/1fr5m75pwtyic.js +0 -1
- package/.next/static/chunks/1gy3bbv7usnjo.js +0 -1
- package/.next/static/chunks/1keib_gt066gc.js +0 -1
- package/.next/static/chunks/1ytnip8wdng93.js +0 -1
- package/.next/static/chunks/21rk8w7bda-su.js +0 -1
- package/.next/static/chunks/247d-r95j8xs0.js +0 -1
- package/.next/static/chunks/28g42tw8tsvhd.js +0 -1
- package/.next/static/chunks/29gyb3nubj51j.js +0 -53
- package/.next/static/chunks/2cwjgkt_hoyhj.js +0 -22
- package/.next/static/chunks/2fq_86jvq9ymq.js +0 -2
- package/.next/static/chunks/2rbf1fpd8n80-.js +0 -22
- package/.next/static/chunks/2ukj8k69y89um.js +0 -1
- package/.next/static/chunks/2uxum7qbyeyxd.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/3xxa2j56-f3nm.js +0 -3
- package/.next/static/chunks/3y40riycdip-i.js +0 -1
- package/.next/static/chunks/45a6zbtkelph9.js +0 -1
- package/.next/static/media/favicon.2vob68tjqpejf.ico +0 -0
- package/features/ai/prompts/memory-tidy.prompt.ts +0 -123
- package/features/ai/tools/tidy.tool.ts +0 -22
- 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/{memory/tidy → bot/memory/files}/route/build-manifest.json +0 -0
- /package/.next/server/app/api/{memory/tidy → bot/memory/files}/route/server-reference-manifest.json +0 -0
- /package/.next/server/app/api/{memory/tidy → bot/memory/files}/route.js.map +0 -0
- /package/.next/static/{X34BUd9bfdH27ripuwwBm → x1cCqY0kWpR_VcYx_lWO5}/_buildManifest.js +0 -0
- /package/.next/static/{X34BUd9bfdH27ripuwwBm → x1cCqY0kWpR_VcYx_lWO5}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{X34BUd9bfdH27ripuwwBm → x1cCqY0kWpR_VcYx_lWO5}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Wake-word listener built on the browser's Web Speech API (no realtime socket
|
|
7
|
+
* while idle). It is a transcriber, not a KWS engine, so matching is fuzzy.
|
|
8
|
+
* Chrome streams idle audio to Google; Firefox has no recognizer (onError only).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** lib.dom types the result objects but not the recognizer itself. */
|
|
12
|
+
type SpeechRecognitionLike = {
|
|
13
|
+
lang: string;
|
|
14
|
+
continuous: boolean;
|
|
15
|
+
interimResults: boolean;
|
|
16
|
+
maxAlternatives: number;
|
|
17
|
+
start(): void;
|
|
18
|
+
stop(): void;
|
|
19
|
+
abort(): void;
|
|
20
|
+
onresult:
|
|
21
|
+
| ((event: {
|
|
22
|
+
resultIndex: number;
|
|
23
|
+
results: SpeechRecognitionResultList;
|
|
24
|
+
}) => void)
|
|
25
|
+
| null;
|
|
26
|
+
onerror: ((event: { error: string }) => void) | null;
|
|
27
|
+
onend: (() => void) | null;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type SpeechRecognitionCtor = new () => SpeechRecognitionLike;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Default recognizer language. English keeps one spelling to compare against
|
|
34
|
+
* regardless of the speaker's accent; callers with a non-English name pass
|
|
35
|
+
* `lang` and `phrases` together.
|
|
36
|
+
*/
|
|
37
|
+
export const WAKE_LANG = "en-US";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Allowed edit distance per word as a fraction of its length ("hey" = 1 edit,
|
|
41
|
+
* "thursday" = 3). Lower misses accents; higher lets ordinary speech wake.
|
|
42
|
+
*/
|
|
43
|
+
const WAKE_TOLERANCE = 0.34;
|
|
44
|
+
|
|
45
|
+
/** Alternatives per result; the second-ranked guess is often the right one. */
|
|
46
|
+
const ALTERNATIVES = 3;
|
|
47
|
+
|
|
48
|
+
/** Delay before restarting after `onend`, so error loops do not spin. */
|
|
49
|
+
const RESTART_MS = 300;
|
|
50
|
+
/** Backoff cap while the recognition service is unreachable. */
|
|
51
|
+
const RESTART_MAX_MS = 10_000;
|
|
52
|
+
|
|
53
|
+
/** Errors a restart cannot fix. */
|
|
54
|
+
const FATAL: Record<string, string> = {
|
|
55
|
+
"not-allowed": "The microphone is not allowed",
|
|
56
|
+
"service-not-allowed": "The microphone is not allowed",
|
|
57
|
+
"audio-capture": "There is no microphone",
|
|
58
|
+
"language-not-supported": "This browser cannot listen in that language",
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/** Case and punctuation vary per recognizer; word boundaries do not. */
|
|
62
|
+
const normalize = (text: string) =>
|
|
63
|
+
text
|
|
64
|
+
.toLowerCase()
|
|
65
|
+
.replace(/[^a-z0-9 ]/g, "")
|
|
66
|
+
.split(/\s+/)
|
|
67
|
+
.filter(Boolean);
|
|
68
|
+
|
|
69
|
+
/** Plain Levenshtein distance between two words. */
|
|
70
|
+
function distance(heard: string, want: string): number {
|
|
71
|
+
let previous = Array.from({ length: heard.length + 1 }, (_, index) => index);
|
|
72
|
+
let current = new Array<number>(heard.length + 1).fill(0);
|
|
73
|
+
|
|
74
|
+
for (let row = 1; row <= want.length; row++) {
|
|
75
|
+
current[0] = row;
|
|
76
|
+
for (let column = 1; column <= heard.length; column++) {
|
|
77
|
+
const substitute =
|
|
78
|
+
previous[column - 1] + (want[row - 1] === heard[column - 1] ? 0 : 1);
|
|
79
|
+
current[column] = Math.min(
|
|
80
|
+
substitute,
|
|
81
|
+
previous[column] + 1,
|
|
82
|
+
current[column - 1] + 1,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
[previous, current] = [current, previous];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return previous[heard.length];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Within `budget` edits; the length check short-circuits most words. */
|
|
92
|
+
const near = (heard: string, want: string, budget: number) =>
|
|
93
|
+
Math.abs(heard.length - want.length) <= budget &&
|
|
94
|
+
distance(heard, want) <= budget;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Whether `want[from..]` is spoken contiguously starting at `heard[at]`.
|
|
98
|
+
* One wanted word may span two heard words ("there's day" for "thursday").
|
|
99
|
+
*/
|
|
100
|
+
function saidAt(
|
|
101
|
+
heard: readonly string[],
|
|
102
|
+
at: number,
|
|
103
|
+
want: readonly string[],
|
|
104
|
+
from: number,
|
|
105
|
+
tolerance: number,
|
|
106
|
+
): boolean {
|
|
107
|
+
if (from === want.length) return true;
|
|
108
|
+
if (at >= heard.length) return false;
|
|
109
|
+
|
|
110
|
+
const word = want[from];
|
|
111
|
+
// Never 0, or "hay" for "hey" fails.
|
|
112
|
+
const budget = Math.max(1, Math.round(word.length * tolerance));
|
|
113
|
+
|
|
114
|
+
if (
|
|
115
|
+
near(heard[at], word, budget) &&
|
|
116
|
+
saidAt(heard, at + 1, want, from + 1, tolerance)
|
|
117
|
+
) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
return (
|
|
121
|
+
at + 1 < heard.length &&
|
|
122
|
+
near(heard[at] + heard[at + 1], word, budget) &&
|
|
123
|
+
saidAt(heard, at + 2, want, from + 1, tolerance)
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Whether `heard` contains `want` as one contiguous phrase. Scoring is per
|
|
129
|
+
* word, not over the joined string, so every word must be present and adjacent.
|
|
130
|
+
*/
|
|
131
|
+
function contains(
|
|
132
|
+
heard: readonly string[],
|
|
133
|
+
want: readonly string[],
|
|
134
|
+
tolerance: number,
|
|
135
|
+
): boolean {
|
|
136
|
+
for (let at = 0; at < heard.length; at++) {
|
|
137
|
+
if (saidAt(heard, at, want, 0, tolerance)) return true;
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
type WakeOptions = {
|
|
143
|
+
/** Releases the microphone when false; must be false while a call is open. */
|
|
144
|
+
enabled: boolean;
|
|
145
|
+
/** Receives the raw transcript that matched. */
|
|
146
|
+
onWake: (heard: string) => void;
|
|
147
|
+
/** Phrases to listen for, matched fuzzily. */
|
|
148
|
+
phrases: readonly string[];
|
|
149
|
+
lang?: string;
|
|
150
|
+
/** See WAKE_TOLERANCE. */
|
|
151
|
+
tolerance?: number;
|
|
152
|
+
/** Unrecoverable failures only (denied or missing microphone). */
|
|
153
|
+
onError?: (reason: string) => void;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export function useWakeWord({
|
|
157
|
+
enabled,
|
|
158
|
+
onWake,
|
|
159
|
+
phrases,
|
|
160
|
+
lang = WAKE_LANG,
|
|
161
|
+
tolerance = WAKE_TOLERANCE,
|
|
162
|
+
onError,
|
|
163
|
+
}: WakeOptions) {
|
|
164
|
+
// Read at result time so re-renders and phrase changes do not restart the
|
|
165
|
+
// recognizer; only `lang` must be set before start.
|
|
166
|
+
const latest = useRef({ onWake, onError, phrases, tolerance });
|
|
167
|
+
latest.current = { onWake, onError, phrases, tolerance };
|
|
168
|
+
|
|
169
|
+
useEffect(() => {
|
|
170
|
+
if (!enabled) return;
|
|
171
|
+
|
|
172
|
+
const global = window as unknown as Record<string, unknown>;
|
|
173
|
+
const Ctor = (global.SpeechRecognition ?? global.webkitSpeechRecognition) as
|
|
174
|
+
| SpeechRecognitionCtor
|
|
175
|
+
| undefined;
|
|
176
|
+
|
|
177
|
+
if (!Ctor) {
|
|
178
|
+
latest.current.onError?.("This browser cannot listen for a wake word");
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const recognition = new Ctor();
|
|
183
|
+
// Latched off on cleanup so late events cannot revive the recognizer.
|
|
184
|
+
let alive = true;
|
|
185
|
+
// Off from a match until the restart that clears the transcript.
|
|
186
|
+
let armed = true;
|
|
187
|
+
let restart: ReturnType<typeof setTimeout> | null = null;
|
|
188
|
+
let delay = RESTART_MS;
|
|
189
|
+
|
|
190
|
+
recognition.lang = lang;
|
|
191
|
+
// Interim results are enough; waiting for isFinal loses a beat.
|
|
192
|
+
recognition.continuous = true;
|
|
193
|
+
recognition.interimResults = true;
|
|
194
|
+
recognition.maxAlternatives = ALTERNATIVES;
|
|
195
|
+
|
|
196
|
+
const listen = () => {
|
|
197
|
+
if (!alive) return;
|
|
198
|
+
armed = true;
|
|
199
|
+
try {
|
|
200
|
+
recognition.start();
|
|
201
|
+
} catch {
|
|
202
|
+
// Already running (InvalidStateError).
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
recognition.onresult = ({ resultIndex, results }) => {
|
|
207
|
+
if (!alive || !armed) return;
|
|
208
|
+
delay = RESTART_MS;
|
|
209
|
+
|
|
210
|
+
const { phrases, tolerance, onWake } = latest.current;
|
|
211
|
+
const targets = phrases.map(normalize).filter((words) => words.length);
|
|
212
|
+
|
|
213
|
+
// Results before resultIndex are final and already seen.
|
|
214
|
+
for (let index = resultIndex; index < results.length; index++) {
|
|
215
|
+
const result = results[index];
|
|
216
|
+
for (let rank = 0; rank < result.length; rank++) {
|
|
217
|
+
const heard = result[rank].transcript;
|
|
218
|
+
const words = normalize(heard);
|
|
219
|
+
if (!targets.some((target) => contains(words, target, tolerance))) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Abort before notifying: the transcript keeps growing and would
|
|
224
|
+
// match again on the next event. `onend` restarts unless the caller
|
|
225
|
+
// has dropped `enabled` by then.
|
|
226
|
+
armed = false;
|
|
227
|
+
recognition.abort();
|
|
228
|
+
onWake(heard.trim());
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
recognition.onerror = ({ error }) => {
|
|
235
|
+
const reason = FATAL[error];
|
|
236
|
+
if (reason) {
|
|
237
|
+
alive = false;
|
|
238
|
+
latest.current.onError?.(reason);
|
|
239
|
+
} else if (error === "network") {
|
|
240
|
+
delay = Math.min(delay * 2, RESTART_MAX_MS);
|
|
241
|
+
}
|
|
242
|
+
// no-speech and aborted are normal for this API.
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// Chrome ends a silent session after about a minute; keep restarting.
|
|
246
|
+
recognition.onend = () => {
|
|
247
|
+
if (!alive) return;
|
|
248
|
+
restart = setTimeout(listen, delay);
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
listen();
|
|
252
|
+
|
|
253
|
+
return () => {
|
|
254
|
+
alive = false;
|
|
255
|
+
if (restart) clearTimeout(restart);
|
|
256
|
+
recognition.onresult = null;
|
|
257
|
+
recognition.onerror = null;
|
|
258
|
+
recognition.onend = null;
|
|
259
|
+
recognition.abort();
|
|
260
|
+
};
|
|
261
|
+
}, [enabled, lang]);
|
|
262
|
+
}
|
package/lib/limits.ts
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The page an OAuth redirect lands on: a detour tab that says how it went and closes itself
|
|
3
|
+
* when it went well. Every sign-in that comes back through a browser draws this one.
|
|
4
|
+
*/
|
|
5
|
+
export function oauthPage(
|
|
6
|
+
title: string,
|
|
7
|
+
detail: string,
|
|
8
|
+
{ autoClose = false }: { autoClose?: boolean } = {},
|
|
9
|
+
): string {
|
|
10
|
+
return `<!doctype html>
|
|
11
|
+
<meta charset="utf-8">
|
|
12
|
+
<title>${escapeHtml(title)}</title>
|
|
13
|
+
<body style="font-family: ui-monospace, monospace; display: grid; place-items: center; min-height: 100dvh; margin: 0">
|
|
14
|
+
<div style="text-align: center; max-width: 28rem; padding: 1.5rem">
|
|
15
|
+
<p style="font-size: 1.125rem; margin: 0 0 .5rem">${escapeHtml(title)}</p>
|
|
16
|
+
<p style="color: #666; font-size: .875rem; margin: 0">${escapeHtml(detail)}</p>
|
|
17
|
+
</div>
|
|
18
|
+
${autoClose ? "<script>setTimeout(() => window.close(), 900)</script>" : ""}
|
|
19
|
+
</body>`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function escapeHtml(text: string) {
|
|
23
|
+
return text
|
|
24
|
+
.replaceAll("&", "&")
|
|
25
|
+
.replaceAll("<", "<")
|
|
26
|
+
.replaceAll(">", ">");
|
|
27
|
+
}
|
package/lib/realtime/openai.ts
CHANGED
|
@@ -6,8 +6,8 @@ import { createWebRtcTransport } from "./realtime.transport";
|
|
|
6
6
|
|
|
7
7
|
const CALLS_URL = "https://api.openai.com/v1/realtime/calls";
|
|
8
8
|
|
|
9
|
-
/** Streams user transcripts as recognized;
|
|
10
|
-
const TRANSCRIPTION_MODEL = "gpt-
|
|
9
|
+
/** Streams user transcripts as recognized; the file models report only after the turn ends. */
|
|
10
|
+
const TRANSCRIPTION_MODEL = "gpt-live-transcribe";
|
|
11
11
|
|
|
12
12
|
export const createOpenAiSession = realtimeSession({
|
|
13
13
|
transport: ({ credential, audio, on }) =>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logger } from "@/lib/logger";
|
|
1
2
|
import { errorToString } from "@/lib/utils";
|
|
2
3
|
import type {
|
|
3
4
|
RealtimeFunctionTool,
|
|
@@ -85,6 +86,9 @@ export function realtimeSession(
|
|
|
85
86
|
} | null = null;
|
|
86
87
|
let ackTimer: ReturnType<typeof setTimeout> | null = null;
|
|
87
88
|
|
|
89
|
+
/** Event types this session has already noted as unhandled (see `default`). */
|
|
90
|
+
const seenUnhandled = new Set<string>();
|
|
91
|
+
|
|
88
92
|
const turns: Turn[] = [];
|
|
89
93
|
const slot = new Map<string, number>();
|
|
90
94
|
const finalized = new Set<string>();
|
|
@@ -339,7 +343,25 @@ export function realtimeSession(
|
|
|
339
343
|
|
|
340
344
|
case "conversation.item.input_audio_transcription.completed":
|
|
341
345
|
write(event.item_id, "user", event.transcript ?? "", "replace");
|
|
342
|
-
|
|
346
|
+
// Not always the end of the turn. xAI sends this while the user is
|
|
347
|
+
// still speaking, each time with a longer transcript and `in_progress`
|
|
348
|
+
// on it; finishing on the first froze the turn at its opening words
|
|
349
|
+
// and every later transcript was written but never reported (`write`
|
|
350
|
+
// reports only what is not finalized). A provider that sends it once
|
|
351
|
+
// omits the field, so anything but `in_progress` ends the turn.
|
|
352
|
+
if (event.status !== "in_progress") finish(event.item_id);
|
|
353
|
+
break;
|
|
354
|
+
|
|
355
|
+
// A turn nobody could read. Without this the item simply never gets
|
|
356
|
+
// words, and an item with no words is not reported at all (`report`) —
|
|
357
|
+
// the turn disappears from the transcript, and every reader downstream
|
|
358
|
+
// sees a conversation the user did not have.
|
|
359
|
+
case "conversation.item.input_audio_transcription.failed":
|
|
360
|
+
on.warn(
|
|
361
|
+
event.error?.message
|
|
362
|
+
? `That turn could not be transcribed: ${event.error.message}`
|
|
363
|
+
: "That turn could not be transcribed.",
|
|
364
|
+
);
|
|
343
365
|
break;
|
|
344
366
|
|
|
345
367
|
case "response.output_audio_transcript.delta":
|
|
@@ -388,6 +410,17 @@ export function realtimeSession(
|
|
|
388
410
|
finish(event.item_id);
|
|
389
411
|
onToolCall(event);
|
|
390
412
|
break;
|
|
413
|
+
|
|
414
|
+
default:
|
|
415
|
+
// Providers speak this protocol with their own additions, and a case
|
|
416
|
+
// that is missing here is invisible: the event is dropped and the
|
|
417
|
+
// conversation quietly loses whatever it carried. Once per type per
|
|
418
|
+
// session, so a busy stream cannot drown the log.
|
|
419
|
+
if (!seenUnhandled.has(event.type)) {
|
|
420
|
+
seenUnhandled.add(event.type);
|
|
421
|
+
logger.debug(`realtime: no case for ${event.type}`);
|
|
422
|
+
}
|
|
423
|
+
break;
|
|
391
424
|
}
|
|
392
425
|
settle();
|
|
393
426
|
};
|
|
@@ -46,7 +46,7 @@ export type OpenAiSessionConfig = {
|
|
|
46
46
|
audio?: {
|
|
47
47
|
input?: {
|
|
48
48
|
format?: RealtimeAudioFormat;
|
|
49
|
-
/** Null turns transcription off.
|
|
49
|
+
/** Null turns transcription off. A file model reports only at turn end; `gpt-live-transcribe` streams deltas. */
|
|
50
50
|
transcription?: {
|
|
51
51
|
model: string;
|
|
52
52
|
language?: string;
|
|
@@ -308,6 +308,12 @@ export type RealtimeServerEvent =
|
|
|
308
308
|
type: "conversation.item.input_audio_transcription.completed";
|
|
309
309
|
item_id: string;
|
|
310
310
|
transcript: string;
|
|
311
|
+
/**
|
|
312
|
+
* xAI sends this event repeatedly while the turn is still being
|
|
313
|
+
* transcribed, each time with more of it and `in_progress` here; only the
|
|
314
|
+
* last carries `completed`. OpenAI sends it once and omits the field.
|
|
315
|
+
*/
|
|
316
|
+
status?: "in_progress" | "completed" | string;
|
|
311
317
|
}
|
|
312
318
|
/** OpenAI only. */
|
|
313
319
|
| {
|
|
@@ -131,3 +131,125 @@ export function createAudioTap(): RealtimeAudio & {
|
|
|
131
131
|
},
|
|
132
132
|
};
|
|
133
133
|
}
|
|
134
|
+
|
|
135
|
+
/** What a face moves with on one frame (createVoiceFollower). */
|
|
136
|
+
export type VoiceFrame = {
|
|
137
|
+
/** How far into its own range the voice is now, 0..1; 0 in silence. */
|
|
138
|
+
level: number;
|
|
139
|
+
/** `level` over about half a second: the phrase rather than the syllable. */
|
|
140
|
+
phrase: number;
|
|
141
|
+
/** Strength of a syllable that started on this frame; 0 when none did. */
|
|
142
|
+
onset: number;
|
|
143
|
+
/** Each band inside its own range, low to high; one the voice never reaches stays 0. */
|
|
144
|
+
bands: number[];
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/** Seconds, except where a line says otherwise. */
|
|
148
|
+
const FOLLOW = {
|
|
149
|
+
/** A band's ceiling jumps to a new peak and sinks back over `ceilRelease`. */
|
|
150
|
+
ceilAttack: 0.03,
|
|
151
|
+
ceilRelease: 2.5,
|
|
152
|
+
/** Its floor drops to the quiet between syllables and creeps back up over `floorRise`. */
|
|
153
|
+
floorFall: 0.12,
|
|
154
|
+
floorRise: 3,
|
|
155
|
+
/** A band whose range is narrower than this (0..1) carries no shape. */
|
|
156
|
+
minSpan: 0.12,
|
|
157
|
+
/** Mean band level (0..1) across which silence turns into voice. */
|
|
158
|
+
quietBelow: 0.03,
|
|
159
|
+
voicedAbove: 0.12,
|
|
160
|
+
fast: 0.05,
|
|
161
|
+
mid: 0.22,
|
|
162
|
+
phrase: 0.45,
|
|
163
|
+
/** A syllable: the fast level rises this far over the mid one, past `onsetMin`, at most once per `onsetGap`. */
|
|
164
|
+
onsetRise: 0.1,
|
|
165
|
+
onsetMin: 0.3,
|
|
166
|
+
onsetGap: 0.12,
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Reads the voice's bands the way a face needs them. A voice is loud and narrow:
|
|
171
|
+
* its bands sit near the top and lean the same way for a whole sentence, so a
|
|
172
|
+
* face driven by them directly stays swollen and still. Here each band is
|
|
173
|
+
* followed inside its own recent range — the quiet between syllables is its
|
|
174
|
+
* floor, the syllables its ceiling — so the face moves with the words whatever
|
|
175
|
+
* the voice or its volume. One per face; it keeps state between frames.
|
|
176
|
+
*/
|
|
177
|
+
export function createVoiceFollower() {
|
|
178
|
+
const ceil = new Array<number>(SPECTRUM_BANDS).fill(0);
|
|
179
|
+
const floor = new Array<number>(SPECTRUM_BANDS).fill(0);
|
|
180
|
+
const frame: VoiceFrame = {
|
|
181
|
+
level: 0,
|
|
182
|
+
phrase: 0,
|
|
183
|
+
onset: 0,
|
|
184
|
+
bands: new Array<number>(SPECTRUM_BANDS).fill(0),
|
|
185
|
+
};
|
|
186
|
+
let fast = 0;
|
|
187
|
+
let mid = 0;
|
|
188
|
+
let sinceOnset = Number.POSITIVE_INFINITY;
|
|
189
|
+
|
|
190
|
+
const toward = (from: number, to: number, seconds: number, dt: number) =>
|
|
191
|
+
from + (to - from) * Math.min(1, dt / seconds);
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
/** Call once per frame with the seconds since the last call. The frame returned is reused. */
|
|
195
|
+
read(input: ArrayLike<number>, dt: number): VoiceFrame {
|
|
196
|
+
let total = 0;
|
|
197
|
+
let within = 0;
|
|
198
|
+
let ranged = 0;
|
|
199
|
+
for (let k = 0; k < SPECTRUM_BANDS; k++) {
|
|
200
|
+
const x = input[k] ?? 0;
|
|
201
|
+
total += x;
|
|
202
|
+
ceil[k] = toward(
|
|
203
|
+
ceil[k],
|
|
204
|
+
x,
|
|
205
|
+
x > ceil[k] ? FOLLOW.ceilAttack : FOLLOW.ceilRelease,
|
|
206
|
+
dt,
|
|
207
|
+
);
|
|
208
|
+
floor[k] = toward(
|
|
209
|
+
floor[k],
|
|
210
|
+
x,
|
|
211
|
+
x < floor[k] ? FOLLOW.floorFall : FOLLOW.floorRise,
|
|
212
|
+
dt,
|
|
213
|
+
);
|
|
214
|
+
const span = ceil[k] - floor[k];
|
|
215
|
+
const at =
|
|
216
|
+
span > FOLLOW.minSpan
|
|
217
|
+
? Math.min(1, Math.max(0, (x - floor[k]) / span))
|
|
218
|
+
: 0;
|
|
219
|
+
frame.bands[k] = at;
|
|
220
|
+
if (span > FOLLOW.minSpan) {
|
|
221
|
+
within += at;
|
|
222
|
+
ranged++;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const mean = total / SPECTRUM_BANDS;
|
|
227
|
+
const edge = Math.min(
|
|
228
|
+
1,
|
|
229
|
+
Math.max(
|
|
230
|
+
0,
|
|
231
|
+
(mean - FOLLOW.quietBelow) / (FOLLOW.voicedAbove - FOLLOW.quietBelow),
|
|
232
|
+
),
|
|
233
|
+
);
|
|
234
|
+
const voiced = edge * edge * (3 - 2 * edge);
|
|
235
|
+
for (let k = 0; k < SPECTRUM_BANDS; k++) frame.bands[k] *= voiced;
|
|
236
|
+
|
|
237
|
+
frame.level = (ranged ? within / ranged : 0) * voiced;
|
|
238
|
+
fast = toward(fast, frame.level, FOLLOW.fast, dt);
|
|
239
|
+
mid = toward(mid, frame.level, FOLLOW.mid, dt);
|
|
240
|
+
frame.phrase = toward(frame.phrase, frame.level, FOLLOW.phrase, dt);
|
|
241
|
+
|
|
242
|
+
sinceOnset += dt;
|
|
243
|
+
frame.onset = 0;
|
|
244
|
+
if (
|
|
245
|
+
fast > mid + FOLLOW.onsetRise &&
|
|
246
|
+
fast > FOLLOW.onsetMin &&
|
|
247
|
+
sinceOnset > FOLLOW.onsetGap
|
|
248
|
+
) {
|
|
249
|
+
sinceOnset = 0;
|
|
250
|
+
frame.onset = Math.min(1, fast);
|
|
251
|
+
}
|
|
252
|
+
return frame;
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
}
|
package/lib/sandbox.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import { join, resolve as pathResolve, relative, sep } from "node:path";
|
|
4
|
+
import { EXEC_KILL_GRACE_MS, EXEC_TIMEOUT_MS } from "@/config";
|
|
4
5
|
|
|
5
6
|
export type ExecResult = { stdout: string; stderr: string; exitCode: number };
|
|
6
7
|
|
|
@@ -53,6 +54,25 @@ const IGNORE = new Set([
|
|
|
53
54
|
"coverage",
|
|
54
55
|
]);
|
|
55
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Every file under `dir`, skipping dot entries and the folders a build or a
|
|
59
|
+
* package manager fills (IGNORE). A folder that cannot be read yields nothing.
|
|
60
|
+
*/
|
|
61
|
+
export async function* walkFiles(dir: string): AsyncGenerator<string> {
|
|
62
|
+
let entries;
|
|
63
|
+
try {
|
|
64
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
65
|
+
} catch {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
for (const e of entries) {
|
|
69
|
+
if (IGNORE.has(e.name) || e.name.startsWith(".")) continue;
|
|
70
|
+
const full = join(dir, e.name);
|
|
71
|
+
if (e.isDirectory()) yield* walkFiles(full);
|
|
72
|
+
else if (e.isFile()) yield full;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
56
76
|
/**
|
|
57
77
|
* Environment for every shell the agent runs: nobody watches it, so no pager
|
|
58
78
|
* may stall it, and secrets do not travel to children. playwright-cli runs
|
|
@@ -100,21 +120,6 @@ export const createSandBox = ({
|
|
|
100
120
|
const res = (p: string) =>
|
|
101
121
|
p.startsWith("/") || /^[A-Za-z]:/.test(p) ? p : pathResolve(cwd, p);
|
|
102
122
|
|
|
103
|
-
async function* walk(dir: string): AsyncGenerator<string> {
|
|
104
|
-
let entries;
|
|
105
|
-
try {
|
|
106
|
-
entries = await readdir(dir, { withFileTypes: true });
|
|
107
|
-
} catch {
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
for (const e of entries) {
|
|
111
|
-
if (IGNORE.has(e.name) || e.name.startsWith(".")) continue;
|
|
112
|
-
const full = join(dir, e.name);
|
|
113
|
-
if (e.isDirectory()) yield* walk(full);
|
|
114
|
-
else if (e.isFile()) yield full;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
123
|
// A very simple glob → regex. Only **/, * and ? are supported
|
|
119
124
|
const globToRe = (g: string) =>
|
|
120
125
|
new RegExp(
|
|
@@ -147,7 +152,7 @@ export const createSandBox = ({
|
|
|
147
152
|
const root = res(path);
|
|
148
153
|
const re = globToRe(pattern);
|
|
149
154
|
const out: string[] = [];
|
|
150
|
-
for await (const f of
|
|
155
|
+
for await (const f of walkFiles(root)) {
|
|
151
156
|
const rel = relative(root, f).split(sep).join("/");
|
|
152
157
|
if (re.test(rel) || re.test(f)) out.push(f);
|
|
153
158
|
if (out.length >= limit) break;
|
|
@@ -155,7 +160,7 @@ export const createSandBox = ({
|
|
|
155
160
|
return out;
|
|
156
161
|
},
|
|
157
162
|
|
|
158
|
-
exec(command, { cwd: c, timeoutMs =
|
|
163
|
+
exec(command, { cwd: c, timeoutMs = EXEC_TIMEOUT_MS, signal, env } = {}) {
|
|
159
164
|
return new Promise((resolve) => {
|
|
160
165
|
const base = shellEnv();
|
|
161
166
|
if (extraPath) base.PATH = `${base.PATH ?? ""}:${extraPath}`;
|
|
@@ -172,28 +177,26 @@ export const createSandBox = ({
|
|
|
172
177
|
child.stdout?.on("data", (d) => (stdout += d));
|
|
173
178
|
child.stderr?.on("data", (d) => (stderr += d));
|
|
174
179
|
|
|
175
|
-
const
|
|
176
|
-
stderr += `\n[${why}]`;
|
|
180
|
+
const group = (name: NodeJS.Signals) => {
|
|
177
181
|
try {
|
|
178
182
|
// A negative pid means the group: the shell and everything under it
|
|
179
|
-
if (child.pid) process.kill(-child.pid,
|
|
183
|
+
if (child.pid) process.kill(-child.pid, name);
|
|
180
184
|
} catch {
|
|
181
185
|
// Already gone, or no process groups on this platform
|
|
182
|
-
child.kill(
|
|
186
|
+
child.kill(name);
|
|
183
187
|
}
|
|
184
188
|
};
|
|
185
189
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
const onAbort = () => stop("Aborted");
|
|
192
|
-
if (signal?.aborted) onAbort();
|
|
193
|
-
else signal?.addEventListener("abort", onAbort, { once: true });
|
|
190
|
+
let settled = false;
|
|
191
|
+
let killing: ReturnType<typeof setTimeout> | undefined;
|
|
192
|
+
let letGo: ReturnType<typeof setTimeout> | undefined;
|
|
194
193
|
|
|
195
194
|
const done = async (result: ExecResult) => {
|
|
195
|
+
if (settled) return;
|
|
196
|
+
settled = true;
|
|
196
197
|
clearTimeout(timer);
|
|
198
|
+
clearTimeout(killing);
|
|
199
|
+
clearTimeout(letGo);
|
|
197
200
|
signal?.removeEventListener("abort", onAbort);
|
|
198
201
|
resolve({
|
|
199
202
|
...result,
|
|
@@ -202,6 +205,34 @@ export const createSandBox = ({
|
|
|
202
205
|
});
|
|
203
206
|
};
|
|
204
207
|
|
|
208
|
+
const stop = (why: string) => {
|
|
209
|
+
if (killing || settled) return;
|
|
210
|
+
stderr += `\n[${why}]`;
|
|
211
|
+
group("SIGTERM");
|
|
212
|
+
// A command that ignores SIGTERM would hold its step, and everything
|
|
213
|
+
// waiting on the job, for good (config EXEC_KILL_GRACE_MS)
|
|
214
|
+
killing = setTimeout(() => {
|
|
215
|
+
stderr += "\n[Killed: it did not exit on SIGTERM]";
|
|
216
|
+
group("SIGKILL");
|
|
217
|
+
// Output held open by a process that left the group never closes;
|
|
218
|
+
// what arrived by now is the result
|
|
219
|
+
letGo = setTimeout(() => {
|
|
220
|
+
child.stdout?.destroy();
|
|
221
|
+
child.stderr?.destroy();
|
|
222
|
+
void done({ stdout, stderr, exitCode: -1 });
|
|
223
|
+
}, 1_000);
|
|
224
|
+
}, EXEC_KILL_GRACE_MS);
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const timer = setTimeout(
|
|
228
|
+
() => stop(`Timed out after ${timeoutMs}ms`),
|
|
229
|
+
timeoutMs,
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
const onAbort = () => stop("Aborted");
|
|
233
|
+
if (signal?.aborted) onAbort();
|
|
234
|
+
else signal?.addEventListener("abort", onAbort, { once: true });
|
|
235
|
+
|
|
205
236
|
child.on("close", (code) =>
|
|
206
237
|
done({ stdout, stderr, exitCode: code ?? -1 }),
|
|
207
238
|
);
|
package/lib/tokens.ts
CHANGED
|
@@ -14,3 +14,22 @@ export function estimateTokens(text: string): number {
|
|
|
14
14
|
}
|
|
15
15
|
return Math.ceil(cjk / 1.3 + other / 4);
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* One assembled prompt measured by its own `##` headings, biggest first. Read
|
|
20
|
+
* off the finished text rather than off what built it: what is counted is then
|
|
21
|
+
* what is actually sent, and a chapter cannot be added without appearing here.
|
|
22
|
+
*/
|
|
23
|
+
export function sectionTokens(
|
|
24
|
+
text: string,
|
|
25
|
+
): { name: string; tokens: number }[] {
|
|
26
|
+
return text
|
|
27
|
+
.split(/\n(?=## )/)
|
|
28
|
+
.map((block) => ({
|
|
29
|
+
name: block.startsWith("## ")
|
|
30
|
+
? block.slice(3, block.indexOf("\n"))
|
|
31
|
+
: "identity",
|
|
32
|
+
tokens: estimateTokens(block),
|
|
33
|
+
}))
|
|
34
|
+
.sort((a, b) => b.tokens - a.tokens);
|
|
35
|
+
}
|