thursday-agent 0.12.0 → 0.13.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 (470) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +3 -3
  3. package/.next/prerender-manifest.json +4 -4
  4. package/.next/server/app/_global-error/page.js.nft.json +1 -1
  5. package/.next/server/app/_global-error.html +1 -1
  6. package/.next/server/app/_global-error.rsc +1 -1
  7. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  10. package/.next/server/app/_not-found/page.js +1 -1
  11. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  12. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  13. package/.next/server/app/_not-found.html +1 -1
  14. package/.next/server/app/_not-found.rsc +18 -19
  15. package/.next/server/app/_not-found.segments/_full.segment.rsc +18 -19
  16. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -15
  17. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  18. package/.next/server/app/api/artifact/route.js +8 -8
  19. package/.next/server/app/api/artifact/route.js.nft.json +1 -1
  20. package/.next/server/app/api/bot/keep-working/route.js +7 -7
  21. package/.next/server/app/api/bot/keep-working/route.js.nft.json +1 -1
  22. package/.next/server/app/api/bot/memory/files/route.js.nft.json +1 -1
  23. package/.next/server/app/api/bot/memory/route.js +7 -7
  24. package/.next/server/app/api/bot/memory/route.js.nft.json +1 -1
  25. package/.next/server/app/api/bot/route.js +7 -7
  26. package/.next/server/app/api/bot/route.js.nft.json +1 -1
  27. package/.next/server/app/api/bot/thread/[id]/route.js +5 -5
  28. package/.next/server/app/api/bot/thread/[id]/route.js.nft.json +1 -1
  29. package/.next/server/app/api/bot/thread/route.js +3 -3
  30. package/.next/server/app/api/bot/thread/route.js.nft.json +1 -1
  31. package/.next/server/app/api/config/route.js +3 -3
  32. package/.next/server/app/api/config/route.js.nft.json +1 -1
  33. package/.next/server/app/api/events/route.js.nft.json +1 -1
  34. package/.next/server/app/api/favicon/[host]/route.js.nft.json +1 -1
  35. package/.next/server/app/api/file/[...path]/route.js +2 -2
  36. package/.next/server/app/api/file/[...path]/route.js.nft.json +1 -1
  37. package/.next/server/app/api/llm-model/catalog/route.js +4 -4
  38. package/.next/server/app/api/llm-model/catalog/route.js.nft.json +1 -1
  39. package/.next/server/app/api/llm-model/credits/route.js +4 -4
  40. package/.next/server/app/api/llm-model/credits/route.js.nft.json +1 -1
  41. package/.next/server/app/api/llm-model/route.js +6 -6
  42. package/.next/server/app/api/llm-model/route.js.nft.json +1 -1
  43. package/.next/server/app/api/llm-model/subscription/route.js +6 -6
  44. package/.next/server/app/api/llm-model/subscription/route.js.nft.json +1 -1
  45. package/.next/server/app/api/mcp/[name]/route.js +5 -5
  46. package/.next/server/app/api/mcp/[name]/route.js.nft.json +1 -1
  47. package/.next/server/app/api/mcp/oauth/callback/route.js +5 -5
  48. package/.next/server/app/api/mcp/oauth/callback/route.js.nft.json +1 -1
  49. package/.next/server/app/api/mcp/route.js +5 -5
  50. package/.next/server/app/api/mcp/route.js.nft.json +1 -1
  51. package/.next/server/app/api/mcp/tools/route.js +5 -5
  52. package/.next/server/app/api/mcp/tools/route.js.nft.json +1 -1
  53. package/.next/server/app/api/memory/edit/route.js +15 -14
  54. package/.next/server/app/api/memory/edit/route.js.nft.json +1 -1
  55. package/.next/server/app/api/memory/route.js +4 -4
  56. package/.next/server/app/api/memory/route.js.nft.json +1 -1
  57. package/.next/server/app/api/reach/route.js +13 -12
  58. package/.next/server/app/api/reach/route.js.nft.json +1 -1
  59. package/.next/server/app/api/routine/route.js +8 -9
  60. package/.next/server/app/api/routine/route.js.nft.json +1 -1
  61. package/.next/server/app/api/signins/route.js.nft.json +1 -1
  62. package/.next/server/app/api/skills/[source]/[name]/route.js +2 -2
  63. package/.next/server/app/api/skills/[source]/[name]/route.js.nft.json +1 -1
  64. package/.next/server/app/api/skills/route.js +2 -2
  65. package/.next/server/app/api/skills/route.js.nft.json +1 -1
  66. package/.next/server/app/api/thursday/call/route.js +6 -6
  67. package/.next/server/app/api/thursday/call/route.js.nft.json +1 -1
  68. package/.next/server/app/api/thursday/skills/route.js +6 -6
  69. package/.next/server/app/api/thursday/skills/route.js.nft.json +1 -1
  70. package/.next/server/app/api/thursday/text/route.js +14 -13
  71. package/.next/server/app/api/thursday/text/route.js.nft.json +1 -1
  72. package/.next/server/app/api/thursday/tool-call/route.js +15 -14
  73. package/.next/server/app/api/thursday/tool-call/route.js.nft.json +1 -1
  74. package/.next/server/app/api/workspace/files/route.js +10 -6
  75. package/.next/server/app/api/workspace/files/route.js.nft.json +1 -1
  76. package/.next/server/app/api/workspace/route.js +9 -4
  77. package/.next/server/app/api/workspace/route.js.nft.json +1 -1
  78. package/.next/server/app/artifact/[...path]/page/server-reference-manifest.json +2 -2
  79. package/.next/server/app/artifact/[...path]/page.js +5 -4
  80. package/.next/server/app/artifact/[...path]/page.js.nft.json +1 -1
  81. package/.next/server/app/artifact/[...path]/page_client-reference-manifest.js +1 -1
  82. package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
  83. package/.next/server/app/manifest.webmanifest/route.js.nft.json +1 -1
  84. package/.next/server/app/page/server-reference-manifest.json +67 -56
  85. package/.next/server/app/page.js +10 -11
  86. package/.next/server/app/page.js.nft.json +1 -1
  87. package/.next/server/app/page_client-reference-manifest.js +1 -1
  88. package/.next/server/chunks/{[root-of-the-server]__0o49ztf._.js → [root-of-the-server]__0-k4_ie._.js} +2 -2
  89. package/.next/server/chunks/[root-of-the-server]__0209ah4._.js +6 -6
  90. package/.next/server/chunks/[root-of-the-server]__04wldw9._.js +1 -1
  91. package/.next/server/chunks/[root-of-the-server]__0h_x6q6._.js +2 -2
  92. package/.next/server/chunks/{[root-of-the-server]__0cfszwn._.js → [root-of-the-server]__0i23tc1._.js} +2 -2
  93. package/.next/server/chunks/[root-of-the-server]__0mrk2it._.js +1 -1
  94. package/.next/server/chunks/[root-of-the-server]__0nr-v8s._.js +1 -1
  95. package/.next/server/chunks/{[root-of-the-server]__1tg5got._.js → [root-of-the-server]__0paqm5y._.js} +2 -2
  96. package/.next/server/chunks/[root-of-the-server]__0qog7sm._.js +8 -0
  97. package/.next/server/chunks/[root-of-the-server]__0sm5xvm._.js +1 -1
  98. package/.next/server/chunks/[root-of-the-server]__0yisc5v._.js +5 -0
  99. package/.next/server/chunks/[root-of-the-server]__1-swlkw._.js +3 -3
  100. package/.next/server/chunks/[root-of-the-server]__12iddfq._.js +2 -2
  101. package/.next/server/chunks/{[root-of-the-server]__0npn_pn._.js → [root-of-the-server]__15p29m3._.js} +2 -2
  102. package/.next/server/chunks/[root-of-the-server]__17ses32._.js +3 -0
  103. package/.next/server/chunks/[root-of-the-server]__18-2tsq._.js +2 -2
  104. package/.next/server/chunks/[root-of-the-server]__1cjxw0-._.js +1 -1
  105. package/.next/server/chunks/[root-of-the-server]__1gi2nns._.js +5 -0
  106. package/.next/server/chunks/{[root-of-the-server]__0rqgb17._.js → [root-of-the-server]__1iauz8a._.js} +2 -2
  107. package/.next/server/chunks/[root-of-the-server]__1k3j52n._.js +1 -1
  108. package/.next/server/chunks/{[root-of-the-server]__209k6pk._.js → [root-of-the-server]__1lb_pxg._.js} +2 -2
  109. package/.next/server/chunks/[root-of-the-server]__1ndi14y._.js +1 -1
  110. package/.next/server/chunks/[root-of-the-server]__1orn-5b._.js +1 -1
  111. package/.next/server/chunks/[root-of-the-server]__1xqsrqq._.js +5 -0
  112. package/.next/server/chunks/[root-of-the-server]__1ygplx1._.js +1 -1
  113. package/.next/server/chunks/[root-of-the-server]__2163i40._.js +5 -0
  114. package/.next/server/chunks/_0ci0abh._.js +3 -0
  115. package/.next/server/chunks/_0eauz4t._.js +1 -1
  116. package/.next/server/chunks/_0f6ts7z._.js +1 -1
  117. package/.next/server/chunks/_0fg-1oj._.js +5 -0
  118. package/.next/server/chunks/_0khihjk._.js +2 -2
  119. package/.next/server/chunks/_0n74zsf._.js +1 -1
  120. package/.next/server/chunks/_0na2fj1._.js +2 -2
  121. package/.next/server/chunks/_0oh_99-._.js +3 -0
  122. package/.next/server/chunks/_0x-ej3f._.js +24 -24
  123. package/.next/server/chunks/_0z3nz21._.js +3 -0
  124. package/.next/server/chunks/_10tkr26._.js +1 -1
  125. package/.next/server/chunks/_12-amdu._.js +2 -2
  126. package/.next/server/chunks/_136j09a._.js +1 -1
  127. package/.next/server/chunks/_15740dw._.js +1 -1
  128. package/.next/server/chunks/_18izeh1._.js +5 -0
  129. package/.next/server/chunks/_1_5mnq4._.js +1 -1
  130. package/.next/server/chunks/_1ajbhv6._.js +2 -2
  131. package/.next/server/chunks/_1dc_d1q._.js +2 -2
  132. package/.next/server/chunks/_1drgfx4._.js +1 -1
  133. package/.next/server/chunks/_1e48czp._.js +2 -2
  134. package/.next/server/chunks/_1f06zo4._.js +3 -0
  135. package/.next/server/chunks/_1ic3bew._.js +2 -2
  136. package/.next/server/chunks/_1jn-aep._.js +2 -2
  137. package/.next/server/chunks/_1jn5btd._.js +6 -0
  138. package/.next/server/chunks/_1kg70wc._.js +2 -2
  139. package/.next/server/chunks/_1m2i5pt._.js +5 -0
  140. package/.next/server/chunks/_1ogqovh._.js +4 -4
  141. package/.next/server/chunks/_1qxheoa._.js +1 -1
  142. package/.next/server/chunks/_1ry6afh._.js +18 -0
  143. package/.next/server/chunks/_1taa4bs._.js +1 -1
  144. package/.next/server/chunks/_1tpr507._.js +1 -1
  145. package/.next/server/chunks/_1y8w80a._.js +1 -1
  146. package/.next/server/chunks/_1zxp-p6._.js +1 -1
  147. package/.next/server/chunks/_204744f._.js +5 -0
  148. package/.next/server/chunks/_20fsr_z._.js +1 -1
  149. package/.next/server/chunks/config_ts_1g4sz5o._.js +1 -1
  150. package/.next/server/chunks/features_0-cnt0l._.js +1 -1
  151. package/.next/server/chunks/features_0hdaci0._.js +1 -1
  152. package/.next/server/chunks/features_0p85gv6._.js +3 -0
  153. package/.next/server/chunks/{node_modules_zod_v4_0_v5xf5._.js → node_modules_zod_v4_01bha1c._.js} +2 -2
  154. package/.next/server/chunks/{node_modules_zod_v4_0q9hvql._.js → node_modules_zod_v4_0b99z6a._.js} +2 -2
  155. package/.next/server/chunks/{node_modules_zod_v4_1jg8iwg._.js → node_modules_zod_v4_1zz6lo1._.js} +2 -2
  156. package/.next/server/chunks/ssr/[root-of-the-server]__04upd5v._.js +3 -0
  157. package/.next/server/chunks/ssr/[root-of-the-server]__0dxszdt._.js +3 -0
  158. package/.next/server/chunks/ssr/[root-of-the-server]__0eezycw._.js +3 -0
  159. package/.next/server/chunks/ssr/[root-of-the-server]__0hpp3nw._.js +1 -1
  160. package/.next/server/chunks/ssr/[root-of-the-server]__0yngu44._.js +3 -0
  161. package/.next/server/chunks/ssr/[root-of-the-server]__16z72ny._.js +6 -0
  162. package/.next/server/chunks/ssr/[root-of-the-server]__1u_ry86._.js +3 -0
  163. package/.next/server/chunks/ssr/{[root-of-the-server]__1iwyp1e._.js → [root-of-the-server]__1unh_ew._.js} +1 -1
  164. package/.next/server/chunks/ssr/[root-of-the-server]__20re7ya._.js +3 -0
  165. package/.next/server/chunks/ssr/_00pgq9d._.js +5 -0
  166. package/.next/server/chunks/ssr/_07ke63g._.js +3 -0
  167. package/.next/server/chunks/ssr/_0agcbk7._.js +3 -0
  168. package/.next/server/chunks/ssr/_0e-gw2_._.js +3 -0
  169. package/.next/server/chunks/ssr/{_0fbi6om._.js → _0f_om6y._.js} +3 -3
  170. package/.next/server/chunks/ssr/_0fqhhwi._.js +1 -1
  171. package/.next/server/chunks/ssr/{_026qnc9._.js → _0gpn71k._.js} +3 -3
  172. package/.next/server/chunks/ssr/_0iizjr9._.js +3 -0
  173. package/.next/server/chunks/ssr/_0ixvzo6._.js +75 -0
  174. package/.next/server/chunks/ssr/_0kkdx7t._.js +3 -0
  175. package/.next/server/chunks/ssr/_0lufvw7._.js +118 -0
  176. package/.next/server/chunks/ssr/_0vi6ky6._.js +115 -0
  177. package/.next/server/chunks/ssr/{_10f-jib._.js → _0yhji3e._.js} +2 -2
  178. package/.next/server/chunks/ssr/_0z38nha._.js +3 -0
  179. package/.next/server/chunks/ssr/{_10mo72m._.js → _11nx2d4._.js} +2 -2
  180. package/.next/server/chunks/ssr/_12mijol._.js +2 -2
  181. package/.next/server/chunks/ssr/_150463c._.js +164 -0
  182. package/.next/server/chunks/ssr/{_0rmaf32._.js → _17d2ikl._.js} +2 -2
  183. package/.next/server/chunks/ssr/_19jez1a._.js +3 -0
  184. package/.next/server/chunks/ssr/_1blui1m._.js +3 -0
  185. package/.next/server/chunks/ssr/_1d423aj._.js +4 -4
  186. package/.next/server/chunks/ssr/_1kbk28w._.js +3 -0
  187. package/.next/server/chunks/ssr/_1kj_r9v._.js +3 -0
  188. package/.next/server/chunks/ssr/_1mqn9mc._.js +1 -1
  189. package/.next/server/chunks/ssr/{_0u5xv6g._.js → _1myh7ss._.js} +2 -2
  190. package/.next/server/chunks/ssr/_1neoj22._.js +1 -1
  191. package/.next/server/chunks/ssr/_1ou5sj0._.js +3 -0
  192. package/.next/server/chunks/ssr/_1p8xorg._.js +3 -0
  193. package/.next/server/chunks/ssr/_1tzacpl._.js +3 -0
  194. package/.next/server/chunks/ssr/_1xoch6f._.js +1 -1
  195. package/.next/server/chunks/ssr/_1yh5bj5._.js +1 -1
  196. package/.next/server/chunks/ssr/{_02c7o30._.js → _2021n9-._.js} +2 -2
  197. package/.next/server/chunks/ssr/_20argo3._.js +3 -0
  198. package/.next/server/chunks/ssr/features_bot_1ll3wp7._.js +3 -0
  199. package/.next/server/chunks/ssr/features_bot_1s_-2vj._.js +1 -1
  200. package/.next/server/chunks/ssr/features_bot_thread_query_ts_023eklt._.js +1 -1
  201. package/.next/server/chunks/ssr/features_config_1qxe5ur._.js +20 -0
  202. package/.next/server/chunks/ssr/features_connectors_components_mcp-setting_tsx_0jmbgvp._.js +3 -3
  203. package/.next/server/chunks/ssr/features_memory_components_memory-setting_tsx_0oa43sw._.js +1 -1
  204. package/.next/server/chunks/ssr/features_routine_components_routine-setting_tsx_055fknz._.js +1 -1
  205. package/.next/server/chunks/ssr/features_workspace_00jz2hu._.js +1 -1
  206. package/.next/server/chunks/ssr/{node_modules_0ky76fz._.js → node_modules_06e0wlv._.js} +1 -1
  207. package/.next/server/chunks/ssr/{node_modules_0-x0zxm._.js → node_modules_0cj26-9._.js} +1 -1
  208. package/.next/server/chunks/ssr/{node_modules_0operbz._.js → node_modules_0d1rf5e._.js} +1 -1
  209. package/.next/server/chunks/ssr/{node_modules_0b0iy01._.js → node_modules_0e86703._.js} +1 -1
  210. package/.next/server/chunks/ssr/{node_modules_05b6e99._.js → node_modules_0es09am._.js} +1 -1
  211. package/.next/server/chunks/ssr/{node_modules_1iau_ea._.js → node_modules_0gr_dzf._.js} +1 -1
  212. package/.next/server/chunks/ssr/{node_modules_1hv-j1l._.js → node_modules_0gtcwz9._.js} +1 -1
  213. package/.next/server/chunks/ssr/{node_modules_1c2ikr9._.js → node_modules_0hi9h8f._.js} +1 -1
  214. package/.next/server/chunks/ssr/{node_modules_01u4ea3._.js → node_modules_0lkop0t._.js} +1 -1
  215. package/.next/server/chunks/ssr/{node_modules_1wvf3oo._.js → node_modules_0nghisk._.js} +1 -1
  216. package/.next/server/chunks/ssr/{node_modules_0yzmxfq._.js → node_modules_0stovz0._.js} +1 -1
  217. package/.next/server/chunks/ssr/{node_modules_00nrzz_._.js → node_modules_0we85kr._.js} +1 -1
  218. package/.next/server/chunks/ssr/{node_modules_01ed0yi._.js → node_modules_0ya7y1s._.js} +1 -1
  219. package/.next/server/chunks/ssr/{node_modules_0m3_5ge._.js → node_modules_0yrbwkj._.js} +1 -1
  220. package/.next/server/chunks/ssr/{node_modules_0v_-uht._.js → node_modules_1-x70ie._.js} +1 -1
  221. package/.next/server/chunks/ssr/{node_modules_07jp6tj._.js → node_modules_12fd9g0._.js} +1 -1
  222. package/.next/server/chunks/ssr/{node_modules_1wgnj8q._.js → node_modules_12wxrgr._.js} +1 -1
  223. package/.next/server/chunks/ssr/{node_modules_0azd56x._.js → node_modules_139uu4q._.js} +1 -1
  224. package/.next/server/chunks/ssr/{node_modules_1omg9xr._.js → node_modules_14gpoe1._.js} +1 -1
  225. package/.next/server/chunks/ssr/{node_modules_1ma_y68._.js → node_modules_18fmji6._.js} +1 -1
  226. package/.next/server/chunks/ssr/{node_modules_0ucridu._.js → node_modules_19kkel8._.js} +1 -1
  227. package/.next/server/chunks/ssr/{node_modules_0fpnetd._.js → node_modules_1gcine1._.js} +1 -1
  228. package/.next/server/chunks/ssr/{node_modules_1z3sh11._.js → node_modules_1h4trkm._.js} +1 -1
  229. package/.next/server/chunks/ssr/{node_modules_0lautv3._.js → node_modules_1mub_qu._.js} +1 -1
  230. package/.next/server/chunks/ssr/{node_modules_0ou0all._.js → node_modules_1p78svs._.js} +1 -1
  231. package/.next/server/chunks/ssr/{node_modules_19b0bir._.js → node_modules_1xp-7ep._.js} +1 -1
  232. package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0tmjn9w._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0ay7_oi._.js} +1 -1
  233. package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_1_e8c3o._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_182ydq_._.js} +1 -1
  234. package/.next/server/middleware-build-manifest.js +3 -3
  235. package/.next/server/pages/404.html +1 -1
  236. package/.next/server/pages/500.html +1 -1
  237. package/.next/server/server-reference-manifest.js +1 -1
  238. package/.next/server/server-reference-manifest.json +69 -58
  239. package/.next/static/chunks/03df16hs01-xi.js +1 -0
  240. package/.next/static/chunks/0crocb7272z8k.js +1 -0
  241. package/.next/static/chunks/{0p9jcyqkpq71f.js → 0dqsenl3uk_8z.js} +1 -1
  242. package/.next/static/chunks/0hhn8mf953761.js +1 -0
  243. package/.next/static/chunks/0n1o3k0z4m7c_.js +1 -0
  244. package/.next/static/chunks/0oe2zavre2a7d.css +1 -0
  245. package/.next/static/chunks/0srg9tv4105-i.js +1 -0
  246. package/.next/static/chunks/0t599wrclly7i.js +18 -0
  247. package/.next/static/chunks/0x34bhth_sn35.js +1 -0
  248. package/.next/static/chunks/{0mscosx0eh3ee.js → 0x_6z8vt8hyf4.js} +3 -3
  249. package/.next/static/chunks/{0p50d-au6-3hx.js → 1_wstwzu1md-6.js} +2 -2
  250. package/.next/static/chunks/{40ij3wsn3vt7h.js → 1aughn81slf90.js} +1 -1
  251. package/.next/static/chunks/1g1j03h_l-e67.js +1 -0
  252. package/.next/static/chunks/1lp02y2nmddxg.js +1 -0
  253. package/.next/static/chunks/211s4xjtecj2l.js +1 -0
  254. package/.next/static/chunks/2244-cat2jlvd.js +1 -0
  255. package/.next/static/chunks/{2r6xnyg7ezyxr.js → 2eev_i93slion.js} +2 -2
  256. package/.next/static/chunks/2sjq4gsimlfqr.js +113 -0
  257. package/.next/static/chunks/{0qgn3u86jkbm7.js → 2uleilh8ph06-.js} +1 -1
  258. package/.next/static/chunks/{1czfknd4luo6t.js → 2zchsg03a1pdp.js} +1 -1
  259. package/.next/static/chunks/32tmfirfjpulv.js +1 -0
  260. package/.next/static/chunks/{2_c7mu6x1lu49.js → 376-s_i83irs-.js} +1 -1
  261. package/.next/static/chunks/3blk-kpn39xr-.js +1 -0
  262. package/.next/static/chunks/3eieehp7wquyu.js +1 -0
  263. package/.next/static/chunks/3kc-e7pcj50ae.js +1 -0
  264. package/.next/static/chunks/3rwpyuo9nrrop.js +17 -0
  265. package/.next/static/chunks/3sf4l25l353ja.js +1 -0
  266. package/.next/static/chunks/3vz7m1w688juj.js +3 -0
  267. package/.next/static/chunks/43skgt0h9woyu.js +1 -0
  268. package/app/api/events/app-event.client.ts +8 -2
  269. package/app/api/events/app-event.server.ts +7 -5
  270. package/app/api/events/app-event.ts +19 -8
  271. package/app/api/query-key.ts +13 -5
  272. package/bin/database.mjs +21 -11
  273. package/bin/thursday.mjs +5 -2
  274. package/components/ui/shiny-text.tsx +2 -2
  275. package/database/migrations/20260920024909_messy_baron_strucker/migration.sql +1 -0
  276. package/database/migrations/20260920024909_messy_baron_strucker/snapshot.json +1937 -0
  277. package/features/artifact/components/artifact-setting.tsx +18 -24
  278. package/features/bot/bot.action.ts +3 -0
  279. package/features/bot/bot.runner.ts +25 -3
  280. package/features/bot/bot.seed.ts +98 -22
  281. package/features/bot/components/bot-badge.tsx +2 -2
  282. package/features/bot/components/bot-mark.tsx +12 -3
  283. package/features/bot/components/bot-room.tsx +33 -10
  284. package/features/bot/components/bot-setting.tsx +88 -34
  285. package/features/bot/components/crew-motion.ts +161 -0
  286. package/features/bot/components/mark-palette.tsx +3 -3
  287. package/features/bot/components/room-conversation.tsx +13 -8
  288. package/features/bot/components/room-list.tsx +9 -5
  289. package/features/bot/components/room-pill.tsx +131 -41
  290. package/features/bot/components/thread-badge.tsx +4 -4
  291. package/features/bot/components/thread-setting.tsx +58 -19
  292. package/features/bot/mark.const.ts +24 -0
  293. package/features/bot/thread.query.ts +32 -14
  294. package/features/bot/thread.store.ts +19 -16
  295. package/features/config/components/config-badge.tsx +2 -2
  296. package/features/config/components/config-setting.tsx +63 -213
  297. package/features/config/config.const.ts +2 -1
  298. package/features/connectors/components/mcp-setting.tsx +60 -29
  299. package/features/intro/components/intro.tsx +2 -2
  300. package/features/memory/components/memory-setting.tsx +11 -7
  301. package/features/reach/channel.ts +9 -1
  302. package/features/reach/components/reach-guide.tsx +606 -105
  303. package/features/reach/discord.ts +18 -3
  304. package/features/reach/reach.schema.ts +6 -0
  305. package/features/reach/reach.ts +55 -2
  306. package/features/reach/slack.ts +2 -1
  307. package/features/reach/telegram.ts +5 -1
  308. package/features/routine/components/routine-setting.tsx +46 -18
  309. package/features/settings/components/setting-ui.tsx +17 -68
  310. package/features/settings/components/settings.tsx +4 -3
  311. package/features/settings/settings.alert.ts +8 -8
  312. package/features/skills/components/skills-setting.tsx +78 -12
  313. package/features/skills/skills.action.ts +12 -0
  314. package/features/skills/skills.query.ts +24 -0
  315. package/features/thursday/components/call-log.tsx +19 -14
  316. package/features/thursday/components/side-captions.tsx +3 -2
  317. package/features/thursday/components/thursday-setting.tsx +5 -6
  318. package/features/thursday/components/thursday.tsx +8 -9
  319. package/features/thursday/components/write-line.tsx +10 -5
  320. package/features/thursday/thursday.action.ts +4 -0
  321. package/features/thursday/thursday.query.ts +3 -2
  322. package/features/thursday/thursday.schema.ts +5 -0
  323. package/features/thursday/use-call-ring.ts +6 -1
  324. package/features/thursday/use-text-call.ts +25 -10
  325. package/features/thursday/use-thursday.ts +10 -1
  326. package/features/workspace/components/artifact-view.tsx +42 -33
  327. package/features/workspace/components/file-thumb.tsx +8 -4
  328. package/features/workspace/components/file-view.tsx +25 -2
  329. package/features/workspace/components/workspace-setting.tsx +27 -23
  330. package/features/workspace/workspace.query.ts +20 -1
  331. package/features/workspace/workspace.ts +14 -0
  332. package/guide/bots.md +27 -4
  333. package/guide/calls.md +5 -3
  334. package/guide/phone.md +23 -12
  335. package/guide/setup.md +20 -5
  336. package/guide/trouble.md +1 -1
  337. package/hooks/use-theme.ts +14 -0
  338. package/lib/protocol/events.client.ts +56 -14
  339. package/lib/protocol/events.server.ts +41 -12
  340. package/lib/utils.ts +1 -1
  341. package/package.json +1 -1
  342. package/scripts/reach-channels.test.mts +15 -2
  343. package/seed-skills/brief/daily-brief/SKILL.md +39 -0
  344. package/seed-skills/brief/daily-brief/page/brief.css +406 -0
  345. package/seed-skills/brief/daily-brief/references/brief.md +106 -0
  346. package/seed-skills/brief/daily-brief/references/preferences.md +67 -0
  347. package/seed-skills/brief/daily-brief/scripts/glance.mjs +128 -0
  348. package/seed-skills/brief/daily-brief/scripts/lib.mjs +182 -0
  349. package/seed-skills/brief/daily-brief/scripts/news.mjs +320 -0
  350. package/seed-skills/brief/daily-brief/scripts/page.mjs +322 -0
  351. package/seed-skills/brief/daily-brief/scripts/story.mjs +265 -0
  352. package/seed-skills/digest/README.md +20 -0
  353. package/seed-skills/digest/media-digest/SKILL.md +32 -0
  354. package/seed-skills/digest/media-digest/references/no-captions.md +20 -0
  355. package/seed-skills/digest/media-digest/references/page.md +56 -0
  356. package/seed-skills/digest/media-digest/references/reading.md +56 -0
  357. package/seed-skills/digest/media-digest/scripts/audio.mjs +232 -0
  358. package/seed-skills/digest/media-digest/scripts/digest.mjs +379 -0
  359. package/seed-skills/digest/media-digest/scripts/lib.mjs +284 -0
  360. package/seed-skills/digest/media-digest/scripts/part.mjs +16 -0
  361. package/seed-skills/digest/media-digest/scripts/text.mjs +287 -0
  362. package/seed-skills/digest/media-digest/scripts/yt.mjs +487 -0
  363. package/seed-skills/docs/README.md +32 -0
  364. package/seed-skills/docs/documents/SKILL.md +34 -0
  365. package/seed-skills/docs/documents/kit/package-lock.json +1596 -0
  366. package/seed-skills/docs/documents/kit/package.json +17 -0
  367. package/seed-skills/docs/documents/references/deck.md +63 -0
  368. package/seed-skills/docs/documents/references/invoice.md +64 -0
  369. package/seed-skills/docs/documents/references/pdf.md +59 -0
  370. package/seed-skills/docs/documents/references/print.md +43 -0
  371. package/seed-skills/docs/documents/references/sheets.md +49 -0
  372. package/seed-skills/docs/documents/references/translate.md +37 -0
  373. package/seed-skills/docs/documents/references/word.md +45 -0
  374. package/seed-skills/docs/documents/scripts/doc.mjs +123 -0
  375. package/seed-skills/docs/documents/scripts/lib/browser.mjs +259 -0
  376. package/seed-skills/docs/documents/scripts/lib/deck-chart.mjs +150 -0
  377. package/seed-skills/docs/documents/scripts/lib/deck.mjs +1132 -0
  378. package/seed-skills/docs/documents/scripts/lib/invoice.mjs +505 -0
  379. package/seed-skills/docs/documents/scripts/lib/kit.mjs +244 -0
  380. package/seed-skills/docs/documents/scripts/lib/pdf.mjs +645 -0
  381. package/seed-skills/docs/documents/scripts/lib/report.mjs +38 -0
  382. package/seed-skills/docs/documents/scripts/lib/sheet.mjs +523 -0
  383. package/seed-skills/docs/documents/scripts/lib/templates.mjs +34 -0
  384. package/seed-skills/docs/documents/scripts/lib/translate.mjs +691 -0
  385. package/seed-skills/docs/documents/scripts/lib/word.mjs +678 -0
  386. package/seed-skills/docs/documents/templates/letter.html +56 -0
  387. package/seed-skills/docs/documents/templates/print.css +219 -0
  388. package/seed-skills/docs/documents/templates/report.html +47 -0
  389. package/seed-skills/docs/documents/templates/resume.html +61 -0
  390. package/seed-skills/insta/instagram/references/post.md +31 -7
  391. package/seed-skills/trip/README.md +11 -0
  392. package/seed-skills/trip/travel/SKILL.md +40 -0
  393. package/seed-skills/trip/travel/page/itinerary.css +488 -0
  394. package/seed-skills/trip/travel/references/itinerary.md +70 -0
  395. package/seed-skills/trip/travel/references/plan.md +75 -0
  396. package/seed-skills/trip/travel/references/watch-and-book.md +39 -0
  397. package/seed-skills/trip/travel/scripts/flights.mjs +374 -0
  398. package/seed-skills/trip/travel/scripts/fx.mjs +57 -0
  399. package/seed-skills/trip/travel/scripts/hotels.mjs +356 -0
  400. package/seed-skills/trip/travel/scripts/itinerary.mjs +482 -0
  401. package/seed-skills/trip/travel/scripts/lib.mjs +120 -0
  402. package/seed-skills/trip/travel/scripts/selectors.mjs +78 -0
  403. package/seed-skills/trip/travel/scripts/watch.mjs +147 -0
  404. package/seed-skills/trip/travel/scripts/weather.mjs +150 -0
  405. package/seed-skills/tutor/picture-book/SKILL.md +6 -0
  406. package/seed-skills/tutor/picture-book/references/picture-book.md +22 -2
  407. package/.next/server/chunks/[root-of-the-server]__014ydvq._.js +0 -8
  408. package/.next/server/chunks/[root-of-the-server]__01d285y._.js +0 -5
  409. package/.next/server/chunks/[root-of-the-server]__04r4h-2._.js +0 -7
  410. package/.next/server/chunks/[root-of-the-server]__0ayldfn._.js +0 -5
  411. package/.next/server/chunks/[root-of-the-server]__17weeeg._.js +0 -5
  412. package/.next/server/chunks/[root-of-the-server]__1ai5lme._.js +0 -8
  413. package/.next/server/chunks/_05v8k21._.js +0 -7
  414. package/.next/server/chunks/_0k6qjcf._.js +0 -5
  415. package/.next/server/chunks/_1d_enhu._.js +0 -3
  416. package/.next/server/chunks/_1jc5g4p._.js +0 -6
  417. package/.next/server/chunks/_1nfr-75._.js +0 -3
  418. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_0md6j0x._.js +0 -3
  419. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_14ih1vy._.js +0 -3
  420. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1d6ez5-._.js +0 -3
  421. package/.next/server/chunks/ssr/[root-of-the-server]__0a967km._.js +0 -3
  422. package/.next/server/chunks/ssr/[root-of-the-server]__0tzekbl._.js +0 -68
  423. package/.next/server/chunks/ssr/[root-of-the-server]__1756892._.js +0 -3
  424. package/.next/server/chunks/ssr/[root-of-the-server]__1mzi-ju._.js +0 -3
  425. package/.next/server/chunks/ssr/[root-of-the-server]__1nqw56a._.js +0 -3
  426. package/.next/server/chunks/ssr/[root-of-the-server]__1r77-88._.js +0 -3
  427. package/.next/server/chunks/ssr/[root-of-the-server]__1w9hqq8._.js +0 -8
  428. package/.next/server/chunks/ssr/_05pl_u8._.js +0 -3
  429. package/.next/server/chunks/ssr/_097-ljd._.js +0 -4
  430. package/.next/server/chunks/ssr/_0euzzf8._.js +0 -3
  431. package/.next/server/chunks/ssr/_0evkpto._.js +0 -3
  432. package/.next/server/chunks/ssr/_0igfcax._.js +0 -20
  433. package/.next/server/chunks/ssr/_0lu1n_x._.js +0 -3
  434. package/.next/server/chunks/ssr/_0nvh1h9._.js +0 -3
  435. package/.next/server/chunks/ssr/_0qmpw35._.js +0 -3
  436. package/.next/server/chunks/ssr/_0uv-qs4._.js +0 -3
  437. package/.next/server/chunks/ssr/_132qkfz._.js +0 -5
  438. package/.next/server/chunks/ssr/_15zqt-t._.js +0 -3
  439. package/.next/server/chunks/ssr/_16gfxpn._.js +0 -3
  440. package/.next/server/chunks/ssr/_184327j._.js +0 -3
  441. package/.next/server/chunks/ssr/_18ujhhq._.js +0 -3
  442. package/.next/server/chunks/ssr/_1lx6w_o._.js +0 -67
  443. package/.next/server/chunks/ssr/_1ntpny5._.js +0 -3
  444. package/.next/server/chunks/ssr/_1o1g4uk._.js +0 -5
  445. package/.next/server/chunks/ssr/_1xx4zlb._.js +0 -3
  446. package/.next/server/chunks/ssr/_2008vmi._.js +0 -164
  447. package/.next/server/chunks/ssr/features_bot_1n1s7bh._.js +0 -3
  448. package/.next/static/chunks/09pkygbht7ki8.js +0 -18
  449. package/.next/static/chunks/0bm-nhblcmmj4.js +0 -17
  450. package/.next/static/chunks/0c_g4-11sha-1.js +0 -1
  451. package/.next/static/chunks/0hhza87hkbqaz.js +0 -1
  452. package/.next/static/chunks/0samf80yu2b17.js +0 -1
  453. package/.next/static/chunks/0vlgzqo9ka90v.js +0 -1
  454. package/.next/static/chunks/1g9yyvculuxj2.js +0 -1
  455. package/.next/static/chunks/1jz8n0mhtdzr8.js +0 -1
  456. package/.next/static/chunks/206laxm6maiz2.js +0 -1
  457. package/.next/static/chunks/23nfu5g4vl_m7.js +0 -1
  458. package/.next/static/chunks/2h-tlhb2ffhkk.js +0 -1
  459. package/.next/static/chunks/2i5_w8olnrvo0.js +0 -3
  460. package/.next/static/chunks/2m6jd3586ijb-.js +0 -1
  461. package/.next/static/chunks/2m8iqwc6sykln.js +0 -1
  462. package/.next/static/chunks/33fyp-byw48ju.js +0 -65
  463. package/.next/static/chunks/35yd_27d9ylra.js +0 -1
  464. package/.next/static/chunks/3ag1i3__yl94f.js +0 -1
  465. package/.next/static/chunks/3m_ejzg-xn21r.css +0 -1
  466. package/.next/static/chunks/3s-jpd3ky49-n.js +0 -1
  467. package/features/reach/components/reach-state.tsx +0 -71
  468. /package/.next/static/{QSe0rTdXU5kh5BBzr2XBB → fiNGsQfna8qldl8wU8wqr}/_buildManifest.js +0 -0
  469. /package/.next/static/{QSe0rTdXU5kh5BBzr2XBB → fiNGsQfna8qldl8wU8wqr}/_clientMiddlewareManifest.js +0 -0
  470. /package/.next/static/{QSe0rTdXU5kh5BBzr2XBB → fiNGsQfna8qldl8wU8wqr}/_ssgManifest.js +0 -0
