thursday-agent 0.10.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.
Files changed (293) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +0 -1
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/prerender-manifest.json +3 -3
  5. package/.next/routes-manifest.json +0 -6
  6. package/.next/server/app/_global-error/page.js.nft.json +1 -1
  7. package/.next/server/app/_global-error.html +1 -1
  8. package/.next/server/app/_global-error.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  12. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  13. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/.next/server/app/_not-found.html +1 -1
  15. package/.next/server/app/_not-found.rsc +2 -2
  16. package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  17. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  18. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  19. package/.next/server/app/api/artifact/route.js.nft.json +1 -1
  20. package/.next/server/app/api/bot/keep-working/route.js.nft.json +1 -1
  21. package/.next/server/app/api/bot/memory/files/route.js.nft.json +1 -1
  22. package/.next/server/app/api/bot/memory/route.js.nft.json +1 -1
  23. package/.next/server/app/api/bot/route.js.nft.json +1 -1
  24. package/.next/server/app/api/bot/thread/[id]/route.js.nft.json +1 -1
  25. package/.next/server/app/api/bot/thread/route.js.nft.json +1 -1
  26. package/.next/server/app/api/config/route.js.nft.json +1 -1
  27. package/.next/server/app/api/events/route.js.nft.json +1 -1
  28. package/.next/server/app/api/favicon/[host]/route.js.nft.json +1 -1
  29. package/.next/server/app/api/file/[...path]/route.js.nft.json +1 -1
  30. package/.next/server/app/api/llm-model/catalog/route.js.nft.json +1 -1
  31. package/.next/server/app/api/llm-model/credits/route.js.nft.json +1 -1
  32. package/.next/server/app/api/llm-model/route.js.nft.json +1 -1
  33. package/.next/server/app/api/llm-model/subscription/route.js.nft.json +1 -1
  34. package/.next/server/app/api/mcp/[name]/route.js.nft.json +1 -1
  35. package/.next/server/app/api/mcp/oauth/callback/route.js.nft.json +1 -1
  36. package/.next/server/app/api/mcp/route.js.nft.json +1 -1
  37. package/.next/server/app/api/mcp/tools/route.js.nft.json +1 -1
  38. package/.next/server/app/api/memory/edit/route.js.nft.json +1 -1
  39. package/.next/server/app/api/memory/route.js.nft.json +1 -1
  40. package/.next/server/app/api/reach/route.js +13 -13
  41. package/.next/server/app/api/reach/route.js.nft.json +1 -1
  42. package/.next/server/app/api/routine/route.js.nft.json +1 -1
  43. package/.next/server/app/api/signins/route.js +1 -1
  44. package/.next/server/app/api/signins/route.js.nft.json +1 -1
  45. package/.next/server/app/api/skills/[source]/[name]/route.js.nft.json +1 -1
  46. package/.next/server/app/api/skills/route.js.nft.json +1 -1
  47. package/.next/server/app/api/thursday/call/route.js.nft.json +1 -1
  48. package/.next/server/app/api/thursday/skills/route.js.nft.json +1 -1
  49. package/.next/server/app/api/thursday/text/route.js.nft.json +1 -1
  50. package/.next/server/app/api/thursday/tool-call/route.js +14 -15
  51. package/.next/server/app/api/thursday/tool-call/route.js.nft.json +1 -1
  52. package/.next/server/app/api/workspace/files/route.js.nft.json +1 -1
  53. package/.next/server/app/api/workspace/route.js.nft.json +1 -1
  54. package/.next/server/app/artifact/[...path]/page/server-reference-manifest.json +1 -1
  55. package/.next/server/app/artifact/[...path]/page.js.nft.json +1 -1
  56. package/.next/server/app/artifact/[...path]/page_client-reference-manifest.js +1 -1
  57. package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
  58. package/.next/server/app/manifest.webmanifest/route.js.nft.json +1 -1
  59. package/.next/server/app/page/server-reference-manifest.json +56 -56
  60. package/.next/server/app/page.js +4 -4
  61. package/.next/server/app/page.js.nft.json +1 -1
  62. package/.next/server/app/page_client-reference-manifest.js +1 -1
  63. package/.next/server/app-paths-manifest.json +0 -1
  64. package/.next/server/chunks/[root-of-the-server]__014ydvq._.js +2 -2
  65. package/.next/server/chunks/[root-of-the-server]__0209ah4._.js +7 -5
  66. package/.next/server/chunks/[root-of-the-server]__04r4h-2._.js +4 -2
  67. package/.next/server/chunks/[root-of-the-server]__0542v_d._.js +3 -0
  68. package/.next/server/chunks/[root-of-the-server]__0gewdg9._.js +2 -2
  69. package/.next/server/chunks/[root-of-the-server]__0mrk2it._.js +1 -1
  70. package/.next/server/chunks/[root-of-the-server]__1-swlkw._.js +2 -2
  71. package/.next/server/chunks/[root-of-the-server]__12iddfq._.js +1 -1
  72. package/.next/server/chunks/[root-of-the-server]__1ai5lme._.js +3 -3
  73. package/.next/server/chunks/[root-of-the-server]__1cjxw0-._.js +2 -2
  74. package/.next/server/chunks/{[root-of-the-server]__1fy2g82._.js → [root-of-the-server]__1k3j52n._.js} +1 -1
  75. package/.next/server/chunks/[root-of-the-server]__1kyjt5m._.js +3 -3
  76. package/.next/server/chunks/[root-of-the-server]__1ygplx1._.js +1 -1
  77. package/.next/server/chunks/_05v8k21._.js +3 -1
  78. package/.next/server/chunks/_0awtznl._.js +1 -1
  79. package/.next/server/chunks/_0na2fj1._.js +1 -1
  80. package/.next/server/chunks/_0x-ej3f._.js +17 -17
  81. package/.next/server/chunks/_12-amdu._.js +2 -2
  82. package/.next/server/chunks/_15740dw._.js +60 -0
  83. package/.next/server/chunks/_1dc_d1q._.js +3 -3
  84. package/.next/server/chunks/_1i350l5._.js +3 -1
  85. package/.next/server/chunks/_1jc5g4p._.js +2 -2
  86. package/.next/server/chunks/_1jn-aep._.js +4 -4
  87. package/.next/server/chunks/_1m88lkh._.js +11 -11
  88. package/.next/server/chunks/_1ogqovh._.js +4 -4
  89. package/.next/server/chunks/_1uuucy-._.js +1 -1
  90. package/.next/server/chunks/_1zxp-p6._.js +1 -1
  91. package/.next/server/chunks/features_0rfvely._.js +5 -3
  92. package/.next/server/chunks/ssr/{[root-of-the-server]__1se25x0._.js → [root-of-the-server]__0hpp3nw._.js} +29 -13
  93. package/.next/server/chunks/ssr/[root-of-the-server]__0m7u9j-._.js +3 -0
  94. package/.next/server/chunks/ssr/[root-of-the-server]__0tzekbl._.js +24 -60
  95. package/.next/server/chunks/ssr/[root-of-the-server]__1cz2rz5._.js +3 -0
  96. package/.next/server/chunks/ssr/{[root-of-the-server]__0tvavy9._.js → [root-of-the-server]__1iwyp1e._.js} +1 -1
  97. package/.next/server/chunks/ssr/[root-of-the-server]__1w9hqq8._.js +1 -1
  98. package/.next/server/chunks/ssr/_097-ljd._.js +1 -1
  99. package/.next/server/chunks/ssr/{_173siic._.js → _0btzccu._.js} +2 -2
  100. package/.next/server/chunks/ssr/_0fqhhwi._.js +1 -1
  101. package/.next/server/chunks/ssr/{_1ng831c._.js → _0hjzb5o._.js} +2 -2
  102. package/.next/server/chunks/ssr/_0ld9_qq._.js +1 -1
  103. package/.next/server/chunks/ssr/{_1doidrw._.js → _0ouzu9f._.js} +2 -2
  104. package/.next/server/chunks/ssr/{_1flsjeo._.js → _0u7ko71._.js} +2 -2
  105. package/.next/server/chunks/ssr/{_0hlxij-._.js → _0wd_o8-._.js} +2 -2
  106. package/.next/server/chunks/ssr/_0ya0cb0._.js +3 -0
  107. package/.next/server/chunks/ssr/_10yyqtk._.js +67 -0
  108. package/.next/server/chunks/ssr/{_0bp-_fi._.js → _12eyown._.js} +3 -3
  109. package/.next/server/chunks/ssr/_12mijol._.js +11 -11
  110. package/.next/server/chunks/ssr/_132qkfz._.js +1 -1
  111. package/.next/server/chunks/ssr/_184327j._.js +1 -1
  112. package/.next/server/chunks/ssr/{_0tg0vf8._.js → _19ps4p3._.js} +7 -7
  113. package/.next/server/chunks/ssr/{_1yk6yak._.js → _1bhy4yg._.js} +2 -2
  114. package/.next/server/chunks/ssr/_1d423aj._.js +7 -5
  115. package/.next/server/chunks/ssr/{_0s41irp._.js → _1kd843u._.js} +2 -2
  116. package/.next/server/chunks/ssr/{_0p9pmxe._.js → _1kq2ye_._.js} +2 -2
  117. package/.next/server/chunks/ssr/_1mqn9mc._.js +1 -1
  118. package/.next/server/chunks/ssr/_1neoj22._.js +1 -1
  119. package/.next/server/chunks/ssr/_1p8xorg._.js +3 -0
  120. package/.next/server/chunks/ssr/{_17v-33v._.js → _1q683w8._.js} +3 -3
  121. package/.next/server/chunks/ssr/_1tawxu2._.js +3 -0
  122. package/.next/server/chunks/ssr/{_05rxyf9._.js → _1w2oba6._.js} +2 -2
  123. package/.next/server/chunks/ssr/_1xoch6f._.js +1 -1
  124. package/.next/server/chunks/ssr/{_1u2jw0i._.js → _1ybo1hd._.js} +3 -3
  125. package/.next/server/chunks/ssr/{features_bot_1yr-_pm._.js → features_bot_0s11m4r._.js} +2 -2
  126. package/.next/server/chunks/ssr/features_connectors_components_mcp-setting_tsx_0jmbgvp._.js +1 -1
  127. package/.next/server/chunks/ssr/features_memory_components_memory-setting_tsx_0oa43sw._.js +1 -1
  128. package/.next/server/chunks/ssr/features_routine_components_routine-setting_tsx_055fknz._.js +1 -1
  129. package/.next/server/chunks/ssr/features_workspace_00jz2hu._.js +1 -1
  130. package/.next/server/chunks/ssr/{node_modules_04t200t._.js → node_modules_00pocul._.js} +1 -1
  131. package/.next/server/chunks/ssr/{node_modules_1dcbvfw._.js → node_modules_02zt4az._.js} +1 -1
  132. package/.next/server/chunks/ssr/{node_modules_0eb4rx7._.js → node_modules_05ojmcl._.js} +1 -1
  133. package/.next/server/chunks/ssr/{node_modules_08g12f-._.js → node_modules_06i6bli._.js} +1 -1
  134. package/.next/server/chunks/ssr/{node_modules_1expacw._.js → node_modules_09dj429._.js} +1 -1
  135. package/.next/server/chunks/ssr/{node_modules_0wq4-ts._.js → node_modules_0auzgvx._.js} +1 -1
  136. package/.next/server/chunks/ssr/{node_modules_048wmmj._.js → node_modules_0evjwh1._.js} +1 -1
  137. package/.next/server/chunks/ssr/{node_modules_1qehmwm._.js → node_modules_0gmdur8._.js} +1 -1
  138. package/.next/server/chunks/ssr/{node_modules_1ksh9gy._.js → node_modules_0j3te-1._.js} +1 -1
  139. package/.next/server/chunks/ssr/{node_modules_1n0h9sf._.js → node_modules_0rbaymq._.js} +1 -1
  140. package/.next/server/chunks/ssr/{node_modules_0511ha-._.js → node_modules_0tf8_1e._.js} +1 -1
  141. package/.next/server/chunks/ssr/{node_modules_18woe20._.js → node_modules_0yqy-0b._.js} +1 -1
  142. package/.next/server/chunks/ssr/{node_modules_0zjqwz7._.js → node_modules_1-53ebx._.js} +1 -1
  143. package/.next/server/chunks/ssr/{node_modules_1zf46lp._.js → node_modules_1-y9aho._.js} +1 -1
  144. package/.next/server/chunks/ssr/{node_modules_1xzo2wn._.js → node_modules_11bltzp._.js} +1 -1
  145. package/.next/server/chunks/ssr/{node_modules_11hbkuj._.js → node_modules_18jn2qo._.js} +1 -1
  146. package/.next/server/chunks/ssr/{node_modules_1zkpek5._.js → node_modules_1_te3bu._.js} +1 -1
  147. package/.next/server/chunks/ssr/{node_modules_0cdu6jq._.js → node_modules_1c0pg3k._.js} +1 -1
  148. package/.next/server/chunks/ssr/{node_modules_0piws2a._.js → node_modules_1en2jx5._.js} +1 -1
  149. package/.next/server/chunks/ssr/{node_modules_1m2ho33._.js → node_modules_1p-c8cr._.js} +1 -1
  150. package/.next/server/chunks/ssr/{node_modules_0pk6i-e._.js → node_modules_1qe-oxg._.js} +1 -1
  151. package/.next/server/chunks/ssr/{node_modules_0s1lwc9._.js → node_modules_1rjt833._.js} +1 -1
  152. package/.next/server/chunks/ssr/{node_modules_1_afpz7._.js → node_modules_1tzw5sn._.js} +1 -1
  153. package/.next/server/chunks/ssr/{node_modules_1cxc8kn._.js → node_modules_1x6uwvn._.js} +1 -1
  154. package/.next/server/chunks/ssr/{node_modules_0f2k5l0._.js → node_modules_1zqddke._.js} +1 -1
  155. package/.next/server/chunks/ssr/{node_modules_1_ban4u._.js → node_modules_211zgim._.js} +1 -1
  156. 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_0y51bqj._.js} +1 -1
  157. 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_1sc114e._.js} +1 -1
  158. package/.next/server/middleware-build-manifest.js +3 -3
  159. package/.next/server/pages/404.html +1 -1
  160. package/.next/server/pages/500.html +1 -1
  161. package/.next/server/server-reference-manifest.js +1 -1
  162. package/.next/server/server-reference-manifest.json +57 -57
  163. package/.next/static/chunks/{0oz3rs47yy3p8.js → 03-724sj0nk_z.js} +2 -2
  164. package/.next/static/chunks/{42890jqk049q1.js → 03bhu4mk8en2q.js} +1 -1
  165. package/.next/static/chunks/{3vynnr71ghvom.js → 04imzjhvzw65d.js} +2 -2
  166. package/.next/static/chunks/{1pjygp7hqib6j.js → 0gljzv9fz1g7d.js} +1 -1
  167. package/.next/static/chunks/{2d4z3er53ng8r.js → 0h0lythv1ql93.js} +1 -1
  168. package/.next/static/chunks/{2o4dndk0d68do.js → 0hzbj7f48cmx0.js} +1 -1
  169. package/.next/static/chunks/{2ky81z-jgvasc.js → 0lwunwbl7u7pt.js} +1 -1
  170. package/.next/static/chunks/{0u9ae9k_2g7k3.js → 0r6nwxg4dv83t.js} +1 -1
  171. package/.next/static/chunks/0rn9nbg6aif9f.js +17 -0
  172. package/.next/static/chunks/{19ojn4lomsk0r.js → 0x_w4shtvrrfg.js} +1 -1
  173. package/.next/static/chunks/{2sdwbfs1x5skp.js → 0ympuji_ufypd.js} +1 -1
  174. package/.next/static/chunks/{3t9huugh-5_ps.js → 0zmrz4zfzm4dt.js} +1 -1
  175. package/.next/static/chunks/{3exjsi8pfdvzj.js → 11tlxkr7a_rpc.js} +1 -1
  176. package/.next/static/chunks/{0o980477kfqez.js → 12scesro428m7.js} +2 -2
  177. package/.next/static/chunks/16s2fx3890_jd.js +65 -0
  178. package/.next/static/chunks/{0oyz95sujd2c7.js → 1qzfgplyo18tc.js} +1 -1
  179. package/.next/static/chunks/{04e-e--5lk26x.js → 1r4zajz14jp9-.js} +1 -1
  180. package/.next/static/chunks/1s6m4cmbn7htf.js +1 -0
  181. package/.next/static/chunks/1wl9t1-3i-q8p.css +1 -0
  182. package/.next/static/chunks/2-asot3p1j3yg.js +1 -0
  183. package/.next/static/chunks/{0u3ktztrzu1fa.js → 2-jn4xscbm-i2.js} +1 -1
  184. package/.next/static/chunks/{0w20wnnfrscq7.js → 22h9gvclqtx_2.js} +1 -1
  185. package/.next/static/chunks/{337g9g_ux2gj-.js → 26-eblxwnykck.js} +1 -1
  186. package/.next/static/chunks/{43q9cgbuw8krx.js → 2kzwhbuobpkw3.js} +2 -2
  187. package/.next/static/chunks/{22shnca_6zfzs.js → 304q9p54tcgm1.js} +1 -1
  188. package/.next/static/chunks/{1u5i-jti87jbo.js → 35wdx3wt7cg4a.js} +1 -1
  189. package/.next/static/chunks/{1p764d75_0lhv.js → 37unh9460im7v.js} +2 -2
  190. package/.next/static/chunks/{01xjer0i_e5z5.js → 39gj5vxz1zpm_.js} +1 -1
  191. package/.next/static/chunks/{35kwn237o4wdn.js → 39vrqy1r_00jc.js} +1 -1
  192. package/.next/static/chunks/{09120tms63y21.js → 3dipmu0mptqum.js} +1 -1
  193. package/.next/static/chunks/3gki-squw28yu.js +1 -0
  194. package/.next/static/chunks/{3obf9n3cxj99k.js → 3jvldk2wvwbge.js} +1 -1
  195. package/.next/static/chunks/{1mwee7xvitlp9.js → 3nnzdl_te20hv.js} +1 -1
  196. package/.next/static/chunks/{2m267diam_vib.js → 3tz4dr0_8pq1-.js} +1 -1
  197. package/.next/static/chunks/{2gocl3dq0_h1s.js → 3x6pggb-2kyrk.js} +1 -1
  198. package/.next/static/chunks/{025bmrgeukcb2.js → 4440mws9jz3mt.js} +1 -1
  199. package/README.md +1 -1
  200. package/app/api/thursday/tool-call/route.ts +1 -15
  201. package/docs/images/demo-still.jpg +0 -0
  202. package/docs/live-calls.md +19 -11
  203. package/features/ai/prompts/live.prompt.ts +51 -26
  204. package/features/ai/prompts/prompt-helper.ts +1 -9
  205. package/features/ai/prompts/thursday.prompt.ts +8 -2
  206. package/features/bot/bot.run.ts +9 -3
  207. package/features/bot/bot.seed.ts +35 -91
  208. package/features/bot/components/bot-room.tsx +0 -6
  209. package/features/bot/components/room-pill.tsx +4 -2
  210. package/features/bot/seed-bots.ts +2 -2
  211. package/features/intro/components/intro.tsx +2 -3
  212. package/features/reach/reach.ts +2 -2
  213. package/features/signins/signins.query.ts +66 -1
  214. package/features/thursday/components/side-captions.tsx +31 -8
  215. package/features/thursday/components/thursday.tsx +97 -39
  216. package/features/thursday/components/write-line.tsx +28 -17
  217. package/features/thursday/thursday.action.ts +4 -20
  218. package/features/thursday/thursday.schema.ts +0 -9
  219. package/features/thursday/thursday.store.ts +11 -15
  220. package/features/thursday/thursday.text.ts +10 -8
  221. package/features/thursday/tool-line.ts +17 -6
  222. package/features/thursday/use-call-ring.ts +1 -5
  223. package/features/thursday/use-text-call.ts +37 -21
  224. package/features/thursday/use-thursday.ts +14 -44
  225. package/features/workspace/workspace.ts +4 -2
  226. package/guide/bots.md +12 -2
  227. package/guide/calls.md +7 -5
  228. package/lib/live/live.session.ts +0 -39
  229. package/package.json +1 -1
  230. package/scripts/live-session.test.mts +19 -26
  231. package/seed-skills/analyst/README.md +3 -4
  232. package/seed-skills/analyst/data-report/SKILL.md +5 -4
  233. package/seed-skills/insta/instagram/SKILL.md +5 -2
  234. package/seed-skills/insta/instagram/references/post.md +2 -2
  235. package/seed-skills/insta/instagram/scripts/compose.mjs +5 -1
  236. package/seed-skills/insta/instagram/scripts/inbox.mjs +5 -1
  237. package/seed-skills/insta/instagram/scripts/lib.mjs +12 -70
  238. package/seed-skills/insta/instagram/scripts/media.mjs +1 -1
  239. package/seed-skills/insta/instagram/scripts/profile.mjs +6 -2
  240. package/seed-skills/insta/instagram/scripts/read.mjs +5 -1
  241. package/seed-skills/insta/instagram/scripts/search.mjs +6 -2
  242. package/seed-skills/insta/instagram/scripts/sheet.mjs +31 -88
  243. package/seed-skills/insta/instagram/templates/carousel.html +1 -1
  244. package/seed-skills/tutor/picture-book/SKILL.md +16 -0
  245. package/seed-skills/tutor/picture-book/book/book.css +181 -0
  246. package/seed-skills/tutor/picture-book/book/book.html +33 -0
  247. package/seed-skills/tutor/picture-book/book/book.js +60 -0
  248. package/seed-skills/tutor/picture-book/references/picture-book.md +104 -0
  249. package/seed-skills/tutor/picture-book/scripts/book-video.mjs +177 -0
  250. package/seed-skills/tutor/picture-book/scripts/book.mjs +98 -0
  251. package/skills/browser/SKILL.md +13 -4
  252. package/skills/browser/references/scripts.md +55 -0
  253. package/{seed-skills/insta/instagram → skills/browser}/scripts/render.mjs +1 -1
  254. package/skills/browser/scripts/session.mjs +105 -0
  255. package/skills/browser/scripts/sheet.mjs +97 -0
  256. package/{seed-skills/insta/instagram → skills/browser}/scripts/webimage.mjs +1 -1
  257. package/skills/interactive-page/SKILL.md +4 -3
  258. package/{seed-skills/analyst/data-report → skills/interactive-page}/scripts/chart.mjs +3 -3
  259. package/skills/interactive-page/scripts/page.mjs +1 -0
  260. package/.next/server/app/api/probe/route/app-paths-manifest.json +0 -3
  261. package/.next/server/app/api/probe/route/build-manifest.json +0 -12
  262. package/.next/server/app/api/probe/route/server-reference-manifest.json +0 -4
  263. package/.next/server/app/api/probe/route.js +0 -10
  264. package/.next/server/app/api/probe/route.js.map +0 -5
  265. package/.next/server/app/api/probe/route.js.nft.json +0 -1
  266. package/.next/server/app/api/probe/route_client-reference-manifest.js +0 -3
  267. package/.next/server/chunks/[root-of-the-server]__012s2o8._.js +0 -3
  268. package/.next/server/chunks/[root-of-the-server]__0z0hc1f._.js +0 -4
  269. package/.next/server/chunks/[root-of-the-server]__1v7-zke._.js +0 -5
  270. package/.next/server/chunks/_0whlpyi._.js +0 -4
  271. package/.next/server/chunks/_next-internal_server_app_api_probe_route_actions_0cqd30j.js +0 -3
  272. package/.next/server/chunks/ssr/[root-of-the-server]__0cxqzfc._.js +0 -103
  273. package/.next/server/chunks/ssr/[root-of-the-server]__1-d2tgh._.js +0 -3
  274. package/.next/server/chunks/ssr/_070nv1_._.js +0 -3
  275. package/.next/server/chunks/ssr/_0u5xv6g._.js +0 -3
  276. package/.next/server/chunks/ssr/_157yfrk._.js +0 -3
  277. package/.next/server/chunks/ssr/_19n08g5._.js +0 -3
  278. package/.next/static/chunks/03kgfyrli_0gz.js +0 -1
  279. package/.next/static/chunks/24dst4mgurbsh.js +0 -1
  280. package/.next/static/chunks/25zq8gzhbdsr7.js +0 -1
  281. package/.next/static/chunks/2qod5tqlpz8b7.js +0 -17
  282. package/.next/static/chunks/30skcjexa57zn.js +0 -101
  283. package/.next/static/chunks/3qq4o8w2rv_e9.css +0 -1
  284. package/lib/probe.server.ts +0 -17
  285. package/lib/probe.ts +0 -55
  286. package/seed-skills/mail/mail/SKILL.md +0 -37
  287. package/seed-skills/mail/mail/recipes/gmail.mjs +0 -448
  288. package/seed-skills/mail/mail/references/recipes.md +0 -55
  289. package/seed-skills/mail/mail/scripts/mail.mjs +0 -202
  290. /package/.next/static/{Ar5V4kMdL4CE1trx8anrH → En1vUPazxcYjPjnnahcN5}/_buildManifest.js +0 -0
  291. /package/.next/static/{Ar5V4kMdL4CE1trx8anrH → En1vUPazxcYjPjnnahcN5}/_clientMiddlewareManifest.js +0 -0
  292. /package/.next/static/{Ar5V4kMdL4CE1trx8anrH → En1vUPazxcYjPjnnahcN5}/_ssgManifest.js +0 -0
  293. /package/{seed-skills/insta/instagram → skills/browser}/scripts/image-size.mjs +0 -0
