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
|
@@ -172,13 +172,15 @@ const ON_A_THREAD = new Set<string>([
|
|
|
172
172
|
* The bot a call's tool reaches, so its row wears that bot's face rather than a glyph —
|
|
173
173
|
* who work went to is a face everywhere else in the app. The one work is handed to, else
|
|
174
174
|
* the bot of the thread the tool names, found as the server finds a thread (thread.query
|
|
175
|
-
* resolveThread): its id, else its label. An answer goes to the bot that asked; "all"
|
|
176
|
-
*
|
|
175
|
+
* resolveThread): its id, else its label. An answer goes to the bot that asked; "all"
|
|
176
|
+
* reaches nobody in particular. A thread the page does not hold — stopped and read, or
|
|
177
|
+
* finished longer ago than the inbox keeps — is named by the tool's answer once it is back.
|
|
177
178
|
*/
|
|
178
179
|
export function toolBot(
|
|
179
180
|
name: string,
|
|
180
181
|
args?: string,
|
|
181
182
|
threads?: Thread[],
|
|
183
|
+
output?: string,
|
|
182
184
|
): string | null {
|
|
183
185
|
const parsed = parseArgs(args);
|
|
184
186
|
if (!parsed) return null;
|
|
@@ -186,19 +188,28 @@ export function toolBot(
|
|
|
186
188
|
return typeof parsed.bot === "string" && parsed.bot.trim()
|
|
187
189
|
? parsed.bot.trim()
|
|
188
190
|
: null;
|
|
189
|
-
if (!ON_A_THREAD.has(name)
|
|
191
|
+
if (!ON_A_THREAD.has(name)) return null;
|
|
190
192
|
const ref = typeof parsed.thread === "string" ? parsed.thread.trim() : "";
|
|
191
193
|
if (!ref || ref.toLowerCase() === "all") return null;
|
|
192
194
|
const lower = ref.toLowerCase();
|
|
193
195
|
const thread =
|
|
194
|
-
threads
|
|
195
|
-
threads
|
|
196
|
-
if (!thread) return
|
|
196
|
+
threads?.find((one) => one.id === ref) ??
|
|
197
|
+
threads?.find((one) => one.label.toLowerCase() === lower);
|
|
198
|
+
if (!thread) return answeredBy(output);
|
|
197
199
|
return name === TOOL_NAMES.thread_answer
|
|
198
200
|
? (thread.room.questions[0]?.bot ?? thread.bot)
|
|
199
201
|
: thread.bot;
|
|
200
202
|
}
|
|
201
203
|
|
|
204
|
+
/** Whom a thread tool's answer says it reached (load-tools): `bot`, or the bot that took it as its answer. */
|
|
205
|
+
function answeredBy(output?: string): string | null {
|
|
206
|
+
const said = parseArgs(output);
|
|
207
|
+
if (!said) return null;
|
|
208
|
+
if (typeof said.bot === "string" && said.bot.trim()) return said.bot.trim();
|
|
209
|
+
const answered = said.answered as { bot?: unknown } | null | undefined;
|
|
210
|
+
return typeof answered?.bot === "string" ? answered.bot : null;
|
|
211
|
+
}
|
|
212
|
+
|
|
202
213
|
/**
|
|
203
214
|
* The label a thread was started under, or null for any other turn. It is
|
|
204
215
|
* how a job is found again from the line that opened it — in her prompt and in
|
|
@@ -6,7 +6,6 @@ import type { Thread } from "@/features/bot/bot.schema";
|
|
|
6
6
|
import { ringingThreads } from "@/features/bot/thread.store";
|
|
7
7
|
import { toDate } from "@/lib/date-like";
|
|
8
8
|
import { createRing } from "@/lib/live/ring";
|
|
9
|
-
import { createProbe } from "@/lib/probe";
|
|
10
9
|
import type { CallBack } from "./thursday.schema";
|
|
11
10
|
import { useThursdayStore } from "./thursday.store";
|
|
12
11
|
|
|
@@ -51,7 +50,6 @@ export function useCallRing({
|
|
|
51
50
|
/** A call in writing holds the screen (use-text-call): nothing rings meanwhile. */
|
|
52
51
|
writing: boolean;
|
|
53
52
|
}) {
|
|
54
|
-
const probe = useRef(createProbe("page")).current;
|
|
55
53
|
/**
|
|
56
54
|
* The call-back rings only for threads that changed after this: the last call's
|
|
57
55
|
* end, the last ring, or the page opening.
|
|
@@ -84,7 +82,6 @@ export function useCallRing({
|
|
|
84
82
|
);
|
|
85
83
|
if (!fresh.length) return;
|
|
86
84
|
|
|
87
|
-
probe("ring", { threads: fresh.map((thread) => thread.id) });
|
|
88
85
|
ringAfter.current = Date.now();
|
|
89
86
|
// Work that is new to it rings again, even if it had rung out
|
|
90
87
|
setRangOutAt(null);
|
|
@@ -92,7 +89,7 @@ export function useCallRing({
|
|
|
92
89
|
...ids,
|
|
93
90
|
...fresh.map((thread) => thread.id).filter((id) => !ids.includes(id)),
|
|
94
91
|
]);
|
|
95
|
-
}, [threads, callBack, resting, writing
|
|
92
|
+
}, [threads, callBack, resting, writing]);
|
|
96
93
|
|
|
97
94
|
// Writing to her answers a ring as calling her does, and what came up while they wrote
|
|
98
95
|
// was that call's to tell: nothing from before its end rings afterwards
|
|
@@ -110,7 +107,6 @@ export function useCallRing({
|
|
|
110
107
|
* A thread added to a ring already going does not restart the clock.
|
|
111
108
|
*/
|
|
112
109
|
const decline = useCallback(() => {
|
|
113
|
-
probe("ring.declined");
|
|
114
110
|
setRingingFor([]);
|
|
115
111
|
setRangOutAt(null);
|
|
116
112
|
}, []);
|
|
@@ -267,34 +267,50 @@ export function useTextCall(): TextCall {
|
|
|
267
267
|
// pages a search read stay through her answer, until the next words are sent, as on a
|
|
268
268
|
// spoken call
|
|
269
269
|
const lastTool = [...parts].reverse().find(isToolUIPart);
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
lastTool.state === "output-available" ||
|
|
282
|
-
lastTool.state === "output-error";
|
|
283
|
-
const sources =
|
|
270
|
+
// The part is copied with every piece of the stream, and its arguments arrive a few
|
|
271
|
+
// characters a piece: the line is a new object only when what it draws changes, or
|
|
272
|
+
// every piece redraws the call screen several times over
|
|
273
|
+
const id = lastTool?.toolCallId ?? null;
|
|
274
|
+
const name = lastTool ? getToolName(lastTool) : null;
|
|
275
|
+
const done =
|
|
276
|
+
lastTool?.state === "output-available" ||
|
|
277
|
+
lastTool?.state === "output-error";
|
|
278
|
+
const output = lastTool?.output;
|
|
279
|
+
const sources = useMemo(
|
|
280
|
+
() =>
|
|
284
281
|
done && name === TOOL_NAMES.web_search
|
|
285
|
-
? searchSourcesOf(JSON.stringify(
|
|
286
|
-
: []
|
|
287
|
-
|
|
288
|
-
|
|
282
|
+
? searchSourcesOf(JSON.stringify(output ?? null))
|
|
283
|
+
: [],
|
|
284
|
+
[done, name, output],
|
|
285
|
+
);
|
|
286
|
+
// A provider's own search names what it looked for in its answer, not in what it was asked
|
|
287
|
+
const asked = (output as { action?: { query?: unknown } } | null | undefined)
|
|
288
|
+
?.action?.query;
|
|
289
|
+
const args = JSON.stringify({
|
|
290
|
+
...(typeof asked === "string" ? { query: asked } : {}),
|
|
291
|
+
...(lastTool?.input ?? {}),
|
|
292
|
+
});
|
|
293
|
+
const bot = name
|
|
294
|
+
? toolBot(
|
|
295
|
+
name,
|
|
296
|
+
args,
|
|
297
|
+
inbox.current,
|
|
298
|
+
output === undefined ? undefined : JSON.stringify(output),
|
|
299
|
+
)
|
|
300
|
+
: null;
|
|
301
|
+
const drawn = name ? toolLine(name, args, bot) : null;
|
|
302
|
+
const answered = last?.type === "text";
|
|
303
|
+
const used = useMemo((): ActivityLine | null => {
|
|
304
|
+
if (!id || !name || (answered && !sources.length)) return null;
|
|
289
305
|
return {
|
|
290
|
-
id
|
|
306
|
+
id,
|
|
291
307
|
name,
|
|
292
|
-
line:
|
|
308
|
+
line: drawn,
|
|
293
309
|
bot,
|
|
294
310
|
done,
|
|
295
311
|
...(sources.length ? { sources } : {}),
|
|
296
312
|
};
|
|
297
|
-
}, [
|
|
313
|
+
}, [id, name, drawn, bot, done, sources, answered]);
|
|
298
314
|
const [lingered, setLingered] = useState<string | null>(null);
|
|
299
315
|
const usedId = used?.done && !used.sources ? used.id : null;
|
|
300
316
|
useEffect(() => {
|
|
@@ -4,13 +4,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
|
|
4
4
|
import { useAppEvent } from "@/app/api/events/app-event.client";
|
|
5
5
|
import { queryKey } from "@/app/api/query-key";
|
|
6
6
|
import { toast } from "@/components/ui/toast";
|
|
7
|
-
import {
|
|
8
|
-
CALL_END,
|
|
9
|
-
CALL_ENDED_MS,
|
|
10
|
-
CALL_IDLE,
|
|
11
|
-
CALL_NUDGE,
|
|
12
|
-
CALL_RELAY,
|
|
13
|
-
} from "@/config";
|
|
7
|
+
import { CALL_END, CALL_ENDED_MS, CALL_IDLE, CALL_RELAY } from "@/config";
|
|
14
8
|
import { TOOL_NAMES } from "@/features/ai/tools/tool-name";
|
|
15
9
|
import { acceptThreadRelaysAction } from "@/features/bot/bot.action";
|
|
16
10
|
import type { Bot, Thread } from "@/features/bot/bot.schema";
|
|
@@ -32,7 +26,6 @@ import {
|
|
|
32
26
|
createAudioTap,
|
|
33
27
|
SPECTRUM_BANDS,
|
|
34
28
|
} from "@/lib/live/live.tap";
|
|
35
|
-
import { createProbe } from "@/lib/probe";
|
|
36
29
|
import { type Result, unwrapResult } from "@/lib/protocol/result";
|
|
37
30
|
import {
|
|
38
31
|
revalidate,
|
|
@@ -216,8 +209,6 @@ export function useThursday(
|
|
|
216
209
|
const onLine = useRef<string[]>([]);
|
|
217
210
|
/** That update's relay rows, accepted once she has voiced it. */
|
|
218
211
|
const onLineRows = useRef<number[]>([]);
|
|
219
|
-
/** The experiment's timer (config CALL_NUDGE): a backend turn the page starts itself. */
|
|
220
|
-
const nudge = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
221
212
|
/** When her voice was last heard, for letting a goodbye finish (CALL_END). */
|
|
222
213
|
const voiced = useRef(0);
|
|
223
214
|
/** The hang-up waiting on her goodbye once the backend called end_call. */
|
|
@@ -267,12 +258,22 @@ export function useThursday(
|
|
|
267
258
|
});
|
|
268
259
|
}, []);
|
|
269
260
|
|
|
261
|
+
/** The tool's answer is back: a thread the page does not hold is named by it (toolBot). */
|
|
262
|
+
const nameTool = useCallback((call: LiveToolCall, output: string) => {
|
|
263
|
+
const bot = toolBot(call.name, call.arguments, latest.current, output);
|
|
264
|
+
if (!bot) return;
|
|
265
|
+
setTool((open) =>
|
|
266
|
+
open?.id === call.id && open.bot !== bot
|
|
267
|
+
? { ...open, bot, line: toolLine(call.name, call.arguments, bot) }
|
|
268
|
+
: open,
|
|
269
|
+
);
|
|
270
|
+
}, []);
|
|
271
|
+
|
|
270
272
|
/**
|
|
271
273
|
* A relay takes the tool line and runs there like a tool does: while it goes in,
|
|
272
274
|
* and while she voices it. Her voice finishing it is what stops the motion
|
|
273
275
|
* (doneReading); after that it stays long enough to read.
|
|
274
276
|
*/
|
|
275
|
-
const probe = useRef(createProbe("page")).current;
|
|
276
277
|
const showRelay = useCallback((run: ActivityLine) => {
|
|
277
278
|
if (linger.current) clearTimeout(linger.current);
|
|
278
279
|
relayOpen.current = true;
|
|
@@ -295,7 +296,6 @@ export function useThursday(
|
|
|
295
296
|
/** The relayed update was voiced, or waited on long enough: the next can go in. */
|
|
296
297
|
const doneReading = useCallback(() => {
|
|
297
298
|
if (reading.current.giveUp) clearTimeout(reading.current.giveUp);
|
|
298
|
-
probe("read.done", { spoke: reading.current.spoke, keys: onLine.current });
|
|
299
299
|
// Her voice started and stopped on it: carried, and only now are its rows accepted.
|
|
300
300
|
// Live's acknowledgement says the text arrived, not that anyone heard it
|
|
301
301
|
if (reading.current.spoke) {
|
|
@@ -419,13 +419,6 @@ export function useThursday(
|
|
|
419
419
|
}
|
|
420
420
|
// Finished work is good news before she says it (an item's key opens with what it is)
|
|
421
421
|
if (first.key.startsWith("done:")) setFaceWord(finished());
|
|
422
|
-
probe("relay.out", {
|
|
423
|
-
kind: first.kind,
|
|
424
|
-
keys: due.map((item) => item.key),
|
|
425
|
-
waiting: open.length - due.length,
|
|
426
|
-
quietMs: Date.now() - heard.current,
|
|
427
|
-
rung,
|
|
428
|
-
});
|
|
429
422
|
readAloud();
|
|
430
423
|
onLine.current = due.map((item) => item.key);
|
|
431
424
|
onLineRows.current = due.flatMap((item) => item.relayIds);
|
|
@@ -439,7 +432,6 @@ export function useThursday(
|
|
|
439
432
|
: `[${lines.length} updates.]\n\n${lines.join("\n\n")}`,
|
|
440
433
|
)
|
|
441
434
|
.then((delivered) => {
|
|
442
|
-
probe("relay.acked", { delivered });
|
|
443
435
|
// Refused: nothing to wait for her voice on
|
|
444
436
|
if (!delivered) {
|
|
445
437
|
onLineRows.current = [];
|
|
@@ -572,12 +564,6 @@ export function useThursday(
|
|
|
572
564
|
async (why: CallEnd | null = null) => {
|
|
573
565
|
if (ending.current) return;
|
|
574
566
|
ending.current = true;
|
|
575
|
-
probe("hangup", {
|
|
576
|
-
why,
|
|
577
|
-
call: callId.current,
|
|
578
|
-
unvoiced: [...unvoiced.current],
|
|
579
|
-
reading: reading.current.on,
|
|
580
|
-
});
|
|
581
567
|
if (leaving.current) clearInterval(leaving.current);
|
|
582
568
|
leaving.current = null;
|
|
583
569
|
attempt.current += 1;
|
|
@@ -665,7 +651,6 @@ export function useThursday(
|
|
|
665
651
|
const leave = useCallback(() => {
|
|
666
652
|
if (leaving.current) return;
|
|
667
653
|
const asked = Date.now();
|
|
668
|
-
probe("end_call", { sinceVoiceMs: asked - voiced.current });
|
|
669
654
|
leaving.current = setInterval(() => {
|
|
670
655
|
const now = Date.now();
|
|
671
656
|
// A goodbye heard just before the backend ended the call counts as said
|
|
@@ -703,7 +688,6 @@ export function useThursday(
|
|
|
703
688
|
const call = useCallback(async () => {
|
|
704
689
|
// Every way in answers a ringing call-back: the face, the wake word, the hotkey
|
|
705
690
|
const calledBack = ring.isRinging;
|
|
706
|
-
probe("call", { calledBack, ringingFor: ring.ringingFor });
|
|
707
691
|
// Guard with a ref, not `status`: three entry points (face, wake word,
|
|
708
692
|
// hotkey) can fire in one frame and both see a stale "idle", opening two sessions
|
|
709
693
|
if (opening.current || ending.current) return;
|
|
@@ -831,8 +815,6 @@ export function useThursday(
|
|
|
831
815
|
if (word) setFaceWord({ text: word, at: Date.now() });
|
|
832
816
|
return reply;
|
|
833
817
|
}
|
|
834
|
-
const began = Date.now();
|
|
835
|
-
probe("tool.run", { name: call.name, id: call.id });
|
|
836
818
|
showTool(call);
|
|
837
819
|
// Exa's search, while its key is set (load-tools): its pages come back with
|
|
838
820
|
// the answer and stay on the line the way the hosted search's do
|
|
@@ -846,6 +828,7 @@ export function useThursday(
|
|
|
846
828
|
settings.webSearch,
|
|
847
829
|
stop.signal,
|
|
848
830
|
);
|
|
831
|
+
nameTool(call, output);
|
|
849
832
|
if (searching)
|
|
850
833
|
kept = keepSearch({
|
|
851
834
|
id: call.id,
|
|
@@ -857,7 +840,6 @@ export function useThursday(
|
|
|
857
840
|
});
|
|
858
841
|
return output;
|
|
859
842
|
} finally {
|
|
860
|
-
probe("tool.done", { id: call.id, ms: Date.now() - began });
|
|
861
843
|
if (!kept) hideTool(call.id);
|
|
862
844
|
}
|
|
863
845
|
},
|
|
@@ -946,19 +928,6 @@ export function useThursday(
|
|
|
946
928
|
.slice(-KEEP_MESSAGES),
|
|
947
929
|
);
|
|
948
930
|
}
|
|
949
|
-
// The experiment (config CALL_NUDGE): their words have settled, and if the voice
|
|
950
|
-
// hands nothing over in a moment the page starts the backend turn itself
|
|
951
|
-
if (CALL_NUDGE.on && turn.done && turn.role === "user" && words) {
|
|
952
|
-
if (nudge.current) clearTimeout(nudge.current);
|
|
953
|
-
const spoke = stirred.current;
|
|
954
|
-
nudge.current = setTimeout(() => {
|
|
955
|
-
nudge.current = null;
|
|
956
|
-
// they went on talking, or the backend has the turn already
|
|
957
|
-
if (stirred.current !== spoke || thinking.current !== null)
|
|
958
|
-
return;
|
|
959
|
-
probe("nudge", { sent: session.current?.nudge() ?? false });
|
|
960
|
-
}, CALL_NUDGE.waitMs);
|
|
961
|
-
}
|
|
962
931
|
if (turn.done) {
|
|
963
932
|
persist(saving, () =>
|
|
964
933
|
saveTurnsAction(line.callId, [
|
|
@@ -1076,6 +1045,7 @@ export function useThursday(
|
|
|
1076
1045
|
status,
|
|
1077
1046
|
hangUp,
|
|
1078
1047
|
showTool,
|
|
1048
|
+
nameTool,
|
|
1079
1049
|
hideTool,
|
|
1080
1050
|
showFace,
|
|
1081
1051
|
wearFace,
|
|
@@ -207,11 +207,13 @@ export const jobShellEnv = (
|
|
|
207
207
|
});
|
|
208
208
|
|
|
209
209
|
/**
|
|
210
|
-
*
|
|
211
|
-
* rather than a model typing the path (skills/interactive-page scripts/page.mjs).
|
|
210
|
+
* What a bot's scripts find in its shell rather than a model typing the path.
|
|
212
211
|
*/
|
|
213
212
|
export const botShellEnv = (bot: string): Record<string, string> => ({
|
|
213
|
+
// Where a script delivers a file (skills/interactive-page scripts/page.mjs)
|
|
214
214
|
THURSDAY_ARTIFACTS: botArtifacts(bot),
|
|
215
|
+
// The shipped skills: a kit script in a bot's folder imports the shared ones from here
|
|
216
|
+
THURSDAY_SKILLS: join(APP_DIR, PATHS.skills.default),
|
|
215
217
|
});
|
|
216
218
|
|
|
217
219
|
/** A participant's browser belongs to the job and its canonical bot name, across every caller. */
|
package/guide/bots.md
CHANGED
|
@@ -10,8 +10,11 @@ switched on; any of them can be switched off there, and added later from the sam
|
|
|
10
10
|
|
|
11
11
|
Every bot has the same kit: a shell on this computer, a real browser of its own or the one
|
|
12
12
|
the user is already signed into, their files, the web, whatever skills and connected services
|
|
13
|
-
are installed, and the other bots. A ready-made bot may also arrive with written methods
|
|
14
|
-
its own for its subject, which no other bot sees
|
|
13
|
+
are installed, and the other bots. A ready-made bot may also arrive with written methods and
|
|
14
|
+
scripts of its own for its subject, which no other bot sees: Insta studies accounts that do well
|
|
15
|
+
in a niche, builds posts the same way and reads messages without opening them (an opened thread
|
|
16
|
+
shows as seen); Analyst fetches published series and draws them; Marketer keeps one brief per
|
|
17
|
+
product in its memory and works from it.
|
|
15
18
|
|
|
16
19
|
## A job is a thread
|
|
17
20
|
|
|
@@ -75,6 +78,13 @@ of itself. Asking her on a call to show a result opens that file on the screen.
|
|
|
75
78
|
stay under **Settings › Files**, one shelf per bot. Its second tab, **All files**, is the
|
|
76
79
|
whole folder, for looking around rather than for the results.
|
|
77
80
|
|
|
81
|
+
Asked to explain something simply, **Tutor** makes a picture book: a picture and a line
|
|
82
|
+
or two a page, turned by swiping, the arrow keys, or tapping the right or left of the page. The
|
|
83
|
+
same book can be a PDF, or a video that reads itself aloud (landscape, or portrait when asked);
|
|
84
|
+
when the request does not say which, it asks first. It remembers what the user already
|
|
85
|
+
knows and how they liked being taught. The video needs a speech model in **Settings › Models**, and a drawn
|
|
86
|
+
picture an image model; without one it asks for it and stops.
|
|
87
|
+
|
|
78
88
|
A bot keeps its own memory — what a job taught it, how the user asked it to work — in its own
|
|
79
89
|
folder, listed on its page in Settings › Bots.
|
|
80
90
|
|
|
@@ -94,7 +104,7 @@ one switch. **When** has three kinds of time, and at most twelve routines exist:
|
|
|
94
104
|
- **Every few hours**: 1, 2, 3, 6, 12 or 24 hours after each start; any other number can be
|
|
95
105
|
asked of Thursday.
|
|
96
106
|
|
|
97
|
-
What is picked is filled
|
|
107
|
+
What is picked is filled blue, and the line under the field says it back in words, with when
|
|
98
108
|
it first starts.
|
|
99
109
|
|
|
100
110
|
Each time it is due, a routine opens an ordinary thread, marked with a repeat sign, and that
|
package/guide/calls.md
CHANGED
|
@@ -11,7 +11,8 @@ Three ways in, and any of them also answers a call she placed:
|
|
|
11
11
|
|
|
12
12
|
She speaks first. From the second call on she is told, as the call opens, when the last one
|
|
13
13
|
was and how it ended, so she can greet as someone who remembers it; a call she places herself
|
|
14
|
-
opens on why she called instead.
|
|
14
|
+
opens on why she called instead. She speaks the language the user speaks and switches when they
|
|
15
|
+
do; the browser's language plays no part, so the very first greeting is in whatever she picks.
|
|
15
16
|
|
|
16
17
|
The wake phrase and the shortcut are in **Settings › Thursday › Starting a call**, where
|
|
17
18
|
either can be switched on or off, or rewritten. The first-run setup offers the wake phrase
|
|
@@ -42,10 +43,11 @@ in **Settings › Thursday**. With nothing to run on it says so and sends nothin
|
|
|
42
43
|
**Settings › API keys**. **Esc** ends it — saying goodbye does not, since there is no
|
|
43
44
|
line for her to drop — and so does starting a spoken call: tapping her face in the middle of
|
|
44
45
|
one ends the written call and places a spoken one. When a turn breaks — a plan's limit, a
|
|
45
|
-
refused key — her face says ERROR and the provider's own words show in red under the line
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
refused key — her face says ERROR and the provider's own words show in red under the line,
|
|
47
|
+
over a blue **Send it again** and the way to **API keys**; writing again tries again too, and
|
|
48
|
+
Esc still ends it. When an OpenAI key is set and was not what broke, the button sends the same
|
|
49
|
+
words again on that key instead; it never switches by itself, since what a turn costs changes
|
|
50
|
+
with what it runs on. It is kept with
|
|
49
51
|
the other calls, marked *in writing*, and she reads it back on the next call like any other.
|
|
50
52
|
During a spoken call the line writes to bots only — but a file put down then (dropped on the
|
|
51
53
|
window, pasted, or picked with the paperclip) is one she is told about as it lands: its chip
|
package/knip.json
CHANGED
package/lib/live/live.session.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { LIVE_CALL } from "@/config";
|
|
2
2
|
import { logger } from "@/lib/logger";
|
|
3
|
-
import { briefLiveEvent, createProbe } from "@/lib/probe";
|
|
4
3
|
import { errorToString } from "@/lib/utils";
|
|
5
4
|
import {
|
|
6
5
|
LIVE_HOSTED_TOOLS,
|
|
@@ -9,8 +8,6 @@ import {
|
|
|
9
8
|
} from "./live.schema";
|
|
10
9
|
import { createWebRtcTransport } from "./live.transport";
|
|
11
10
|
|
|
12
|
-
const probe = createProbe("live");
|
|
13
|
-
|
|
14
11
|
/** A revisable display group, independent of audio playback and backend responses. */
|
|
15
12
|
export type LiveTurn = {
|
|
16
13
|
id: string;
|
|
@@ -313,12 +310,6 @@ export const createLiveSession = ({ initialize, audio, on }: LiveOptions) => {
|
|
|
313
310
|
return;
|
|
314
311
|
const [next] = appends;
|
|
315
312
|
pendingAppend = crypto.randomUUID();
|
|
316
|
-
probe("out", {
|
|
317
|
-
type: `session.${next.kind}.append`,
|
|
318
|
-
client: pendingAppend,
|
|
319
|
-
head: next.chunks[0].slice(0, 80),
|
|
320
|
-
chunksLeft: next.chunks.length,
|
|
321
|
-
});
|
|
322
313
|
transport.send({
|
|
323
314
|
type: `session.${next.kind}.append`,
|
|
324
315
|
event_id: pendingAppend,
|
|
@@ -405,7 +396,6 @@ export const createLiveSession = ({ initialize, audio, on }: LiveOptions) => {
|
|
|
405
396
|
return;
|
|
406
397
|
response.continued = true;
|
|
407
398
|
await Promise.all(response.calls.values());
|
|
408
|
-
probe("out", { type: "response.create", closing, closed });
|
|
409
399
|
if (!closing && !closed) {
|
|
410
400
|
continuedAt = performance.now();
|
|
411
401
|
transport.send({
|
|
@@ -422,8 +412,6 @@ export const createLiveSession = ({ initialize, audio, on }: LiveOptions) => {
|
|
|
422
412
|
events.add(event.event_id);
|
|
423
413
|
}
|
|
424
414
|
timeline = Math.max(timeline, event.offset_ms ?? 0, event.end_ms ?? 0);
|
|
425
|
-
if (!event.type.endsWith(".delta") && !event.event?.type.endsWith(".delta"))
|
|
426
|
-
probe("in", briefLiveEvent(event));
|
|
427
415
|
switch (event.type) {
|
|
428
416
|
case "session.started":
|
|
429
417
|
started = true;
|
|
@@ -576,12 +564,6 @@ export const createLiveSession = ({ initialize, audio, on }: LiveOptions) => {
|
|
|
576
564
|
.catch((cause) => `Error: ${errorToString(cause)}`)
|
|
577
565
|
.then((output) => {
|
|
578
566
|
tools.delete(item.call_id);
|
|
579
|
-
probe("out", {
|
|
580
|
-
type: "function_call_output",
|
|
581
|
-
name: item.name,
|
|
582
|
-
call: item.call_id,
|
|
583
|
-
size: output.length,
|
|
584
|
-
});
|
|
585
567
|
if (!closed && !closing)
|
|
586
568
|
transport.send({
|
|
587
569
|
type: "response.item.create",
|
|
@@ -690,7 +672,6 @@ export const createLiveSession = ({ initialize, audio, on }: LiveOptions) => {
|
|
|
690
672
|
cleanup();
|
|
691
673
|
}, LIVE_CALL.closeMs);
|
|
692
674
|
audio.element.muted = true;
|
|
693
|
-
probe("out", { type: "session.close" });
|
|
694
675
|
transport.send({ type: "session.close", event_id: crypto.randomUUID() });
|
|
695
676
|
return closePromise;
|
|
696
677
|
},
|
|
@@ -708,25 +689,5 @@ export const createLiveSession = ({ initialize, audio, on }: LiveOptions) => {
|
|
|
708
689
|
appendNext();
|
|
709
690
|
});
|
|
710
691
|
},
|
|
711
|
-
/**
|
|
712
|
-
* Starts a backend turn the voice did not hand over: the same `response.create` that
|
|
713
|
-
* continues delegated work, sent with nothing pending. Does nothing while the backend
|
|
714
|
-
* already holds the turn, or once the call is closing. False when it was not sent.
|
|
715
|
-
*/
|
|
716
|
-
nudge(): boolean {
|
|
717
|
-
const busy =
|
|
718
|
-
[...responses.values()].some((response) => !response.terminal) ||
|
|
719
|
-
tools.size > 0 ||
|
|
720
|
-
performance.now() - continuedAt < CONTINUE_GAP_MS;
|
|
721
|
-
if (closed || closing || busy) return false;
|
|
722
|
-
probe("out", { type: "response.create", nudge: true });
|
|
723
|
-
continuedAt = performance.now();
|
|
724
|
-
transport.send({
|
|
725
|
-
type: "response.create",
|
|
726
|
-
event_id: crypto.randomUUID(),
|
|
727
|
-
});
|
|
728
|
-
activity();
|
|
729
|
-
return true;
|
|
730
|
-
},
|
|
731
692
|
};
|
|
732
693
|
};
|
package/package.json
CHANGED
|
@@ -860,7 +860,7 @@ test("stored settings keep OpenAI choices and the shared instruction, drop a Gro
|
|
|
860
860
|
assert.deepEqual(migrateLiveSettings(null), LIVE_DEFAULTS);
|
|
861
861
|
});
|
|
862
862
|
|
|
863
|
-
test("both call prompts open as one Thursday: the voice gets
|
|
863
|
+
test("both call prompts open as one Thursday: the voice gets the guide's delegation policy and memory and no earlier calls, the backend carries work on in threads, and every call has an opening", async () => {
|
|
864
864
|
let profileFacts = 200;
|
|
865
865
|
let samFacts = 2;
|
|
866
866
|
const botMock = mock.module("../features/bot/bot.query.ts", {
|
|
@@ -949,13 +949,12 @@ test("both call prompts open as one Thursday: the voice gets a two-line delegati
|
|
|
949
949
|
);
|
|
950
950
|
const on = await loadLivePrompt({
|
|
951
951
|
voicePrompt: "Use a calm voice.",
|
|
952
|
-
locale: "ko-KR",
|
|
953
952
|
});
|
|
954
953
|
assert.match(on.text, /modeled on Friday, the AI in \*Iron Man\*/);
|
|
955
954
|
assert.match(on.text, /Prefer brief replies/);
|
|
956
955
|
assert.match(
|
|
957
956
|
on.text,
|
|
958
|
-
/\n\n## Always\n\nIMPORTANT — always follow this: [^\n]+\n\nBackchannel policy: Use moderate backchannels\. .*\n\nInterruption policy: Stop speaking when the user interrupts\. Listen to what they say\.\n\
|
|
957
|
+
/\n\n## Always\n\nIMPORTANT — always follow this: [^\n]+\n\nBackchannel policy: Use moderate backchannels\. .*\n\nInterruption policy: Stop speaking when the user interrupts\. Listen to what they say\.\n\nSpeak the language the user is speaking, [^\n]+\n\nDelegation policy:\nBackend tools:\n- Ending the call: [^\n]+\n(- [^\n]+\n){4}\nDelegate to the backend when:\n- The user wants the call to end\.\n(- [^\n]+\n)+\nDo not delegate to the backend when:\n(- [^\n]+\n)+\nDelegate before giving an answer that depends on backend work\. Do not guess the result while waiting\.\n\n## What you know about them\n/,
|
|
959
958
|
);
|
|
960
959
|
// Only the ending rule carries the stamp
|
|
961
960
|
assert.equal(on.text.split("IMPORTANT").length, 2);
|
|
@@ -963,13 +962,10 @@ test("both call prompts open as one Thursday: the voice gets a two-line delegati
|
|
|
963
962
|
on.text,
|
|
964
963
|
/When the user speaks at length, acknowledge now and then/,
|
|
965
964
|
);
|
|
966
|
-
// What the backend can do,
|
|
967
|
-
assert.equal(
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
),
|
|
971
|
-
false,
|
|
972
|
-
);
|
|
965
|
+
// What the backend can do, never how: no skills, connected tools or bots by name
|
|
966
|
+
assert.equal(/What bots can reach for|- Web:/.test(on.text), false);
|
|
967
|
+
// Stopping her voice is hers, stopping a job the backend's
|
|
968
|
+
assert.match(on.text, /or only want you to stop talking/);
|
|
973
969
|
assert.match(on.text, /- people\/sam — Their brother \(2\) "Sam"/);
|
|
974
970
|
assert.match(on.text, /What is in these notes, the backend recalls\./);
|
|
975
971
|
// The roster, threads and earlier calls are the backend's to read
|
|
@@ -979,32 +975,33 @@ test("both call prompts open as one Thursday: the voice gets a two-line delegati
|
|
|
979
975
|
false,
|
|
980
976
|
);
|
|
981
977
|
assert.equal("input" in on, false);
|
|
982
|
-
assert.equal(
|
|
983
|
-
/ko-KR|browser's setting|language they use/.test(on.text),
|
|
984
|
-
false,
|
|
985
|
-
);
|
|
986
978
|
assert.equal(/memory_|generate_|load_skill|`/.test(on.text), false);
|
|
987
|
-
// The one tool name the voice holds: the ending rule, where it makes ending a thing to do
|
|
979
|
+
// The one tool name the voice holds: the ending rule, where it makes ending a thing to do.
|
|
980
|
+
// It holds no tools, so the rule is to hand the turn over, not to use the tool
|
|
988
981
|
assert.equal(on.text.split("end_call").length, 2);
|
|
989
982
|
assert.match(
|
|
990
983
|
on.text,
|
|
991
|
-
/
|
|
984
|
+
/answer yes in one word of their language and hand it to the backend at once\. Only the backend can end the line, with the end_call tool/,
|
|
992
985
|
);
|
|
993
986
|
assert.equal(on.text.endsWith("Use a calm voice."), true);
|
|
994
987
|
assert.match(on.opening, /The call has just started/);
|
|
995
988
|
|
|
996
|
-
// One Thursday: the backend opens with the voice's own
|
|
989
|
+
// One Thursday: the backend opens with the voice's own identity and is never told it is a
|
|
990
|
+
// part; its ending rule is its own, since only it can end the line
|
|
997
991
|
const backend = await loadThursdayPrompt(null);
|
|
998
992
|
const withoutClock = (text: string) =>
|
|
999
993
|
text.replace(/\*\*Now\*\*: [^\n]+/, "");
|
|
1000
994
|
const identity = on.text.slice(0, on.text.indexOf("\n\n## Always"));
|
|
1001
|
-
const ending = /IMPORTANT — always follow this: [^\n]+/.exec(on.text)?.[0];
|
|
1002
995
|
assert.equal(
|
|
1003
996
|
withoutClock(backend).startsWith(
|
|
1004
|
-
`${withoutClock(identity)}\n\
|
|
997
|
+
`${withoutClock(identity)}\n\nIMPORTANT — always follow this: `,
|
|
1005
998
|
),
|
|
1006
999
|
true,
|
|
1007
1000
|
);
|
|
1001
|
+
assert.match(
|
|
1002
|
+
backend,
|
|
1003
|
+
/forget every other task, answer yes in one word of their language, then immediately, without thinking, use the end_call tool\./,
|
|
1004
|
+
);
|
|
1008
1005
|
assert.match(
|
|
1009
1006
|
backend,
|
|
1010
1007
|
/\*\*Keep memory clean as you write\.\*\* A fact that repeats, narrows or changes one already in the note replaces it/,
|
|
@@ -1045,7 +1042,7 @@ test("both call prompts open as one Thursday: the voice gets a two-line delegati
|
|
|
1045
1042
|
|
|
1046
1043
|
// Tidying memory is the backend's: the voice neither reads it nor opens with it
|
|
1047
1044
|
samFacts = 60;
|
|
1048
|
-
const heavy = await loadLivePrompt({
|
|
1045
|
+
const heavy = await loadLivePrompt({});
|
|
1049
1046
|
assert.equal(/tidying|grown past/.test(heavy.text), false);
|
|
1050
1047
|
assert.match(heavy.opening, /The call has just started/);
|
|
1051
1048
|
assert.match(
|
|
@@ -1055,19 +1052,15 @@ test("both call prompts open as one Thursday: the voice gets a two-line delegati
|
|
|
1055
1052
|
samFacts = 2;
|
|
1056
1053
|
|
|
1057
1054
|
profileFacts = 0;
|
|
1058
|
-
const first = await loadLivePrompt({
|
|
1055
|
+
const first = await loadLivePrompt({});
|
|
1059
1056
|
assert.match(first.text, /## First call/);
|
|
1060
1057
|
// The one call that opens with nothing: she says who she is, then learns who they are
|
|
1061
1058
|
assert.match(first.opening ?? "", /say who you are/);
|
|
1062
1059
|
assert.match(first.opening ?? "", /ask what to call them/);
|
|
1063
|
-
assert.match(first.opening ?? "", /ko-KR/);
|
|
1064
1060
|
assert.match(first.text, /what they do, how old they are, where they live/);
|
|
1065
1061
|
|
|
1066
1062
|
// A call the page placed says so, ahead of even the first-call opening
|
|
1067
|
-
const rung = await loadLivePrompt({
|
|
1068
|
-
locale: "ko-KR",
|
|
1069
|
-
calledBack: true,
|
|
1070
|
-
});
|
|
1063
|
+
const rung = await loadLivePrompt({ calledBack: true });
|
|
1071
1064
|
assert.match(rung.opening, /You placed this call/);
|
|
1072
1065
|
assert.match(rung.opening, /say that is why you called/);
|
|
1073
1066
|
} finally {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Analyst's kit
|
|
2
|
+
|
|
3
|
+
One skill the Analyst seed is born with, copied into `.agents/skills/` in its own folder when it is
|
|
4
|
+
created. It is listed to that bot alone.
|
|
5
|
+
|
|
6
|
+
| Skill | What it adds |
|
|
7
|
+
|---|---|
|
|
8
|
+
| `data-report` | Seven report forms, each with its structure and a filled example; `fetch.mjs`, which pulls a published series (FRED, World Bank, Yahoo Finance, ECB rates, SEC filings, Wikipedia pageviews) into a CSV that names its source |
|
|
9
|
+
|
|
10
|
+
Written for this app. The forms borrow their shape from answer-first business writing and from the
|
|
11
|
+
report outlines in [anthropics/financial-services](https://github.com/anthropics/financial-services)
|
|
12
|
+
and [anthropics/knowledge-work-plugins](https://github.com/anthropics/knowledge-work-plugins)
|
|
13
|
+
(Apache-2.0); no text is copied from either. Those skills write Word, PowerPoint and Excel files and
|
|
14
|
+
read paid data connectors, which is why they are not taken whole.
|
|
15
|
+
|
|
16
|
+
Charts are drawn by the shipped `interactive-page` skill's `chart.mjs` into its quick page. The
|
|
17
|
+
scripts need Node and no packages. Every source they call answers without a key; `references/sources.md` names the
|
|
18
|
+
ones that need one.
|