@@ -11,7 +11,7 @@ import {
11
11
  Wrench,
12
12
  X,
13
13
  } from "lucide-react";
14
- import { type ReactNode, useState } from "react";
14
+ import { type ReactNode, useEffect, useRef, useState } from "react";
15
15
  import { queryKey } from "@/app/api/query-key";
16
16
  import { Button } from "@/components/ui/button";
17
17
  import {
@@ -34,6 +34,11 @@ import { ShinyText } from "@/components/ui/shiny-text";
34
34
  import { Skeleton } from "@/components/ui/skeleton";
35
35
  import { Switch } from "@/components/ui/switch";
36
36
  import { Textarea } from "@/components/ui/textarea";
37
+ import {
38
+ Tooltip,
39
+ TooltipContent,
40
+ TooltipTrigger,
41
+ } from "@/components/ui/tooltip";
37
42
  import { APP_NAME, BOT_RUN, PROMPT_CROWDED } from "@/config";
38
43
  import { ModelPicker } from "@/features/ai/components/model-picker";
39
44
  import {
@@ -74,6 +79,7 @@ import {
74
79
  mediaModelWords,
75
80
  } from "@/features/config/config.const";
76
81
  import {
82
+ PICKED_ROW,
77
83
  SettingDialogContent,
78
84
  SettingError,
79
85
  SettingNote,
@@ -99,9 +105,18 @@ import { revalidate, useServerRoute } from "@/lib/protocol/use-server-route";
99
105
  import { cn, formatCount, WAITING_INK } from "@/lib/utils";
100
106
  import { MARK_SHAPES, MARK_SYSTEM } from "../mark.const";
101
107
 
108
+ /** A new bot's Create, raised from its page to the rail at the foot of the screen. */
109
+ type DraftCreate = {
110
+ /** What the form still lacks, in words ("a name"); empty when it can be created. */
111
+ missing: string[];
112
+ creating: boolean;
113
+ submit: () => void;
114
+ };
115
+
102
116
  /**
103
117
  * The bots Thursday hands background work to. Roster on the left, the picked bot's
104
- * page on the right. Fields save on blur or on pick; only a new bot has a Create button.
118
+ * page on the right. Fields save on blur or on pick; only a new bot has a Create button,
119
+ * in the rail at the foot where every other form keeps its own.
105
120
  */
106
121
  export function BotSetting() {
107
122
  const { data, isLoading, error } = useServerRoute<Bot[]>(queryKey.bot);
@@ -121,6 +136,7 @@ export function BotSetting() {
121
136
  * no two rosters look alike; a seed itself carries none).
122
137
  */
123
138
  const [faces] = useState(rollSeedIcons);
139
+ const [draft, setDraft] = useState<DraftCreate | null>(null);
124
140
 
125
141
  if (isLoading) return <SettingPanesSkeleton />;
126
142
  if (error) return <SettingError message={error.message} />;
@@ -135,7 +151,11 @@ export function BotSetting() {
135
151
 
136
152
  return (
137
153
  <SettingPanes
138
- footer={bots.length === 0 ? null : <BotRail bot={current} />}
154
+ footer={
155
+ bots.length === 0 && !drafting ? null : (
156
+ <BotRail bot={current} draft={drafting ? draft : null} />
157
+ )
158
+ }
139
159
  left={
140
160
  <div className="flex flex-col py-2">
141
161
  {/* The two ways to get a bot, on one line and apart from the roster
@@ -147,7 +167,7 @@ export function BotSetting() {
147
167
  className={cn(
148
168
  "flex flex-1 items-center gap-2 rounded-md px-2 py-1.5 text-left text-[13px] outline-none focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:ring-inset",
149
169
  drafting
150
- ? "bg-secondary text-foreground"
170
+ ? PICKED_ROW
151
171
  : "text-muted-foreground hover:bg-muted/60 hover:text-foreground",
152
172
  )}
153
173
  >
@@ -190,7 +210,7 @@ export function BotSetting() {
190
210
  key="new"
191
211
  jobs={[]}
192
212
  onDone={(name) => setPicked(name)}
193
- onCancel={() => setPicked(null)}
213
+ onDraft={setDraft}
194
214
  />
195
215
  ) : current ? (
196
216
  // Keyed by name so field state does not carry over to the next bot.
@@ -213,7 +233,7 @@ export function BotSetting() {
213
233
  ) : (
214
234
  <div className="space-y-4 p-8">
215
235
  <p className="text-sm leading-relaxed text-muted-foreground">
216
- The agent talks; bots do the rest — search the web, draft a reply,
236
+ Thursday talks; bots do the rest — search the web, draft a reply,
217
237
  check a schedule. Give one a job and a model, and work gets handed
218
238
  over mid-call while the conversation keeps going.
219
239
  </p>
@@ -253,7 +273,7 @@ function RosterRow({
253
273
  onClick={onPick}
254
274
  className={cn(
255
275
  "mx-2 flex items-center gap-2.5 rounded-md px-2 py-2 text-left outline-none focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:ring-inset",
256
- active ? "bg-secondary" : "hover:bg-muted/60",
276
+ active ? PICKED_ROW : "hover:bg-muted/60",
257
277
  )}
258
278
  >
259
279
  <BotMark
@@ -424,7 +444,7 @@ function SeedRows({
424
444
  owned
425
445
  ? "opacity-45 ring-border/60"
426
446
  : on
427
- ? "ring-brand"
447
+ ? cn(PICKED_ROW, "ring-brand")
428
448
  : "ring-border/60",
429
449
  )}
430
450
  >
@@ -669,15 +689,15 @@ function BotPage({
669
689
  bot,
670
690
  jobs,
671
691
  onDone,
672
- onCancel,
692
+ onDraft,
673
693
  }: {
674
694
  bot?: Bot;
675
695
  /** Recent jobs for this bot (RECENT); empty for a new bot. */
676
696
  jobs: Thread[];
677
697
  /** Created or deleted; where the roster should look next. */
678
698
  onDone: (name: string | null) => void;
679
- /** New bot only: folds the form. */
680
- onCancel?: () => void;
699
+ /** New bot only: where its Create goes, the rail at the foot (BotRail). */
700
+ onDraft?: (draft: DraftCreate | null) => void;
681
701
  }) {
682
702
  // Lazy init so a new bot's icon is not re-rolled per render; the page is keyed per bot.
683
703
  const [fields, patch] = useObjectState(() => ({
@@ -743,6 +763,11 @@ function BotPage({
743
763
 
744
764
  const ready =
745
765
  name.trim() && description.trim() && provider && model.trim() && !creating;
766
+ const missing = [
767
+ !name.trim() && "a name",
768
+ !description.trim() && "a description",
769
+ !(provider && model.trim()) && "a model",
770
+ ].filter((one): one is string => Boolean(one));
746
771
 
747
772
  const submit = () => {
748
773
  if (!ready || !provider) return;
@@ -760,11 +785,24 @@ function BotPage({
760
785
  });
761
786
  };
762
787
 
788
+ // The rail draws the Create: told when what it needs changes, never every render
789
+ const submitNow = useRef(submit);
790
+ submitNow.current = submit;
791
+ const lacking = missing.join(",");
792
+ useEffect(() => {
793
+ onDraft?.({
794
+ missing: lacking ? lacking.split(",") : [],
795
+ creating,
796
+ submit: () => submitNow.current(),
797
+ });
798
+ }, [onDraft, lacking, creating]);
799
+ useEffect(() => () => onDraft?.(null), [onDraft]);
800
+
763
801
  const confirmRemove = async () => {
764
802
  if (!bot) return;
765
803
  const confirmed = await notify.confirm({
766
804
  title: `Delete ${bot.name}?`,
767
- description: "The agent can no longer hand work to it.",
805
+ description: "Thursday can no longer hand work to it.",
768
806
  okText: "Delete",
769
807
  destructive: true,
770
808
  });
@@ -785,13 +823,19 @@ function BotPage({
785
823
  off
786
824
  </span>
787
825
  )}
788
- <Switch
789
- checked={!bot.disabled}
790
- onCheckedChange={(on) => commit({ disabled: !on })}
791
- aria-label={`${bot.name} on or off`}
792
- /* mr-1 keeps it off Delete: one is a setting, the other is not. */
793
- className="mr-1 shrink-0"
794
- />
826
+ {/* what on means, said where it is switched: the word "on" alone does not */}
827
+ <Tooltip>
828
+ <TooltipTrigger render={<span className="mr-1 flex shrink-0" />}>
829
+ <Switch
830
+ checked={!bot.disabled}
831
+ onCheckedChange={(on) => commit({ disabled: !on })}
832
+ aria-label={`${bot.name} on or off`}
833
+ />
834
+ </TooltipTrigger>
835
+ <TooltipContent side="bottom">
836
+ Thursday can hand it work
837
+ </TooltipContent>
838
+ </Tooltip>
795
839
  <Button
796
840
  size="icon-sm"
797
841
  variant="ghost"
@@ -951,17 +995,6 @@ function BotPage({
951
995
 
952
996
  {bot && <Recent jobs={jobs} />}
953
997
 
954
- {!bot && (
955
- <div className="flex items-center justify-end gap-2 pt-1">
956
- <Button variant="ghost" onClick={onCancel}>
957
- Cancel
958
- </Button>
959
- <Button loading={creating} disabled={!ready} onClick={submit}>
960
- Create
961
- </Button>
962
- </div>
963
- )}
964
-
965
998
  {createError && (
966
999
  <p className="font-mono text-xs text-destructive">{createError}</p>
967
1000
  )}
@@ -1123,7 +1156,14 @@ const underItsRow = (content: string) =>
1123
1156
  * any bot's: whether bots keep their own memory, and whether their work goes on once
1124
1157
  * every tab is closed.
1125
1158
  */
1126
- function BotRail({ bot }: { bot: Bot | null }) {
1159
+ function BotRail({
1160
+ bot,
1161
+ draft = null,
1162
+ }: {
1163
+ bot: Bot | null;
1164
+ /** A new bot on the page: its Create stands at the end of the rail. */
1165
+ draft?: DraftCreate | null;
1166
+ }) {
1127
1167
  const { data: memoryOn, mutate } = useServerRoute<boolean>(
1128
1168
  queryKey.botMemory,
1129
1169
  );
@@ -1151,9 +1191,13 @@ function BotRail({ bot }: { bot: Bot | null }) {
1151
1191
  <span className="px-1.5 opacity-50">·</span>
1152
1192
  since {whenOf(bot.createdAt)}
1153
1193
  </span>
1154
- ) : (
1155
- "Becomes a bot once it has a name and a model"
1156
- )}
1194
+ ) : draft?.missing.length ? (
1195
+ `Needs ${
1196
+ draft.missing.length > 1
1197
+ ? `${draft.missing.slice(0, -1).join(", ")} and ${draft.missing.at(-1)}`
1198
+ : draft.missing[0]
1199
+ }`
1200
+ ) : null}
1157
1201
  </SettingRailNote>
1158
1202
  <KeepWorkingSwitch />
1159
1203
  <span className="h-4 w-px shrink-0 bg-border" />
@@ -1169,6 +1213,16 @@ function BotRail({ bot }: { bot: Bot | null }) {
1169
1213
  }}
1170
1214
  aria-label="Bots keep their own memory"
1171
1215
  />
1216
+ {draft && (
1217
+ <Button
1218
+ size="sm"
1219
+ loading={draft.creating}
1220
+ disabled={draft.missing.length > 0}
1221
+ onClick={draft.submit}
1222
+ >
1223
+ Create bot
1224
+ </Button>
1225
+ )}
1172
1226
  </>
1173
1227
  );
1174
1228
  }
@@ -0,0 +1,161 @@
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+
3
+ /**
4
+ * The crew's body language: what a face does when something happens to the bot behind it.
5
+ *
6
+ * At rest every face breathes and nothing else moves. A gesture then plays ONCE, on the ONE face
7
+ * the event belongs to, over the breath — so the pill says what happened where it happened,
8
+ * instead of leaving it to the bubble that only has room for one thing at a time.
9
+ *
10
+ * A jump reads as weight only when the body and the shape run on different curves, so a face is a
11
+ * stack of layers (room-pill `Crew`) and a gesture names which of them it takes: `body` moves it
12
+ * through the air, `shape` presses and flattens it, `turn` rotates it. A layer a gesture does not
13
+ * name keeps doing what it was: the shape layer goes back to breathing.
14
+ *
15
+ * The keyframes are in `app/globals.css`; every duration here is the length that file cuts them to.
16
+ */
17
+
18
+ /** What a face can be doing. `wave` is the whole row arriving; every other one happens to one bot. */
19
+ export type Gesture =
20
+ | "wave"
21
+ | "done"
22
+ | "ask"
23
+ | "took"
24
+ | "give"
25
+ | "take"
26
+ | "stop"
27
+ | "read";
28
+
29
+ /** One bot, and what its face should do. */
30
+ export type BotGesture = { bot: string; gesture: Gesture };
31
+
32
+ /** How long each runs, ms. */
33
+ const GESTURE_MS: Record<Gesture, number> = {
34
+ wave: 740,
35
+ done: 740,
36
+ ask: 820,
37
+ took: 740,
38
+ give: 900,
39
+ take: 520,
40
+ stop: 1150,
41
+ read: 560,
42
+ };
43
+
44
+ /** The wave walks along the row: a face's own start is its place times this. */
45
+ const WAVE_STEP_MS = 70;
46
+
47
+ /** Which layer takes which animation. */
48
+ const LAYERS: Record<
49
+ Gesture,
50
+ { body?: string; shape?: string; turn?: string }
51
+ > = {
52
+ wave: { body: "animate-crew-hop", shape: "animate-crew-hop-shape" },
53
+ took: { body: "animate-crew-hop", shape: "animate-crew-hop-shape" },
54
+ done: {
55
+ body: "animate-crew-hop",
56
+ shape: "animate-crew-hop-shape",
57
+ turn: "animate-crew-spin",
58
+ },
59
+ ask: { body: "animate-crew-ask", turn: "animate-crew-ask-turn" },
60
+ give: { turn: "animate-crew-give" },
61
+ take: { body: "animate-crew-take", shape: "animate-crew-take-shape" },
62
+ stop: { body: "animate-crew-sink", shape: "animate-crew-sink-shape" },
63
+ read: { body: "animate-crew-nod", shape: "animate-crew-nod-shape" },
64
+ };
65
+
66
+ /** A gesture on a face, and the token that makes React start it over rather than let it run on. */
67
+ export type Playing = { gesture: Gesture; id: number };
68
+
69
+ /** What each face is in the middle of, by bot name. */
70
+ export type CrewPlaying = Map<string, Playing>;
71
+
72
+ /** What each face's layers wear this render. */
73
+ export type CrewMotion = {
74
+ /** Changes with every gesture, so the layers remount and the animation restarts. */
75
+ key: number;
76
+ body: string;
77
+ shape: string;
78
+ turn: string;
79
+ /** Only the wave staggers; everything else starts at once on the face it belongs to. */
80
+ delayMs: number;
81
+ };
82
+
83
+ /** The resting state, which a gesture borrows a layer from and gives back. */
84
+ const RESTING: CrewMotion = {
85
+ key: 0,
86
+ body: "",
87
+ shape: "animate-crew-breath",
88
+ turn: "",
89
+ delayMs: 0,
90
+ };
91
+
92
+ /**
93
+ * What one face is doing. `at` is its place in the row, which only the wave reads; a face's breath
94
+ * takes its phase from the same number, so a row at rest ripples instead of pulsing as one object.
95
+ */
96
+ export function motionOf(playing: Playing | undefined, at: number): CrewMotion {
97
+ if (!playing) return { ...RESTING, delayMs: at * -370 };
98
+ const layers = LAYERS[playing.gesture];
99
+ return {
100
+ key: playing.id,
101
+ body: layers.body ?? "",
102
+ shape: layers.shape ?? "animate-crew-breath",
103
+ turn: layers.turn ?? "",
104
+ delayMs: playing.gesture === "wave" ? at * WAVE_STEP_MS : 0,
105
+ };
106
+ }
107
+
108
+ /**
109
+ * The gestures playing right now, and how to start more.
110
+ *
111
+ * One at a time per bot: a second event on the same face replaces the first rather than queueing,
112
+ * because two gestures are two layers fighting over the same transform, and the newer thing is the
113
+ * true one. A gesture clears itself when it is over, which is what puts the face back to resting.
114
+ */
115
+ export function useCrewGestures(): [
116
+ CrewPlaying,
117
+ (played: BotGesture[]) => void,
118
+ ] {
119
+ const [playing, setPlaying] = useState<CrewPlaying>(new Map());
120
+ const timers = useRef(new Map<string, ReturnType<typeof setTimeout>>());
121
+ const token = useRef(0);
122
+
123
+ useEffect(() => {
124
+ const held = timers.current;
125
+ return () => {
126
+ for (const timer of held.values()) clearTimeout(timer);
127
+ held.clear();
128
+ };
129
+ }, []);
130
+
131
+ const play = useCallback((played: BotGesture[]) => {
132
+ if (!played.length) return;
133
+ setPlaying((was) => {
134
+ const now = new Map(was);
135
+ for (const { bot, gesture } of played) {
136
+ token.current += 1;
137
+ now.set(bot, { gesture, id: token.current });
138
+ const running = timers.current.get(bot);
139
+ if (running) clearTimeout(running);
140
+ timers.current.set(
141
+ bot,
142
+ setTimeout(
143
+ () => {
144
+ timers.current.delete(bot);
145
+ setPlaying((held) => {
146
+ const next = new Map(held);
147
+ next.delete(bot);
148
+ return next;
149
+ });
150
+ // The wave's last face starts a row-length after the first one does.
151
+ },
152
+ GESTURE_MS[gesture] + (gesture === "wave" ? WAVE_STEP_MS * 20 : 0),
153
+ ),
154
+ );
155
+ }
156
+ return now;
157
+ });
158
+ }, []);
159
+
160
+ return [playing, play];
161
+ }
@@ -11,8 +11,7 @@ import {
11
11
 
12
12
  /**
13
13
  * A mark's colours in one line under the face they paint: the theme dot, the
14
- * colours, then the paints. Thursday's face and a bot's page both pick from it;
15
- * they differ only in what the dot means.
14
+ * colours, then the paints. On a narrow bot page the dots shrink rather than wrap.
16
15
  */
17
16
  export function MarkPalette({
18
17
  color,
@@ -31,7 +30,8 @@ export function MarkPalette({
31
30
  onPaint?: (paint: MarkPaint) => void;
32
31
  }) {
33
32
  return (
34
- <div className="flex flex-wrap items-center justify-center gap-2 px-2">
33
+ // As wide as the page, not as its dots: they shrink to it rather than push past it
34
+ <div className="flex w-full items-center justify-center gap-1.5 px-2">
35
35
  <Swatch color={null} picked={themePicked} onPick={onTheme} />
36
36
  {MARK_PALETTE.map((each) => (
37
37
  <Swatch
@@ -41,7 +41,6 @@ import {
41
41
  type BotRef,
42
42
  type Chatter,
43
43
  heardBy,
44
- rosterOf,
45
44
  type ThreadItem,
46
45
  type ThreadView,
47
46
  type ThreadViewStatus,
@@ -100,12 +99,6 @@ export function ThreadHeader({
100
99
  />
101
100
  <span className="min-w-0 flex-1 truncate text-[13px] font-medium">
102
101
  {thread.label}
103
- <span
104
- title={thread.id}
105
- className="ml-1.5 font-mono text-[10px] font-normal text-muted-foreground/60"
106
- >
107
- {thread.id.slice(0, 8)}
108
- </span>
109
102
  </span>
110
103
  <ThreadFacts thread={thread} />
111
104
  <FoldButton onClick={onClose} />
@@ -273,7 +266,7 @@ export function Conversation({
273
266
  }) {
274
267
  const scroller = useRef<HTMLDivElement>(null);
275
268
  const following = useRef(true);
276
- const roster = rosterOf(thread);
269
+ const roster = thread.roster;
277
270
  const side =
278
271
  onTab && tab !== thread.bot.name && roster.some((bot) => bot.name === tab)
279
272
  ? tab
@@ -295,6 +288,18 @@ export function Conversation({
295
288
  if (box && following.current) box.scrollTop = box.scrollHeight;
296
289
  }, [thread.lines, thread.status, side]);
297
290
 
291
+ // A page or a picture under a message is drawn a moment after the words and grows the
292
+ // turn it is under: without this the last tiles end up behind the composer
293
+ useEffect(() => {
294
+ const box = scroller.current;
295
+ if (!box) return;
296
+ const watch = new ResizeObserver(() => {
297
+ if (following.current) box.scrollTop = box.scrollHeight;
298
+ });
299
+ for (const child of Array.from(box.children)) watch.observe(child);
300
+ return () => watch.disconnect();
301
+ }, [items.length, thread.lines, side]);
302
+
298
303
  return (
299
304
  // One FileViewer per conversation, not per bubble.
300
305
  <FileViewer>
@@ -28,15 +28,19 @@ import { RoutineMark } from "@/features/routine/components/routine-mark";
28
28
  import { type DateLike, shortAgo, toDate } from "@/lib/date-like";
29
29
  import { type ServerPages } from "@/lib/protocol/use-server-pages";
30
30
  import { cn, plainText, WAITING_INK } from "@/lib/utils";
31
- import { lastSaid, rosterOf, type ThreadView } from "../thread.store";
31
+ import { lastSaid, type ThreadView } from "../thread.store";
32
32
  import { FoldButton, TAB } from "./room-conversation";
33
33
 
34
34
  /** The room open on its lists: what is happening now, and the history behind it. Split out of bot-room by subject; see it for the room as a whole. */
35
35
 
36
36
  export const needsYou = needsThreadReply;
37
37
 
38
- /** An ending nobody has opened. It needs the user too, to read rather than to answer. */
39
- const isUnread = (thread: ThreadView) =>
38
+ /**
39
+ * An ending nobody has opened. It needs the user too, to read rather than to
40
+ * answer. Takes the two fields it reads, as `needsThreadReply` does, so a
41
+ * stored row answers it as well as a drawn one.
42
+ */
43
+ export const isUnread = (thread: { status: string; seen: boolean }) =>
40
44
  thread.status === "done" && !thread.seen;
41
45
 
42
46
  /** The room's two lists. */
@@ -359,7 +363,7 @@ export function ThreadRow({
359
363
  outline={thread.bot.icon?.outline}
360
364
  paint={thread.bot.icon?.paint}
361
365
  state={thread.status === "working" ? "thinking" : "idle"}
362
- notify={attention}
366
+ notify={attention || isUnread(thread)}
363
367
  crossed={thread.status === "cancelled"}
364
368
  />
365
369
  </span>
@@ -398,7 +402,7 @@ export function ThreadRow({
398
402
  <RoutineMark className="size-3 text-muted-foreground/80" />
399
403
  </span>
400
404
  )}
401
- <BotRoster bots={rosterOf(thread)} />
405
+ <BotRoster bots={thread.roster} />
402
406
  <span className="flex-1" />
403
407
  <span className="shrink-0 font-mono text-[11px] leading-4 text-muted-foreground/70 tabular-nums">
404
408
  {shortAgo(thread.updatedAt)}