thursday-agent 0.9.0 → 0.11.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 +0 -1
- package/.next/build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/routes-manifest.json +0 -6
- package/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +2 -2
- package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/api/artifact/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/keep-working/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/memory/files/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/memory/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/thread/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/thread/route.js.nft.json +1 -1
- package/.next/server/app/api/config/route.js.nft.json +1 -1
- package/.next/server/app/api/events/route.js.nft.json +1 -1
- package/.next/server/app/api/favicon/[host]/route.js.nft.json +1 -1
- package/.next/server/app/api/file/[...path]/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/catalog/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/credits/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/subscription/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/[name]/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/oauth/callback/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/tools/route.js.nft.json +1 -1
- package/.next/server/app/api/memory/edit/route.js.nft.json +1 -1
- package/.next/server/app/api/memory/route.js.nft.json +1 -1
- package/.next/server/app/api/reach/route.js +13 -13
- package/.next/server/app/api/reach/route.js.nft.json +1 -1
- package/.next/server/app/api/routine/route.js.nft.json +1 -1
- package/.next/server/app/api/signins/route.js +1 -1
- package/.next/server/app/api/signins/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/[source]/[name]/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/call/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/text/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/tool-call/route.js +14 -15
- package/.next/server/app/api/thursday/tool-call/route.js.nft.json +1 -1
- package/.next/server/app/api/workspace/files/route.js.nft.json +1 -1
- package/.next/server/app/api/workspace/route.js.nft.json +1 -1
- package/.next/server/app/artifact/[...path]/page/server-reference-manifest.json +1 -1
- package/.next/server/app/artifact/[...path]/page.js.nft.json +1 -1
- package/.next/server/app/artifact/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
- package/.next/server/app/manifest.webmanifest/route.js.nft.json +1 -1
- package/.next/server/app/page/server-reference-manifest.json +56 -56
- package/.next/server/app/page.js +4 -4
- 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 +0 -1
- package/.next/server/chunks/[root-of-the-server]__014ydvq._.js +2 -2
- package/.next/server/chunks/[root-of-the-server]__0209ah4._.js +7 -5
- package/.next/server/chunks/[root-of-the-server]__04r4h-2._.js +4 -2
- package/.next/server/chunks/[root-of-the-server]__0542v_d._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0gewdg9._.js +2 -2
- package/.next/server/chunks/[root-of-the-server]__0mrk2it._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__1-swlkw._.js +2 -2
- package/.next/server/chunks/[root-of-the-server]__12iddfq._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__1ai5lme._.js +3 -3
- package/.next/server/chunks/[root-of-the-server]__1cjxw0-._.js +2 -2
- package/.next/server/chunks/{[root-of-the-server]__1fy2g82._.js → [root-of-the-server]__1k3j52n._.js} +1 -1
- package/.next/server/chunks/[root-of-the-server]__1kyjt5m._.js +3 -3
- package/.next/server/chunks/[root-of-the-server]__1ygplx1._.js +1 -1
- package/.next/server/chunks/_05v8k21._.js +3 -1
- package/.next/server/chunks/_0awtznl._.js +1 -1
- package/.next/server/chunks/_0na2fj1._.js +1 -1
- package/.next/server/chunks/_0x-ej3f._.js +17 -17
- package/.next/server/chunks/_12-amdu._.js +2 -2
- package/.next/server/chunks/_15740dw._.js +60 -0
- package/.next/server/chunks/_1dc_d1q._.js +3 -3
- package/.next/server/chunks/_1i350l5._.js +3 -1
- package/.next/server/chunks/_1jc5g4p._.js +2 -2
- package/.next/server/chunks/_1jn-aep._.js +4 -4
- package/.next/server/chunks/_1m88lkh._.js +11 -11
- package/.next/server/chunks/_1ogqovh._.js +4 -4
- package/.next/server/chunks/_1uuucy-._.js +1 -1
- package/.next/server/chunks/_1zxp-p6._.js +1 -1
- package/.next/server/chunks/features_0rfvely._.js +5 -3
- package/.next/server/chunks/ssr/{[root-of-the-server]__1se25x0._.js → [root-of-the-server]__0hpp3nw._.js} +29 -13
- package/.next/server/chunks/ssr/[root-of-the-server]__0m7u9j-._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0tzekbl._.js +24 -60
- package/.next/server/chunks/ssr/[root-of-the-server]__1cz2rz5._.js +3 -0
- package/.next/server/chunks/ssr/{[root-of-the-server]__0tvavy9._.js → [root-of-the-server]__1iwyp1e._.js} +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__1w9hqq8._.js +1 -1
- package/.next/server/chunks/ssr/_097-ljd._.js +1 -1
- package/.next/server/chunks/ssr/{_1tmu8z-._.js → _0btzccu._.js} +2 -2
- package/.next/server/chunks/ssr/_0fqhhwi._.js +1 -1
- package/.next/server/chunks/ssr/_0hjzb5o._.js +3 -0
- package/.next/server/chunks/ssr/_0ld9_qq._.js +1 -1
- package/.next/server/chunks/ssr/{_0urj3mc._.js → _0ouzu9f._.js} +2 -2
- package/.next/server/chunks/ssr/{_1-zyvdb._.js → _0u7ko71._.js} +2 -2
- package/.next/server/chunks/ssr/{_0ltz2q5._.js → _0wd_o8-._.js} +2 -2
- package/.next/server/chunks/ssr/_0ya0cb0._.js +3 -0
- package/.next/server/chunks/ssr/_10yyqtk._.js +67 -0
- package/.next/server/chunks/ssr/{_1uy0xom._.js → _12eyown._.js} +3 -3
- package/.next/server/chunks/ssr/_12mijol._.js +11 -11
- package/.next/server/chunks/ssr/_132qkfz._.js +1 -1
- package/.next/server/chunks/ssr/_184327j._.js +1 -1
- package/.next/server/chunks/ssr/{_1z74dmq._.js → _19ps4p3._.js} +7 -7
- package/.next/server/chunks/ssr/{_1flbs77._.js → _1bhy4yg._.js} +2 -2
- package/.next/server/chunks/ssr/_1d423aj._.js +7 -5
- package/.next/server/chunks/ssr/{_1a-nuxx._.js → _1kd843u._.js} +2 -2
- package/.next/server/chunks/ssr/{_0ital-2._.js → _1kq2ye_._.js} +2 -2
- package/.next/server/chunks/ssr/_1mqn9mc._.js +1 -1
- package/.next/server/chunks/ssr/_1neoj22._.js +1 -1
- package/.next/server/chunks/ssr/_1p8xorg._.js +3 -0
- package/.next/server/chunks/ssr/{_1x-kojb._.js → _1q683w8._.js} +3 -3
- package/.next/server/chunks/ssr/_1tawxu2._.js +3 -0
- package/.next/server/chunks/ssr/{_0scf7xy._.js → _1w2oba6._.js} +2 -2
- package/.next/server/chunks/ssr/_1xoch6f._.js +1 -1
- package/.next/server/chunks/ssr/{_0kjj5ts._.js → _1ybo1hd._.js} +3 -3
- package/.next/server/chunks/ssr/features_bot_0s11m4r._.js +3 -0
- package/.next/server/chunks/ssr/features_connectors_components_mcp-setting_tsx_0jmbgvp._.js +1 -1
- package/.next/server/chunks/ssr/features_memory_components_memory-setting_tsx_0oa43sw._.js +1 -1
- package/.next/server/chunks/ssr/features_routine_components_routine-setting_tsx_055fknz._.js +1 -1
- package/.next/server/chunks/ssr/features_workspace_00jz2hu._.js +1 -1
- package/.next/server/chunks/ssr/{node_modules_1af3w91._.js → node_modules_00pocul._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0th357h._.js → node_modules_02zt4az._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0jljel9._.js → node_modules_05ojmcl._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0wnlddw._.js → node_modules_06i6bli._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_06bb4q8._.js → node_modules_09dj429._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_068ws5v._.js → node_modules_0auzgvx._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1ogczzz._.js → node_modules_0evjwh1._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_13kwejm._.js → node_modules_0gmdur8._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1s-gb-1._.js → node_modules_0j3te-1._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1bucjlk._.js → node_modules_0rbaymq._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1wbkzl9._.js → node_modules_0tf8_1e._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_12et8d5._.js → node_modules_0yqy-0b._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1lkix61._.js → node_modules_1-53ebx._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_03d0rfz._.js → node_modules_1-y9aho._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0x630an._.js → node_modules_11bltzp._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0uyh_vb._.js → node_modules_18jn2qo._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0dzm4um._.js → node_modules_1_te3bu._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_07rs_p2._.js → node_modules_1c0pg3k._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0sgy-dc._.js → node_modules_1en2jx5._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0ubdtk-._.js → node_modules_1p-c8cr._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1z1lrbk._.js → node_modules_1qe-oxg._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_04_sbbc._.js → node_modules_1rjt833._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0yvz9e0._.js → node_modules_1tzw5sn._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1uwg-gz._.js → node_modules_1x6uwvn._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0t_ez6-._.js → node_modules_1zqddke._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_05ybmta._.js → node_modules_211zgim._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0r443fh._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0y51bqj._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_10wnlbk._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_1sc114e._.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 +57 -57
- package/.next/static/chunks/{13ixzheqw7o-w.js → 03-724sj0nk_z.js} +2 -2
- package/.next/static/chunks/03bhu4mk8en2q.js +1 -0
- package/.next/static/chunks/{3h2e-xv1zxh_f.js → 04imzjhvzw65d.js} +2 -2
- package/.next/static/chunks/{27tvv1i-smrck.js → 0gljzv9fz1g7d.js} +1 -1
- package/.next/static/chunks/{37j27b3e1gt7r.js → 0h0lythv1ql93.js} +1 -1
- package/.next/static/chunks/{2o4dndk0d68do.js → 0hzbj7f48cmx0.js} +1 -1
- package/.next/static/chunks/0lwunwbl7u7pt.js +1 -0
- package/.next/static/chunks/{0u9ae9k_2g7k3.js → 0r6nwxg4dv83t.js} +1 -1
- package/.next/static/chunks/0rn9nbg6aif9f.js +17 -0
- package/.next/static/chunks/{3bbzsitgiltnt.js → 0x_w4shtvrrfg.js} +1 -1
- package/.next/static/chunks/{2sdwbfs1x5skp.js → 0ympuji_ufypd.js} +1 -1
- package/.next/static/chunks/{3t9huugh-5_ps.js → 0zmrz4zfzm4dt.js} +1 -1
- package/.next/static/chunks/{0qmd8n52hp-6v.js → 11tlxkr7a_rpc.js} +1 -1
- package/.next/static/chunks/{2c64drdevdla3.js → 12scesro428m7.js} +2 -2
- package/.next/static/chunks/16s2fx3890_jd.js +65 -0
- package/.next/static/chunks/{0oyz95sujd2c7.js → 1qzfgplyo18tc.js} +1 -1
- package/.next/static/chunks/{04e-e--5lk26x.js → 1r4zajz14jp9-.js} +1 -1
- package/.next/static/chunks/1s6m4cmbn7htf.js +1 -0
- package/.next/static/chunks/1wl9t1-3i-q8p.css +1 -0
- package/.next/static/chunks/2-asot3p1j3yg.js +1 -0
- package/.next/static/chunks/{0u3ktztrzu1fa.js → 2-jn4xscbm-i2.js} +1 -1
- package/.next/static/chunks/22h9gvclqtx_2.js +1 -0
- package/.next/static/chunks/{337g9g_ux2gj-.js → 26-eblxwnykck.js} +1 -1
- package/.next/static/chunks/{43q9cgbuw8krx.js → 2kzwhbuobpkw3.js} +2 -2
- package/.next/static/chunks/2m6jd3586ijb-.js +1 -0
- package/.next/static/chunks/{0_awn8tucznxc.js → 304q9p54tcgm1.js} +1 -1
- package/.next/static/chunks/{1u5i-jti87jbo.js → 35wdx3wt7cg4a.js} +1 -1
- package/.next/static/chunks/37unh9460im7v.js +18 -0
- package/.next/static/chunks/{01xjer0i_e5z5.js → 39gj5vxz1zpm_.js} +1 -1
- package/.next/static/chunks/{427qsc6kahxyo.js → 39vrqy1r_00jc.js} +1 -1
- package/.next/static/chunks/{245hb85nsy6j4.js → 3dipmu0mptqum.js} +1 -1
- package/.next/static/chunks/3gki-squw28yu.js +1 -0
- package/.next/static/chunks/{3obf9n3cxj99k.js → 3jvldk2wvwbge.js} +1 -1
- package/.next/static/chunks/{38301ji-85i30.js → 3nnzdl_te20hv.js} +1 -1
- package/.next/static/chunks/{2m267diam_vib.js → 3tz4dr0_8pq1-.js} +1 -1
- package/.next/static/chunks/{2gocl3dq0_h1s.js → 3x6pggb-2kyrk.js} +1 -1
- package/.next/static/chunks/{025bmrgeukcb2.js → 4440mws9jz3mt.js} +1 -1
- package/README.md +4 -0
- package/app/api/thursday/tool-call/route.ts +1 -15
- package/components/ui/radio-group.tsx +2 -2
- package/docs/images/demo-still.jpg +0 -0
- package/docs/live-calls.md +19 -11
- package/features/ai/prompts/live.prompt.ts +51 -26
- package/features/ai/prompts/prompt-helper.ts +1 -9
- package/features/ai/prompts/thursday.prompt.ts +8 -2
- package/features/bot/bot.run.ts +9 -3
- package/features/bot/bot.seed.ts +35 -91
- package/features/bot/components/bot-room.tsx +0 -6
- package/features/bot/components/bot-setting.tsx +4 -4
- package/features/bot/components/room-pill.tsx +4 -2
- package/features/bot/seed-bots.ts +2 -2
- package/features/intro/components/intro.tsx +2 -3
- package/features/reach/reach.ts +2 -2
- package/features/routine/components/routine-setting.tsx +1 -1
- package/features/signins/signins.query.ts +66 -1
- package/features/thursday/components/side-captions.tsx +31 -8
- package/features/thursday/components/thursday-setting.tsx +5 -5
- package/features/thursday/components/thursday.tsx +97 -39
- package/features/thursday/components/write-line.tsx +28 -17
- package/features/thursday/thursday.action.ts +4 -20
- package/features/thursday/thursday.schema.ts +0 -9
- package/features/thursday/thursday.store.ts +11 -15
- package/features/thursday/thursday.text.ts +10 -8
- package/features/thursday/tool-line.ts +17 -6
- package/features/thursday/use-call-ring.ts +1 -5
- package/features/thursday/use-text-call.ts +37 -21
- package/features/thursday/use-thursday.ts +14 -44
- package/features/workspace/workspace.ts +4 -2
- package/guide/bots.md +13 -3
- package/guide/calls.md +7 -5
- package/knip.json +1 -0
- package/lib/live/live.session.ts +0 -39
- package/package.json +1 -1
- package/scripts/live-session.test.mts +19 -26
- package/seed-skills/analyst/README.md +18 -0
- package/seed-skills/analyst/data-report/SKILL.md +60 -0
- package/seed-skills/analyst/data-report/references/brief.md +31 -0
- package/seed-skills/analyst/data-report/references/company.md +41 -0
- package/seed-skills/analyst/data-report/references/comparison.md +42 -0
- package/seed-skills/analyst/data-report/references/decision.md +28 -0
- package/seed-skills/analyst/data-report/references/market-size.md +49 -0
- package/seed-skills/analyst/data-report/references/price-check.md +33 -0
- package/seed-skills/analyst/data-report/references/sources.md +51 -0
- package/seed-skills/analyst/data-report/references/trend.md +39 -0
- package/seed-skills/analyst/data-report/scripts/fetch.mjs +521 -0
- package/seed-skills/insta/instagram/SKILL.md +36 -0
- package/seed-skills/insta/instagram/references/dm.md +49 -0
- package/seed-skills/insta/instagram/references/post.md +74 -0
- package/seed-skills/insta/instagram/scripts/compose.mjs +333 -0
- package/seed-skills/insta/instagram/scripts/inbox.mjs +163 -0
- package/seed-skills/insta/instagram/scripts/lib.mjs +33 -0
- package/seed-skills/insta/instagram/scripts/media.mjs +104 -0
- package/seed-skills/insta/instagram/scripts/profile.mjs +143 -0
- package/seed-skills/insta/instagram/scripts/read.mjs +244 -0
- package/seed-skills/insta/instagram/scripts/search.mjs +137 -0
- package/seed-skills/insta/instagram/scripts/selectors.mjs +56 -0
- package/seed-skills/insta/instagram/scripts/sheet.mjs +43 -0
- package/seed-skills/insta/instagram/templates/carousel.html +62 -0
- package/seed-skills/marketer/README.md +6 -4
- package/seed-skills/marketer/copywriting/SKILL.md +1 -1
- package/seed-skills/marketer/emails/SKILL.md +1 -1
- package/seed-skills/marketer/launch/SKILL.md +1 -1
- package/seed-skills/marketer/product-marketing/SKILL.md +5 -5
- package/seed-skills/marketer/seo-audit/SKILL.md +1 -1
- package/seed-skills/marketer/social/SKILL.md +1 -1
- package/seed-skills/tutor/picture-book/SKILL.md +16 -0
- package/seed-skills/tutor/picture-book/book/book.css +181 -0
- package/seed-skills/tutor/picture-book/book/book.html +33 -0
- package/seed-skills/tutor/picture-book/book/book.js +60 -0
- package/seed-skills/tutor/picture-book/references/picture-book.md +104 -0
- package/seed-skills/tutor/picture-book/scripts/book-video.mjs +177 -0
- package/seed-skills/tutor/picture-book/scripts/book.mjs +98 -0
- package/skills/browser/SKILL.md +13 -4
- package/skills/browser/references/scripts.md +55 -0
- package/skills/browser/scripts/image-size.mjs +30 -0
- package/skills/browser/scripts/render.mjs +126 -0
- package/skills/browser/scripts/session.mjs +105 -0
- package/skills/browser/scripts/sheet.mjs +97 -0
- package/skills/browser/scripts/webimage.mjs +105 -0
- package/skills/interactive-page/SKILL.md +4 -3
- package/skills/interactive-page/scripts/chart.mjs +679 -0
- package/skills/interactive-page/scripts/page.mjs +1 -0
- package/.next/server/app/api/probe/route/app-paths-manifest.json +0 -3
- package/.next/server/app/api/probe/route/build-manifest.json +0 -12
- package/.next/server/app/api/probe/route/server-reference-manifest.json +0 -4
- package/.next/server/app/api/probe/route.js +0 -10
- package/.next/server/app/api/probe/route.js.map +0 -5
- package/.next/server/app/api/probe/route.js.nft.json +0 -1
- package/.next/server/app/api/probe/route_client-reference-manifest.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__012s2o8._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0z0hc1f._.js +0 -4
- package/.next/server/chunks/[root-of-the-server]__1v7-zke._.js +0 -5
- package/.next/server/chunks/_0whlpyi._.js +0 -4
- package/.next/server/chunks/_next-internal_server_app_api_probe_route_actions_0cqd30j.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0oo-wlv._.js +0 -103
- package/.next/server/chunks/ssr/[root-of-the-server]__0zuns7e._.js +0 -3
- package/.next/server/chunks/ssr/_070nv1_._.js +0 -3
- package/.next/server/chunks/ssr/_0dshsh4._.js +0 -3
- package/.next/server/chunks/ssr/_0l_mcfz._.js +0 -3
- package/.next/server/chunks/ssr/_0u5xv6g._.js +0 -3
- package/.next/server/chunks/ssr/_157yfrk._.js +0 -3
- package/.next/server/chunks/ssr/features_bot_1nr94ks._.js +0 -3
- package/.next/static/chunks/06nt9tatr-dug.js +0 -1
- package/.next/static/chunks/0ge-hx0ed83a1.js +0 -1
- package/.next/static/chunks/0pda_vf-abwvx.js +0 -1
- package/.next/static/chunks/0rowp9rx3qbxu.js +0 -18
- package/.next/static/chunks/1grkt5gr8pbg1.js +0 -17
- package/.next/static/chunks/1yugpvvi8qrbu.js +0 -101
- package/.next/static/chunks/27lkanxvtdn09.js +0 -1
- package/.next/static/chunks/3fec-aeteddr-.js +0 -1
- package/.next/static/chunks/3go3hwp1z2_h2.css +0 -1
- package/.next/static/chunks/3h2bun3hcggg8.js +0 -1
- package/.next/static/chunks/3j1y335o95mj7.js +0 -1
- package/lib/probe.server.ts +0 -17
- package/lib/probe.ts +0 -55
- /package/.next/static/{u8kJqShUAa49vY7BxAWsw → En1vUPazxcYjPjnnahcN5}/_buildManifest.js +0 -0
- /package/.next/static/{u8kJqShUAa49vY7BxAWsw → En1vUPazxcYjPjnnahcN5}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{u8kJqShUAa49vY7BxAWsw → En1vUPazxcYjPjnnahcN5}/_ssgManifest.js +0 -0
|
@@ -123,6 +123,7 @@ export function SideCaptions({
|
|
|
123
123
|
under = null,
|
|
124
124
|
ahead = false,
|
|
125
125
|
workOf,
|
|
126
|
+
typed = false,
|
|
126
127
|
}: {
|
|
127
128
|
turns: Turn[];
|
|
128
129
|
pinned: Pinned;
|
|
@@ -139,6 +140,8 @@ export function SideCaptions({
|
|
|
139
140
|
ahead?: boolean;
|
|
140
141
|
/** The work behind one of her turns, drawn under it while it is level; null for none. */
|
|
141
142
|
workOf?: (turn: string) => ReactNode;
|
|
143
|
+
/** A call in writing: their words were typed whole, so they arrive whole. */
|
|
144
|
+
typed?: boolean;
|
|
142
145
|
}) {
|
|
143
146
|
const last = turns.at(-1);
|
|
144
147
|
return (
|
|
@@ -154,6 +157,7 @@ export function SideCaptions({
|
|
|
154
157
|
under={role === "assistant" ? under : null}
|
|
155
158
|
ahead={role === "assistant" && ahead}
|
|
156
159
|
workOf={role === "assistant" ? workOf : undefined}
|
|
160
|
+
whole={typed && role === "user"}
|
|
157
161
|
/>
|
|
158
162
|
))}
|
|
159
163
|
</>
|
|
@@ -169,6 +173,7 @@ function SideColumn({
|
|
|
169
173
|
under,
|
|
170
174
|
ahead,
|
|
171
175
|
workOf,
|
|
176
|
+
whole,
|
|
172
177
|
}: {
|
|
173
178
|
role: Role;
|
|
174
179
|
/** This side's turns only. */
|
|
@@ -180,28 +185,38 @@ function SideColumn({
|
|
|
180
185
|
under: ReactNode;
|
|
181
186
|
ahead: boolean;
|
|
182
187
|
workOf: ((turn: string) => ReactNode) | undefined;
|
|
188
|
+
/** The latest turn is drawn at once rather than letter by letter. */
|
|
189
|
+
whole: boolean;
|
|
183
190
|
}) {
|
|
184
191
|
const mine = role === "user";
|
|
185
192
|
// Heights as laid out, before any transform: text grows while it is said, and
|
|
186
193
|
// receding clamps a turn to four lines
|
|
187
194
|
const [heights, setHeights] = useState<Record<string, number>>({});
|
|
195
|
+
// Widths too: what stands under her level words is as wide as they are
|
|
196
|
+
const [widths, setWidths] = useState<Record<string, number>>({});
|
|
188
197
|
const watch = useRef<ResizeObserver | null>(null);
|
|
189
198
|
useEffect(() => () => watch.current?.disconnect(), []);
|
|
190
199
|
const measure = useCallback((node: HTMLDivElement | null) => {
|
|
191
200
|
if (!node) return;
|
|
192
|
-
|
|
193
|
-
|
|
201
|
+
const sizes =
|
|
202
|
+
(read: (box: HTMLElement) => number) =>
|
|
203
|
+
(was: Record<string, number>, entries: ResizeObserverEntry[]) => {
|
|
194
204
|
let next = was;
|
|
195
205
|
for (const entry of entries) {
|
|
196
206
|
const box = entry.target as HTMLElement;
|
|
197
207
|
const id = box.dataset.turn;
|
|
198
|
-
if (!id || was[id] === box
|
|
208
|
+
if (!id || was[id] === read(box)) continue;
|
|
199
209
|
if (next === was) next = { ...was };
|
|
200
|
-
next[id] = box
|
|
210
|
+
next[id] = read(box);
|
|
201
211
|
}
|
|
202
212
|
return next;
|
|
203
|
-
}
|
|
204
|
-
);
|
|
213
|
+
};
|
|
214
|
+
const tall = sizes((box) => box.offsetHeight);
|
|
215
|
+
const wide = sizes((box) => box.offsetWidth);
|
|
216
|
+
watch.current ??= new ResizeObserver((entries) => {
|
|
217
|
+
setHeights((was) => tall(was, entries));
|
|
218
|
+
setWidths((was) => wide(was, entries));
|
|
219
|
+
});
|
|
205
220
|
watch.current.observe(node);
|
|
206
221
|
return () => watch.current?.unobserve(node);
|
|
207
222
|
}, []);
|
|
@@ -273,6 +288,9 @@ function SideColumn({
|
|
|
273
288
|
}
|
|
274
289
|
className={cn(
|
|
275
290
|
"group/turn absolute top-0 w-max max-w-full text-[17px] leading-[1.675] tracking-[0.3px] text-foreground transition-transform duration-[520ms] ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none",
|
|
291
|
+
whole &&
|
|
292
|
+
k === turns.length - 1 &&
|
|
293
|
+
"animate-in fade-in duration-200 motion-reduce:animate-none",
|
|
276
294
|
mine ? "left-0 origin-top-left" : "right-0 origin-top-right",
|
|
277
295
|
!shown && "invisible",
|
|
278
296
|
pickable && "pointer-events-auto cursor-pointer",
|
|
@@ -299,7 +317,7 @@ function SideColumn({
|
|
|
299
317
|
/>
|
|
300
318
|
)}
|
|
301
319
|
{/* Each side's latest turn keeps its letters wherever it sits, so going back and returning never draws it again */}
|
|
302
|
-
{k === turns.length - 1 ? (
|
|
320
|
+
{k === turns.length - 1 && !whole ? (
|
|
303
321
|
<Letters text={turn.text} />
|
|
304
322
|
) : (
|
|
305
323
|
turn.text
|
|
@@ -312,7 +330,12 @@ function SideColumn({
|
|
|
312
330
|
<div
|
|
313
331
|
ref={measure}
|
|
314
332
|
data-turn={UNDER}
|
|
315
|
-
style={{
|
|
333
|
+
style={{
|
|
334
|
+
transform: `translateY(${(underTop ?? 0).toFixed(1)}px)`,
|
|
335
|
+
// under her words it takes their width, so its lines start where they do;
|
|
336
|
+
// before she has said any, the side's
|
|
337
|
+
width: at < turns.length ? widths[turns[at].id] : undefined,
|
|
338
|
+
}}
|
|
316
339
|
className={cn(
|
|
317
340
|
"absolute top-0 flex w-full transition-transform duration-[520ms] ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none",
|
|
318
341
|
mine ? "left-0 justify-start" : "right-0 justify-end",
|
|
@@ -405,7 +405,7 @@ function BackendModelPicker({
|
|
|
405
405
|
className={cn(
|
|
406
406
|
"min-w-0 space-y-0.5 rounded-lg border px-3 py-2.5 text-left outline-none transition-colors focus-visible:ring-3 focus-visible:ring-ring/50",
|
|
407
407
|
picked
|
|
408
|
-
? "border-
|
|
408
|
+
? "border-brand bg-brand/4"
|
|
409
409
|
: "border-border/60 hover:bg-muted/50",
|
|
410
410
|
)}
|
|
411
411
|
>
|
|
@@ -413,7 +413,7 @@ function BackendModelPicker({
|
|
|
413
413
|
<span className="min-w-0 flex-1 truncate text-sm font-medium">
|
|
414
414
|
{model.label}
|
|
415
415
|
</span>
|
|
416
|
-
{picked && <Check className="size-3.5 shrink-0" />}
|
|
416
|
+
{picked && <Check className="size-3.5 shrink-0 text-brand" />}
|
|
417
417
|
</span>
|
|
418
418
|
<span className="block truncate font-mono text-[11px] text-muted-foreground">
|
|
419
419
|
{model.id} · {model.tier}
|
|
@@ -714,7 +714,7 @@ function Slider({
|
|
|
714
714
|
{...range}
|
|
715
715
|
value={value}
|
|
716
716
|
onChange={(event) => onChange(Number(event.target.value))}
|
|
717
|
-
className="min-w-0 flex-1 accent-
|
|
717
|
+
className="min-w-0 flex-1 accent-brand"
|
|
718
718
|
/>
|
|
719
719
|
<span className="w-10 shrink-0 text-right font-mono text-[11px] text-muted-foreground tabular-nums">
|
|
720
720
|
{format(value)}
|
|
@@ -762,7 +762,7 @@ function Captions({
|
|
|
762
762
|
className={cn(
|
|
763
763
|
"min-w-0 space-y-3 rounded-xl border p-3 text-left outline-none transition-colors focus-visible:ring-3 focus-visible:ring-ring/50",
|
|
764
764
|
picked
|
|
765
|
-
? "border-
|
|
765
|
+
? "border-brand bg-brand/4"
|
|
766
766
|
: "border-border/60 hover:bg-muted/50",
|
|
767
767
|
)}
|
|
768
768
|
>
|
|
@@ -776,7 +776,7 @@ function Captions({
|
|
|
776
776
|
{CAPTION_LABEL[view].hint}
|
|
777
777
|
</span>
|
|
778
778
|
</span>
|
|
779
|
-
{picked && <Check className="size-4 shrink-0" />}
|
|
779
|
+
{picked && <Check className="size-4 shrink-0 text-brand" />}
|
|
780
780
|
</span>
|
|
781
781
|
</button>
|
|
782
782
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { formatDistanceStrict } from "date-fns";
|
|
4
4
|
import {
|
|
5
5
|
ChevronDown,
|
|
6
6
|
ChevronUp,
|
|
@@ -187,14 +187,14 @@ function CallScreen({
|
|
|
187
187
|
if (lastRole !== "user") setMaking(false);
|
|
188
188
|
else if (work.on) setMaking(true);
|
|
189
189
|
}, [lastRole, work.on]);
|
|
190
|
-
// The work behind each of her turns stays with it for the call
|
|
191
|
-
//
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
//
|
|
190
|
+
// The work behind each of her turns stays with it for the call, under her words and again
|
|
191
|
+
// when she is gone back to; work she has said nothing after yet is her turn in the making
|
|
192
|
+
const kept = useKeptWork(talk, work.lines, work.on);
|
|
193
|
+
// the last turn is still being said: her voice is on, or yours came after hers and she has
|
|
194
|
+
// not answered — never yours in writing, which was sent whole
|
|
195
195
|
const saying =
|
|
196
196
|
(lastRole === "assistant" && status === "speaking") ||
|
|
197
|
-
(lastRole === "user" && status === "listening");
|
|
197
|
+
(lastRole === "user" && status === "listening" && !writing);
|
|
198
198
|
const calling = ringing !== null && ringing.missedAt === null;
|
|
199
199
|
const ringWord = useRingWord(calling);
|
|
200
200
|
return (
|
|
@@ -262,18 +262,24 @@ function CallScreen({
|
|
|
262
262
|
live={saying}
|
|
263
263
|
onPick={turns.pick}
|
|
264
264
|
under={
|
|
265
|
-
work.held &&
|
|
266
|
-
<WorkStack lines={
|
|
265
|
+
work.held && kept.pending.length > 0 ? (
|
|
266
|
+
<WorkStack lines={kept.pending} shown={work.on} />
|
|
267
267
|
) : null
|
|
268
268
|
}
|
|
269
269
|
// with the work gone and her answer not yet begun, her last words come back level;
|
|
270
270
|
// thinking alone takes nothing from them, since it stands under her face
|
|
271
|
-
ahead={making && work.held &&
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
271
|
+
ahead={making && work.held && kept.pending.length > 0}
|
|
272
|
+
typed={writing}
|
|
273
|
+
workOf={(turn) => {
|
|
274
|
+
// with her words last, work she has said nothing after stands under them
|
|
275
|
+
const lines = [
|
|
276
|
+
...(kept.turns[turn] ?? []),
|
|
277
|
+
...(turn === kept.latest && lastRole === "assistant"
|
|
278
|
+
? kept.pending
|
|
279
|
+
: []),
|
|
280
|
+
];
|
|
281
|
+
return lines.length ? <WorkStack lines={lines} shown /> : null;
|
|
282
|
+
}}
|
|
277
283
|
/>
|
|
278
284
|
)}
|
|
279
285
|
</div>
|
|
@@ -1011,34 +1017,72 @@ function useWork(drawn: ActivityLine | null, thinking: boolean) {
|
|
|
1011
1017
|
return { lines, on, held };
|
|
1012
1018
|
}
|
|
1013
1019
|
|
|
1020
|
+
type Kept = {
|
|
1021
|
+
/** Her turns by id, each with the lines behind it in the order they became its. */
|
|
1022
|
+
turns: Record<string, ActivityLine[]>;
|
|
1023
|
+
/** Whose each line is, once it is anyone's. */
|
|
1024
|
+
owner: Record<string, string>;
|
|
1025
|
+
/** Her words as they stood when each line was first drawn (`said`). */
|
|
1026
|
+
seen: Record<string, string | null>;
|
|
1027
|
+
};
|
|
1028
|
+
|
|
1029
|
+
const KEPT_NONE: Kept = { turns: {}, owner: {}, seen: {} };
|
|
1030
|
+
|
|
1014
1031
|
/**
|
|
1015
|
-
* The lines of work behind each of her turns, by turn
|
|
1016
|
-
*
|
|
1017
|
-
*
|
|
1018
|
-
*
|
|
1032
|
+
* The lines of work behind each of her turns, by turn. A line is the turn of the first words
|
|
1033
|
+
* she says after it is drawn — a turn she starts or one she goes on with — and stays that
|
|
1034
|
+
* turn's for the call, however long it lingers: the pages a search left on the line belong
|
|
1035
|
+
* to the answer they led to, never to the next one. Work that stops with her words last and
|
|
1036
|
+
* nothing said since is her last turn's. Until then a line is `pending`: her turn in the
|
|
1037
|
+
* making, or, with her words last, drawn under them. Their words arriving late (a transcript
|
|
1038
|
+
* lags the audio) moves nothing, since only hers decide. A new call starts empty.
|
|
1019
1039
|
*/
|
|
1020
|
-
function useKeptWork(talk: Turn[], lines: ActivityLine[]) {
|
|
1021
|
-
const [kept, setKept] = useState<
|
|
1022
|
-
const
|
|
1023
|
-
const
|
|
1040
|
+
function useKeptWork(talk: Turn[], lines: ActivityLine[], on: boolean) {
|
|
1041
|
+
const [kept, setKept] = useState<Kept>(KEPT_NONE);
|
|
1042
|
+
const hers = talk.findLast((turn) => turn.role === "assistant");
|
|
1043
|
+
const latest = hers?.id ?? null;
|
|
1044
|
+
const said = hers ? `${hers.id}:${hers.text.length}` : null;
|
|
1045
|
+
const closing = !on && talk.at(-1)?.role === "assistant";
|
|
1024
1046
|
useEffect(() => {
|
|
1025
|
-
if (!owner || !lines.length) return;
|
|
1026
1047
|
setKept((was) => {
|
|
1027
|
-
|
|
1028
|
-
const
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1048
|
+
let next = was;
|
|
1049
|
+
for (const line of lines) {
|
|
1050
|
+
const key = lineKey(line);
|
|
1051
|
+
let turn = next.owner[key];
|
|
1052
|
+
if (turn === undefined) {
|
|
1053
|
+
const seen = key in next.seen ? next.seen[key] : said;
|
|
1054
|
+
if (!(key in next.seen))
|
|
1055
|
+
next = { ...next, seen: { ...next.seen, [key]: said } };
|
|
1056
|
+
if (!latest || (seen === said && !closing)) continue;
|
|
1057
|
+
turn = latest;
|
|
1058
|
+
next = { ...next, owner: { ...next.owner, [key]: turn } };
|
|
1059
|
+
}
|
|
1060
|
+
// the line as it is drawn now: finished, or with the pages its search read
|
|
1061
|
+
const list = next.turns[turn] ?? [];
|
|
1062
|
+
const at = list.findIndex((one) => lineKey(one) === key);
|
|
1063
|
+
if (at >= 0 && list[at] === line) continue;
|
|
1064
|
+
next = {
|
|
1065
|
+
...next,
|
|
1066
|
+
turns: {
|
|
1067
|
+
...next.turns,
|
|
1068
|
+
[turn]:
|
|
1069
|
+
at >= 0
|
|
1070
|
+
? list.map((one, k) => (k === at ? line : one))
|
|
1071
|
+
: [...list, line],
|
|
1072
|
+
},
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
return next;
|
|
1036
1076
|
});
|
|
1037
|
-
}, [
|
|
1077
|
+
}, [lines, said, latest, closing]);
|
|
1038
1078
|
useEffect(() => {
|
|
1039
|
-
if (!talk.length) setKept(
|
|
1079
|
+
if (!talk.length) setKept(KEPT_NONE);
|
|
1040
1080
|
}, [talk.length]);
|
|
1041
|
-
|
|
1081
|
+
const pending = useMemo(
|
|
1082
|
+
() => lines.filter((line) => kept.owner[lineKey(line)] === undefined),
|
|
1083
|
+
[lines, kept.owner],
|
|
1084
|
+
);
|
|
1085
|
+
return { turns: kept.turns, pending, latest };
|
|
1042
1086
|
}
|
|
1043
1087
|
|
|
1044
1088
|
/**
|
|
@@ -1060,7 +1104,7 @@ function WorkStack({
|
|
|
1060
1104
|
<div
|
|
1061
1105
|
aria-hidden={!shown}
|
|
1062
1106
|
className={cn(
|
|
1063
|
-
"pointer-events-auto relative w-
|
|
1107
|
+
"pointer-events-auto relative w-full transition-opacity duration-300 ease-out",
|
|
1064
1108
|
!shown && "pointer-events-none opacity-0",
|
|
1065
1109
|
)}
|
|
1066
1110
|
style={{ height: rows * WORK_ROW }}
|
|
@@ -1159,8 +1203,7 @@ function Incoming({
|
|
|
1159
1203
|
<p className="mb-1.5 flex items-center gap-2 font-mono text-[11px] text-muted-foreground">
|
|
1160
1204
|
<PhoneMissed className="size-3" />
|
|
1161
1205
|
<span>
|
|
1162
|
-
Missed ·{
|
|
1163
|
-
{formatDistanceToNowStrict(missedAt, { addSuffix: true })}
|
|
1206
|
+
Missed · <MissedWhen at={missedAt} />
|
|
1164
1207
|
</span>
|
|
1165
1208
|
<button
|
|
1166
1209
|
type="button"
|
|
@@ -1276,6 +1319,21 @@ function Incoming({
|
|
|
1276
1319
|
);
|
|
1277
1320
|
}
|
|
1278
1321
|
|
|
1322
|
+
/** Said again every half minute: a missed call left on the screen is read hours later too. */
|
|
1323
|
+
const MISSED_TICK_MS = 30_000;
|
|
1324
|
+
|
|
1325
|
+
/** How long ago it rang out: "just now" for the first minute, then minutes and hours. */
|
|
1326
|
+
function MissedWhen({ at }: { at: number }) {
|
|
1327
|
+
const [now, setNow] = useState(() => Date.now());
|
|
1328
|
+
useEffect(() => {
|
|
1329
|
+
const tick = setInterval(() => setNow(Date.now()), MISSED_TICK_MS);
|
|
1330
|
+
return () => clearInterval(tick);
|
|
1331
|
+
}, []);
|
|
1332
|
+
return now - at < 60_000
|
|
1333
|
+
? "just now"
|
|
1334
|
+
: formatDistanceStrict(at, now, { addSuffix: true });
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1279
1337
|
/** The wake phrase as the screen shows it wherever saying it does something. */
|
|
1280
1338
|
function SaidPhrase({ phrase }: { phrase: string }) {
|
|
1281
1339
|
return (
|
|
@@ -494,24 +494,31 @@ export function WriteLine({
|
|
|
494
494
|
{written.error}
|
|
495
495
|
</p>
|
|
496
496
|
)}
|
|
497
|
-
{/*
|
|
498
|
-
|
|
499
|
-
|
|
497
|
+
{/* The way on from a turn that broke: the same words again, and where keys are set.
|
|
498
|
+
Never by itself: what a turn costs changes with what it runs on, so they press it */}
|
|
499
|
+
{toHer && written?.error && (
|
|
500
|
+
<div className="flex items-center justify-center gap-3">
|
|
500
501
|
<Button
|
|
501
502
|
type="button"
|
|
502
503
|
size="sm"
|
|
503
504
|
variant="brand"
|
|
504
505
|
disabled={written.busy}
|
|
505
506
|
onClick={() => {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
507
|
+
if (written.fallback)
|
|
508
|
+
useThursdayStore
|
|
509
|
+
.getState()
|
|
510
|
+
.patch({ textModel: written.fallback });
|
|
509
511
|
written.again();
|
|
510
512
|
}}
|
|
511
513
|
>
|
|
512
514
|
<RotateCw />
|
|
513
|
-
|
|
515
|
+
{written.fallback
|
|
516
|
+
? "Send it again on your OpenAI key"
|
|
517
|
+
: "Send it again"}
|
|
514
518
|
</Button>
|
|
519
|
+
<span className="font-mono text-[10.5px] text-muted-foreground/70">
|
|
520
|
+
<KeysLink>API keys</KeysLink>
|
|
521
|
+
</span>
|
|
515
522
|
</div>
|
|
516
523
|
)}
|
|
517
524
|
<p className="flex flex-wrap items-center justify-center gap-x-2 gap-y-1 font-mono text-[10.5px] text-muted-foreground/70">
|
|
@@ -549,15 +556,6 @@ function RunsOn({ runsOn }: { runsOn: TextModelRef | null }) {
|
|
|
549
556
|
onChange={(textModel) => useThursdayStore.getState().patch({ textModel })}
|
|
550
557
|
/>
|
|
551
558
|
);
|
|
552
|
-
const toKeys = (words: string) => (
|
|
553
|
-
<button
|
|
554
|
-
type="button"
|
|
555
|
-
onClick={() => openSettings("keys")}
|
|
556
|
-
className="rounded-sm text-foreground/80 underline underline-offset-3 outline-none hover:text-foreground focus-visible:ring-3 focus-visible:ring-ring/50"
|
|
557
|
-
>
|
|
558
|
-
{words}
|
|
559
|
-
</button>
|
|
560
|
-
);
|
|
561
559
|
if (runsOn)
|
|
562
560
|
return (
|
|
563
561
|
<>
|
|
@@ -572,13 +570,26 @@ function RunsOn({ runsOn }: { runsOn: TextModelRef | null }) {
|
|
|
572
570
|
<span>Writing to her needs a {plan}, an OpenAI key, or</span>
|
|
573
571
|
{pick}
|
|
574
572
|
<Dot />
|
|
575
|
-
|
|
573
|
+
<KeysLink>API keys</KeysLink>
|
|
576
574
|
</>
|
|
577
575
|
);
|
|
578
576
|
}
|
|
579
577
|
|
|
580
578
|
const Dot = () => <span className="text-muted-foreground/40">·</span>;
|
|
581
579
|
|
|
580
|
+
/** The way to Settings › API keys, as words in the line's small print. */
|
|
581
|
+
function KeysLink({ children }: { children: string }) {
|
|
582
|
+
return (
|
|
583
|
+
<button
|
|
584
|
+
type="button"
|
|
585
|
+
onClick={() => openSettings("keys")}
|
|
586
|
+
className="rounded-sm text-foreground/80 underline underline-offset-3 outline-none hover:text-foreground focus-visible:ring-3 focus-visible:ring-ring/50"
|
|
587
|
+
>
|
|
588
|
+
{children}
|
|
589
|
+
</button>
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
|
|
582
593
|
function Mark({ bot, size }: { bot: BotRef; size: number }) {
|
|
583
594
|
if (bot === HER) return <ThursdayMark size={size} className="shrink-0" />;
|
|
584
595
|
return (
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { asSchema } from "ai";
|
|
4
4
|
import z from "zod";
|
|
5
|
-
import { LIVE_PROVIDER } from "@/features/ai/live.schema";
|
|
5
|
+
import { LIVE_PROVIDER, LiveSettingsSchema } from "@/features/ai/live.schema";
|
|
6
6
|
import { loadTools } from "@/features/ai/load-tools";
|
|
7
7
|
import { textModelRefSchema } from "@/features/ai/model.schema";
|
|
8
8
|
import { loadLastCall } from "@/features/ai/prompts/call-last";
|
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
type ToolManifest,
|
|
21
21
|
} from "@/lib/live/live.schema";
|
|
22
22
|
import { acceptedReasoning, createLiveCall } from "@/lib/live/live.server";
|
|
23
|
-
import { createServerProbe } from "@/lib/probe.server";
|
|
24
23
|
import { serverAction } from "@/lib/protocol/server-action";
|
|
25
24
|
import { publicError } from "@/lib/public-error";
|
|
26
25
|
import {
|
|
@@ -37,7 +36,6 @@ import {
|
|
|
37
36
|
CallThoughtSchema,
|
|
38
37
|
CallTurnSchema,
|
|
39
38
|
type TextCallHandshake,
|
|
40
|
-
ThursdaySettingsSchema,
|
|
41
39
|
} from "./thursday.schema";
|
|
42
40
|
import { openTextCall } from "./thursday.text";
|
|
43
41
|
|
|
@@ -81,7 +79,7 @@ export const openCallAction = serverAction(
|
|
|
81
79
|
sdp: unknown,
|
|
82
80
|
calledBack?: unknown,
|
|
83
81
|
): Promise<CallHandshake> => {
|
|
84
|
-
const thursday =
|
|
82
|
+
const thursday = LiveSettingsSchema.parse(settings);
|
|
85
83
|
const offer = z.string().min(1).max(SDP_MAX_LENGTH).parse(sdp);
|
|
86
84
|
const apiKey = await readConfig(LIVE_PROVIDER.apiKeyName);
|
|
87
85
|
if (!apiKey) {
|
|
@@ -97,7 +95,6 @@ export const openCallAction = serverAction(
|
|
|
97
95
|
await Promise.all([
|
|
98
96
|
loadLivePrompt({
|
|
99
97
|
voicePrompt: thursday.voicePrompt,
|
|
100
|
-
locale: thursday.locale,
|
|
101
98
|
calledBack: rang,
|
|
102
99
|
remembers: last !== null,
|
|
103
100
|
}),
|
|
@@ -134,17 +131,6 @@ export const openCallAction = serverAction(
|
|
|
134
131
|
model: LIVE_MODEL,
|
|
135
132
|
backendModel: thursday.backendModel,
|
|
136
133
|
});
|
|
137
|
-
|
|
138
|
-
createServerProbe("server")("call.open", {
|
|
139
|
-
callId,
|
|
140
|
-
calledBack,
|
|
141
|
-
opening: voice.opening,
|
|
142
|
-
standing,
|
|
143
|
-
voiceChars: voice.text.length,
|
|
144
|
-
backendChars: backend.length,
|
|
145
|
-
webSearch: thursday.webSearch,
|
|
146
|
-
exa: Boolean(exaKey),
|
|
147
|
-
});
|
|
148
134
|
return {
|
|
149
135
|
callId,
|
|
150
136
|
sdp: connection.transport.sdp,
|
|
@@ -162,12 +148,10 @@ export const openCallAction = serverAction(
|
|
|
162
148
|
*/
|
|
163
149
|
export const openTextCallAction = serverAction(
|
|
164
150
|
async (settings: unknown, runsOn?: unknown): Promise<TextCallHandshake> => {
|
|
165
|
-
|
|
166
|
-
|
|
151
|
+
return openTextCall(
|
|
152
|
+
LiveSettingsSchema.parse(settings),
|
|
167
153
|
textModelRefSchema.nullish().parse(runsOn),
|
|
168
154
|
);
|
|
169
|
-
createServerProbe("server")("call.open.text", { callId: opened.callId });
|
|
170
|
-
return opened;
|
|
171
155
|
},
|
|
172
156
|
);
|
|
173
157
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import z from "zod";
|
|
2
2
|
import { ASCII_FACE } from "@/config";
|
|
3
|
-
import { LiveSettingsSchema } from "@/features/ai/live.schema";
|
|
4
3
|
import { TEXT_MODEL_PROVIDERS } from "@/features/ai/model.schema";
|
|
5
4
|
import type { ThreadStatus } from "@/features/bot/bot.schema";
|
|
6
5
|
import { ASCII_CHARSETS } from "@/features/thursday/face.const";
|
|
@@ -9,14 +8,6 @@ import { LiveFragmentSchema } from "@/lib/live/live.schema";
|
|
|
9
8
|
|
|
10
9
|
// Shared by server and browser: nothing here may touch the DB.
|
|
11
10
|
|
|
12
|
-
/** The payload openCallAction parses. Browser-only settings live in thursday.store. */
|
|
13
|
-
export const ThursdaySettingsSchema = LiveSettingsSchema.extend({
|
|
14
|
-
/** `navigator.language` (e.g. "ko-KR"). Only the first call's opening line uses it. */
|
|
15
|
-
locale: z.string().max(35).nullish(),
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
export type ThursdaySettings = z.infer<typeof ThursdaySettingsSchema>;
|
|
19
|
-
|
|
20
11
|
/**
|
|
21
12
|
* A word on the face: one `emote` put there, or the screen's own while she rings. `at` tells a
|
|
22
13
|
* second showing of the same word from the first; `hold` is how long it stays lit, in seconds,
|
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import { create } from "zustand";
|
|
4
4
|
import { persist } from "zustand/middleware";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
LIVE_DEFAULTS,
|
|
7
|
+
type LiveSettings,
|
|
8
|
+
LiveSettingsSchema,
|
|
9
|
+
migrateLiveSettings,
|
|
10
|
+
} from "@/features/ai/live.schema";
|
|
6
11
|
import {
|
|
7
12
|
type TextModelRef,
|
|
8
13
|
textModelRefSchema,
|
|
@@ -16,18 +21,16 @@ import {
|
|
|
16
21
|
HOTKEY_DEFAULT,
|
|
17
22
|
type Hotkey,
|
|
18
23
|
HotkeySchema,
|
|
19
|
-
type ThursdaySettings,
|
|
20
|
-
ThursdaySettingsSchema,
|
|
21
24
|
WAKE_DEFAULT,
|
|
22
25
|
type Wake,
|
|
23
26
|
WakeSchema,
|
|
24
27
|
} from "./thursday.schema";
|
|
25
28
|
|
|
26
|
-
// Call settings persisted per browser. Only `
|
|
29
|
+
// Call settings persisted per browser. Only `LiveSettings` is sent to the
|
|
27
30
|
// server (openCallAction); the rest never leaves the page.
|
|
28
31
|
|
|
29
32
|
/** Everything this browser stores, sent or not. */
|
|
30
|
-
const StoredSchema =
|
|
33
|
+
const StoredSchema = LiveSettingsSchema.extend({
|
|
31
34
|
// `.catch` so settings stored before a field existed keep the other fields.
|
|
32
35
|
wake: WakeSchema.catch(WAKE_DEFAULT),
|
|
33
36
|
hotkey: HotkeySchema.catch(HOTKEY_DEFAULT),
|
|
@@ -36,7 +39,7 @@ const StoredSchema = ThursdaySettingsSchema.extend({
|
|
|
36
39
|
textModel: textModelRefSchema.nullable().catch(null),
|
|
37
40
|
});
|
|
38
41
|
|
|
39
|
-
type Stored =
|
|
42
|
+
type Stored = LiveSettings & {
|
|
40
43
|
wake: Wake;
|
|
41
44
|
hotkey: Hotkey;
|
|
42
45
|
callBack: CallBack;
|
|
@@ -91,12 +94,5 @@ export const useThursdayStore = create<ThursdayStore>()(
|
|
|
91
94
|
);
|
|
92
95
|
|
|
93
96
|
/** What openCallAction needs and nothing else. */
|
|
94
|
-
export const thursdaySettings = ():
|
|
95
|
-
|
|
96
|
-
const locale =
|
|
97
|
-
typeof navigator === "undefined" ? null : (navigator.language ?? null);
|
|
98
|
-
return ThursdaySettingsSchema.parse({
|
|
99
|
-
...useThursdayStore.getState(),
|
|
100
|
-
locale,
|
|
101
|
-
});
|
|
102
|
-
};
|
|
97
|
+
export const thursdaySettings = (): LiveSettings =>
|
|
98
|
+
LiveSettingsSchema.parse(useThursdayStore.getState());
|
|
@@ -13,7 +13,11 @@ import {
|
|
|
13
13
|
} from "ai";
|
|
14
14
|
import { ZodError, z } from "zod";
|
|
15
15
|
import { TEXT_CALL } from "@/config";
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
LIVE_PROVIDER,
|
|
18
|
+
type LiveSettings,
|
|
19
|
+
LiveSettingsSchema,
|
|
20
|
+
} from "@/features/ai/live.schema";
|
|
17
21
|
import { loadTools } from "@/features/ai/load-tools";
|
|
18
22
|
import { getTextModel, modelErrorToString } from "@/features/ai/model";
|
|
19
23
|
import {
|
|
@@ -38,8 +42,6 @@ import {
|
|
|
38
42
|
TEXT_CALL_PROVIDERS,
|
|
39
43
|
type TextCallHandshake,
|
|
40
44
|
type TextCallProvider,
|
|
41
|
-
type ThursdaySettings,
|
|
42
|
-
ThursdaySettingsSchema,
|
|
43
45
|
textCallRunsOn,
|
|
44
46
|
} from "./thursday.schema";
|
|
45
47
|
import { toolLine } from "./tool-line";
|
|
@@ -70,7 +72,7 @@ export async function readTextCallProvider(): Promise<TextCallProvider | null> {
|
|
|
70
72
|
* in, else the OpenAI key, on the call's own backend model.
|
|
71
73
|
*/
|
|
72
74
|
async function runsOnOf(
|
|
73
|
-
settings:
|
|
75
|
+
settings: LiveSettings,
|
|
74
76
|
picked: TextModelRef | null | undefined,
|
|
75
77
|
): Promise<TextModelRef> {
|
|
76
78
|
if (picked) return picked;
|
|
@@ -81,7 +83,7 @@ async function runsOnOf(
|
|
|
81
83
|
|
|
82
84
|
/** The row a call in writing is kept under, and what stood open as it began. */
|
|
83
85
|
export async function openTextCall(
|
|
84
|
-
settings:
|
|
86
|
+
settings: LiveSettings,
|
|
85
87
|
picked?: TextModelRef | null,
|
|
86
88
|
): Promise<TextCallHandshake> {
|
|
87
89
|
const ref = await runsOnOf(settings, picked);
|
|
@@ -98,7 +100,7 @@ export async function openTextCall(
|
|
|
98
100
|
|
|
99
101
|
const BodySchema = z.object({
|
|
100
102
|
callId: z.string().min(1),
|
|
101
|
-
settings:
|
|
103
|
+
settings: LiveSettingsSchema,
|
|
102
104
|
/** What stood open as the call opened (ai/prompts/call-standing), as the page was handed it. */
|
|
103
105
|
standing: z.string().nullish(),
|
|
104
106
|
/** The model picked on the write line; absent, the rule decides (runsOnOf). */
|
|
@@ -149,7 +151,7 @@ export async function streamTextCall(
|
|
|
149
151
|
*/
|
|
150
152
|
export async function answerInWriting(input: {
|
|
151
153
|
callId: string;
|
|
152
|
-
settings:
|
|
154
|
+
settings: LiveSettings;
|
|
153
155
|
standing: string | null;
|
|
154
156
|
messages: ModelMessage[];
|
|
155
157
|
said: string | null;
|
|
@@ -246,7 +248,7 @@ const standingHead = (standing: string | null | undefined): ModelMessage[] =>
|
|
|
246
248
|
/** What a turn runs on, whoever holds the conversation: the model, her prompt, her tools. */
|
|
247
249
|
async function loadRun(
|
|
248
250
|
callId: string,
|
|
249
|
-
settings:
|
|
251
|
+
settings: LiveSettings,
|
|
250
252
|
picked?: TextModelRef | null,
|
|
251
253
|
) {
|
|
252
254
|
const ref = await runsOnOf(settings, picked);
|