thursday-agent 0.10.0 → 0.12.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]__04wldw9._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0542v_d._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0cfszwn._.js +1 -1
- 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]__0npn_pn._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0nr-v8s._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0rqgb17._.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]__1tg5got._.js +1 -1
- 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/_0f6ts7z._.js +1 -1
- package/.next/server/chunks/_0na2fj1._.js +1 -1
- package/.next/server/chunks/_0x-ej3f._.js +17 -17
- package/.next/server/chunks/_10tkr26._.js +1 -1
- 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/_1taa4bs._.js +1 -1
- package/.next/server/chunks/_1tpr507._.js +1 -1
- 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]__0a967km._.js +3 -0
- 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]__0tzekbl._.js +24 -60
- 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]__1r77-88._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1w9hqq8._.js +1 -1
- package/.next/server/chunks/ssr/_026qnc9._.js +1 -1
- package/.next/server/chunks/ssr/{_173siic._.js → _02c7o30._.js} +2 -2
- package/.next/server/chunks/ssr/_097-ljd._.js +1 -1
- package/.next/server/chunks/ssr/_0euzzf8._.js +3 -0
- package/.next/server/chunks/ssr/{_1u2jw0i._.js → _0fbi6om._.js} +3 -3
- package/.next/server/chunks/ssr/_0fqhhwi._.js +1 -1
- package/.next/server/chunks/ssr/_0igfcax._.js +20 -0
- package/.next/server/chunks/ssr/_0ld9_qq._.js +1 -1
- package/.next/server/chunks/ssr/_0nvh1h9._.js +3 -0
- package/.next/server/chunks/ssr/_0qmpw35._.js +3 -0
- package/.next/server/chunks/ssr/{_1yk6yak._.js → _0rmaf32._.js} +2 -2
- package/.next/server/chunks/ssr/_0uv-qs4._.js +3 -0
- package/.next/server/chunks/ssr/{_1doidrw._.js → _10f-jib._.js} +2 -2
- package/.next/server/chunks/ssr/{_05rxyf9._.js → _10mo72m._.js} +2 -2
- package/.next/server/chunks/ssr/_12mijol._.js +11 -11
- package/.next/server/chunks/ssr/_132qkfz._.js +1 -1
- package/.next/server/chunks/ssr/_15zqt-t._.js +1 -1
- package/.next/server/chunks/ssr/_16gfxpn._.js +3 -0
- package/.next/server/chunks/ssr/_184327j._.js +1 -1
- package/.next/server/chunks/ssr/{_1ng831c._.js → _18ujhhq._.js} +2 -2
- package/.next/server/chunks/ssr/_1d423aj._.js +7 -5
- package/.next/server/chunks/ssr/_1lx6w_o._.js +67 -0
- package/.next/server/chunks/ssr/_1mqn9mc._.js +1 -1
- package/.next/server/chunks/ssr/_1neoj22._.js +1 -1
- package/.next/server/chunks/ssr/{_19n08g5._.js → _1ntpny5._.js} +2 -2
- package/.next/server/chunks/ssr/{_0bp-_fi._.js → _1o1g4uk._.js} +3 -3
- package/.next/server/chunks/ssr/_1xoch6f._.js +1 -1
- package/.next/server/chunks/ssr/_1yh5bj5._.js +1 -1
- package/.next/server/chunks/ssr/{_0tg0vf8._.js → _2008vmi._.js} +8 -8
- package/.next/server/chunks/ssr/{features_bot_1yr-_pm._.js → features_bot_1n1s7bh._.js} +2 -2
- 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_1n0h9sf._.js → node_modules_0-x0zxm._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1xzo2wn._.js → node_modules_00nrzz_._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0511ha-._.js → node_modules_01ed0yi._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_18woe20._.js → node_modules_01u4ea3._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1expacw._.js → node_modules_05b6e99._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0eb4rx7._.js → node_modules_07jp6tj._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0piws2a._.js → node_modules_0azd56x._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0f2k5l0._.js → node_modules_0b0iy01._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0wq4-ts._.js → node_modules_0fpnetd._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_11hbkuj._.js → node_modules_0ky76fz._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_048wmmj._.js → node_modules_0lautv3._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1dcbvfw._.js → node_modules_0m3_5ge._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0pk6i-e._.js → node_modules_0operbz._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1zkpek5._.js → node_modules_0ou0all._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_04t200t._.js → node_modules_0ucridu._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_08g12f-._.js → node_modules_0v_-uht._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1m2ho33._.js → node_modules_0yzmxfq._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1ksh9gy._.js → node_modules_19b0bir._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1qehmwm._.js → node_modules_1c2ikr9._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1_afpz7._.js → node_modules_1hv-j1l._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0cdu6jq._.js → node_modules_1iau_ea._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1zf46lp._.js → node_modules_1ma_y68._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0s1lwc9._.js → node_modules_1omg9xr._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1cxc8kn._.js → node_modules_1wgnj8q._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0zjqwz7._.js → node_modules_1wvf3oo._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1_ban4u._.js → node_modules_1z3sh11._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0avl8-1._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0tmjn9w._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_14oslro._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_1_e8c3o._.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/09pkygbht7ki8.js +18 -0
- package/.next/static/chunks/0bm-nhblcmmj4.js +17 -0
- package/.next/static/chunks/{22shnca_6zfzs.js → 0hhza87hkbqaz.js} +1 -1
- package/.next/static/chunks/{19ojn4lomsk0r.js → 0mscosx0eh3ee.js} +1 -1
- package/.next/static/chunks/{3vynnr71ghvom.js → 0p50d-au6-3hx.js} +2 -2
- package/.next/static/chunks/{42890jqk049q1.js → 0p9jcyqkpq71f.js} +1 -1
- package/.next/static/chunks/{35kwn237o4wdn.js → 0qgn3u86jkbm7.js} +1 -1
- package/.next/static/chunks/{2d4z3er53ng8r.js → 0samf80yu2b17.js} +1 -1
- package/.next/static/chunks/{1pjygp7hqib6j.js → 1czfknd4luo6t.js} +1 -1
- package/.next/static/chunks/{09120tms63y21.js → 1jz8n0mhtdzr8.js} +1 -1
- package/.next/static/chunks/206laxm6maiz2.js +1 -0
- package/.next/static/chunks/{24dst4mgurbsh.js → 2_c7mu6x1lu49.js} +1 -1
- package/.next/static/chunks/{2ky81z-jgvasc.js → 2h-tlhb2ffhkk.js} +1 -1
- package/.next/static/chunks/{0oz3rs47yy3p8.js → 2i5_w8olnrvo0.js} +2 -2
- package/.next/static/chunks/{1mwee7xvitlp9.js → 2m8iqwc6sykln.js} +1 -1
- package/.next/static/chunks/{0o980477kfqez.js → 2r6xnyg7ezyxr.js} +1 -1
- package/.next/static/chunks/33fyp-byw48ju.js +65 -0
- package/.next/static/chunks/{2ndw4w4u2x51r.js → 35yd_27d9ylra.js} +1 -1
- package/.next/static/chunks/{3exjsi8pfdvzj.js → 3ag1i3__yl94f.js} +1 -1
- package/.next/static/chunks/{3obf9n3cxj99k.js → 3jvldk2wvwbge.js} +1 -1
- package/.next/static/chunks/3m_ejzg-xn21r.css +1 -0
- package/.next/static/chunks/{0w20wnnfrscq7.js → 3s-jpd3ky49-n.js} +1 -1
- package/.next/static/chunks/40ij3wsn3vt7h.js +1 -0
- package/README.md +1 -1
- package/app/api/thursday/tool-call/route.ts +1 -15
- package/docs/images/demo-still.jpg +0 -0
- package/docs/live-calls.md +19 -11
- package/features/ai/components/model-picker.tsx +8 -2
- package/features/ai/model.schema.ts +26 -0
- 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 +3 -12
- package/features/bot/components/room-list.tsx +1 -1
- package/features/bot/components/room-pill.tsx +44 -55
- package/features/bot/seed-bots.ts +2 -2
- package/features/config/components/config-badge.tsx +6 -0
- package/features/config/components/config-setting.tsx +20 -1
- package/features/config/components/models-badge.tsx +5 -7
- package/features/config/components/voice-key.tsx +24 -1
- package/features/config/config.const.ts +7 -0
- package/features/intro/components/intro.tsx +4 -19
- package/features/reach/reach.ts +2 -2
- package/features/settings/components/setting-ui.tsx +14 -5
- package/features/settings/components/settings.tsx +3 -1
- package/features/settings/settings.alert.ts +12 -11
- package/features/signins/signins.query.ts +66 -1
- package/features/thursday/components/side-captions.tsx +31 -8
- package/features/thursday/components/tab-state.tsx +5 -2
- package/features/thursday/components/thursday.tsx +158 -68
- package/features/thursday/components/write-line.tsx +37 -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 +12 -2
- package/guide/calls.md +9 -6
- package/guide/setup.md +4 -1
- 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 +3 -4
- package/seed-skills/analyst/data-report/SKILL.md +5 -4
- package/seed-skills/insta/instagram/SKILL.md +5 -2
- package/seed-skills/insta/instagram/references/post.md +2 -2
- package/seed-skills/insta/instagram/scripts/compose.mjs +5 -1
- package/seed-skills/insta/instagram/scripts/inbox.mjs +5 -1
- package/seed-skills/insta/instagram/scripts/lib.mjs +12 -70
- package/seed-skills/insta/instagram/scripts/media.mjs +1 -1
- package/seed-skills/insta/instagram/scripts/profile.mjs +6 -2
- package/seed-skills/insta/instagram/scripts/read.mjs +5 -1
- package/seed-skills/insta/instagram/scripts/search.mjs +6 -2
- package/seed-skills/insta/instagram/scripts/sheet.mjs +31 -88
- package/seed-skills/insta/instagram/templates/carousel.html +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/{seed-skills/insta/instagram → skills/browser}/scripts/render.mjs +1 -1
- package/skills/browser/scripts/session.mjs +105 -0
- package/skills/browser/scripts/sheet.mjs +97 -0
- package/{seed-skills/insta/instagram → skills/browser}/scripts/webimage.mjs +1 -1
- package/skills/interactive-page/SKILL.md +4 -3
- package/{seed-skills/analyst/data-report → skills/interactive-page}/scripts/chart.mjs +3 -3
- 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]__0cxqzfc._.js +0 -103
- package/.next/server/chunks/ssr/[root-of-the-server]__1-d2tgh._.js +0 -3
- package/.next/server/chunks/ssr/_070nv1_._.js +0 -3
- package/.next/server/chunks/ssr/_0hlxij-._.js +0 -3
- package/.next/server/chunks/ssr/_0p9pmxe._.js +0 -3
- package/.next/server/chunks/ssr/_0s41irp._.js +0 -3
- package/.next/server/chunks/ssr/_157yfrk._.js +0 -3
- package/.next/server/chunks/ssr/_17v-33v._.js +0 -20
- package/.next/server/chunks/ssr/_1flsjeo._.js +0 -3
- package/.next/static/chunks/03kgfyrli_0gz.js +0 -1
- package/.next/static/chunks/1p764d75_0lhv.js +0 -18
- package/.next/static/chunks/25zq8gzhbdsr7.js +0 -1
- package/.next/static/chunks/2qod5tqlpz8b7.js +0 -17
- package/.next/static/chunks/30skcjexa57zn.js +0 -101
- package/.next/static/chunks/3qq4o8w2rv_e9.css +0 -1
- package/lib/probe.server.ts +0 -17
- package/lib/probe.ts +0 -55
- package/seed-skills/mail/mail/SKILL.md +0 -37
- package/seed-skills/mail/mail/recipes/gmail.mjs +0 -448
- package/seed-skills/mail/mail/references/recipes.md +0 -55
- package/seed-skills/mail/mail/scripts/mail.mjs +0 -202
- /package/.next/static/{Ar5V4kMdL4CE1trx8anrH → QSe0rTdXU5kh5BBzr2XBB}/_buildManifest.js +0 -0
- /package/.next/static/{Ar5V4kMdL4CE1trx8anrH → QSe0rTdXU5kh5BBzr2XBB}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{Ar5V4kMdL4CE1trx8anrH → QSe0rTdXU5kh5BBzr2XBB}/_ssgManifest.js +0 -0
- /package/{seed-skills/insta/instagram → skills/browser}/scripts/image-size.mjs +0 -0
package/features/bot/bot.seed.ts
CHANGED
|
@@ -19,9 +19,9 @@ import { type BotIcon, randomBotIcons } from "./bot.schema";
|
|
|
19
19
|
* Every field stays within the bot form's limits (lib/limits COMMON_VALIDATE), or an
|
|
20
20
|
* edit to it cannot be saved.
|
|
21
21
|
*
|
|
22
|
-
* The list is flat and grows
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
* The list is flat and grows, and every seed is offered alike: a key saved outside the
|
|
23
|
+
* intro installs them all (seed-bots), the intro's opening loop shows every face, and the
|
|
24
|
+
* intro and Settings › Bots offer every seed, all ticked.
|
|
25
25
|
*/
|
|
26
26
|
export type BotSeed = {
|
|
27
27
|
name: string;
|
|
@@ -30,8 +30,6 @@ export type BotSeed = {
|
|
|
30
30
|
/** What the screen shows under the name: one line, never wrapped. */
|
|
31
31
|
hint: string;
|
|
32
32
|
systemPrompt: string;
|
|
33
|
-
/** Ticked by default in the intro, which offers every seed. */
|
|
34
|
-
recommended?: boolean;
|
|
35
33
|
/**
|
|
36
34
|
* Studio models this bot cannot work without (config MEDIA_MODEL_KEYS). Unset
|
|
37
35
|
* ones are named on its row, because an unset media model is not a fallback —
|
|
@@ -48,7 +46,6 @@ export const BOT_SEEDS: BotSeed[] = [
|
|
|
48
46
|
description:
|
|
49
47
|
"Anything nobody else is for — orders, bookings, forms, the web, this computer",
|
|
50
48
|
hint: "Takes whatever nobody else is for",
|
|
51
|
-
recommended: true,
|
|
52
49
|
systemPrompt: "",
|
|
53
50
|
},
|
|
54
51
|
{
|
|
@@ -75,42 +72,40 @@ export const BOT_SEEDS: BotSeed[] = [
|
|
|
75
72
|
{
|
|
76
73
|
name: "Analyst",
|
|
77
74
|
description:
|
|
78
|
-
"
|
|
75
|
+
"Answers with numbers — costs, markets, trends, companies, which to pick — cited and charted",
|
|
79
76
|
hint: "Finds the numbers and draws them",
|
|
80
|
-
|
|
81
|
-
systemPrompt: `Questions answered with numbers are yours — a market, prices, a comparison, a trend, a budget, what changed and by how much.
|
|
77
|
+
systemPrompt: `Questions answered with numbers are yours — what something costs, how big a market is, how a figure moved and why, how a company is doing, which option to pick, whether to buy now or wait.
|
|
82
78
|
|
|
83
|
-
**
|
|
79
|
+
**Answer first.** Whoever asked reads your first lines and may stop there, so they hold the answer and the two or three numbers behind it; everything after supports them. A title says the finding ("Rent rose faster than pay"), not the topic.
|
|
84
80
|
|
|
85
|
-
**
|
|
81
|
+
**Size the answer to the ask.** A few numbers are your final text and nothing more. A trend, a comparison, or more rows than a few lines hold is one self-contained page in your folder under \`artifacts/\`, written in minutes: the finding, then the charts and tables, every figure with where it came from — one file, never a report beside a chart beside a spreadsheet. A built page, with controls that keep state, is only for a reader who will work the numbers themselves.
|
|
86
82
|
|
|
87
|
-
**
|
|
83
|
+
**Work by a skill.** Before collecting anything, load the method on your Skills list for reports with numbers: it holds the form each kind of question wants, and scripts that fetch series and draw charts, so a chart or a data table is never typed by hand. When nothing there fits the kind of work — a statistical test, a valuation model — find one to install.
|
|
88
84
|
|
|
89
|
-
**
|
|
85
|
+
**Get the real numbers.** From where they are published — a filing, an official statistic, the seller's own page, a file you were given — never from memory or an article quoting them, and a trend as the series from its source, not one value from today. Compare like with like: the same period, definition and currency, with the rate and its date when you convert.
|
|
90
86
|
|
|
91
|
-
**
|
|
87
|
+
**Show your working.** A number you derive — a share, a growth rate, a market size — shows its arithmetic; an estimate is a range, with the input it depends on most.
|
|
92
88
|
|
|
93
|
-
|
|
89
|
+
**Mark what is not solid.** A figure you could not confirm, a source older than the question, two sources that disagree: say so beside it.
|
|
90
|
+
|
|
91
|
+
**One bash call per step.** Fetch every series in one call, draw every chart in the next.
|
|
92
|
+
|
|
93
|
+
Your final text gives the answer with its numbers, what is not solid, and the page's path when there is one.`,
|
|
94
94
|
},
|
|
95
95
|
{
|
|
96
|
-
name: "
|
|
96
|
+
name: "Tutor",
|
|
97
97
|
description:
|
|
98
|
-
"
|
|
99
|
-
hint: "
|
|
100
|
-
|
|
101
|
-
systemPrompt: `Work that will be done again is yours to turn into code — a report pulled every morning, files converted the same way each time, a check run over a list, one value read off a page. Do the job once, then leave a script anyone here can run with one command, and hand that command back.
|
|
102
|
-
|
|
103
|
-
**Read your map first.** Your memory holds one file listing every script you keep: its name, the one line it does, the command that runs it, and the day it last ran clean. Read it before building anything. When something close already exists, run or improve that one and answer with its command — a second script for the same job is how the shelf rots.
|
|
104
|
-
|
|
105
|
-
**One folder per script**, under \`projects/\`, named for what it does: the script itself, whatever it needs installed beside it (a virtualenv, packages), and a \`README.md\` holding the one-line command and its options. Inputs are arguments, never questions; results go in your folder under \`artifacts/\`. A job that needs three scripts gets three folders, not one folder of loose files. Write the simplest version that gives the right result.
|
|
98
|
+
"Explains anything simply, as a picture book — a picture and a line or two a page — or a video of it",
|
|
99
|
+
hint: "Explains anything like a picture book",
|
|
100
|
+
systemPrompt: `Explaining is yours — anything someone wants to understand, told so that a person who knows nothing about it follows every step. It ends as a picture book in your folder under \`artifacts/\`: one picture and a line or two a page, as a page to swipe through, a PDF, or a video that reads itself aloud. Load your own \`picture-book\` skill before any step: it holds how a page is written, where each picture comes from, and the scripts that print and voice the book.
|
|
106
101
|
|
|
107
|
-
**
|
|
102
|
+
**Ask which one, once.** When the request does not say a page, a PDF or a video, send Thursday one \`${TOOL_NAMES.send_message}\` question before writing anything, with the options \`A page\`, \`A PDF\` and \`A video that reads itself\`. A video spends a speech model on every page.
|
|
108
103
|
|
|
109
|
-
**
|
|
104
|
+
**Simple, never wrong.** Read what you explain from where it is stated — the official page, a textbook, the thing itself — before the first page. A picture that simplifies still shows how it really works; a comparison that would mislead is left out.
|
|
110
105
|
|
|
111
|
-
**
|
|
106
|
+
**Start where they are.** Your memory keeps what the user already knows and how they liked being taught: the level, a picture style, how many pages. Read it before the story, and after a book keep what this one showed, dated.
|
|
112
107
|
|
|
113
|
-
Your final text gives the
|
|
108
|
+
Your final text gives the path, and in one sentence the idea the book leaves them with.`,
|
|
114
109
|
},
|
|
115
110
|
{
|
|
116
111
|
name: "Marketer",
|
|
@@ -126,82 +121,31 @@ Your final text gives the command, what it produced this run, and where.`,
|
|
|
126
121
|
**Hand off what is not marketing work.** Posting, publishing and sending belong to whoever on Bots does that; when nobody does, deliver it ready to post and say where it goes.
|
|
127
122
|
|
|
128
123
|
Your final text gives the file's path, the angle you would lead with, and what to test first.`,
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
name: "Shorts",
|
|
132
|
-
description:
|
|
133
|
-
"Vertical short videos — writes the script, draws the scenes, voices them, and builds the mp4",
|
|
134
|
-
hint: "Turns an idea into a short video",
|
|
135
|
-
requires: ["image", "speech"],
|
|
136
|
-
systemPrompt: `A short is a vertical video watched with the sound off and still followed — it ends as one mp4 in your folder under \`artifacts/\`, 1080x1920.
|
|
137
|
-
|
|
138
|
-
**Check the tools before you plan.** \`${TOOL_NAMES.tool_search}\` with \`server: "${STUDIO_SERVER}"\` and \`tools: ["${STUDIO_TOOLS.generate_image}", "${STUDIO_TOOLS.generate_speech}"]\`. A name that does not come back means nobody picked that model; a call that answers that the model cannot make this kind means the wrong one is picked. Either way the tool is not yours to use: send Thursday a \`${TOOL_NAMES.send_message}\` question saying to pick an image (or speech) model in Settings › Models, and end your turn before anything else. Never work around it.
|
|
139
|
-
|
|
140
|
-
**Write the script as lines, one line per scene.** Each line is one spoken sentence, short enough to read at a glance. The first line has to earn the next three seconds.
|
|
141
|
-
|
|
142
|
-
**Make the voice first, and let it set the timing.** Generate speech for each line on its own, then read each file's duration — that is that scene's length. Never guess it, and never transcribe your own audio to find it.
|
|
143
|
-
|
|
144
|
-
**Draw one picture per scene**, \`aspectRatio: "9:16"\`. Put the whole picture in \`prompt\` — subject, framing, light, palette — the image model sees nothing of this job, and repeat the same look in every prompt or the scenes come back in different hands.
|
|
145
|
-
|
|
146
|
-
**Frames are HTML, not a video filter.** ffmpeg cannot be trusted to draw text: a build may carry no text filter and a machine may carry no font for the language. So build each scene as one HTML file — picture, caption, layout — then \`resize 1080 1920\`, serve it and open it the way the \`${BROWSER_SKILL}\` skill does, and \`screenshot\`. Load that skill before any of it. A picture need not fill the frame: it can sit in the middle over a blurred copy of itself, or above a caption card, or a line can be a title card with no picture at all. Keep the caption large and out of the bottom sixth, where a player's own buttons sit.
|
|
147
|
-
|
|
148
|
-
**Assemble with ffmpeg, and expect it to be missing.** Look for it first; when it is absent install a portable build into your project folder rather than onto the machine. Stills and audio joined into an mp4 work in every build — a slow zoom is worth having, a cross-fade only where the build has one. h264 and yuv420p, or phones refuse to play it.
|
|
149
|
-
|
|
150
|
-
**Keep the toolchain, not just the video.** Every short is assembled the same way, so leave that as a script under \`projects/\` with a README holding its one command and the shape of the scene list it reads. The next job edits the scene list and runs it. Play what you wrote before you report it.
|
|
151
|
-
|
|
152
|
-
**A video model is not the default.** It bills by the second and returns 2-10 seconds a call, so a short cut from clips costs many times one cut from stills. Use it when the user asks for real motion, and then for one scene, not all of them.
|
|
153
|
-
|
|
154
|
-
Your final text gives the mp4's path, how long it runs, and the opening line.`,
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
name: "Mail",
|
|
158
|
-
description:
|
|
159
|
-
"The user's mail — what arrived, what it needs, what it says, and the reply that goes back",
|
|
160
|
-
hint: "Reads the inbox and writes the replies",
|
|
161
|
-
systemPrompt: `Mail is yours — what arrived, what it says, what it needs, and the reply that goes back. The user's mail lives on the web, so this is browser work: load the \`${BROWSER_SKILL}\` skill before any step.
|
|
162
|
-
|
|
163
|
-
**Sign in once, then never again.** Ask for the kept sign-in first: \`${TOOL_NAMES.sign_in_use}\` with the mail site. Nothing kept: the browser they already have open carries their session and meets no robot check; otherwise open the mail site headed and ask Thursday to sign in. Hand it over with \`${TOOL_NAMES.sign_in_keep}\` the moment you are in.
|
|
164
|
-
|
|
165
|
-
**Reading a web app costs tokens. Work like it does.**
|
|
166
|
-
- **Filter in the url.** Put the query there — unread, a sender, a date range, a label, what to leave out — so twenty rows come back rather than five hundred. Scrolling a list to find something is the expensive way.
|
|
167
|
-
- **Extract, never snapshot, to read a list.** \`--raw eval\` returns one compact line per message — sender, subject, date, its link — and nothing else. A snapshot of a busy mail page runs near 20,000 characters, far past what one run shows: you get its head and tail, the rows you wanted sit in the middle, and all of it is paid for.
|
|
168
|
-
- **One bash call, not five.** Chain the steps of a look with \`&&\`: load the state, go, extract. A turn per command is where the minutes go.
|
|
169
|
-
- **Long text goes to a file.** Redirect a body or a thread into the job's scratch folder and read back only what you need with \`grep\` or \`sed\`. A file costs nothing until it is read.
|
|
170
|
-
|
|
171
|
-
**Keep the recipe, not the result.** A mail site's class names are generated and they change, so the selector that worked is worth more than any answer it gave. Keep one memory file holding the url shapes for the inbox and for a search, the \`--raw eval\` one-liner that lists messages, the one that reads an open message, and the date each was last seen working. Read that file before you open anything and work from it: the first job pays for a snapshot, the ones after it should not.
|
|
172
|
-
|
|
173
|
-
**An empty extract means the page changed, not that there is no mail.** Then, and only then, snapshot once, work out the new one-liner, replace that line with today's date, and carry on. Never run a recipe twice after it has come back empty.
|
|
174
|
-
|
|
175
|
-
Send Thursday a \`${TOOL_NAMES.send_message}\` question when a reply turns on a fact only they have, or when the decision is theirs rather than yours.
|
|
176
|
-
|
|
177
|
-
Your final text says what is waiting, what you did about it, and the path of anything you wrote.`,
|
|
178
124
|
},
|
|
179
125
|
{
|
|
180
126
|
name: "Insta",
|
|
181
127
|
description:
|
|
182
|
-
"Instagram end to end —
|
|
183
|
-
hint: "
|
|
184
|
-
systemPrompt: `
|
|
185
|
-
|
|
186
|
-
Settle it in one question. Ask only for what the request leaves open, in a single \`${TOOL_NAMES.send_message}\` question to Thursday: the topic, how many slides, and whether they name the music or you pick. A request that already says its topic is not asked for it again. Told to decide yourself: \`${TOOL_NAMES.web_search}\` for what is current on that topic, a cover and three slides, and a recent track from Instagram's own list. Never ask twice for one round.
|
|
128
|
+
"Instagram end to end — studies accounts that work, builds posts like theirs, posts them, reads DMs",
|
|
129
|
+
hint: "Posts like the best in a niche, reads DMs",
|
|
130
|
+
systemPrompt: `Instagram is yours end to end — find what works in a niche, build the post the same way, put it up, and read the account's messages. Load the \`${BROWSER_SKILL}\` skill and your own \`instagram\` skill before any step: its scripts read Instagram in a few lines where a snapshot costs twenty thousand characters, so run them first, chain them in one bash call, and snapshot only when a script says the page changed.
|
|
187
131
|
|
|
188
|
-
Sign in once, and into an account made for this rather than their own — the session is kept and every later job posts as whoever it is.
|
|
132
|
+
Sign in once, and into an account made for this rather than their own — the session is kept and every later job posts as whoever it is. \`${TOOL_NAMES.sign_in_use}\` with \`instagram.com\` before anything else; the login page is \`https://www.instagram.com/accounts/login/\` when nothing is kept.
|
|
189
133
|
|
|
190
|
-
|
|
134
|
+
Settle a post in one question. Ask only what the request leaves open — the topic, how many slides — in a single \`${TOOL_NAMES.send_message}\` question to Thursday. Told to decide yourself: \`${TOOL_NAMES.web_search}\` for what is current on the topic. Never ask twice for one round.
|
|
191
135
|
|
|
192
|
-
**
|
|
136
|
+
**Copy what already works.** Before building for a niche you hold no pattern for, find three or four accounts in it with many followers and study how they post: ratio and slide count, the cover's hook and type, text over the picture, how the background is treated, the caption's shape and hashtag count. Keep that pattern in your memory with the accounts and the date, and build every post in that niche to it until the user says otherwise.
|
|
193
137
|
|
|
194
|
-
**A picture
|
|
138
|
+
**A picture tells the truth about what it is.** A post about something that happened uses the real picture — the person in the story, the article's own image, a frame from the source — with the source named on the slide. A post about an idea or a mood can use a drawn one: \`${STUDIO_TOOLS.generate_image}\` through \`${TOOL_NAMES.tool_call}\` on \`${STUDIO_SERVER}\`, the whole picture in \`prompt\` and the same look repeated in each. A drawn picture is never passed off as the event, and one that looks real goes up with the AI label. \`${STUDIO_TOOLS.generate_image}\` not coming back from \`${TOOL_NAMES.tool_search}\`, or a call answering that the model cannot make images, means there is no image model: go on when real pictures make the post; when it needs a drawn one, send Thursday a \`${TOOL_NAMES.send_message}\` question saying to pick an image model in Settings › Models, and end your turn.
|
|
195
139
|
|
|
196
|
-
|
|
140
|
+
**Every slide is HTML rendered at the post's exact size**, one size for the whole carousel: that is how text comes out right and nothing is cut. Look at the finished slides once, side by side, before they go anywhere.
|
|
197
141
|
|
|
198
|
-
Write the caption to be read: what this is, in the user's voice, no invented facts,
|
|
142
|
+
Write the caption to be read: what this is, in the user's voice, no invented facts, hashtags as the pattern has them.
|
|
199
143
|
|
|
200
|
-
|
|
144
|
+
**Ask before it goes up.** Fill the composer to its last screen and leave it there; send Thursday a \`${TOOL_NAMES.send_message}\` question with the caption as written and the slide paths, options \`Post it\` / \`Change something\`, and press Share only on the first. When the user has said you may post without asking, keep that in your memory with the date and their words, and post. Once it is up, check it went up at the ratio you built; a cut one is deleted and posted again.
|
|
201
145
|
|
|
202
|
-
|
|
146
|
+
Messages are read without being opened: list the inbox first and open only the threads the job is about, since opening one marks it seen. A reply goes out only when the user asked for one.
|
|
203
147
|
|
|
204
|
-
|
|
148
|
+
Answer with the post's url and the file paths, or with what the messages say and what they need.`,
|
|
205
149
|
},
|
|
206
150
|
];
|
|
207
151
|
|
|
@@ -7,7 +7,6 @@ import { INBOX_FINISHED, PAGE_SIZE } from "@/config";
|
|
|
7
7
|
import { type Bot, type Thread } from "@/features/bot/bot.schema";
|
|
8
8
|
import { ThreadReply } from "@/features/bot/components/thread-reply";
|
|
9
9
|
import { toDate } from "@/lib/date-like";
|
|
10
|
-
import { createProbe } from "@/lib/probe";
|
|
11
10
|
import { useServerPages } from "@/lib/protocol/use-server-pages";
|
|
12
11
|
import { useServerRoute } from "@/lib/protocol/use-server-route";
|
|
13
12
|
import {
|
|
@@ -26,7 +25,6 @@ import { Conversation, ThreadHeader } from "./room-conversation";
|
|
|
26
25
|
import {
|
|
27
26
|
Empty,
|
|
28
27
|
HistoryList,
|
|
29
|
-
isUnread,
|
|
30
28
|
ListHeader,
|
|
31
29
|
needsYou,
|
|
32
30
|
Quiet,
|
|
@@ -191,11 +189,6 @@ export const BotRoom = memo(function BotRoom() {
|
|
|
191
189
|
|
|
192
190
|
// Reading a thread is reading its ending; that is what clears its dot.
|
|
193
191
|
useSeenOnDetail(open ? current : null);
|
|
194
|
-
// TEMPORARY test instrumentation (lib/probe)
|
|
195
|
-
const openThread = open ? (current?.id ?? null) : null;
|
|
196
|
-
useEffect(() => {
|
|
197
|
-
createProbe("room")("open", { thread: openThread });
|
|
198
|
-
}, [openThread]);
|
|
199
192
|
|
|
200
193
|
// Thursday put a job in front of the user (`thread` `open`): the room opens on it
|
|
201
194
|
useAppEvent({
|
|
@@ -217,7 +210,6 @@ export const BotRoom = memo(function BotRoom() {
|
|
|
217
210
|
|
|
218
211
|
const busy = threads.filter((entry) => entry.status === "working").length;
|
|
219
212
|
const pending = newest.filter(needsYou).length;
|
|
220
|
-
const unread = newest.filter(isUnread);
|
|
221
213
|
// In the open room the list says what each bot is on; its foot keeps the faces,
|
|
222
214
|
// so a step's words never come and go under the list.
|
|
223
215
|
const faces = useMemo(
|
|
@@ -326,15 +318,14 @@ export const BotRoom = memo(function BotRoom() {
|
|
|
326
318
|
crew={crew}
|
|
327
319
|
more={more}
|
|
328
320
|
bubble={bubble}
|
|
321
|
+
// What waits on the user and nothing else: a finished job's result is the left
|
|
322
|
+
// corner's card (artifact-view), and one notice is enough
|
|
329
323
|
rows={newest.filter(
|
|
330
|
-
(thread) =>
|
|
331
|
-
!rung.includes(thread.id) &&
|
|
332
|
-
(needsYou(thread) || isUnread(thread)),
|
|
324
|
+
(thread) => !rung.includes(thread.id) && needsYou(thread),
|
|
333
325
|
)}
|
|
334
326
|
count={threads.length}
|
|
335
327
|
busy={busy}
|
|
336
328
|
pending={pending}
|
|
337
|
-
unread={unread.length}
|
|
338
329
|
onPick={(id) => {
|
|
339
330
|
setPicked(id);
|
|
340
331
|
setOpen(true);
|
|
@@ -36,7 +36,7 @@ import { FoldButton, TAB } from "./room-conversation";
|
|
|
36
36
|
export const needsYou = needsThreadReply;
|
|
37
37
|
|
|
38
38
|
/** An ending nobody has opened. It needs the user too, to read rather than to answer. */
|
|
39
|
-
|
|
39
|
+
const isUnread = (thread: ThreadView) =>
|
|
40
40
|
thread.status === "done" && !thread.seen;
|
|
41
41
|
|
|
42
42
|
/** The room's two lists. */
|
|
@@ -17,6 +17,11 @@ import {
|
|
|
17
17
|
useState,
|
|
18
18
|
} from "react";
|
|
19
19
|
import { ShinyText } from "@/components/ui/shiny-text";
|
|
20
|
+
import {
|
|
21
|
+
Tooltip,
|
|
22
|
+
TooltipContent,
|
|
23
|
+
TooltipTrigger,
|
|
24
|
+
} from "@/components/ui/tooltip";
|
|
20
25
|
import { type Bot, type BotIcon, isAppStop } from "@/features/bot/bot.schema";
|
|
21
26
|
import { BotMark } from "@/features/bot/components/bot-mark";
|
|
22
27
|
import { ThursdayMark } from "@/features/thursday/components/thursday-mark";
|
|
@@ -411,23 +416,18 @@ export function crewOf(
|
|
|
411
416
|
* What the room itself is doing, and nothing else — the right side of the pill.
|
|
412
417
|
*
|
|
413
418
|
* It says two things only, in muted ink: someone waits on the user, or work is
|
|
414
|
-
* running. Endings say nothing here
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
* quiet. The one time an ending is said here is while the write line keeps the
|
|
418
|
-
* row from growing (`folded`): the notice has nowhere else to stand.
|
|
419
|
+
* running. Endings say nothing here: a finished job's result is the left
|
|
420
|
+
* corner's card. With nothing going on and nothing grown, the pill offers a
|
|
421
|
+
* hand; with a row grown and nothing going on, it is quiet.
|
|
419
422
|
*/
|
|
420
423
|
function restingState({
|
|
421
424
|
busy,
|
|
422
425
|
pending,
|
|
423
426
|
grown,
|
|
424
|
-
folded = 0,
|
|
425
427
|
}: {
|
|
426
428
|
busy: number;
|
|
427
429
|
pending: number;
|
|
428
430
|
grown: boolean;
|
|
429
|
-
/** Unread endings whose rows the write line is holding back. */
|
|
430
|
-
folded?: number;
|
|
431
431
|
}): { text: string; shine: boolean; spin?: boolean } | null {
|
|
432
432
|
if (pending > 0)
|
|
433
433
|
return {
|
|
@@ -435,11 +435,6 @@ function restingState({
|
|
|
435
435
|
shine: true,
|
|
436
436
|
};
|
|
437
437
|
if (busy > 0) return { text: "working", shine: true, spin: true };
|
|
438
|
-
if (folded > 0)
|
|
439
|
-
return {
|
|
440
|
-
text: folded === 1 ? "1 new result" : `${folded} new results`,
|
|
441
|
-
shine: false,
|
|
442
|
-
};
|
|
443
438
|
if (!grown) return { text: "Need a hand?", shine: false };
|
|
444
439
|
return null;
|
|
445
440
|
}
|
|
@@ -454,8 +449,9 @@ function restingState({
|
|
|
454
449
|
* takes the glyph away. Anything that passes between two parties is neither, so
|
|
455
450
|
* it rides above a face in a bubble.
|
|
456
451
|
*
|
|
457
|
-
* Questions and
|
|
458
|
-
*
|
|
452
|
+
* Questions and stops grow above the row until answered; a finished job's
|
|
453
|
+
* result is the left corner's card, never a row here. The pill's own click
|
|
454
|
+
* opens the room to read the full history.
|
|
459
455
|
*
|
|
460
456
|
* The corner radius does not animate with the height: interpolating a pill radius
|
|
461
457
|
* down to a card radius while the box is also resizing warps the corners in flight.
|
|
@@ -468,7 +464,6 @@ export function Chip({
|
|
|
468
464
|
count,
|
|
469
465
|
busy,
|
|
470
466
|
pending,
|
|
471
|
-
unread,
|
|
472
467
|
onPick,
|
|
473
468
|
onOpen,
|
|
474
469
|
}: {
|
|
@@ -476,17 +471,16 @@ export function Chip({
|
|
|
476
471
|
more: number;
|
|
477
472
|
/** The hand-off up, if any (useHandoff). */
|
|
478
473
|
bubble: Handoff | null;
|
|
479
|
-
/** Open questions and
|
|
474
|
+
/** Open questions and stops, newest first. */
|
|
480
475
|
rows: ThreadView[];
|
|
481
476
|
count: number;
|
|
482
477
|
busy: number;
|
|
483
478
|
pending: number;
|
|
484
|
-
unread: number;
|
|
485
479
|
onPick: (id: string) => void;
|
|
486
480
|
onOpen: () => void;
|
|
487
481
|
}) {
|
|
488
482
|
// The write line stands where the card would grow: while it is up the pill stays a pill,
|
|
489
|
-
// and its own words say what
|
|
483
|
+
// and its own words say what waits
|
|
490
484
|
const lineUp = useWriteLineUp();
|
|
491
485
|
const grown = rows.length > 0 && !lineUp;
|
|
492
486
|
|
|
@@ -503,33 +497,20 @@ export function Chip({
|
|
|
503
497
|
: "rounded-full shadow-sm shadow-black/3",
|
|
504
498
|
)}
|
|
505
499
|
>
|
|
506
|
-
{/* One box, two heights: the rows grow out of nothing rather than appearing.
|
|
500
|
+
{/* One box, two heights: the rows grow out of nothing rather than appearing.
|
|
501
|
+
Folded they are still laid out, so their width is contained: the pill is as
|
|
502
|
+
wide as its own row, not as the card it folded from. */}
|
|
507
503
|
<div
|
|
508
504
|
className={cn(
|
|
509
|
-
"grid overflow-hidden transition-[grid-template-rows] duration-300 ease-out",
|
|
505
|
+
"grid overflow-hidden contain-inline-size transition-[grid-template-rows] duration-300 ease-out",
|
|
510
506
|
grown ? "grid-rows-[1fr]" : "grid-rows-[0fr]",
|
|
511
507
|
)}
|
|
512
508
|
>
|
|
513
509
|
{/* Collapsed, this is still in the tree so the height can animate — inert
|
|
514
510
|
keeps it out of the tab order and out of the way of a click. */}
|
|
515
511
|
<div className="min-h-0 overflow-hidden" inert={!grown}>
|
|
516
|
-
<p className="
|
|
517
|
-
|
|
518
|
-
{[
|
|
519
|
-
pending > 0
|
|
520
|
-
? `${pending} ${pending === 1 ? "needs" : "need"} a reply`
|
|
521
|
-
: "",
|
|
522
|
-
]
|
|
523
|
-
.filter(Boolean)
|
|
524
|
-
.join(" · ")}
|
|
525
|
-
{unread > 0 && (
|
|
526
|
-
// What just arrived is what the card most needs you to see
|
|
527
|
-
<span className="text-brand">
|
|
528
|
-
{pending > 0 && " · "}
|
|
529
|
-
{unread} new {unread === 1 ? "result" : "results"}
|
|
530
|
-
</span>
|
|
531
|
-
)}
|
|
532
|
-
</span>
|
|
512
|
+
<p className="px-3 py-2 font-mono text-[10px] tracking-wide text-muted-foreground">
|
|
513
|
+
{pending} {pending === 1 ? "needs" : "need"} a reply
|
|
533
514
|
</p>
|
|
534
515
|
{/* px-1: a row keeps its own 8px, so its mark lands on the rail while
|
|
535
516
|
the shape it lights up on hover stays inside the card's corners */}
|
|
@@ -559,14 +540,7 @@ export function Chip({
|
|
|
559
540
|
bubble={grown ? null : bubble}
|
|
560
541
|
label={count ? `Threads (${count})` : "Bots"}
|
|
561
542
|
onClick={onOpen}
|
|
562
|
-
side={
|
|
563
|
-
<RoomState
|
|
564
|
-
busy={busy}
|
|
565
|
-
pending={pending}
|
|
566
|
-
grown={grown}
|
|
567
|
-
folded={lineUp ? unread : 0}
|
|
568
|
-
/>
|
|
569
|
-
}
|
|
543
|
+
side={<RoomState busy={busy} pending={pending} grown={grown} />}
|
|
570
544
|
onWrite={writeLine.open}
|
|
571
545
|
/>
|
|
572
546
|
</div>
|
|
@@ -603,14 +577,30 @@ export function CrewRow({
|
|
|
603
577
|
}) {
|
|
604
578
|
return (
|
|
605
579
|
<div className="flex shrink-0 items-center gap-2 py-1.5 pr-3 pl-1.5">
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
580
|
+
{/* The tip names the key too: `/` is the way in that nothing else on screen shows */}
|
|
581
|
+
<Tooltip>
|
|
582
|
+
<TooltipTrigger
|
|
583
|
+
render={
|
|
584
|
+
<button
|
|
585
|
+
type="button"
|
|
586
|
+
onClick={onWrite}
|
|
587
|
+
aria-label="Write to Thursday or a bot"
|
|
588
|
+
className="grid size-7 shrink-0 place-items-center rounded-full bg-muted text-foreground outline-none transition-colors hover:bg-accent focus-visible:ring-3 focus-visible:ring-ring/50"
|
|
589
|
+
/>
|
|
590
|
+
}
|
|
591
|
+
>
|
|
592
|
+
<Plus className="size-3.5" />
|
|
593
|
+
</TooltipTrigger>
|
|
594
|
+
<TooltipContent>
|
|
595
|
+
Write to Thursday or a bot
|
|
596
|
+
<kbd
|
|
597
|
+
data-slot="kbd"
|
|
598
|
+
className="bg-background/20 px-1.5 font-mono text-[10px]"
|
|
599
|
+
>
|
|
600
|
+
/
|
|
601
|
+
</kbd>
|
|
602
|
+
</TooltipContent>
|
|
603
|
+
</Tooltip>
|
|
614
604
|
<button
|
|
615
605
|
type="button"
|
|
616
606
|
onClick={onClick}
|
|
@@ -632,7 +622,6 @@ export function RoomState(props: {
|
|
|
632
622
|
busy: number;
|
|
633
623
|
pending: number;
|
|
634
624
|
grown: boolean;
|
|
635
|
-
folded?: number;
|
|
636
625
|
}) {
|
|
637
626
|
const state = restingState(props);
|
|
638
627
|
if (!state) return null;
|
|
@@ -20,12 +20,12 @@ type SeedPick = {
|
|
|
20
20
|
/**
|
|
21
21
|
* Creates the seed roster. Call only after a voice key is saved: seed bots
|
|
22
22
|
* need a resolvable default model. Fire-and-forget; failure only toasts.
|
|
23
|
-
* Without picks,
|
|
23
|
+
* Without picks, every seed is created on the app default model.
|
|
24
24
|
*/
|
|
25
25
|
export function installSeedBots(picks?: SeedPick[]) {
|
|
26
26
|
const wanted =
|
|
27
27
|
picks ??
|
|
28
|
-
BOT_SEEDS.
|
|
28
|
+
BOT_SEEDS.map((seed) => ({
|
|
29
29
|
name: seed.name,
|
|
30
30
|
}));
|
|
31
31
|
if (!wanted.length) return;
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
groupSatisfied,
|
|
8
8
|
isConfigSet,
|
|
9
9
|
} from "@/features/config/config.const";
|
|
10
|
+
import { NavBadge } from "@/features/settings/components/setting-ui";
|
|
10
11
|
import type { SectionAlert } from "@/features/settings/settings.alert";
|
|
11
12
|
import { useServerRoute } from "@/lib/protocol/use-server-route";
|
|
12
13
|
|
|
@@ -20,3 +21,8 @@ export function useConfigAlert(): SectionAlert {
|
|
|
20
21
|
);
|
|
21
22
|
return unmet ? "amber" : null;
|
|
22
23
|
}
|
|
24
|
+
|
|
25
|
+
/** The API keys section's dot: a call has no key to run on. A dot, not a count. */
|
|
26
|
+
export function ConfigBadge() {
|
|
27
|
+
return useConfigAlert() ? <NavBadge tone="amber" /> : null;
|
|
28
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { formatDistanceToNowStrict } from "date-fns";
|
|
4
4
|
import {
|
|
5
|
+
ArrowUpRight,
|
|
5
6
|
AudioLines,
|
|
6
7
|
Captions,
|
|
7
8
|
Check,
|
|
@@ -923,11 +924,13 @@ function ConfigDialog({
|
|
|
923
924
|
}
|
|
924
925
|
footer={
|
|
925
926
|
<>
|
|
927
|
+
{/* set apart from what saves, at the far end and in red */}
|
|
926
928
|
{set && (
|
|
927
929
|
<Button
|
|
928
930
|
variant="ghost"
|
|
929
931
|
loading={removing}
|
|
930
932
|
onClick={() => remove(entry.key)}
|
|
933
|
+
className="mr-auto text-destructive hover:text-destructive"
|
|
931
934
|
>
|
|
932
935
|
Remove
|
|
933
936
|
</Button>
|
|
@@ -953,11 +956,27 @@ function ConfigDialog({
|
|
|
953
956
|
if (event.key === "Enter" && value.trim().length >= 8)
|
|
954
957
|
save(entry.key, value);
|
|
955
958
|
}}
|
|
956
|
-
|
|
959
|
+
// what a key looks like says more than the setting's name, which is above
|
|
960
|
+
placeholder={
|
|
961
|
+
set
|
|
962
|
+
? "New value — replaces the current key"
|
|
963
|
+
: (entry.keyLooks ?? "Paste the key")
|
|
964
|
+
}
|
|
957
965
|
spellCheck={false}
|
|
958
966
|
type="password"
|
|
959
967
|
autoFocus
|
|
960
968
|
/>
|
|
969
|
+
{!set && entry.keysAt && (
|
|
970
|
+
<a
|
|
971
|
+
href={entry.keysAt}
|
|
972
|
+
target="_blank"
|
|
973
|
+
rel="noreferrer"
|
|
974
|
+
className="flex w-fit items-center gap-1 px-0.5 text-[13px] text-foreground underline underline-offset-3 hover:text-foreground/80"
|
|
975
|
+
>
|
|
976
|
+
Get a key at {new URL(entry.keysAt).host}
|
|
977
|
+
<ArrowUpRight className="size-3.5" />
|
|
978
|
+
</a>
|
|
979
|
+
)}
|
|
961
980
|
{credits && "refused" in credits && (
|
|
962
981
|
<SettingNote className="wrap-break-word text-destructive">
|
|
963
982
|
{credits.refused}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { queryKey } from "@/app/api/query-key";
|
|
4
|
-
import { useConfigAlert } from "@/features/config/components/config-badge";
|
|
5
4
|
import {
|
|
6
5
|
CONFIG_GROUPS,
|
|
7
6
|
type ConfigStatus,
|
|
@@ -29,17 +28,16 @@ const MODEL_KEYS = CONFIG_GROUPS.filter(
|
|
|
29
28
|
* that only clears when every kind is set is a standing demand to spend. What is
|
|
30
29
|
* worth saying once is that the whole drawer exists, because until one is picked
|
|
31
30
|
* a bot that would have drawn just finds out mid-job. Picking any answers it.
|
|
32
|
-
*
|
|
31
|
+
* Blue, not amber: nothing waits on it. A loading read reports nothing rather
|
|
32
|
+
* than flashing a dot on every open.
|
|
33
33
|
*/
|
|
34
34
|
export function useModelsAlert(): SectionAlert {
|
|
35
35
|
const { data, isLoading } = useServerRoute<ConfigStatus[]>(queryKey.config);
|
|
36
36
|
if (isLoading || !data) return null;
|
|
37
|
-
return MODEL_KEYS.every((key) => !isConfigSet(data, key)) ? "
|
|
37
|
+
return MODEL_KEYS.every((key) => !isConfigSet(data, key)) ? "brand" : null;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
/** The section's dot:
|
|
40
|
+
/** The Models section's dot: the studio is empty. A dot, not a count. */
|
|
41
41
|
export function ModelsBadge() {
|
|
42
|
-
|
|
43
|
-
const studio = useModelsAlert();
|
|
44
|
-
return keys || studio ? <NavBadge tone="amber" /> : null;
|
|
42
|
+
return useModelsAlert() ? <NavBadge tone="brand" /> : null;
|
|
45
43
|
}
|