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
@@ -24,6 +24,12 @@ import {
24
24
  } from "@/components/ui/tooltip";
25
25
  import { type Bot, type BotIcon, isAppStop } from "@/features/bot/bot.schema";
26
26
  import { BotMark } from "@/features/bot/components/bot-mark";
27
+ import {
28
+ type BotGesture,
29
+ type CrewMotion,
30
+ type CrewPlaying,
31
+ motionOf,
32
+ } from "@/features/bot/components/crew-motion";
27
33
  import { ThursdayMark } from "@/features/thursday/components/thursday-mark";
28
34
  import { cn, plainText, WAITING_INK } from "@/lib/utils";
29
35
  import { MARK_PALETTE, MARK_SHAPES } from "../mark.const";
@@ -31,7 +37,6 @@ import {
31
37
  type BotRef,
32
38
  type Chatter,
33
39
  latestPerBot,
34
- rosterOf,
35
40
  type ThreadView,
36
41
  type ThreadViewStatus,
37
42
  useWriteLineUp,
@@ -39,12 +44,12 @@ import {
39
44
  } from "../thread.store";
40
45
 
41
46
  import { leadOf, stepOf, THURSDAY } from "./room-conversation";
42
- import { ThreadRow } from "./room-list";
47
+ import { isUnread, ThreadRow } from "./room-list";
43
48
 
44
49
  /** The room folded: the pill at the foot of the call screen, the faces on it, and what passed between two parties riding above one for a moment. Split out of bot-room by subject; see it for the room as a whole. */
45
50
 
46
- /** Faces the row draws before the count takes over. */
47
- const CREW_MAX = 10;
51
+ /** Faces the row draws before it starts hiding them (the user's pick). */
52
+ const CREW_MAX = 14;
48
53
 
49
54
  /** How long a hand-off stays above a face, ms. */
50
55
  const HANDOFF_MS = 3400;
@@ -58,8 +63,10 @@ type CrewFace = {
58
63
  icon?: BotIcon | null;
59
64
  /** Working or waiting: both are awake. */
60
65
  awake: boolean;
61
- /** Waiting on an answer; carries the dot. */
66
+ /** Waiting on an answer; carries the amber dot. */
62
67
  waiting: boolean;
68
+ /** A result of its nobody has opened; carries the blue one. */
69
+ unread: boolean;
63
70
  /** The step it is on, in the model's own words. Null when it is not working. */
64
71
  word: string | null;
65
72
  /** A stand-in for an install with no bots; dimmed with the rest. */
@@ -174,6 +181,10 @@ export function participantStates(threads: ThreadView[]): Map<string, string> {
174
181
  * stand for a sync: a failure, then a question, then an ending or a stop, then
175
182
  * messages, then a job arriving, an answer, a bot finishing its part or going
176
183
  * back to work.
184
+ *
185
+ * The same pass says what each face should do about it (`crew-motion`). One sync
186
+ * shows one bubble but every gesture it turned up, because a gesture sits on the
187
+ * face it belongs to and two of them never cover each other.
177
188
  */
178
189
  export function happenedIn(
179
190
  thread: ThreadView,
@@ -181,15 +192,38 @@ export function happenedIn(
181
192
  had: Set<string>,
182
193
  stood: Map<string, string>,
183
194
  stands: Map<string, string>,
184
- ): Happening[] {
195
+ ): { moments: Happening[]; gestures: BotGesture[] } {
196
+ const own = thread.bot;
197
+ const felt: BotGesture[] = [];
198
+ // A thread this sync is the first to see — just opened, or back in the inbox
199
+ // after dropping out of it — has no history here, so every line in it would
200
+ // read as having just happened. Being taken on is the news; nothing else is.
201
+ if (!was) {
202
+ return thread.status === "working"
203
+ ? {
204
+ moments: [
205
+ {
206
+ rank: 1,
207
+ at: own.name,
208
+ from: THURSDAY,
209
+ to: [own],
210
+ text: `took on “${clipWord(thread.label)}”`,
211
+ },
212
+ ],
213
+ gestures: [{ bot: own.name, gesture: "took" }],
214
+ }
215
+ : { moments: [], gestures: [] };
216
+ }
217
+
185
218
  const out: Happening[] = [];
186
219
  const fresh = thread.lines.filter((line) => !had.has(line.id));
187
- const own = thread.bot;
188
220
 
189
221
  // Bot to bot. A giver handing work to several at once is one bubble naming them all.
190
222
  const rounds = new Map<string, Chatter[]>();
191
223
  for (const line of fresh) {
192
224
  if (line.kind === "user") {
225
+ if (line.steppedIn) felt.push({ bot: line.bot.name, gesture: "read" });
226
+ else felt.push({ bot: line.bot.name, gesture: "took" });
193
227
  out.push(
194
228
  line.steppedIn
195
229
  ? // the words were up while they waited (waitingStepIn); read, the bot says so
@@ -209,6 +243,7 @@ export function happenedIn(
209
243
  },
210
244
  );
211
245
  } else if (line.kind === "stop") {
246
+ felt.push({ bot: line.bot.name, gesture: "stop" });
212
247
  out.push({
213
248
  rank: 3,
214
249
  at: line.bot.name,
@@ -218,6 +253,7 @@ export function happenedIn(
218
253
  text: leadOf(line.text),
219
254
  });
220
255
  } else if (line.kind === "ask" && line.to?.name === THURSDAY.name) {
256
+ if (line.question) felt.push({ bot: line.bot.name, gesture: "ask" });
221
257
  out.push(
222
258
  line.question
223
259
  ? {
@@ -245,6 +281,9 @@ export function happenedIn(
245
281
  for (const line of round) {
246
282
  if (line.to) reached.set(line.to.name, line.to);
247
283
  }
284
+ felt.push({ bot: giver, gesture: "give" });
285
+ for (const bot of reached.values())
286
+ felt.push({ bot: bot.name, gesture: "take" });
248
287
  out.push({
249
288
  rank: 2,
250
289
  at: giver,
@@ -258,7 +297,7 @@ export function happenedIn(
258
297
  }
259
298
 
260
299
  // Another bot done with its part and no last word; the thread's own ending is the job's.
261
- for (const bot of rosterOf(thread)) {
300
+ for (const bot of thread.roster) {
262
301
  const key = `${thread.id}\n${bot.name}`;
263
302
  if (
264
303
  bot.name !== own.name &&
@@ -270,6 +309,7 @@ export function happenedIn(
270
309
  (line.kind === "ask" || line.kind === "say"),
271
310
  )
272
311
  ) {
312
+ felt.push({ bot: bot.name, gesture: "done" });
273
313
  out.push({
274
314
  rank: 1,
275
315
  at: bot.name,
@@ -281,21 +321,10 @@ export function happenedIn(
281
321
  }
282
322
  }
283
323
 
284
- if (!was) {
285
- if (thread.status === "working") {
286
- out.push({
287
- rank: 1,
288
- at: own.name,
289
- from: THURSDAY,
290
- to: [own],
291
- text: `took on “${clipWord(thread.label)}”`,
292
- });
293
- }
294
- return out;
295
- }
296
- if (was === thread.status) return out;
324
+ if (was === thread.status) return { moments: out, gestures: felt };
297
325
  const label = clipWord(thread.label);
298
326
  if (thread.status === "cancelled") {
327
+ felt.push({ bot: own.name, gesture: "stop" });
299
328
  out.push({
300
329
  rank: 3,
301
330
  at: own.name,
@@ -306,10 +335,13 @@ export function happenedIn(
306
335
  });
307
336
  } else if (thread.status === "done") {
308
337
  // A job's own ending is the card in the screen's left corner (artifact-view); the pill
309
- // keeps its bubbles for what is still going on
338
+ // keeps its bubbles for what is still going on. The face still says it happened — a
339
+ // somersault is on the bot, not over it, so it never stands where the card's notice would.
340
+ felt.push({ bot: own.name, gesture: "done" });
310
341
  } else if (thread.status === "working") {
311
342
  // Words from the user already have their bubble.
312
343
  if (!fresh.some((line) => line.kind === "user")) {
344
+ felt.push({ bot: own.name, gesture: "took" });
313
345
  out.push({
314
346
  rank: 1,
315
347
  at: own.name,
@@ -324,6 +356,7 @@ export function happenedIn(
324
356
  !out.some((one) => one.sign === "question" || one.sign === "stopped")
325
357
  ) {
326
358
  // A stop with no stop line of its own: the turn limit, or a room gone idle
359
+ felt.push({ bot: own.name, gesture: "stop" });
327
360
  out.push({
328
361
  rank: 3,
329
362
  at: own.name,
@@ -333,7 +366,7 @@ export function happenedIn(
333
366
  text: "paused",
334
367
  });
335
368
  }
336
- return out;
369
+ return { moments: out, gestures: felt };
337
370
  }
338
371
 
339
372
  /** The step a bot is on, as the model labelled it. */
@@ -346,7 +379,7 @@ function wordOf(line: Chatter | null): string | null {
346
379
  /**
347
380
  * Who is in the row, in the order it is drawn.
348
381
  *
349
- * Anyone moving sorts to the front, so the count at the tail only ever hides
382
+ * Anyone moving sorts to the front, so the mark at the tail only ever hides
350
383
  * idle bots — a bot with something to say always has a face to say it from,
351
384
  * which is what lets a hand-off point at one. A bot that spoke inside somebody
352
385
  * else's job is in the room too, whether or not it owns a thread (latestPerBot).
@@ -355,6 +388,10 @@ export function crewOf(
355
388
  bots: Bot[] | undefined,
356
389
  threads: ThreadView[],
357
390
  ): { crew: CrewFace[]; more: number } {
391
+ // What is finished and unopened is the other thing a face can carry (the user's pick)
392
+ const unread = new Set(
393
+ threads.filter(isUnread).map((thread) => thread.bot.name),
394
+ );
358
395
  const live = new Map<string, { waiting: boolean; word: string | null }>();
359
396
  for (const entry of latestPerBot(threads)) {
360
397
  const { thread, bot, line } = entry;
@@ -374,6 +411,7 @@ export function crewOf(
374
411
  icon: bot.icon,
375
412
  awake: live.has(bot.name),
376
413
  waiting: live.get(bot.name)?.waiting ?? false,
414
+ unread: unread.has(bot.name),
377
415
  word: live.get(bot.name)?.word ?? null,
378
416
  });
379
417
  }
@@ -386,6 +424,7 @@ export function crewOf(
386
424
  icon: null,
387
425
  awake: true,
388
426
  waiting: doing.waiting,
427
+ unread: unread.has(name),
389
428
  word: doing.word,
390
429
  });
391
430
  }
@@ -399,8 +438,9 @@ export function crewOf(
399
438
  if (!named.has(ghost.name)) roster.push(ghost);
400
439
  }
401
440
 
441
+ // Who needs the user first, then who is moving, then who left something to read
402
442
  const rank = (face: CrewFace) =>
403
- face.waiting ? 0 : face.awake ? 1 : face.standIn ? 3 : 2;
443
+ face.waiting ? 0 : face.awake ? 1 : face.unread ? 2 : face.standIn ? 4 : 3;
404
444
  const sorted = roster
405
445
  .map((face, at) => ({ face, at }))
406
446
  .sort((a, b) => rank(a.face) - rank(b.face) || a.at - b.at)
@@ -464,6 +504,7 @@ export function Chip({
464
504
  count,
465
505
  busy,
466
506
  pending,
507
+ playing,
467
508
  onPick,
468
509
  onOpen,
469
510
  }: {
@@ -476,6 +517,8 @@ export function Chip({
476
517
  count: number;
477
518
  busy: number;
478
519
  pending: number;
520
+ /** The gesture each face is in the middle of, by bot name (crew-motion). */
521
+ playing: CrewPlaying;
479
522
  onPick: (id: string) => void;
480
523
  onOpen: () => void;
481
524
  }) {
@@ -540,6 +583,7 @@ export function Chip({
540
583
  bubble={grown ? null : bubble}
541
584
  label={count ? `Threads (${count})` : "Bots"}
542
585
  onClick={onOpen}
586
+ playing={playing}
543
587
  side={<RoomState busy={busy} pending={pending} grown={grown} />}
544
588
  onWrite={writeLine.open}
545
589
  />
@@ -560,6 +604,7 @@ export function CrewRow({
560
604
  crew,
561
605
  more,
562
606
  bubble,
607
+ playing,
563
608
  side,
564
609
  label,
565
610
  onClick,
@@ -568,6 +613,8 @@ export function CrewRow({
568
613
  crew: CrewFace[];
569
614
  more: number;
570
615
  bubble: Handoff | null;
616
+ /** The gesture each face is in the middle of, by bot name (crew-motion). */
617
+ playing: CrewPlaying;
571
618
  /** The right side: the room's state, or what just happened. */
572
619
  side: ReactNode;
573
620
  label: string;
@@ -607,7 +654,7 @@ export function CrewRow({
607
654
  aria-label={label}
608
655
  className="flex min-w-0 flex-1 items-center gap-2 rounded-full text-left outline-none focus-visible:ring-3 focus-visible:ring-ring/50"
609
656
  >
610
- <Crew crew={crew} more={more} bubble={bubble} />
657
+ <Crew crew={crew} more={more} bubble={bubble} playing={playing} />
611
658
  {side}
612
659
  </button>
613
660
  </div>
@@ -659,6 +706,7 @@ const GHOSTS: CrewFace[] = ["alto", "brio", "cinder", "delta"].map(
659
706
  },
660
707
  awake: false,
661
708
  waiting: false,
709
+ unread: false,
662
710
  word: null,
663
711
  standIn: true,
664
712
  }),
@@ -677,21 +725,26 @@ const FLOOR = 3;
677
725
  *
678
726
  * A face that is moving is awake: lifted and swollen a little. That layer alone
679
727
  * survives every collision — three bots at once are three awake faces, with no
680
- * order to decide.
728
+ * order to decide, and it sits outside the gesture layers so a bot at work is
729
+ * still lifted while it jumps.
730
+ *
731
+ * Under that, a face is three layers and a mark (`crew-motion`): the body through
732
+ * the air, the shape pressing and flattening, the turn. At rest the shape layer
733
+ * breathes. They are keyed on the gesture, so a second event on one face starts
734
+ * over rather than landing mid-way through the first.
681
735
  */
682
736
  function Crew({
683
737
  crew,
684
738
  more,
685
739
  bubble,
740
+ playing,
686
741
  }: {
687
742
  crew: CrewFace[];
688
743
  more: number;
689
744
  /** The hand-off up, drawn over the face it points at. */
690
745
  bubble: Handoff | null;
746
+ playing: CrewPlaying;
691
747
  }) {
692
- // While anyone is moving or waiting, the rest step back so it reads at a glance who is
693
- const lit = (face: CrewFace) => face.awake || face.waiting || face.word;
694
- const astir = crew.some(lit);
695
748
  return (
696
749
  <span className="flex min-w-0 shrink items-center">
697
750
  {crew.map((face, index) => {
@@ -702,8 +755,7 @@ function Crew({
702
755
  // these are not circles. Earlier faces sit on top, so the dot on a
703
756
  // waiting face is never covered by its neighbour.
704
757
  className={cn(
705
- "relative shrink-0 transition-[margin,opacity] duration-500 ease-out",
706
- astir && !lit(face) && "opacity-40",
758
+ "relative shrink-0 transition-[margin] duration-500 ease-out",
707
759
  // A word to the left has already broken the shingle.
708
760
  index > 0 && !crew[index - 1].word && "-ml-2",
709
761
  face.standIn && "opacity-35",
@@ -717,7 +769,10 @@ function Crew({
717
769
  face.awake && "-translate-y-0.5 scale-110",
718
770
  )}
719
771
  >
720
- <CrewMark face={face} />
772
+ <CrewBody
773
+ face={face}
774
+ motion={motionOf(playing.get(face.name), index)}
775
+ />
721
776
  </span>
722
777
  {bubble?.at === face.name && <HandoffBubble handoff={bubble} />}
723
778
  </span>
@@ -736,22 +791,57 @@ function Crew({
736
791
  );
737
792
  })}
738
793
  {more > 0 && (
739
- // Past CREW_MAX the row stops growing. Only idle bots are ever behind it:
740
- // anyone moving sorted to the front.
794
+ // Past CREW_MAX the row stops growing. Only idle bots are ever behind it —
795
+ // anyone moving sorted to the front — so the tail says there are more and not
796
+ // how many (the user's pick): a count nobody can act on is a number to read.
797
+ // Brand, but tinted rather than filled: filled brand is what waits on the user,
798
+ // and nothing here does.
741
799
  <span
742
- className={cn(
743
- "grid size-7 shrink-0 place-items-center rounded-[9px] bg-muted font-mono text-[10px] text-muted-foreground ring-1 ring-border/50",
744
- !crew.at(-1)?.word && "-ml-2",
745
- )}
800
+ className="ml-2 flex h-[18px] shrink-0 items-center rounded-full bg-brand/10 px-2 font-mono text-[10.5px] leading-none font-medium text-brand"
746
801
  title={`${more} more`}
747
802
  >
748
- +{more}
803
+ +
749
804
  </span>
750
805
  )}
751
806
  </span>
752
807
  );
753
808
  }
754
809
 
810
+ /**
811
+ * A face's moving parts: the body, the shape and the turn, outermost first.
812
+ *
813
+ * Each layer animates one thing, because one element can only animate `transform`
814
+ * once and a jump needs its height and its squash on different curves. `data-crew-motion`
815
+ * is what a machine asked to hold still switches off (app/globals.css).
816
+ */
817
+ function CrewBody({ face, motion }: { face: CrewFace; motion: CrewMotion }) {
818
+ const delay = motion.delayMs
819
+ ? { animationDelay: `${motion.delayMs}ms` }
820
+ : undefined;
821
+ return (
822
+ <span
823
+ key={motion.key}
824
+ data-crew-motion
825
+ className={cn("flex", motion.body)}
826
+ style={delay}
827
+ >
828
+ <span
829
+ data-crew-motion
830
+ className={cn("flex origin-bottom", motion.shape)}
831
+ style={delay}
832
+ >
833
+ <span
834
+ data-crew-motion
835
+ className={cn("flex", motion.turn)}
836
+ style={delay}
837
+ >
838
+ <CrewMark face={face} />
839
+ </span>
840
+ </span>
841
+ </span>
842
+ );
843
+ }
844
+
755
845
  /** One crew face. Who is moving reads from the lift its row gives it. */
756
846
  function CrewMark({ face }: { face: CrewFace }) {
757
847
  return (
@@ -762,7 +852,7 @@ function CrewMark({ face }: { face: CrewFace }) {
762
852
  shape={face.icon?.shape}
763
853
  outline={face.icon?.outline}
764
854
  paint={face.icon?.paint}
765
- notify={face.waiting}
855
+ notify={face.waiting || face.unread}
766
856
  />
767
857
  );
768
858
  }
@@ -8,8 +8,8 @@ import { useServerRoute } from "@/lib/protocol/use-server-route";
8
8
 
9
9
  /**
10
10
  * What the thread list owes the user, from the inbox key both readings share:
11
- * jobs waiting on an answer and endings nobody has opened — amber, because both
12
- * wait on them. Unread endings remain in the inbox until opened, so every one
11
+ * jobs waiting on an answer and endings nobody has opened — the brand colour,
12
+ * because both want them. Unread endings remain in the inbox until opened, so every one
13
13
  * counted has a row to open. A job never ends as a failure (a model that breaks
14
14
  * pauses it as waiting), so nothing here is red.
15
15
  */
@@ -25,10 +25,10 @@ export function useThreadReport() {
25
25
  }
26
26
 
27
27
  export function useThreadAlert(): SectionAlert {
28
- return useThreadReport().owed > 0 ? "amber" : null;
28
+ return useThreadReport().owed > 0 ? "brand" : null;
29
29
  }
30
30
 
31
31
  /** The nav counts what is owed. */
32
32
  export function ThreadBadge() {
33
- return <NavBadge tone="amber" count={useThreadReport().owed} />;
33
+ return <NavBadge tone="brand" count={useThreadReport().owed} />;
34
34
  }
@@ -47,8 +47,8 @@ import {
47
47
  Conversation,
48
48
  ThreadFacts,
49
49
  } from "@/features/bot/components/room-conversation";
50
+ import { isUnread } from "@/features/bot/components/room-list";
50
51
  import {
51
- rosterOf,
52
52
  screenActs,
53
53
  threadFromRow,
54
54
  useSeenOnDetail,
@@ -105,6 +105,13 @@ export function ThreadSetting() {
105
105
 
106
106
  // A thread that leaves the list (deleted, cleared) closes the sheet.
107
107
  const reading = threads.find((thread) => thread.id === openId);
108
+ // The list carries lines only for a thread that can still move (thread.query
109
+ // listThreadHistory), so the sheet reads the one it opens whole. The key sits
110
+ // under the list's, so the same signal keeps both live.
111
+ const { data: opened } = useServerRoute<Thread | null>(
112
+ openId ? queryKey.thread(openId) : null,
113
+ );
114
+ const whole = opened?.id === openId ? opened : undefined;
108
115
  // Opening a thread is reading its ending; that is what clears its dot.
109
116
  useSeenOnDetail(reading);
110
117
 
@@ -172,9 +179,12 @@ export function ThreadSetting() {
172
179
  if (isLoading) return <SettingSkeleton rows={3} />;
173
180
  if (error) return <SettingError message={error.message} />;
174
181
 
182
+ // Both halves of what the nav's one number counts (thread-badge), told apart
183
+ // here because the foot has the room the badge does not.
175
184
  const waiting = threads.filter(
176
185
  (thread) => thread.status === "waiting",
177
186
  ).length;
187
+ const unread = threads.filter(isUnread).length;
178
188
 
179
189
  return (
180
190
  <>
@@ -182,12 +192,22 @@ export function ThreadSetting() {
182
192
  footer={
183
193
  <>
184
194
  <SettingRailNote>
185
- {threads.length} loaded
195
+ {/* "loaded" is the paging word, not the reader's: more arrive as the list is scrolled */}
196
+ {threads.length} shown
186
197
  {waiting > 0 && ` · ${waiting} waiting on you`}
198
+ {unread > 0 &&
199
+ ` · ${unread} new result${unread === 1 ? "" : "s"}`}
187
200
  </SettingRailNote>
188
- <Button variant="outline" size="sm" onClick={confirmClear}>
189
- Clear finished
190
- </Button>
201
+ {/* only with something to clear: a page not read yet may hold some */}
202
+ {(hasMore ||
203
+ threads.some(
204
+ (thread) =>
205
+ thread.status === "done" || thread.status === "cancelled",
206
+ )) && (
207
+ <Button variant="outline" size="sm" onClick={confirmClear}>
208
+ Clear finished
209
+ </Button>
210
+ )}
191
211
  </>
192
212
  }
193
213
  >
@@ -205,7 +225,7 @@ export function ThreadSetting() {
205
225
  {shown.length === 0 ? (
206
226
  <p className="p-4 text-sm leading-relaxed text-muted-foreground">
207
227
  {needle
208
- ? "Nothing loaded matches. Keep scrolling to search further back."
228
+ ? "Nothing here matches. Keep scrolling to search further back."
209
229
  : "Nothing yet. When Thursday hands a job to a bot mid-call, it shows up here — while it runs, and after."}
210
230
  </p>
211
231
  ) : (
@@ -234,6 +254,7 @@ export function ThreadSetting() {
234
254
 
235
255
  <ThreadSheet
236
256
  thread={reading}
257
+ whole={whole}
237
258
  bots={bots}
238
259
  onClose={() => setOpenId(null)}
239
260
  onStop={(thread) => stop(thread.id)}
@@ -287,7 +308,7 @@ function Row({
287
308
  seed={thread.bot}
288
309
  {...markOf(thread.bot, bots)}
289
310
  state={running ? "thinking" : "idle"}
290
- notify={needsThreadReply(thread)}
311
+ notify={needsThreadReply(thread) || isUnread(thread)}
291
312
  crossed={thread.status === "cancelled"}
292
313
  className="shrink-0"
293
314
  />
@@ -302,7 +323,7 @@ function Row({
302
323
  <RoutineMark className="size-3 text-muted-foreground/80" />
303
324
  </span>
304
325
  )}
305
- <BotRoster bots={rosterOf(view)} />
326
+ <BotRoster bots={view.roster} />
306
327
  {running && (
307
328
  <Loader2 className="size-3 shrink-0 animate-spin text-muted-foreground" />
308
329
  )}
@@ -404,13 +425,16 @@ function ThreadMenu({
404
425
  */
405
426
  function ThreadSheet({
406
427
  thread,
428
+ whole,
407
429
  bots,
408
430
  onClose,
409
431
  onStop,
410
432
  onDelete,
411
433
  }: {
412
- /** The open thread; none closes the sheet. */
434
+ /** The open thread; none closes the sheet. The row, which may carry no lines. */
413
435
  thread?: Thread;
436
+ /** The same thread with every line, once it has been read (ThreadSetting). */
437
+ whole?: Thread;
414
438
  bots?: Bot[];
415
439
  onClose: () => void;
416
440
  onStop: (thread: Thread) => void;
@@ -428,6 +452,13 @@ function ThreadSheet({
428
452
  () => (thread ? threadFromRow(thread, bots) : null),
429
453
  [thread, bots],
430
454
  );
455
+ // The conversation is the one part that needs the lines. A row that carries
456
+ // them — a thread still going — draws at once and the whole one takes over.
457
+ const talk = useMemo(
458
+ () =>
459
+ whole ? threadFromRow(whole, bots) : view?.lines.length ? view : null,
460
+ [whole, view, bots],
461
+ );
431
462
  const side = thread ? (sides[thread.id] ?? null) : null;
432
463
 
433
464
  return (
@@ -486,15 +517,23 @@ function ThreadSheet({
486
517
  </DialogClose>
487
518
  </div>
488
519
 
489
- <Conversation
490
- thread={view}
491
- tab={side}
492
- onTab={(next) =>
493
- setSides((was) => ({ ...was, [thread.id]: next }))
494
- }
495
- className="max-h-none min-h-0 flex-1 px-5"
496
- tabsClassName="px-5"
497
- />
520
+ {talk ? (
521
+ <Conversation
522
+ thread={talk}
523
+ tab={side}
524
+ onTab={(next) =>
525
+ setSides((was) => ({ ...was, [thread.id]: next }))
526
+ }
527
+ className="max-h-none min-h-0 flex-1 px-5"
528
+ tabsClassName="px-5"
529
+ />
530
+ ) : (
531
+ <div className="flex min-h-0 flex-1 flex-col gap-2 px-5 py-4">
532
+ <Skeleton className="h-3 w-4/5" />
533
+ <Skeleton className="h-3 w-3/5" />
534
+ <Skeleton className="h-3 w-2/3" />
535
+ </div>
536
+ )}
498
537
 
499
538
  <div className="shrink-0 px-5 pt-2 pb-4">
500
539
  <ThreadReply
@@ -506,7 +545,7 @@ function ThreadSheet({
506
545
  room: thread.room,
507
546
  }}
508
547
  status={thread.status}
509
- faces={rosterOf(view)}
548
+ faces={view.roster}
510
549
  to={side ?? thread.bot}
511
550
  />
512
551
  </div>
@@ -57,6 +57,30 @@ export const MARK_PALETTE: string[] = MARK_COLORS.filter(
57
57
  /** The app's two status colours. A face wearing one reads as a state it is not in, so no roll lands on them. */
58
58
  const STATUS_INK: string[] = [MARK_INK.amber, MARK_INK.red];
59
59
 
60
+ /**
61
+ * What a colour is drawn as on a white page. Nine of the eighteen are too light
62
+ * to read there — a yellow face on paper is 1.9:1 against a 3:1 floor for a
63
+ * shape — so each has a twin at the same hue and chroma, lightness lowered
64
+ * until it clears 3.2:1. The dark theme keeps the colour as it is, where all
65
+ * eighteen already pass. The stored value never changes: the picker, the name
66
+ * the user chose and the roll all stay on the original.
67
+ */
68
+ const ON_PAPER: Record<string, string> = {
69
+ [MARK_INK.cyan]: "#009dbb",
70
+ [MARK_INK.teal]: "#00a190",
71
+ [MARK_INK.green]: "#00a640",
72
+ [MARK_INK.lime]: "#5ca100",
73
+ [MARK_INK.amber]: "#d07b00",
74
+ [MARK_INK.orange]: "#ec6700",
75
+ [MARK_INK.sky]: "#0098dc",
76
+ [MARK_INK.emerald]: "#00a46d",
77
+ [MARK_INK.yellow]: "#bb8600",
78
+ };
79
+
80
+ /** The colour as this theme should draw it. Anything not in the table is itself. */
81
+ export const markInk = (color: string, dark: boolean): string =>
82
+ dark ? color : (ON_PAPER[color] ?? color);
83
+
60
84
  /** How the renderer lays a paint's colours out. */
61
85
  export type MarkPaintLook = "flow" | "duo" | "aurora";
62
86