@@ -1,3 +1,4 @@
1
+ import { TOOL_NAMES } from "@/features/ai/tools/tool-name";
1
2
  import {
2
3
  listAlwaysLoaded,
3
4
  listNoteIndex,
@@ -10,7 +11,6 @@ import {
10
11
  type MemoryNoteView,
11
12
  } from "@/features/memory/memory.schema";
12
13
  import {
13
- callEnding,
14
14
  carriedLines,
15
15
  expandedFacts,
16
16
  logPromptSize,
@@ -21,16 +21,15 @@ import {
21
21
 
22
22
  /**
23
23
  * Everything the Live voice hears: who Thursday is and how a call ends (the words the backend
24
- * opens with too), the guide's starter backchannel and interruption policies, a two-line
25
- * delegation policy, and what she knows about the user. What the backend can do, how work is
26
- * handed over, earlier calls and tidying memory are the backend's (thursday.prompt): the voice
27
- * hands everything but conversation over and answers from what comes back. Assembled on every
28
- * call, never cached.
24
+ * opens with too), the guide's starter backchannel and interruption policies, its delegation
25
+ * policy (what the backend can do, when to hand over and when not), and what she knows about
26
+ * the user. How the work is done, earlier calls and tidying memory are the backend's
27
+ * (thursday.prompt): the voice hands everything but conversation over and answers from what
28
+ * comes back. Assembled on every call, never cached.
29
29
  */
30
30
  export async function loadLivePrompt(options: {
31
31
  /** Settings › Thursday › Voice instructions; added to, never replacing, what is below. */
32
32
  voicePrompt?: string | null;
33
- locale?: string | null;
34
33
  /** The page placed this call because background work waits on the user (call-back). */
35
34
  calledBack?: boolean;
36
35
  /** The last call is in the conversation ahead of the opening (call-last). */
@@ -45,11 +44,6 @@ export async function loadLivePrompt(options: {
45
44
 
46
45
  const first = !open.notes.find((note) => note.path === "profile")?.facts
47
46
  .length;
48
- // Only the first-call greeting reads it: before they have said anything, the
49
- // browser's locale is the one sign of their language
50
- const language = options.locale
51
- ? `the language of \`${options.locale}\`, their browser's setting`
52
- : "the language they use";
53
47
 
54
48
  const text = [
55
49
  thursdayIdentity(),
@@ -69,7 +63,7 @@ export async function loadLivePrompt(options: {
69
63
  opening: options.calledBack
70
64
  ? "You placed this call because background work has something for the user; it comes in next. Speak first: greet them in one line and say that is why you called."
71
65
  : first
72
- ? `Open the call now, in ${language}: say who you are and what you are here to do for them, in a line or two, then ask what to call them. Then stop and listen.`
66
+ ? "Open the call now: say who you are and what you are here to do for them, in a line or two, then ask what to call them. Then stop and listen."
73
67
  : options.remembers
74
68
  ? "The call has just started. Speak first: greet the user naturally, in one line, as someone who remembers the last call."
75
69
  : "The call has just started. Speak first: greet the user naturally, in one line.",
@@ -79,40 +73,71 @@ export async function loadLivePrompt(options: {
79
73
  /**
80
74
  * The rules for every turn, together right under the identity, where the model weighs most.
81
75
  * One heading marks them; `IMPORTANT` stays on the ending rule alone, the one that failed
82
- * without it (09-17), so stamping it on all four would thin it out.
76
+ * without it (09-17), so stamping it on all of them would thin it out.
83
77
  */
84
78
  const always = () => `## Always
85
79
 
86
- ${callEnding()}
80
+ ${ending()}
87
81
 
88
82
  ${speaking()}
89
83
 
90
84
  ${delegation()}`;
91
85
 
86
+ /**
87
+ * How a call ends, as the voice can end it: it holds no tools, so it says yes and hands the turn
88
+ * over, and the backend runs the tool. The rule both prompts shared until 09-20 ("forget every
89
+ * other task, answer yes, then use the tool") left the voice saying yes and going quiet with the
90
+ * line still open, four times in five. The tool is still named, the one name the voice sees, so
91
+ * ending reads as something done rather than said.
92
+ */
93
+ const ending = () =>
94
+ `IMPORTANT — always follow this: when the user wants the call to end, however they say it, 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 ${TOOL_NAMES.end_call} tool; saying yes alone leaves it open.`;
95
+
92
96
  /**
93
97
  * The guide's starter lines on listening and interruptions, labels kept as it says. The
94
98
  * backchannel line is the one it invites changing: a long turn heard in silence reads as
95
- * nobody listening.
99
+ * nobody listening. The language is the one being spoken, never the browser's: a first call
100
+ * opened in the browser's language kept an English speaker in Korean.
96
101
  */
97
102
  function speaking(): string {
98
103
  return `Backchannel policy: Use moderate backchannels. When the user speaks at length, acknowledge now and then with a short listening sound so they know you are following, without competing with the main response.
99
104
 
100
- Interruption policy: Stop speaking when the user interrupts. Listen to what they say.`;
105
+ Interruption policy: Stop speaking when the user interrupts. Listen to what they say.
106
+
107
+ Speak the language the user is speaking, whatever language came before; when they switch, switch with them.`;
101
108
  }
102
109
 
103
110
  /**
104
- * Under the guide's label, and no longer than this: Live decides for itself whether to hand a
105
- * turn over, and with no rule it answered from its own knowledge or only said it would act.
106
- * What the backend can do and how is the backend's to know. What they say about themselves is
107
- * named on its own: nothing is kept that is not handed over, and it is rarely a request. The
108
- * voice sees only profile and preferences whole, so "already written" is a rough filter; the
109
- * backend merges the rest (thursday.prompt memory).
111
+ * The guide's three labels, as it asks: Live decides for itself whether to hand a turn over,
112
+ * and reads that from what the list says the backend can do. Without the list (09-17 to 09-19)
113
+ * it said "yes" to a hang-up, a stop or a routine and handed nothing over: 1 `end_call` in 21
114
+ * calls, then none in 34, where the list had drawn 5 in 7. The list names what can be done,
115
+ * never how: tools, bots and threads stay the backend's. What they say about themselves is
116
+ * its own line: nothing is kept that is not handed over, and it is rarely a request. The voice
117
+ * sees only profile and preferences whole, so "already written" is a rough filter; the backend
118
+ * merges the rest (thursday.prompt memory). Stopping her voice is not stopping a job (the
119
+ * guide's interruptions): the one is hers, the other the backend's.
110
120
  */
111
121
  function delegation(): string {
112
122
  return `Delegation policy:
113
- Delegate to the backend everything the user asks for, tells you or asks about, except greetings, small talk and a brief clarification.
114
- Whatever they tell you about themselves, the people in their life, their plans or how they want things done goes to the backend as they say it, however small — unless it is already written below.
115
- Answer from what the backend returns, and do not guess the result while waiting.`;
123
+ Backend tools:
124
+ - Ending the call: hangs up the line.
125
+ - Background work: hands a job to a bot, passes words on, stops or changes a job, answers a bot's question, says how the work stands.
126
+ - Routines: jobs that start by themselves later.
127
+ - Memory: keeps what the user tells you about themselves, and looks it up.
128
+ - This computer and the web: runs a command, searches.
129
+
130
+ Delegate to the backend when:
131
+ - The user wants the call to end.
132
+ - They ask for anything on that list, or change or stop work already asked for.
133
+ - They tell you about themselves, the people in their life, their plans or how they want things done, however small, unless it is already written below.
134
+
135
+ Do not delegate to the backend when:
136
+ - They greet you, make small talk, or only want you to stop talking.
137
+ - You can answer from the conversation or a result still current.
138
+ - You need a brief clarification to understand the request.
139
+
140
+ Delegate before giving an answer that depends on backend work. Do not guess the result while waiting.`;
116
141
  }
117
142
 
118
143
  /**
@@ -22,7 +22,7 @@ import { clip } from "@/lib/utils";
22
22
  /**
23
23
  * Row-to-line formatters shared by the call prompts (live, thursday) and bot.prompt, plus the tidying check.
24
24
  * Nothing here wraps a sentence; headings and paragraphs live in the prompt that says them. The
25
- * exceptions are `thursdayIdentity` and `callEnding`, the words both call prompts open with.
25
+ * exception is `thursdayIdentity`, the words both call prompts open with.
26
26
  */
27
27
 
28
28
  /**
@@ -85,14 +85,6 @@ export const thursdayIdentity = (now = new Date()) =>
85
85
 
86
86
  What they tell you is kept, so you know them better over time, and whatever they want done can be done for them in the background.`;
87
87
 
88
- /**
89
- * How a call ends, in the same words right after the identity in both call prompts. It names
90
- * the tool in both, the one tool name the voice sees: without it, ending the call read as
91
- * something to say rather than do, and the line stayed open through repeated requests.
92
- */
93
- export const callEnding = () =>
94
- `IMPORTANT — always follow this: when the user wants the call to end, however they say it, forget every other task, answer yes in one word of their language, then immediately, without thinking, use the ${TOOL_NAMES.end_call} tool.`;
95
-
96
88
  /**
97
89
  * When a call happened, the one way every prompt and tool says it: local, the
98
90
  * same shape as `Now` (clockNow), with how long ago. A UTC stamp beside a local
@@ -28,7 +28,6 @@ import {
28
28
  import { openWorkspace } from "@/features/workspace/workspace";
29
29
  import { listConnectedToolNames } from "../tools/connected";
30
30
  import {
31
- callEnding,
32
31
  carriedLines,
33
32
  expandedFacts,
34
33
  logPromptSize,
@@ -88,7 +87,7 @@ export async function loadThursdayPrompt(
88
87
  // Order matters: earlier calls go last so the current call follows them in time order
89
88
  const text = [
90
89
  thursdayIdentity(),
91
- written ? "" : callEnding(),
90
+ written ? "" : ending(),
92
91
  memory(index, carried, open.notes),
93
92
  // A skill is named once, on the side that can read it: this computer's chapter
94
93
  // when the setting hands the call the tool, the bots' reach when it does not
@@ -110,6 +109,13 @@ export async function loadThursdayPrompt(
110
109
  return text;
111
110
  }
112
111
 
112
+ /**
113
+ * How a call ends, right after the identity. Only the backend can end the line, so its rule is
114
+ * to do it at once, with the tool named; the voice's own rule (live.prompt) is to hand it over.
115
+ */
116
+ const ending = () =>
117
+ `IMPORTANT — always follow this: when the user wants the call to end, however they say it, forget every other task, answer yes in one word of their language, then immediately, without thinking, use the ${TOOL_NAMES.end_call} tool.`;
118
+
113
119
  /**
114
120
  * What goes back is said aloud, so it is what a tool or a note confirmed and nothing more — or
115
121
  * the one question the voice puts to the user before work is handed over (backgroundWork).
@@ -30,6 +30,7 @@ import {
30
30
  type JobBot,
31
31
  type TokenUsage,
32
32
  } from "@/features/bot/bot.schema";
33
+ import { renewSignIns } from "@/features/signins/signins.query";
33
34
  import {
34
35
  botBrowserSession,
35
36
  filesOnDisk,
@@ -154,6 +155,9 @@ export async function runBot(
154
155
  : null,
155
156
  openBotFolders(name),
156
157
  ]);
158
+ const session =
159
+ options.session ??
160
+ (options.threadId ? botBrowserSession(options.threadId, name) : null);
157
161
  const [prompt, tools] = await Promise.all([
158
162
  loadBotPrompt(
159
163
  name,
@@ -170,9 +174,7 @@ export async function runBot(
170
174
  target: "bot",
171
175
  bot: name,
172
176
  thread: options.threadId ?? null,
173
- session:
174
- options.session ??
175
- (options.threadId ? botBrowserSession(options.threadId, name) : null),
177
+ session,
176
178
  model,
177
179
  }),
178
180
  ]);
@@ -474,6 +476,10 @@ export async function runBot(
474
476
  quiet.end();
475
477
  acknowledgeStep();
476
478
  await Promise.allSettled([...inFlight]);
479
+ if (session)
480
+ void renewSignIns(session).catch((cause) =>
481
+ logger.warn(`${name}: kept sign-ins were not renewed`, cause),
482
+ );
477
483
  }
478
484
 
479
485
  try {
@@ -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. `recommended` is the only split: the seeds a key saved
23
- * outside the intro installs, where nobody picks (seed-bots), and the faces the intro's
24
- * opening loop shows. The intro and Settings › Bots offer every seed, all ticked.
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
- "Research with numbers — prices, markets, trends and comparisons, as tables and charts with sources",
75
+ "Answers with numbers — costs, markets, trends, companies, which to pick — cited and charted",
79
76
  hint: "Finds the numbers and draws them",
80
- recommended: true,
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
- **Size the answer to the ask.** A few numbers are your final text and nothing more. Anything longer is one self-contained page in your folder under \`artifacts/\`, written by hand in minutes: the finding first, then the charts, the tables and the rows behind them, every figure with where it came from — one file the user opens, 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.
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
- **Get the real numbers.** Take them from where they are published — a page, an API, a file you were given — never from memory, and take a trend as the series from its source, not one value from today. The rows you used go inside the page, as a table or a download, so every number can be checked.
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
- **Show, then say.** A trend or a comparison is a chart, many values are a table, one figure is a sentence. Title a chart with what it shows. Inline svg and a little script are enough.
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
- **Work by a skill when one fits.** Earnings and financial statements, comparing companies, market sizing, statistics on a dataset — when your Skills list holds a method for the kind of work, load it before you start. Install one only when this job needs it.
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
- **Mark what is not solid.** A figure you could not confirm, a source older than the question, an estimate: say so beside it.
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
- Your final text gives the two or three numbers that answer the question, and the page's path when there is one.`,
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: "Lambda",
96
+ name: "Tutor",
97
97
  description:
98
- "Turns work that repeats into scripts anyone can run, and runs them on a schedule when allowed",
99
- hint: "Makes a job a script and runs it again",
100
- recommended: true,
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
- **Done means it ran.** Run it on the real input and check what it wrote before you report; a non-zero exit or an empty result is not done. When you change a script, change only what the job needs, and run it again.
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
- **A schedule is the user's to allow.** When a script should run on its own — every morning, every hour — ask Thursday one \`${TOOL_NAMES.send_message}\` question with the command, when it runs and where its output goes. Add it to this machine's scheduler only on a yes, and say how to remove it.
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
- **Keep the map current** as you go: add the line when a script is born, fix it when the command changes, drop it when the folder is gone. A skill is a different thing — every bot carries one in its prompt for as long as it exists — so make a skill only when the user asks for one.
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 command, what it produced this run, and where.`,
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 — settles what the post is, builds the slides, writes it, and puts it up",
183
- hint: "Builds the slides, writes the caption, posts it",
184
- systemPrompt: `One job takes a post from nothing to live on instagram.com — settle what it is, sign in, build it, write it, put it up. Load the \`${BROWSER_SKILL}\` skill before any browser step.
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. The app keeps it: \`${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, and the skill carries the rest.
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
- Anything covering the screen is closed before you read what is under it. It is in the way, not a sign-in problem.
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
- **A slide is built, not drawn.** Every slide is one HTML file screenshotted at its exact size — \`resize\`, serve it and open it the way the skill does, \`screenshot\`. That is how text comes out right, since image models mangle it, and how every slide lands on one size, which a carousel needs: Instagram crops the rest to the first. Feed is 1080x1350 (4:5) or 1080x1080 (1:1), a story or reel 1080x1920. Pick one for the post and build every slide at it.
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 goes inside a slide**, as its background with \`object-fit: cover\`, and never stands in for one: the image model has no 4:5, so what it returns is never the right size alone. Draw it when the post is an idea or a mood — \`${STUDIO_TOOLS.generate_image}\` through \`${TOOL_NAMES.tool_call}\` on \`${STUDIO_SERVER}\`, the whole picture in \`prompt\` (subject, framing, light, palette; the model sees nothing of this job) and the same look repeated in each. Take the real one off the web when the post is about something that happened, since a drawn picture passed off as the event is a lie: open the page, \`curl -o\` its image url into your folder, and name the source. Blur or darken a photo under text until the text reads. A slide can also be text alone.
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
- \`${STUDIO_TOOLS.generate_image}\` not coming back from \`${TOOL_NAMES.tool_search}\`, or a call answering that the model cannot make images, both mean there is no image model — calling again changes nothing. When real pictures and text make the post, go on without it. When the post 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.
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, no wall of tags.
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
- The composer is the one part not to memorise. Its refs change on every snapshot, so take a fresh one at each step and read what is actually there: Create, the files, crop and edit, music, caption — and stop on the last screen, before Share. **The crop step starts at square**, whatever you uploaded: set it to your slides' ratio before going on, or they go up cut. Music: search the name they gave, or take a current one that fits. No music step on this format means there is none.
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
- Ask before it goes up: a \`${TOOL_NAMES.send_message}\` question to Thursday with the caption as written, the slide paths and the track; options \`Post it\` / \`Change something\`, and 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.
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
- Once it is up, open the post and check its image is the size you built; a cut one is deleted and posted again. Answer with the post's url and the file paths.`,
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 {
@@ -191,11 +190,6 @@ export const BotRoom = memo(function BotRoom() {
191
190
 
192
191
  // Reading a thread is reading its ending; that is what clears its dot.
193
192
  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
193
 
200
194
  // Thursday put a job in front of the user (`thread` `open`): the room opens on it
201
195
  useAppEvent({
@@ -503,10 +503,12 @@ export function Chip({
503
503
  : "rounded-full shadow-sm shadow-black/3",
504
504
  )}
505
505
  >
506
- {/* One box, two heights: the rows grow out of nothing rather than appearing. */}
506
+ {/* One box, two heights: the rows grow out of nothing rather than appearing.
507
+ Folded they are still laid out, so their width is contained: the pill is as
508
+ wide as its own row, not as the card it folded from. */}
507
509
  <div
508
510
  className={cn(
509
- "grid overflow-hidden transition-[grid-template-rows] duration-300 ease-out",
511
+ "grid overflow-hidden contain-inline-size transition-[grid-template-rows] duration-300 ease-out",
510
512
  grown ? "grid-rows-[1fr]" : "grid-rows-[0fr]",
511
513
  )}
512
514
  >
@@ -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, the recommended seeds are created on the app default model.
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.filter((seed) => seed.recommended).map((seed) => ({
28
+ BOT_SEEDS.map((seed) => ({
29
29
  name: seed.name,
30
30
  }));
31
31
  if (!wanted.length) return;
@@ -823,7 +823,6 @@ function useDemo(playing: boolean) {
823
823
 
824
824
  /** The pill and the corner where finished work lands, as they stand during the loop. */
825
825
  function DemoCorners({ stage, icons }: { stage: DemoStage; icons: BotIcon[] }) {
826
- const crew = BOT_SEEDS.filter((seed) => seed.recommended);
827
826
  return (
828
827
  <>
829
828
  {/* The card finished work really lands as, drawn here with nothing behind it */}
@@ -843,12 +842,12 @@ function DemoCorners({ stage, icons }: { stage: DemoStage; icons: BotIcon[] }) {
843
842
  )}
844
843
  <div className="absolute right-5 bottom-5 flex h-10 items-center gap-2.5 rounded-full bg-background pr-3.5 pl-2.5 ring-1 ring-border">
845
844
  <span className="flex">
846
- {crew.map((seed, index) => (
845
+ {BOT_SEEDS.map((seed, index) => (
847
846
  <BotMark
848
847
  key={seed.name}
849
848
  size={22}
850
849
  seed={seed.name}
851
- {...icons[BOT_SEEDS.indexOf(seed)]}
850
+ {...icons[index]}
852
851
  state={
853
852
  stage === "working" && seed.name === "Analyst"
854
853
  ? "thinking"
@@ -2,6 +2,7 @@ import { readFile, stat } from "node:fs/promises";
2
2
  import type { ModelMessage } from "ai";
3
3
  import { appEvents } from "@/app/api/events/app-event.server";
4
4
  import { REACH } from "@/config";
5
+ import { LiveSettingsSchema } from "@/features/ai/live.schema";
5
6
  import { modelErrorToString } from "@/features/ai/model";
6
7
  import { answerThread } from "@/features/bot/bot.runner";
7
8
  import type { Thread } from "@/features/bot/bot.schema";
@@ -14,7 +15,6 @@ import {
14
15
  } from "@/features/config/config.query";
15
16
  import { openWork } from "@/features/thursday/open-work";
16
17
  import { endCall, isCallOpen } from "@/features/thursday/thursday.query";
17
- import { ThursdaySettingsSchema } from "@/features/thursday/thursday.schema";
18
18
  import {
19
19
  answerInWriting,
20
20
  openTextCall,
@@ -353,7 +353,7 @@ async function answer(
353
353
  4_000,
354
354
  );
355
355
  try {
356
- const settings = ThursdaySettingsSchema.parse({});
356
+ const settings = LiveSettingsSchema.parse({});
357
357
  // Quiet for long enough, or closed under it (the tab went, the server restarted): the
358
358
  // next words open a new call, which reads the last one back under Earlier calls
359
359
  const kept = live.line;