thursday-agent 0.5.0 → 0.7.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 (667) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +3 -2
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/prerender-manifest.json +3 -3
  5. package/.next/required-server-files.json +8 -1
  6. package/.next/routes-manifest.json +18 -6
  7. package/.next/server/app/_global-error/page.js.nft.json +1 -1
  8. package/.next/server/app/_global-error.html +1 -1
  9. package/.next/server/app/_global-error.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  14. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  15. package/.next/server/app/_not-found.html +1 -1
  16. package/.next/server/app/_not-found.rsc +13 -13
  17. package/.next/server/app/_not-found.segments/_full.segment.rsc +13 -13
  18. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +12 -12
  19. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  20. package/.next/server/app/api/artifact/route.js +10 -6
  21. package/.next/server/app/api/artifact/route.js.nft.json +1 -1
  22. package/.next/server/app/api/bot/memory/files/route.js +5 -4
  23. package/.next/server/app/api/bot/memory/files/route.js.nft.json +1 -1
  24. package/.next/server/app/api/bot/memory/route.js +8 -8
  25. package/.next/server/app/api/bot/memory/route.js.nft.json +1 -1
  26. package/.next/server/app/api/bot/route.js +8 -8
  27. package/.next/server/app/api/bot/route.js.nft.json +1 -1
  28. package/.next/server/app/api/bot/thread/[id]/route/app-paths-manifest.json +3 -0
  29. package/.next/server/app/api/bot/thread/[id]/route.js +15 -0
  30. package/.next/server/app/api/bot/thread/[id]/route.js.nft.json +1 -0
  31. package/.next/server/app/api/bot/thread/[id]/route_client-reference-manifest.js +3 -0
  32. package/.next/server/app/api/bot/thread/route/app-paths-manifest.json +3 -0
  33. package/.next/server/app/api/bot/thread/route.js +15 -0
  34. package/.next/server/app/api/bot/thread/route.js.nft.json +1 -0
  35. package/.next/server/app/api/bot/thread/route_client-reference-manifest.js +3 -0
  36. package/.next/server/app/api/config/route.js +7 -7
  37. package/.next/server/app/api/config/route.js.nft.json +1 -1
  38. package/.next/server/app/api/events/route.js +3 -3
  39. package/.next/server/app/api/events/route.js.nft.json +1 -1
  40. package/.next/server/app/api/file/[...path]/route.js +4 -4
  41. package/.next/server/app/api/file/[...path]/route.js.nft.json +1 -1
  42. package/.next/server/app/api/llm-model/catalog/route.js +8 -8
  43. package/.next/server/app/api/llm-model/catalog/route.js.nft.json +1 -1
  44. package/.next/server/app/api/llm-model/credits/route.js +8 -8
  45. package/.next/server/app/api/llm-model/credits/route.js.nft.json +1 -1
  46. package/.next/server/app/api/llm-model/route.js +8 -8
  47. package/.next/server/app/api/llm-model/route.js.nft.json +1 -1
  48. package/.next/server/app/api/llm-model/subscription/route.js +8 -8
  49. package/.next/server/app/api/llm-model/subscription/route.js.nft.json +1 -1
  50. package/.next/server/app/api/mcp/[name]/route.js +6 -6
  51. package/.next/server/app/api/mcp/[name]/route.js.nft.json +1 -1
  52. package/.next/server/app/api/mcp/oauth/callback/route.js +4 -4
  53. package/.next/server/app/api/mcp/oauth/callback/route.js.nft.json +1 -1
  54. package/.next/server/app/api/mcp/route.js +6 -6
  55. package/.next/server/app/api/mcp/route.js.nft.json +1 -1
  56. package/.next/server/app/api/mcp/tools/route.js +6 -6
  57. package/.next/server/app/api/mcp/tools/route.js.nft.json +1 -1
  58. package/.next/server/app/api/memory/edit/route.js +15 -15
  59. package/.next/server/app/api/memory/edit/route.js.nft.json +1 -1
  60. package/.next/server/app/api/memory/route.js +7 -6
  61. package/.next/server/app/api/memory/route.js.nft.json +1 -1
  62. package/.next/server/app/api/skills/[source]/[name]/route.js +6 -6
  63. package/.next/server/app/api/skills/[source]/[name]/route.js.nft.json +1 -1
  64. package/.next/server/app/api/skills/route.js +6 -6
  65. package/.next/server/app/api/skills/route.js.nft.json +1 -1
  66. package/.next/server/app/api/thursday/call/route.js +10 -11
  67. package/.next/server/app/api/thursday/call/route.js.nft.json +1 -1
  68. package/.next/server/app/api/thursday/skills/route.js +9 -10
  69. package/.next/server/app/api/thursday/skills/route.js.nft.json +1 -1
  70. package/.next/server/app/api/thursday/tool-call/route.js +15 -15
  71. package/.next/server/app/api/thursday/tool-call/route.js.nft.json +1 -1
  72. package/.next/server/app/api/workspace/missing/route/app-paths-manifest.json +3 -0
  73. package/.next/server/app/api/workspace/missing/route/build-manifest.json +12 -0
  74. package/.next/server/app/api/workspace/missing/route/server-reference-manifest.json +4 -0
  75. package/.next/server/app/api/workspace/missing/route.js +12 -0
  76. package/.next/server/app/api/workspace/missing/route.js.map +5 -0
  77. package/.next/server/app/api/workspace/missing/route.js.nft.json +1 -0
  78. package/.next/server/app/api/workspace/missing/route_client-reference-manifest.js +3 -0
  79. package/.next/server/app/api/workspace/route.js +5 -5
  80. package/.next/server/app/api/workspace/route.js.nft.json +1 -1
  81. package/.next/server/app/artifact/[...path]/page/server-reference-manifest.json +1 -1
  82. package/.next/server/app/artifact/[...path]/page.js +4 -3
  83. package/.next/server/app/artifact/[...path]/page.js.nft.json +1 -1
  84. package/.next/server/app/artifact/[...path]/page_client-reference-manifest.js +1 -1
  85. package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
  86. package/.next/server/app/page/server-reference-manifest.json +67 -45
  87. package/.next/server/app/page.js +11 -9
  88. package/.next/server/app/page.js.nft.json +1 -1
  89. package/.next/server/app/page_client-reference-manifest.js +1 -1
  90. package/.next/server/app-paths-manifest.json +3 -2
  91. package/.next/server/chunks/[root-of-the-server]__0-06t3f._.js +8 -0
  92. package/.next/server/chunks/[root-of-the-server]__02846fb._.js +8 -0
  93. package/.next/server/chunks/[root-of-the-server]__05luceg._.js +3 -0
  94. package/.next/server/chunks/[root-of-the-server]__06--3q3._.js +8 -0
  95. package/.next/server/chunks/[root-of-the-server]__06l05n_._.js +3 -0
  96. package/.next/server/chunks/[root-of-the-server]__07bnah_._.js +3 -0
  97. package/.next/server/chunks/[root-of-the-server]__08eae20._.js +8 -0
  98. package/.next/server/chunks/[root-of-the-server]__0_ue8oe._.js +3 -0
  99. package/.next/server/chunks/[root-of-the-server]__0btqrct._.js +3 -0
  100. package/.next/server/chunks/[root-of-the-server]__0c9fgca._.js +5 -0
  101. package/.next/server/chunks/[root-of-the-server]__0h_x6q6._.js +5 -0
  102. package/.next/server/chunks/{[root-of-the-server]__0pa2n68._.js → [root-of-the-server]__0kxrvxf._.js} +2 -2
  103. package/.next/server/chunks/{[root-of-the-server]__0q0vvt2._.js → [root-of-the-server]__0ov3iir._.js} +2 -2
  104. package/.next/server/chunks/[root-of-the-server]__0qwtlmx._.js +8 -0
  105. package/.next/server/chunks/[root-of-the-server]__0sm5xvm._.js +3 -0
  106. package/.next/server/chunks/[root-of-the-server]__0zp35ul._.js +5 -0
  107. package/.next/server/chunks/[root-of-the-server]__144u9d_._.js +3 -0
  108. package/.next/server/chunks/[root-of-the-server]__18-2tsq._.js +5 -0
  109. package/.next/server/chunks/[root-of-the-server]__1e-ci13._.js +3 -0
  110. package/.next/server/chunks/[root-of-the-server]__1e2365e._.js +8 -0
  111. package/.next/server/chunks/[root-of-the-server]__1fix932._.js +8 -0
  112. package/.next/server/chunks/{[root-of-the-server]__0ce8nf8._.js → [root-of-the-server]__1g3t7vn._.js} +3 -3
  113. package/.next/server/chunks/[root-of-the-server]__1kp-83u._.js +5 -0
  114. package/.next/server/chunks/[root-of-the-server]__1kv8p3e._.js +5 -0
  115. package/.next/server/chunks/[root-of-the-server]__1orn-5b._.js +2 -3
  116. package/.next/server/chunks/_02szwk8._.js +5 -0
  117. package/.next/server/chunks/_05h2cii._.js +1 -1
  118. package/.next/server/chunks/_06c9kie._.js +216 -0
  119. package/.next/server/chunks/{_1x6kd1r._.js → _07y2gj-._.js} +1 -1
  120. package/.next/server/chunks/_0a43a-0._.js +3 -0
  121. package/.next/server/chunks/_0bujjey._.js +5 -0
  122. package/.next/server/chunks/_0c9j6rb._.js +1 -1
  123. package/.next/server/chunks/_0dkd549._.js +3 -0
  124. package/.next/server/chunks/_0ej1kvv._.js +3 -0
  125. package/.next/server/chunks/_0f6ts7z._.js +3 -0
  126. package/.next/server/chunks/_0gm9hyv._.js +55 -0
  127. package/.next/server/chunks/_0i55en5._.js +25 -0
  128. package/.next/server/chunks/_0k50n27._.js +18 -0
  129. package/.next/server/chunks/_0khihjk._.js +3 -3
  130. package/.next/server/chunks/_0n74zsf._.js +5 -5
  131. package/.next/server/chunks/_0nvm9ef._.js +3 -0
  132. package/.next/server/chunks/_0p56fn0._.js +3 -0
  133. package/.next/server/chunks/_0recufk._.js +1 -1
  134. package/.next/server/chunks/_0xbkz10._.js +3 -0
  135. package/.next/server/chunks/_10tkr26._.js +3 -0
  136. package/.next/server/chunks/{_0ioz3nd._.js → _11u2kp7._.js} +1 -1
  137. package/.next/server/chunks/_11ujpya._.js +3 -0
  138. package/.next/server/chunks/_127nx_p._.js +5 -0
  139. package/.next/server/chunks/_17l5ygt._.js +1 -1
  140. package/.next/server/chunks/_19uauj6._.js +3 -0
  141. package/.next/server/chunks/_1_5mnq4._.js +5 -5
  142. package/.next/server/chunks/_1abl6t9._.js +55 -0
  143. package/.next/server/chunks/_1d0j961._.js +3 -3
  144. package/.next/server/chunks/_1dbixxz._.js +3 -0
  145. package/.next/server/chunks/_1dc_d1q._.js +98 -0
  146. package/.next/server/chunks/_1drgfx4._.js +5 -5
  147. package/.next/server/chunks/_1e48czp._.js +3 -3
  148. package/.next/server/chunks/_1f31u5x._.js +5 -0
  149. package/.next/server/chunks/_1gnjfli._.js +3 -0
  150. package/.next/server/chunks/_1kg70wc._.js +3 -3
  151. package/.next/server/chunks/_1klbcft._.js +5 -0
  152. package/.next/server/chunks/_1krd9rf._.js +4 -1
  153. package/.next/server/chunks/_1kwmzab._.js +3 -0
  154. package/.next/server/chunks/{_1pf4qe-._.js → _1oxj9fa._.js} +1 -1
  155. package/.next/server/chunks/{_1f46eie._.js → _1pcmdbb._.js} +1 -1
  156. package/.next/server/chunks/_1qgix-j._.js +3 -0
  157. package/.next/server/chunks/{lib_logger_ts_1exwjut._.js → _1rpchta._.js} +5 -5
  158. package/.next/server/chunks/_1vsej-s._.js +5 -0
  159. package/.next/server/chunks/_1yx0p_s._.js +5 -0
  160. package/.next/server/chunks/{_0rnf-6n._.js → _20fsr_z._.js} +6 -6
  161. package/.next/server/chunks/_2128hkj._.js +5 -0
  162. package/.next/server/chunks/_next-internal_server_app_api_bot_thread_[id]_route_actions_1fstjn2.js +3 -0
  163. package/.next/server/chunks/_next-internal_server_app_api_bot_thread_route_actions_003ccso.js +3 -0
  164. package/.next/server/chunks/_next-internal_server_app_api_workspace_missing_route_actions_1fidi6p.js +3 -0
  165. package/.next/server/chunks/config_ts_1g4sz5o._.js +1 -1
  166. package/.next/server/chunks/features_0-cnt0l._.js +4 -0
  167. package/.next/server/chunks/features_0hdaci0._.js +4 -0
  168. package/.next/server/chunks/features_1e39j7-._.js +8 -0
  169. package/.next/server/chunks/features_bot_bot_query_ts_12mgkl2._.js +1 -1
  170. package/.next/server/chunks/features_memory_1uqvksn._.js +1 -1
  171. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0v_7drq._.js → node_modules_consola_dist_chunks_prompt_mjs_0a7-pbw._.js} +1 -1
  172. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_17k8irj._.js → node_modules_consola_dist_chunks_prompt_mjs_0gzvgfv._.js} +1 -1
  173. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0i7cvml._.js → node_modules_consola_dist_chunks_prompt_mjs_107pxfn._.js} +1 -1
  174. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0icqaqw._.js → node_modules_consola_dist_chunks_prompt_mjs_15hbel8._.js} +1 -1
  175. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_18i-23e._.js +3 -0
  176. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1bewgf3._.js +3 -0
  177. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1nlww1m._.js +3 -0
  178. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1q77nfx._.js +3 -0
  179. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1xqdzx_._.js +3 -0
  180. package/.next/server/chunks/{node_modules_zod_v4_1801ax7._.js → node_modules_zod_v4_0lkux5-._.js} +2 -2
  181. package/.next/server/chunks/{node_modules_zod_v4_1cha_bw._.js → node_modules_zod_v4_1jmr9cq._.js} +2 -2
  182. package/.next/server/chunks/{node_modules_zod_v4_0ubdflj._.js → node_modules_zod_v4_1yrxmek._.js} +2 -2
  183. package/.next/server/chunks/node_modules_zod_v4_classic_errors_0tp0y-q.js +3 -0
  184. package/.next/server/chunks/ssr/[root-of-the-server]__0-e011f._.js +3 -0
  185. package/.next/server/chunks/ssr/[root-of-the-server]__09q9med._.js +1 -1
  186. package/.next/server/chunks/ssr/[root-of-the-server]__0lyi8i2._.js +1 -1
  187. package/.next/server/chunks/ssr/[root-of-the-server]__0pd8yr7._.js +3 -0
  188. package/.next/server/chunks/ssr/[root-of-the-server]__16jc196._.js +1 -1
  189. package/.next/server/chunks/ssr/[root-of-the-server]__18dthbp._.js +3 -0
  190. package/.next/server/chunks/ssr/[root-of-the-server]__18e68w8._.js +3 -0
  191. package/.next/server/chunks/ssr/[root-of-the-server]__1mzi-ju._.js +1 -1
  192. package/.next/server/chunks/ssr/[root-of-the-server]__1nygfik._.js +5 -0
  193. package/.next/server/chunks/ssr/[root-of-the-server]__1sdphb0._.js +8 -0
  194. package/.next/server/chunks/ssr/[root-of-the-server]__1vdmfbe._.js +3 -0
  195. package/.next/server/chunks/ssr/[root-of-the-server]__1waa3rc._.js +85 -0
  196. package/.next/server/chunks/ssr/_0-2qsxo._.js +3 -0
  197. package/.next/server/chunks/ssr/_00rehl_._.js +3 -0
  198. package/.next/server/chunks/ssr/{_1lz_cq2._.js → _02oc_h5._.js} +76 -97
  199. package/.next/server/chunks/ssr/_05yghx6._.js +3 -0
  200. package/.next/server/chunks/ssr/_07nbwap._.js +3 -0
  201. package/.next/server/chunks/ssr/_08c4z-9._.js +3 -0
  202. package/.next/server/chunks/ssr/_0_qfdlw._.js +4 -0
  203. package/.next/server/chunks/ssr/{_0m2lsd1._.js → _0b6_jv-._.js} +2 -2
  204. package/.next/server/chunks/ssr/_0evkpto._.js +3 -0
  205. package/.next/server/chunks/ssr/_0geg212._.js +3 -0
  206. package/.next/server/chunks/ssr/{_1jdgjv9._.js → _0hxt2ul._.js} +2 -2
  207. package/.next/server/chunks/ssr/_0i_6q1w._.js +2 -2
  208. package/.next/server/chunks/ssr/_0lu1n_x._.js +1 -1
  209. package/.next/server/chunks/ssr/_0no7i1o._.js +3 -0
  210. package/.next/server/chunks/ssr/_0tzg--b._.js +3 -0
  211. package/.next/server/chunks/ssr/{_15j2_8f._.js → _0wzaht-._.js} +2 -2
  212. package/.next/server/chunks/ssr/_0z-g0uy._.js +3 -0
  213. package/.next/server/chunks/ssr/_114ylv7._.js +7 -0
  214. package/.next/server/chunks/ssr/_123y-ah._.js +3 -0
  215. package/.next/server/chunks/ssr/_12c-iwh._.js +3 -0
  216. package/.next/server/chunks/ssr/_12w42gi._.js +3 -0
  217. package/.next/server/chunks/ssr/_161kqav._.js +3 -0
  218. package/.next/server/chunks/ssr/_167hchk._.js +3 -0
  219. package/.next/server/chunks/ssr/{_0ap2fzn._.js → _16c6_xm._.js} +2 -2
  220. package/.next/server/chunks/ssr/_18opah7._.js +3 -0
  221. package/.next/server/chunks/ssr/_1_krfph._.js +3 -0
  222. package/.next/server/chunks/ssr/{_0rzucul._.js → _1_x5spz._.js} +3 -3
  223. package/.next/server/chunks/ssr/_1a61fc9._.js +3 -0
  224. package/.next/server/chunks/ssr/_1ey7qn-._.js +70 -0
  225. package/.next/server/chunks/ssr/_1g9vs7q._.js +3 -0
  226. package/.next/server/chunks/ssr/_1jmz33w._.js +3 -0
  227. package/.next/server/chunks/ssr/_1rgtxhr._.js +5 -0
  228. package/.next/server/chunks/ssr/_1slp25y._.js +3 -0
  229. package/.next/server/chunks/ssr/_1w_t7h7._.js +101 -0
  230. package/.next/server/chunks/ssr/{_0s99bgb._.js → _1wyfaq_._.js} +9 -3
  231. package/.next/server/chunks/ssr/_200gt-m._.js +102 -0
  232. package/.next/server/chunks/ssr/components_ui_tabs_tsx_0ej40b4._.js +3 -0
  233. package/.next/server/chunks/ssr/features_bot_thread_query_ts_023eklt._.js +4 -0
  234. package/.next/server/chunks/ssr/features_config_components_config-setting_tsx_0xfwqdg._.js +1 -1
  235. package/.next/server/chunks/ssr/features_connectors_components_mcp-setting_tsx_0jmbgvp._.js +1 -1
  236. package/.next/server/chunks/ssr/features_workspace_00jz2hu._.js +8 -0
  237. package/.next/server/chunks/ssr/{_1-teee5._.js → lib_logger_ts_0-olsze._.js} +2 -7
  238. package/.next/server/chunks/ssr/{node_modules_05-n6dz._.js → node_modules_02ai7br._.js} +1 -1
  239. package/.next/server/chunks/ssr/{node_modules_1chvq5f._.js → node_modules_02deexj._.js} +1 -1
  240. package/.next/server/chunks/ssr/{node_modules_0m383r2._.js → node_modules_07za262._.js} +1 -1
  241. package/.next/server/chunks/ssr/{node_modules_07dsgkx._.js → node_modules_0_s9get._.js} +1 -1
  242. package/.next/server/chunks/ssr/{node_modules_1a0kmjl._.js → node_modules_0n3c7h0._.js} +1 -1
  243. package/.next/server/chunks/ssr/{node_modules_0zeg3x_._.js → node_modules_0od6jwr._.js} +1 -1
  244. package/.next/server/chunks/ssr/{node_modules_1y79i83._.js → node_modules_0rdt7pk._.js} +1 -1
  245. package/.next/server/chunks/ssr/{node_modules_17bwo43._.js → node_modules_0tusx4d._.js} +3 -3
  246. package/.next/server/chunks/ssr/{node_modules_04sksn9._.js → node_modules_0w1z7kz._.js} +1 -1
  247. package/.next/server/chunks/ssr/{node_modules_1uyk91w._.js → node_modules_0wdupfz._.js} +1 -1
  248. package/.next/server/chunks/ssr/{node_modules_0ifp389._.js → node_modules_1720w0a._.js} +1 -1
  249. package/.next/server/chunks/ssr/{node_modules_0u9da1h._.js → node_modules_18lsg7d._.js} +1 -1
  250. package/.next/server/chunks/ssr/{node_modules_1k6n600._.js → node_modules_1b94fxf._.js} +1 -1
  251. package/.next/server/chunks/ssr/{node_modules_20c28js._.js → node_modules_1ecdo1s._.js} +1 -1
  252. package/.next/server/chunks/ssr/{node_modules_0fkeku3._.js → node_modules_1fa9ag7._.js} +1 -1
  253. package/.next/server/chunks/ssr/{node_modules_1j8j4pt._.js → node_modules_1fv-_si._.js} +1 -1
  254. package/.next/server/chunks/ssr/{node_modules_0-dr_o7._.js → node_modules_1hf461g._.js} +1 -1
  255. package/.next/server/chunks/ssr/{node_modules_1lz0oxc._.js → node_modules_1m71ta-._.js} +1 -1
  256. package/.next/server/chunks/ssr/{node_modules_1t77e4n._.js → node_modules_1n93n-g._.js} +1 -1
  257. package/.next/server/chunks/ssr/{node_modules_16mkxuo._.js → node_modules_1n9lqu9._.js} +1 -1
  258. package/.next/server/chunks/ssr/{node_modules_1k7z2j6._.js → node_modules_1npnsvd._.js} +1 -1
  259. package/.next/server/chunks/ssr/{node_modules_056qgf-._.js → node_modules_1rn2r0n._.js} +1 -1
  260. package/.next/server/chunks/ssr/{node_modules_0x3-ply._.js → node_modules_1seuurl._.js} +2 -2
  261. package/.next/server/chunks/ssr/{node_modules_0bvopy2._.js → node_modules_1vq-mfk._.js} +1 -1
  262. package/.next/server/chunks/ssr/{node_modules_0iur59s._.js → node_modules_1vzyw1t._.js} +1 -1
  263. package/.next/server/chunks/ssr/{node_modules_1k8j8n0._.js → node_modules_1w359w4._.js} +1 -1
  264. package/.next/server/chunks/ssr/{node_modules_1x0ztf4._.js → node_modules_1yajbl6._.js} +1 -1
  265. package/.next/server/chunks/ssr/{node_modules_1-y7nni._.js → node_modules_20eqhy8._.js} +1 -1
  266. package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0bkwd0u._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_019sbis._.js} +1 -1
  267. package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0p9a9su._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0s6piq3._.js} +1 -1
  268. package/.next/server/chunks/ssr/{node_modules_@vercel_oidc_dist_1o553h-._.js → node_modules_@vercel_oidc_dist_05itbb1._.js} +1 -1
  269. package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_trash_mjs_0px2gzr._.js +3 -0
  270. package/.next/server/middleware-build-manifest.js +3 -3
  271. package/.next/server/pages/404.html +1 -1
  272. package/.next/server/pages/500.html +1 -1
  273. package/.next/server/server-reference-manifest.js +1 -1
  274. package/.next/server/server-reference-manifest.json +68 -46
  275. package/.next/static/chunks/07vqbk_9t4em4.js +2 -0
  276. package/.next/static/chunks/08krja1zx7nqg.js +1 -0
  277. package/.next/static/chunks/0g4b348l0pve7.js +1 -0
  278. package/.next/static/chunks/0qcuwdk132jjt.js +1 -0
  279. package/.next/static/chunks/0vlgzqo9ka90v.js +1 -0
  280. package/.next/static/chunks/{11r2yp_yh9_qn.js → 0z69gvysmave7.js} +1 -1
  281. package/.next/static/chunks/0zrupxgci5gqc.js +1 -0
  282. package/.next/static/chunks/12fvz1w5ows75.js +1 -0
  283. package/.next/static/chunks/14t03ned286iu.js +1 -0
  284. package/.next/static/chunks/156xxbkrz5lzu.js +18 -0
  285. package/.next/static/chunks/1fz14reod4lft.js +1 -0
  286. package/.next/static/chunks/1g23dbabl-vkr.js +1 -0
  287. package/.next/static/chunks/1ohslk4568tm3.js +1 -0
  288. package/.next/static/chunks/1qy_rc3oxfxok.js +1 -0
  289. package/.next/static/chunks/1sbmwswc9k2-1.js +1 -0
  290. package/.next/static/chunks/1vqezbhjm3goq.js +1 -0
  291. package/.next/static/chunks/1zea7qdl-7365.js +1 -0
  292. package/.next/static/chunks/1zvqumkg7kmo0.css +1 -0
  293. package/.next/static/chunks/26hpqt3y6bogl.js +12 -0
  294. package/.next/static/chunks/2_j5mqm6d-z18.js +99 -0
  295. package/.next/static/chunks/2dt6tu7a22svs.js +1 -0
  296. package/.next/static/chunks/2h5ls3d86kya6.js +1 -0
  297. package/.next/static/chunks/2wc6y1ht6dfuy.js +1 -0
  298. package/.next/static/chunks/{435atsnhs7ksu.js → 2xr0zpll7thyi.js} +2 -2
  299. package/.next/static/chunks/31jztc-yq30m7.js +1 -0
  300. package/.next/static/chunks/391af0i8eljzk.js +1 -0
  301. package/.next/static/chunks/3bfwpww3m3x4u.js +1 -0
  302. package/.next/static/chunks/{38gweqdo4jpfj.js → 3izg0a3k87q1-.js} +5 -5
  303. package/.next/static/chunks/3p2s2cjokx1aa.js +5 -0
  304. package/.next/static/chunks/{408ueu4hfvcg7.js → 41s3fml5oivsa.js} +1 -1
  305. package/README.md +45 -193
  306. package/app/api/events/app-event.ts +6 -4
  307. package/app/api/query-key.ts +25 -16
  308. package/bin/database.mjs +37 -0
  309. package/bin/thursday.mjs +44 -28
  310. package/components/ui/custom-icon.tsx +0 -60
  311. package/components/ui/radio-group.tsx +37 -0
  312. package/components/ui/shiny-text.tsx +33 -6
  313. package/database/migrations/{20260910173049_numerous_spirit → 20260916145701_clever_multiple_man}/migration.sql +80 -24
  314. package/database/migrations/{20260910173049_numerous_spirit → 20260916145701_clever_multiple_man}/snapshot.json +689 -110
  315. package/database/migrations/20260916160713_thread_cancelled/migration.sql +2 -0
  316. package/database/migrations/{20260911030320_opposite_jack_power → 20260916160713_thread_cancelled}/snapshot.json +706 -84
  317. package/docs/how-it-works.md +40 -35
  318. package/docs/images/keeps-going.png +0 -0
  319. package/docs/images/two-minds.png +0 -0
  320. package/docs/live-calls.md +236 -0
  321. package/docs/thread-rooms.md +71 -0
  322. package/features/ai/chatgpt.ts +1 -2
  323. package/features/ai/live.schema.ts +128 -0
  324. package/features/ai/load-tools.ts +152 -79
  325. package/features/ai/model.schema.ts +2 -120
  326. package/features/ai/model.ts +52 -88
  327. package/features/ai/prompts/bot.prompt.ts +91 -214
  328. package/features/ai/prompts/live.prompt.ts +169 -0
  329. package/features/ai/prompts/prompt-helper.ts +92 -32
  330. package/features/ai/prompts/thursday.prompt.ts +164 -190
  331. package/features/ai/tools/bot.tool.ts +38 -145
  332. package/features/ai/tools/call.tool.ts +33 -5
  333. package/features/ai/tools/connected.ts +5 -3
  334. package/features/ai/tools/mcp.tool.ts +7 -4
  335. package/features/ai/tools/memory.tool.ts +35 -31
  336. package/features/ai/tools/skills.tool.ts +11 -6
  337. package/features/ai/tools/studio.tool.ts +26 -21
  338. package/features/ai/tools/tool-name.ts +3 -5
  339. package/features/ai/tools/workspace.tool.ts +33 -10
  340. package/features/artifact/artifact.action.ts +2 -2
  341. package/features/artifact/artifact.query.ts +112 -61
  342. package/features/artifact/artifact.schema.ts +8 -6
  343. package/features/artifact/components/artifact-setting.tsx +83 -33
  344. package/features/bot/bot.action.ts +43 -36
  345. package/features/bot/bot.memory.ts +137 -20
  346. package/features/bot/bot.query.ts +28 -11
  347. package/features/bot/bot.run.ts +296 -744
  348. package/features/bot/bot.runner.ts +463 -864
  349. package/features/bot/bot.schema.ts +93 -82
  350. package/features/bot/bot.seed.ts +141 -64
  351. package/features/bot/components/bot-mark.tsx +324 -232
  352. package/features/bot/components/bot-room.tsx +1613 -697
  353. package/features/bot/components/bot-roster.tsx +8 -13
  354. package/features/bot/components/bot-setting.tsx +58 -42
  355. package/features/bot/components/bot-tool.tsx +55 -87
  356. package/features/bot/components/mark-palette.tsx +35 -32
  357. package/features/bot/components/path-chips.tsx +33 -3
  358. package/features/bot/components/thread-badge.tsx +34 -0
  359. package/features/bot/components/thread-reply.tsx +639 -0
  360. package/features/bot/components/thread-setting.tsx +674 -0
  361. package/features/bot/mark.const.ts +79 -31
  362. package/features/bot/room.query.ts +889 -0
  363. package/features/bot/room.schema.ts +64 -0
  364. package/features/bot/seed-bots.ts +2 -1
  365. package/features/bot/thread.query.ts +971 -0
  366. package/features/bot/thread.store.ts +587 -0
  367. package/features/config/components/voice-key.tsx +113 -88
  368. package/features/config/config.const.ts +5 -9
  369. package/features/connectors/mcp.schema.ts +0 -2
  370. package/features/intro/components/intro.tsx +169 -139
  371. package/features/memory/components/memory-setting.tsx +2 -7
  372. package/features/memory/memory.schema.ts +14 -23
  373. package/features/settings/components/setting-ui.tsx +0 -44
  374. package/features/settings/components/settings.tsx +12 -12
  375. package/features/settings/settings.alert.ts +3 -3
  376. package/features/settings/settings.store.ts +1 -1
  377. package/features/skills/skills.schema.ts +0 -2
  378. package/features/thursday/ascii.const.ts +74 -0
  379. package/features/thursday/components/ascii-field.tsx +24 -44
  380. package/features/thursday/components/ascii-orb.tsx +525 -381
  381. package/features/thursday/components/call-log.tsx +46 -44
  382. package/features/thursday/components/face.tsx +40 -10
  383. package/features/thursday/components/side-captions.tsx +273 -0
  384. package/features/thursday/components/tab-state.tsx +14 -6
  385. package/features/thursday/components/thursday-mark.tsx +1 -0
  386. package/features/thursday/components/thursday-setting.tsx +633 -333
  387. package/features/thursday/components/thursday.tsx +315 -274
  388. package/features/thursday/components/voice-picker.tsx +255 -0
  389. package/features/thursday/face.store.ts +5 -0
  390. package/features/thursday/thursday.action.ts +96 -72
  391. package/features/thursday/thursday.query.ts +49 -30
  392. package/features/thursday/thursday.schema.ts +37 -35
  393. package/features/thursday/thursday.store.ts +14 -7
  394. package/features/thursday/tool-call.ts +3 -3
  395. package/features/thursday/tool-line.ts +5 -3
  396. package/features/thursday/use-thursday.ts +801 -457
  397. package/features/workspace/file-kind.ts +9 -0
  398. package/features/workspace/workspace.query.ts +17 -0
  399. package/features/workspace/workspace.ts +82 -36
  400. package/hooks/use-hotkey.ts +3 -2
  401. package/lib/live/live.schema.ts +26 -0
  402. package/lib/live/live.server.ts +153 -0
  403. package/lib/live/live.session.ts +574 -0
  404. package/lib/{realtime/realtime.tap.ts → live/live.tap.ts} +41 -12
  405. package/lib/live/live.transport.ts +152 -0
  406. package/lib/protocol/use-server-pages.ts +9 -5
  407. package/lib/utils.ts +21 -20
  408. package/package.json +2 -2
  409. package/public/voices/alloy.ogg +0 -0
  410. package/public/voices/ash.ogg +0 -0
  411. package/public/voices/ballad.ogg +0 -0
  412. package/public/voices/beacon.ogg +0 -0
  413. package/public/voices/bossa.ogg +0 -0
  414. package/public/voices/cedar.ogg +0 -0
  415. package/public/voices/cinder.ogg +0 -0
  416. package/public/voices/coral.ogg +0 -0
  417. package/public/voices/delta.ogg +0 -0
  418. package/public/voices/echo.ogg +0 -0
  419. package/public/voices/gleam.ogg +0 -0
  420. package/public/voices/marin.ogg +0 -0
  421. package/public/voices/meridian.ogg +0 -0
  422. package/public/voices/quartz.ogg +0 -0
  423. package/public/voices/ripple.ogg +0 -0
  424. package/public/voices/sage.ogg +0 -0
  425. package/public/voices/shimmer.ogg +0 -0
  426. package/public/voices/stone.ogg +0 -0
  427. package/public/voices/tempo.ogg +0 -0
  428. package/public/voices/verse.ogg +0 -0
  429. package/public/voices/vesper.ogg +0 -0
  430. package/public/voices/willow.ogg +0 -0
  431. package/scripts/artifact-paths.test.mts +38 -0
  432. package/scripts/bot-context.test.mts +1408 -0
  433. package/scripts/live-history.test.mts +115 -0
  434. package/scripts/live-session.test.mts +938 -0
  435. package/server.js +1 -1
  436. package/skills/browser/SKILL.md +11 -1
  437. package/skills/computer/SKILL.md +1 -1
  438. package/skills/find-skills/SKILL.md +3 -8
  439. package/skills/interactive-page/SKILL.md +12 -7
  440. package/skills/interactive-page/references/diagram.md +89 -0
  441. package/skills/interactive-page/scripts/archify/LICENSE +22 -0
  442. package/skills/interactive-page/scripts/archify/README.md +25 -0
  443. package/skills/interactive-page/scripts/archify/assets/JetBrainsMono-OFL.txt +93 -0
  444. package/skills/interactive-page/scripts/archify/assets/template.html +14934 -0
  445. package/skills/interactive-page/scripts/archify/bin/archify.mjs +2126 -0
  446. package/skills/interactive-page/scripts/archify/bin/open-artifact.mjs +86 -0
  447. package/skills/interactive-page/scripts/archify/bin/preview.mjs +653 -0
  448. package/skills/interactive-page/scripts/archify/bin/visual-check.mjs +829 -0
  449. package/skills/interactive-page/scripts/archify/delta/architecture-delta.mjs +1223 -0
  450. package/skills/interactive-page/scripts/archify/examples/agent-run.lifecycle.json +60 -0
  451. package/skills/interactive-page/scripts/archify/examples/agent-tool-call.workflow.json +94 -0
  452. package/skills/interactive-page/scripts/archify/examples/async-job-roundtrip.sequence.json +61 -0
  453. package/skills/interactive-page/scripts/archify/examples/cache-miss-request.sequence.json +82 -0
  454. package/skills/interactive-page/scripts/archify/examples/checkout-platform.base.architecture.json +31 -0
  455. package/skills/interactive-page/scripts/archify/examples/checkout-platform.head.architecture.json +31 -0
  456. package/skills/interactive-page/scripts/archify/examples/deployment-release.lifecycle.json +49 -0
  457. package/skills/interactive-page/scripts/archify/examples/event-stream.dataflow.json +57 -0
  458. package/skills/interactive-page/scripts/archify/examples/incident-response.workflow.json +64 -0
  459. package/skills/interactive-page/scripts/archify/examples/product-analytics.dataflow.json +76 -0
  460. package/skills/interactive-page/scripts/archify/examples/production-deployment.architecture.json +71 -0
  461. package/skills/interactive-page/scripts/archify/examples/release-delivery.workflow.json +62 -0
  462. package/skills/interactive-page/scripts/archify/examples/web-app.architecture.json +46 -0
  463. package/skills/interactive-page/scripts/archify/migrations/workflow-v2.mjs +279 -0
  464. package/skills/interactive-page/scripts/archify/recipes/scenarios.mjs +284 -0
  465. package/skills/interactive-page/scripts/archify/references/authoring-contract.md +243 -0
  466. package/skills/interactive-page/scripts/archify/references/delivery-contract.md +120 -0
  467. package/skills/interactive-page/scripts/archify/references/viewer-runtime.md +45 -0
  468. package/skills/interactive-page/scripts/archify/renderers/architecture/grid.mjs +62 -0
  469. package/skills/interactive-page/scripts/archify/renderers/architecture/render-architecture.mjs +1078 -0
  470. package/skills/interactive-page/scripts/archify/renderers/dataflow/README.md +104 -0
  471. package/skills/interactive-page/scripts/archify/renderers/dataflow/render-dataflow.mjs +483 -0
  472. package/skills/interactive-page/scripts/archify/renderers/lifecycle/README.md +115 -0
  473. package/skills/interactive-page/scripts/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
  474. package/skills/interactive-page/scripts/archify/renderers/sequence/README.md +114 -0
  475. package/skills/interactive-page/scripts/archify/renderers/sequence/render-sequence.mjs +464 -0
  476. package/skills/interactive-page/scripts/archify/renderers/shared/brand-marks.mjs +563 -0
  477. package/skills/interactive-page/scripts/archify/renderers/shared/cli.mjs +218 -0
  478. package/skills/interactive-page/scripts/archify/renderers/shared/desktop-readability.mjs +26 -0
  479. package/skills/interactive-page/scripts/archify/renderers/shared/diagnostics.mjs +127 -0
  480. package/skills/interactive-page/scripts/archify/renderers/shared/engineering-profiles.mjs +157 -0
  481. package/skills/interactive-page/scripts/archify/renderers/shared/generated-brand-marks.mjs +3 -0
  482. package/skills/interactive-page/scripts/archify/renderers/shared/generated-validators.mjs +13 -0
  483. package/skills/interactive-page/scripts/archify/renderers/shared/geometry.mjs +1423 -0
  484. package/skills/interactive-page/scripts/archify/renderers/shared/i18n.mjs +595 -0
  485. package/skills/interactive-page/scripts/archify/renderers/shared/layout-report.mjs +40 -0
  486. package/skills/interactive-page/scripts/archify/renderers/shared/legend.mjs +217 -0
  487. package/skills/interactive-page/scripts/archify/renderers/shared/output-path.mjs +340 -0
  488. package/skills/interactive-page/scripts/archify/renderers/shared/repository-evidence.mjs +238 -0
  489. package/skills/interactive-page/scripts/archify/renderers/shared/repository-location.mjs +58 -0
  490. package/skills/interactive-page/scripts/archify/renderers/shared/text-fit.mjs +49 -0
  491. package/skills/interactive-page/scripts/archify/renderers/shared/utils.mjs +232 -0
  492. package/skills/interactive-page/scripts/archify/renderers/shared/validator.mjs +86 -0
  493. package/skills/interactive-page/scripts/archify/renderers/workflow/README.md +223 -0
  494. package/skills/interactive-page/scripts/archify/renderers/workflow/render-workflow.mjs +35 -0
  495. package/skills/interactive-page/scripts/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
  496. package/skills/interactive-page/scripts/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
  497. package/skills/interactive-page/scripts/archify/schemas/README.md +211 -0
  498. package/skills/interactive-page/scripts/archify/schemas/architecture.schema.json +178 -0
  499. package/skills/interactive-page/scripts/archify/schemas/common.schema.json +115 -0
  500. package/skills/interactive-page/scripts/archify/schemas/dataflow.schema.json +243 -0
  501. package/skills/interactive-page/scripts/archify/schemas/lifecycle.schema.json +266 -0
  502. package/skills/interactive-page/scripts/archify/schemas/sequence.schema.json +223 -0
  503. package/skills/interactive-page/scripts/archify/schemas/workflow.schema.json +428 -0
  504. package/skills/interactive-page/scripts/archify/scripts/check-render-output.mjs +916 -0
  505. package/skills/interactive-page/scripts/archify/scripts/render-examples.mjs +26 -0
  506. package/skills/interactive-page/scripts/bundle-artifact.sh +9 -8
  507. package/skills/skill-creator/SKILL.md +4 -5
  508. package/.next/server/app/api/bot/task/[id]/route/app-paths-manifest.json +0 -3
  509. package/.next/server/app/api/bot/task/[id]/route.js +0 -16
  510. package/.next/server/app/api/bot/task/[id]/route.js.nft.json +0 -1
  511. package/.next/server/app/api/bot/task/[id]/route_client-reference-manifest.js +0 -3
  512. package/.next/server/app/api/bot/task/route/app-paths-manifest.json +0 -3
  513. package/.next/server/app/api/bot/task/route.js +0 -16
  514. package/.next/server/app/api/bot/task/route.js.nft.json +0 -1
  515. package/.next/server/app/api/bot/task/route_client-reference-manifest.js +0 -3
  516. package/.next/server/chunks/[root-of-the-server]__026u77e._.js +0 -5
  517. package/.next/server/chunks/[root-of-the-server]__02va5wz._.js +0 -3
  518. package/.next/server/chunks/[root-of-the-server]__050zri4._.js +0 -3
  519. package/.next/server/chunks/[root-of-the-server]__0bg9710._.js +0 -3
  520. package/.next/server/chunks/[root-of-the-server]__0ffbg85._.js +0 -3
  521. package/.next/server/chunks/[root-of-the-server]__0in-d5s._.js +0 -3
  522. package/.next/server/chunks/[root-of-the-server]__0ncs9uu._.js +0 -3
  523. package/.next/server/chunks/[root-of-the-server]__0pa9k-f._.js +0 -8
  524. package/.next/server/chunks/[root-of-the-server]__0qi_m0e._.js +0 -8
  525. package/.next/server/chunks/[root-of-the-server]__0tq0m_k._.js +0 -3
  526. package/.next/server/chunks/[root-of-the-server]__0xep6j_._.js +0 -3
  527. package/.next/server/chunks/[root-of-the-server]__1-ga0kh._.js +0 -5
  528. package/.next/server/chunks/[root-of-the-server]__11n256_._.js +0 -5
  529. package/.next/server/chunks/[root-of-the-server]__144l7qt._.js +0 -8
  530. package/.next/server/chunks/[root-of-the-server]__1_04mcf._.js +0 -5
  531. package/.next/server/chunks/[root-of-the-server]__1anc1p9._.js +0 -3
  532. package/.next/server/chunks/[root-of-the-server]__1fizn31._.js +0 -3
  533. package/.next/server/chunks/[root-of-the-server]__1jg8t86._.js +0 -8
  534. package/.next/server/chunks/[root-of-the-server]__1lldzy9._.js +0 -3
  535. package/.next/server/chunks/[root-of-the-server]__1llj3-b._.js +0 -3
  536. package/.next/server/chunks/[root-of-the-server]__1p8m3f4._.js +0 -3
  537. package/.next/server/chunks/[root-of-the-server]__1peqvsi._.js +0 -3
  538. package/.next/server/chunks/[root-of-the-server]__1w8gm5h._.js +0 -10
  539. package/.next/server/chunks/[root-of-the-server]__1xt39nn._.js +0 -8
  540. package/.next/server/chunks/[root-of-the-server]__1zb8mfo._.js +0 -8
  541. package/.next/server/chunks/_0-73of8._.js +0 -55
  542. package/.next/server/chunks/_0-fjql4._.js +0 -31
  543. package/.next/server/chunks/_00xh_q0._.js +0 -25
  544. package/.next/server/chunks/_051qh8u._.js +0 -3
  545. package/.next/server/chunks/_05sovvj._.js +0 -3
  546. package/.next/server/chunks/_091bi8d._.js +0 -3
  547. package/.next/server/chunks/_0fzqt2y._.js +0 -142
  548. package/.next/server/chunks/_0lr92vd._.js +0 -3
  549. package/.next/server/chunks/_0mecv0f._.js +0 -31
  550. package/.next/server/chunks/_0mh8j4m._.js +0 -3
  551. package/.next/server/chunks/_0sb7tlz._.js +0 -3
  552. package/.next/server/chunks/_0si7xzn._.js +0 -3
  553. package/.next/server/chunks/_0u10wmw._.js +0 -55
  554. package/.next/server/chunks/_0wp4le3._.js +0 -3
  555. package/.next/server/chunks/_0y5fg-x._.js +0 -3
  556. package/.next/server/chunks/_1230wbx._.js +0 -10
  557. package/.next/server/chunks/_12nheja._.js +0 -3
  558. package/.next/server/chunks/_13510xh._.js +0 -3
  559. package/.next/server/chunks/_15hq89o._.js +0 -3
  560. package/.next/server/chunks/_18-1wcc._.js +0 -55
  561. package/.next/server/chunks/_18d7-5p._.js +0 -3
  562. package/.next/server/chunks/_1c78vw5._.js +0 -237
  563. package/.next/server/chunks/_1h5_8sk._.js +0 -3
  564. package/.next/server/chunks/_1hdnxb7._.js +0 -3
  565. package/.next/server/chunks/_1j8k_j5._.js +0 -3
  566. package/.next/server/chunks/_1nula3v._.js +0 -3
  567. package/.next/server/chunks/_1php4eq._.js +0 -3
  568. package/.next/server/chunks/_1yuv4vp._.js +0 -3
  569. package/.next/server/chunks/_1zkfbaa._.js +0 -3
  570. package/.next/server/chunks/_next-internal_server_app_api_bot_task_[id]_route_actions_1_k_8u3.js +0 -3
  571. package/.next/server/chunks/_next-internal_server_app_api_bot_task_route_actions_0xln6ra.js +0 -3
  572. package/.next/server/chunks/features_0rpfxbk._.js +0 -3
  573. package/.next/server/chunks/features_1qf3cei._.js +0 -8
  574. package/.next/server/chunks/features_bot_0zt92oh._.js +0 -3
  575. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1jidylq._.js +0 -3
  576. package/.next/server/chunks/ssr/[externals]__0dizgek._.js +0 -3
  577. package/.next/server/chunks/ssr/[root-of-the-server]__0-nob52._.js +0 -4
  578. package/.next/server/chunks/ssr/[root-of-the-server]__092knlj._.js +0 -3
  579. package/.next/server/chunks/ssr/[root-of-the-server]__0mz33a7._.js +0 -3
  580. package/.next/server/chunks/ssr/[root-of-the-server]__0s4wb56._.js +0 -3
  581. package/.next/server/chunks/ssr/[root-of-the-server]__0s9dq5u._.js +0 -3
  582. package/.next/server/chunks/ssr/[root-of-the-server]__0sd3izg._.js +0 -3
  583. package/.next/server/chunks/ssr/[root-of-the-server]__0vnmv-s._.js +0 -3
  584. package/.next/server/chunks/ssr/[root-of-the-server]__1-13tcn._.js +0 -43
  585. package/.next/server/chunks/ssr/[root-of-the-server]__10-dz5o._.js +0 -61
  586. package/.next/server/chunks/ssr/[root-of-the-server]__1zidpfl._.js +0 -3
  587. package/.next/server/chunks/ssr/_001_ms1._.js +0 -33
  588. package/.next/server/chunks/ssr/_01n3o0m._.js +0 -3
  589. package/.next/server/chunks/ssr/_04z2c7b._.js +0 -3
  590. package/.next/server/chunks/ssr/_06tssji._.js +0 -3
  591. package/.next/server/chunks/ssr/_0fk4c7d._.js +0 -3
  592. package/.next/server/chunks/ssr/_0h42fze._.js +0 -3
  593. package/.next/server/chunks/ssr/_0h7zdni._.js +0 -3
  594. package/.next/server/chunks/ssr/_0mggveb._.js +0 -3
  595. package/.next/server/chunks/ssr/_0nbpc_8._.js +0 -3
  596. package/.next/server/chunks/ssr/_0q0zan6._.js +0 -70
  597. package/.next/server/chunks/ssr/_0zcjrbg._.js +0 -3
  598. package/.next/server/chunks/ssr/_0zhoc3r._.js +0 -3
  599. package/.next/server/chunks/ssr/_11j92tu._.js +0 -3
  600. package/.next/server/chunks/ssr/_13fke0x._.js +0 -3
  601. package/.next/server/chunks/ssr/_18cymkq._.js +0 -3
  602. package/.next/server/chunks/ssr/_1ebxla1._.js +0 -3
  603. package/.next/server/chunks/ssr/_1nj4niv._.js +0 -42
  604. package/.next/server/chunks/ssr/_1ov37if._.js +0 -3
  605. package/.next/server/chunks/ssr/_1ovmc9s._.js +0 -3
  606. package/.next/server/chunks/ssr/_1sji22n._.js +0 -3
  607. package/.next/server/chunks/ssr/_1vi_uo7._.js +0 -3
  608. package/.next/server/chunks/ssr/_1vyd1ji._.js +0 -3
  609. package/.next/server/chunks/ssr/_1xx4zlb._.js +0 -3
  610. package/.next/server/chunks/ssr/components_ui_1y1l7yj._.js +0 -3
  611. package/.next/server/chunks/ssr/features_bot_1s_-2vj._.js +0 -3
  612. package/.next/server/chunks/ssr/lib_realtime_12wg_dd._.js +0 -27
  613. package/.next/server/chunks/ssr/lib_realtime_15h_v1v._.js +0 -27
  614. package/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_0npjf8-._.js +0 -3
  615. package/.next/static/chunks/0n3jle9kbqfr2.js +0 -40
  616. package/.next/static/chunks/0r5qcctujhxli.js +0 -1
  617. package/.next/static/chunks/10teww7wt-f6h.js +0 -1
  618. package/.next/static/chunks/126fj4pga_72k.js +0 -1
  619. package/.next/static/chunks/159udiexf6dom.js +0 -2
  620. package/.next/static/chunks/18a96rkhi4_yd.js +0 -1
  621. package/.next/static/chunks/18gi7o2ibrgh8.js +0 -3
  622. package/.next/static/chunks/1dkjg87pxk0n9.js +0 -28
  623. package/.next/static/chunks/1q9z1tohil7xn.js +0 -1
  624. package/.next/static/chunks/1sjgfets224re.js +0 -1
  625. package/.next/static/chunks/1wszsr83t01gh.js +0 -1
  626. package/.next/static/chunks/22_zkas21rpna.js +0 -1
  627. package/.next/static/chunks/2fh4-3z_q8b_w.js +0 -1
  628. package/.next/static/chunks/2h0sd2vumhb15.js +0 -1
  629. package/.next/static/chunks/2szintyibu0s5.js +0 -1
  630. package/.next/static/chunks/2t4vhi3m8ahya.js +0 -1
  631. package/.next/static/chunks/2tigdtz3dpcy8.js +0 -1
  632. package/.next/static/chunks/3-b6b7entqazs.js +0 -1
  633. package/.next/static/chunks/3786h5vayt80-.js +0 -28
  634. package/.next/static/chunks/3cq5g494yuke8.css +0 -1
  635. package/.next/static/chunks/3dmbhsv2u_8kj.js +0 -1
  636. package/.next/static/chunks/3hibbjnt85-k6.js +0 -1
  637. package/.next/static/chunks/3i0a5px-oufgc.js +0 -1
  638. package/.next/static/chunks/3wexk9qo8zuj3.js +0 -18
  639. package/.next/static/chunks/3xk7yqgomx7yq.js +0 -1
  640. package/.next/static/chunks/3xre8bu_pk1p_.js +0 -1
  641. package/.next/static/chunks/3zv6t3i77xhr_.js +0 -1
  642. package/database/migrations/20260911030320_opposite_jack_power/migration.sql +0 -1
  643. package/docs/images/task-thread.png +0 -0
  644. package/features/bot/components/task-badge.tsx +0 -42
  645. package/features/bot/components/task-reply.tsx +0 -328
  646. package/features/bot/components/task-setting.tsx +0 -524
  647. package/features/bot/task.query.ts +0 -1079
  648. package/features/bot/task.store.ts +0 -487
  649. package/lib/realtime/client-secret.ts +0 -86
  650. package/lib/realtime/open-session.ts +0 -31
  651. package/lib/realtime/openai.ts +0 -35
  652. package/lib/realtime/realtime.audio.ts +0 -196
  653. package/lib/realtime/realtime.driver.ts +0 -489
  654. package/lib/realtime/realtime.protocol.ts +0 -393
  655. package/lib/realtime/realtime.schema.ts +0 -37
  656. package/lib/realtime/realtime.session.ts +0 -106
  657. package/lib/realtime/realtime.transport.ts +0 -384
  658. package/lib/realtime/xai.ts +0 -39
  659. /package/.next/server/app/api/bot/{task → thread}/[id]/route/build-manifest.json +0 -0
  660. /package/.next/server/app/api/bot/{task → thread}/[id]/route/server-reference-manifest.json +0 -0
  661. /package/.next/server/app/api/bot/{task → thread}/[id]/route.js.map +0 -0
  662. /package/.next/server/app/api/bot/{task → thread}/route/build-manifest.json +0 -0
  663. /package/.next/server/app/api/bot/{task → thread}/route/server-reference-manifest.json +0 -0
  664. /package/.next/server/app/api/bot/{task → thread}/route.js.map +0 -0
  665. /package/.next/static/{G6-gNoyGDEcgAsq-L4PE1 → yyRVBc0xxYQsh3GpbQX9b}/_buildManifest.js +0 -0
  666. /package/.next/static/{G6-gNoyGDEcgAsq-L4PE1 → yyRVBc0xxYQsh3GpbQX9b}/_clientMiddlewareManifest.js +0 -0
  667. /package/.next/static/{G6-gNoyGDEcgAsq-L4PE1 → yyRVBc0xxYQsh3GpbQX9b}/_ssgManifest.js +0 -0
@@ -9,7 +9,7 @@ import {
9
9
  MicOff,
10
10
  Settings2,
11
11
  } from "lucide-react";
12
- import { type ReactNode, useEffect, useRef, useState } from "react";
12
+ import { type ReactNode, useEffect, useMemo, useRef, useState } from "react";
13
13
  import { queryKey } from "@/app/api/query-key";
14
14
  import { Button } from "@/components/ui/button";
15
15
  import { ButtonGroup } from "@/components/ui/button-group";
@@ -21,7 +21,8 @@ import {
21
21
  TooltipProvider,
22
22
  TooltipTrigger,
23
23
  } from "@/components/ui/tooltip";
24
- import { SPEACH_MODEL_PROVIDER_LIST } from "@/features/ai/model.schema";
24
+ import { CALL_IDLE } from "@/config";
25
+ import { LIVE_PROVIDER } from "@/features/ai/live.schema";
25
26
  import { type Bot, DEFAULT_BOT } from "@/features/bot/bot.schema";
26
27
  import { BotMark } from "@/features/bot/components/bot-mark";
27
28
  import { BotRoom } from "@/features/bot/components/bot-room";
@@ -42,15 +43,22 @@ import {
42
43
  type CallStatus,
43
44
  type CaptionView,
44
45
  FACE_DEFAULT,
46
+ type FaceWord,
45
47
  type ThursdayFace,
46
48
  } from "@/features/thursday/thursday.schema";
47
49
  import { useThursdayStore } from "@/features/thursday/thursday.store";
48
- import { type ToolRun, useThursday } from "@/features/thursday/use-thursday";
50
+ import {
51
+ type ActivityLine,
52
+ type CallEnd,
53
+ type Ringing,
54
+ useThursday,
55
+ } from "@/features/thursday/use-thursday";
49
56
  import { ArtifactView } from "@/features/workspace/components/artifact-view";
50
57
  import { useHotkeyLabel } from "@/hooks/use-hotkey";
51
58
  import { useServerRoute } from "@/lib/protocol/use-server-route";
52
59
  import { cn } from "@/lib/utils";
53
60
  import { Face } from "./face";
61
+ import { SideCaptions, turnsOf, useTurnFocus } from "./side-captions";
54
62
  import { TabState } from "./tab-state";
55
63
 
56
64
  /**
@@ -63,10 +71,24 @@ export type { CaptionView };
63
71
 
64
72
  export type CallScreenProps = {
65
73
  status: CallStatus;
74
+ /** A call just failed to open or dropped; the face says so for a few seconds. */
75
+ failed?: boolean;
66
76
  /** Oldest first; the last one is the turn being spoken. */
67
77
  messages: CallMessage[];
68
- tool: ToolRun | null;
78
+ tool: ActivityLine | null;
79
+ /** When the backend picked the turn up (ms); null when it is not working. */
80
+ thinkingSince?: number | null;
81
+ /** What the backend's latest reasoning summary says it is doing. */
82
+ thinkingTitle?: string | null;
83
+ /** Why the last call ended, when the user did not end it. */
84
+ ended?: CallEnd | null;
85
+ /** The word `emote` last put on the face. */
86
+ faceWord?: FaceWord | null;
69
87
  onTap: () => void;
88
+ /** A call-back ringing; the tap answers it. */
89
+ ringing?: Ringing | null;
90
+ /** Stops the ringing without answering. */
91
+ onDecline?: () => void;
70
92
  /** Wake phrase; null when the tap is the only entry point. */
71
93
  wakePhrase?: string | null;
72
94
  /** Hotkey in readable form (use-hotkey); null if none. */
@@ -75,8 +97,6 @@ export type CallScreenProps = {
75
97
  idleLeft?: number | null;
76
98
  /** When the line opened (ms). */
77
99
  since?: number | null;
78
- /** Mic muted (use-thursday micOff): a tool is running or a relay is being read. */
79
- micOff?: boolean;
80
100
  getSpectrum?: () => ArrayLike<number>;
81
101
  /** The user's own mic bands, for the listening meter. */
82
102
  getMicSpectrum?: () => ArrayLike<number>;
@@ -89,14 +109,20 @@ export type CallScreenProps = {
89
109
 
90
110
  export function CallScreen({
91
111
  status,
112
+ failed = false,
92
113
  messages,
93
114
  tool,
115
+ thinkingSince = null,
116
+ thinkingTitle = null,
117
+ ended = null,
118
+ faceWord = null,
94
119
  onTap,
120
+ ringing = null,
121
+ onDecline,
95
122
  wakePhrase = null,
96
123
  hotkeyLabel = null,
97
124
  idleLeft = null,
98
125
  since = null,
99
- micOff = false,
100
126
  getSpectrum,
101
127
  getMicSpectrum,
102
128
  captionView = "center",
@@ -106,14 +132,20 @@ export function CallScreen({
106
132
  // without a key the face opens the key prompt instead of a call
107
133
  const [asking, setAsking] = useState(false);
108
134
  const asleep = !callable && status === "idle";
109
- const busy = status === "connecting";
110
- const live = status !== "idle" && status !== "connecting";
135
+ const busy = status === "connecting" || status === "ending";
136
+ const live = status !== "idle" && !busy;
111
137
  // The caption box holds her words and nothing else, so the line she just
112
138
  // said stays put while she listens or works.
113
139
  const hers =
114
140
  messages.findLast((turn) => turn.role === "assistant")?.text ?? "";
115
141
  const sided = captionView === "sides" && status !== "idle";
116
- const turns = useTurnPager(messages);
142
+ const talk = useMemo(() => turnsOf(messages), [messages]);
143
+ const turns = useTurnFocus(talk, sided);
144
+ const lastRole = talk.at(-1)?.role;
145
+ // the last turn is still being said: her voice is on, or yours came after hers and she has not answered
146
+ const saying =
147
+ (lastRole === "assistant" && status === "speaking") ||
148
+ (lastRole === "user" && status === "listening");
117
149
  return (
118
150
  <div className="relative flex h-full flex-col">
119
151
  <div className="absolute top-5 right-5 z-10">
@@ -140,7 +172,9 @@ export function CallScreen({
140
172
  ? "Add a speech key"
141
173
  : live
142
174
  ? "End the call"
143
- : "Call Thursday"
175
+ : ringing
176
+ ? "Answer Thursday"
177
+ : "Call Thursday"
144
178
  }
145
179
  // the face never moves under the cursor; only press gives a little
146
180
  className={cn(
@@ -149,15 +183,25 @@ export function CallScreen({
149
183
  asleep && "opacity-35",
150
184
  )}
151
185
  >
152
- <Face
153
- look={face}
154
- status={status}
155
- getSpectrum={getSpectrum}
156
- className="w-full"
157
- />
186
+ {/* Ringing, the face swells twice and rests, like a phone's ring */}
187
+ <span
188
+ className={cn("block", ringing && "motion-safe:animate-ringing")}
189
+ >
190
+ <Face
191
+ look={face}
192
+ status={status}
193
+ failed={failed}
194
+ word={faceWord}
195
+ getSpectrum={getSpectrum}
196
+ getMicSpectrum={getMicSpectrum}
197
+ className="w-full"
198
+ />
199
+ </span>
158
200
  </button>
159
201
 
160
- {sided && <SideCaptions shown={turns.shown} give={turns.give} />}
202
+ {sided && (
203
+ <SideCaptions turns={talk} focus={turns.focus} live={saying} />
204
+ )}
161
205
  </div>
162
206
 
163
207
  {/* The column is wider than the text (40rem); the side margins hold the caption chevrons (Flow) */}
@@ -169,8 +213,10 @@ export function CallScreen({
169
213
  (use-thursday). */}
170
214
  <ActivityRow
171
215
  tool={tool}
172
- micOff={micOff}
173
- listening={status === "listening"}
216
+ thinkingSince={thinkingSince}
217
+ thinkingTitle={thinkingTitle}
218
+ listening={status === "listening" && thinkingSince === null}
219
+ ringing={ringing}
174
220
  getMicSpectrum={getMicSpectrum}
175
221
  />
176
222
 
@@ -178,8 +224,7 @@ export function CallScreen({
178
224
  `text-balance`: rebalancing changes the line count under the pager. */}
179
225
  <Flow
180
226
  text={sided || status === "idle" ? "" : hers}
181
- fixed
182
- paged
227
+ fadeIn
183
228
  className="w-full max-w-160 text-center text-base"
184
229
  />
185
230
 
@@ -205,6 +250,10 @@ export function CallScreen({
205
250
  status={status}
206
251
  idleLeft={idleLeft}
207
252
  since={since}
253
+ ended={ended}
254
+ behind={sided && turns.back > 0}
255
+ ringing={ringing !== null}
256
+ onDecline={onDecline}
208
257
  wakePhrase={wakePhrase}
209
258
  hotkeyLabel={hotkeyLabel}
210
259
  />
@@ -213,7 +262,7 @@ export function CallScreen({
213
262
  </div>
214
263
  </div>
215
264
 
216
- {/* Documents a finished task produced; opens itself on the artifact event */}
265
+ {/* Documents a finished thread produced; opens itself on the artifact event */}
217
266
  <ArtifactView />
218
267
 
219
268
  <BotRoom />
@@ -223,7 +272,7 @@ export function CallScreen({
223
272
 
224
273
  /**
225
274
  * The corner is one group: three rooms as single buttons, everything else
226
- * behind the gear. Tasks is left out because the other corner is that room.
275
+ * behind the gear. Threads is left out because the other corner is that room.
227
276
  */
228
277
  const CORNER = ["thursday", "memory", "bot"] as const;
229
278
 
@@ -312,46 +361,6 @@ function SettingsCorner() {
312
361
  /** Lines the caption box holds. */
313
362
  const CAPTION_LINES = 3;
314
363
 
315
- /**
316
- * Lines a side caption keeps, by age. The newest turn is the one being said, so
317
- * it gets room for a whole spoken sentence; what came before is context and
318
- * decays with the type size beside it.
319
- */
320
- const CAPTION_TURN_LINES = [5, 3, 2];
321
-
322
- /**
323
- * Type size by age. The step is a real one — 18 / 15 / 13 — because it is what
324
- * tells two turns apart once neither carries an edge.
325
- */
326
- const CAPTION_SIZES = ["text-lg", "text-[15px]", "text-[13px]"];
327
-
328
- /** Inner padding by age: a smaller turn sits on a smaller plate. */
329
- const CAPTION_PADS = ["px-4 py-3", "px-3.5 py-2.5", "px-3 py-2"];
330
-
331
- /**
332
- * The plate a turn sits on: fill, no edge. The call screen's ground is the
333
- * page's own — the ascii field is boot and intro only — so an outline covers
334
- * nothing and ends up being the whole design.
335
- *
336
- * Yours is the surface `Bubble` already gives your words in the task thread
337
- * (primary, light ink); hers is the page's grey. Age recedes in the fill, not
338
- * in the block's opacity, which would take the ink down with it.
339
- */
340
- const CAPTION_HERS = ["bg-muted", "bg-muted/70", "bg-muted/45"];
341
- const CAPTION_YOURS = ["bg-primary", "bg-primary/75", "bg-primary/60"];
342
-
343
- /** A step off a ramp above. The ramps run out at VISIBLE; the last step is the floor. */
344
- function atAge(ramp: readonly string[], age: number) {
345
- return ramp[age] ?? ramp[ramp.length - 1];
346
- }
347
-
348
- /** Ink by age. Hers recedes with her plate; yours stays legible on every step of its own. */
349
- const CAPTION_HERS_INK = [
350
- "text-foreground",
351
- "text-muted-foreground",
352
- "text-muted-foreground/60",
353
- ];
354
-
355
364
  /**
356
365
  * Line height as a number, not a class: box height and page offset divide by
357
366
  * it, and a class could be overridden by a later `text-*` utility.
@@ -364,20 +373,14 @@ const CAPTION_LEADING = 1.625;
364
373
  */
365
374
  function Flow({
366
375
  text,
367
- lead,
368
376
  lines = CAPTION_LINES,
369
- fixed = false,
370
- paged = false,
377
+ fadeIn = false,
371
378
  className,
372
379
  }: {
373
380
  text: string;
374
- /** Sits inline before the text. */
375
- lead?: ReactNode;
376
381
  lines?: number;
377
- /** Reserve the full height even with no text. */
378
- fixed?: boolean;
379
- /** Page overflow with chevrons; needs side margin. */
380
- paged?: boolean;
382
+ /** Each new character fades in as it arrives; what is already drawn stays put. */
383
+ fadeIn?: boolean;
381
384
  className?: string;
382
385
  }) {
383
386
  const box = useRef<HTMLParagraphElement>(null);
@@ -392,7 +395,7 @@ function Flow({
392
395
  // re-measure on width change and on every text change (streaming moves the last line)
393
396
  useEffect(() => {
394
397
  const node = box.current;
395
- if (!node || !paged) return;
398
+ if (!node) return;
396
399
  const measure = () => {
397
400
  const line = Number.parseFloat(getComputedStyle(node).lineHeight);
398
401
  if (!line) return;
@@ -402,7 +405,7 @@ function Flow({
402
405
  const watch = new ResizeObserver(measure);
403
406
  watch.observe(node);
404
407
  return () => watch.disconnect();
405
- }, [text, paged]);
408
+ }, [text]);
406
409
 
407
410
  const steps = Math.ceil(Math.max(0, rows - lines) / lines);
408
411
  const at = Math.max(0, rows - lines - Math.min(back, steps) * lines);
@@ -413,32 +416,24 @@ function Flow({
413
416
  ref={box}
414
417
  style={{
415
418
  lineHeight: CAPTION_LEADING,
416
- ...(paged
417
- ? { transform: `translateY(-${at * CAPTION_LEADING}em)` }
418
- : {}),
419
+ transform: `translateY(-${at * CAPTION_LEADING}em)`,
419
420
  }}
420
- className={cn("break-keep", paged && "transition-transform duration-200")}
421
+ className="break-keep transition-transform duration-200"
421
422
  >
422
- {lead}
423
- {text}
423
+ {fadeIn
424
+ ? // char + index: a character already drawn keeps its key and never fades twice
425
+ Array.from(text).map((char, index) => (
426
+ <span
427
+ key={`${char}${index}`}
428
+ className="animate-in fade-in duration-300 motion-reduce:animate-none"
429
+ >
430
+ {char}
431
+ </span>
432
+ ))
433
+ : text}
424
434
  </p>
425
435
  );
426
436
 
427
- // No room for chevrons here (side captions): overflow is clipped and the fading last line is the only cue
428
- if (!paged) {
429
- return (
430
- <div
431
- style={fixed ? { height } : { maxHeight: height }}
432
- className={cn(
433
- "overflow-hidden mask-[linear-gradient(to_bottom,black_calc(100%-0.7rem),transparent_100%)]",
434
- className,
435
- )}
436
- >
437
- {body}
438
- </div>
439
- );
440
- }
441
-
442
437
  return (
443
438
  <div className="relative flex w-full justify-center">
444
439
  <div
@@ -626,7 +621,7 @@ function Ear({
626
621
  * which has no container either, so an outline under one of the two read as the
627
622
  * line changing shape rather than changing state.
628
623
  */
629
- function Activity({ tool, micOff }: { tool: ToolRun; micOff: boolean }) {
624
+ function Activity({ tool }: { tool: ActivityLine }) {
630
625
  // a relay from a bot is a flag, like the answer tool — unless it names the bot
631
626
  const relay = tool.kind === "relay";
632
627
  const Icon = relay ? Flag : toolIcon(tool.name);
@@ -662,17 +657,7 @@ function Activity({ tool, micOff }: { tool: ToolRun; micOff: boolean }) {
662
657
  {tool.done ? (
663
658
  <span className={cn(look, "text-muted-foreground")}>{text}</span>
664
659
  ) : (
665
- <ShinyText text={text} speed={2.4} className={look} />
666
- )}
667
- {/* The mic is closed because this is running, so it travels with the line.
668
- A chip, where the line has none: on the bare page a second glyph beside
669
- the tool's own shares strokes with it and reads as a smudge, and the
670
- plate is what says this is a state rather than more of the sentence. */}
671
- {micOff && (
672
- <span className="flex shrink-0 items-center gap-1 rounded-full bg-muted/60 py-0.5 pr-2 pl-1.5 font-mono text-[11px] leading-4 text-muted-foreground">
673
- <MicOff className="size-3 shrink-0" />
674
- mic off
675
- </span>
660
+ <ShinyText text={text} motion="pulse" className={look} />
676
661
  )}
677
662
  </span>
678
663
  );
@@ -691,7 +676,7 @@ function Mark({
691
676
  icon: Icon,
692
677
  running,
693
678
  }: {
694
- tool: ToolRun;
679
+ tool: ActivityLine;
695
680
  /** The row the name resolved to, when the roster has one; its look, not its identity. */
696
681
  bot: Bot | null;
697
682
  icon: LucideIcon;
@@ -703,10 +688,10 @@ function Mark({
703
688
  <BotMark
704
689
  size={16}
705
690
  seed={tool.bot}
706
- vary={tool.bot}
707
691
  color={bot?.icon?.color}
708
692
  shape={bot?.icon?.shape}
709
693
  outline={bot?.icon?.outline}
694
+ paint={bot?.icon?.paint}
710
695
  notify={false}
711
696
  />
712
697
  </span>
@@ -724,19 +709,25 @@ function Mark({
724
709
  }
725
710
 
726
711
  /**
727
- * The activity slot: 28px, one fact at a time, two faces. Both stay mounted and
712
+ * The activity slot: 28px, one fact at a time, three faces. All stay mounted and
728
713
  * cross-fade, so the line leaves wearing its last words instead of blinking
729
- * out, and the caption below never moves while they trade places.
714
+ * out, and the caption below never moves while they trade places. A tool wins
715
+ * over thinking: it is the same stretch of work, said more exactly.
730
716
  */
731
717
  function ActivityRow({
732
718
  tool,
733
- micOff,
719
+ thinkingSince,
720
+ thinkingTitle,
734
721
  listening,
722
+ ringing,
735
723
  getMicSpectrum,
736
724
  }: {
737
- tool: ToolRun | null;
738
- micOff: boolean;
725
+ tool: ActivityLine | null;
726
+ thinkingSince: number | null;
727
+ thinkingTitle: string | null;
739
728
  listening: boolean;
729
+ /** Only while idle, when nothing else takes the slot. */
730
+ ringing: Ringing | null;
740
731
  getMicSpectrum?: () => ArrayLike<number>;
741
732
  }) {
742
733
  // held past the tool so the pill has something to fade out with
@@ -744,13 +735,45 @@ function ActivityRow({
744
735
  useEffect(() => {
745
736
  if (tool) setShown(tool);
746
737
  }, [tool]);
738
+ // and past the thinking, for the same reason
739
+ const [since, setSince] = useState(thinkingSince);
740
+ const [title, setTitle] = useState(thinkingTitle);
741
+ useEffect(() => {
742
+ if (thinkingSince === null) return;
743
+ setSince(thinkingSince);
744
+ setTitle(thinkingTitle);
745
+ }, [thinkingSince, thinkingTitle]);
747
746
 
748
747
  const hearing = listening && !tool;
749
748
  return (
750
- <div className="grid h-7 max-w-full items-center justify-items-center">
749
+ // Ringing, who calls sits right under the face, over the hint. It leaves at
750
+ // once rather than fading, so the hint does not slide under a fading line
751
+ <div
752
+ className={cn(
753
+ "grid h-7 max-w-full items-center justify-items-center",
754
+ ringing && "order-first",
755
+ )}
756
+ >
757
+ {ringing && (
758
+ <span className="col-start-1 row-start-1 flex max-w-full animate-in fade-in duration-500">
759
+ <Ringer ringing={ringing} />
760
+ </span>
761
+ )}
751
762
  {shown && (
752
763
  <Fade at="col-start-1 row-start-1 max-w-full" shown={tool !== null}>
753
- <Activity tool={shown} micOff={micOff} />
764
+ <Activity tool={shown} />
765
+ </Fade>
766
+ )}
767
+ {since !== null && (
768
+ <Fade
769
+ at="col-start-1 row-start-1"
770
+ shown={thinkingSince !== null && !tool}
771
+ >
772
+ <Thinking
773
+ since={since}
774
+ title={title}
775
+ running={thinkingSince !== null && !tool}
776
+ />
754
777
  </Fade>
755
778
  )}
756
779
  <Fade at="col-start-1 row-start-1" shown={hearing}>
@@ -760,6 +783,45 @@ function ActivityRow({
760
783
  );
761
784
  }
762
785
 
786
+ /**
787
+ * Who a ringing call-back is for: the bot's face and what it has, then the job,
788
+ * and how many more rang with it. The words pulse while it rings.
789
+ */
790
+ function Ringer({ ringing }: { ringing: Ringing }) {
791
+ const bots = useServerRoute<Bot[]>(queryKey.bot).data;
792
+ const bot = bots?.find((one) => one.name === ringing.bot) ?? null;
793
+ const says =
794
+ ringing.kind === "question"
795
+ ? `${ringing.bot} has a question`
796
+ : ringing.kind === "done"
797
+ ? `Answer from ${ringing.bot}`
798
+ : `${ringing.bot} stopped`;
799
+ return (
800
+ <span className="flex max-w-full items-center gap-1.5 text-[13px] leading-5 text-muted-foreground">
801
+ <span className="grid size-4.5 shrink-0 place-items-center">
802
+ <BotMark
803
+ size={16}
804
+ seed={ringing.bot}
805
+ color={bot?.icon?.color}
806
+ shape={bot?.icon?.shape}
807
+ outline={bot?.icon?.outline}
808
+ paint={bot?.icon?.paint}
809
+ // a question or a stop waits on the user; an answer only waits to be read
810
+ notify={ringing.kind !== "done"}
811
+ />
812
+ </span>
813
+ <ShinyText text={says} motion="pulse" className="shrink-0" />
814
+ <span className="text-muted-foreground/40">·</span>
815
+ <span className="min-w-0 truncate break-keep">{ringing.label}</span>
816
+ {ringing.more > 0 && (
817
+ <span className="shrink-0 rounded-full bg-muted px-1.5 font-mono text-[10px] leading-4.5 text-foreground/70">
818
+ +{ringing.more}
819
+ </span>
820
+ )}
821
+ </span>
822
+ );
823
+ }
824
+
763
825
  /** One face of a stacked slot. Hidden means invisible, untouchable and unread. */
764
826
  function Fade({
765
827
  at,
@@ -784,25 +846,43 @@ function Fade({
784
846
  );
785
847
  }
786
848
 
849
+ /** Why a call ended, as the hint says it. */
850
+ const ENDED: Record<CallEnd, string> = {
851
+ quiet: `${CALL_IDLE.hangUpMs / 1000}s of quiet`,
852
+ hungUp: "Thursday hung up",
853
+ closed: "Live closed the call",
854
+ dropped: "the connection dropped",
855
+ };
856
+
787
857
  /**
788
858
  * The only hint line on screen. Says one thing at a time; outside a call it
789
- * shows one way in (wake phrase or hotkey), inside a call how to end and the
790
- * elapsed time. Keyed so changes fade.
859
+ * shows why the last one ended when the user did not end it, else one way in
860
+ * (wake phrase or hotkey); inside a call how to end and the elapsed time. Keyed
861
+ * so changes fade.
791
862
  */
792
863
  function Hint({
793
864
  status,
794
865
  idleLeft,
795
866
  since,
867
+ ended,
868
+ behind,
869
+ ringing,
870
+ onDecline,
796
871
  wakePhrase,
797
872
  hotkeyLabel,
798
873
  }: {
799
874
  status: CallStatus;
800
875
  idleLeft: number | null;
801
876
  since: number | null;
877
+ ended: CallEnd | null;
878
+ /** The side captions are on an earlier turn. */
879
+ behind: boolean;
880
+ ringing: boolean;
881
+ onDecline?: () => void;
802
882
  wakePhrase: string | null;
803
883
  hotkeyLabel: string | null;
804
884
  }) {
805
- const busy = status === "connecting";
885
+ const busy = status === "connecting" || status === "ending";
806
886
  const live = status !== "idle" && !busy;
807
887
 
808
888
  let body: React.ReactNode;
@@ -813,6 +893,7 @@ function Hint({
813
893
  body = (
814
894
  <ShinyText
815
895
  text={status === "connecting" ? "Connecting…" : "Ending…"}
896
+ motion="pulse"
816
897
  className="text-muted-foreground/70"
817
898
  />
818
899
  );
@@ -820,12 +901,50 @@ function Hint({
820
901
  key = "quiet";
821
902
  body = `Quiet — ending in ${idleLeft}s. Say anything to stay.`;
822
903
  } else if (live) {
823
- key = "live";
904
+ key = behind ? "behind" : "live";
824
905
  body = (
825
906
  <>
826
907
  <span>Tap Thursday to end</span>
827
908
  <span className="text-muted-foreground/40">·</span>
828
- <Elapsed since={since} />
909
+ {behind ? (
910
+ <>
911
+ <kbd className="rounded-md border border-border bg-background px-1.5 py-0.5 font-mono text-[10px] text-foreground/80 shadow-[0_1px_0_var(--border)]">
912
+ ↓
913
+ </kbd>
914
+ <span>back to now</span>
915
+ </>
916
+ ) : (
917
+ <Elapsed since={since} />
918
+ )}
919
+ </>
920
+ );
921
+ } else if (ringing) {
922
+ key = "ringing";
923
+ // Every way in answers, so the tap stands for them all; declining is the one other act
924
+ body = (
925
+ <>
926
+ <span>Tap Thursday to answer</span>
927
+ <span className="text-muted-foreground/40">·</span>
928
+ <button
929
+ type="button"
930
+ onClick={onDecline}
931
+ className="flex items-center gap-1.5 rounded-full bg-muted py-0.5 pr-0.5 pl-2 text-foreground/80 transition-colors hover:bg-accent"
932
+ >
933
+ Not now
934
+ <kbd className="rounded-md border border-border bg-background px-1.5 font-mono text-[10px] text-foreground/70 shadow-[0_1px_0_var(--border)]">
935
+ Esc
936
+ </kbd>
937
+ </button>
938
+ </>
939
+ );
940
+ } else if (ended) {
941
+ key = `ended:${ended}`;
942
+ // Where the quiet warning stood, until the next call
943
+ body = (
944
+ <>
945
+ <span>Ended — {ENDED[ended]}</span>
946
+ <span className="text-muted-foreground/40">·</span>
947
+ <span>Tap to call again</span>
829
948
  </>
830
949
  );
831
950
  } else if (wakePhrase) {
@@ -910,7 +1029,6 @@ function NeedsKey({
910
1029
  <BotMark
911
1030
  size={24}
912
1031
  seed={DEFAULT_BOT.name}
913
- vary={DEFAULT_BOT.name}
914
1032
  color={DEFAULT_BOT.icon?.color}
915
1033
  shape={DEFAULT_BOT.icon?.shape}
916
1034
  outline={DEFAULT_BOT.icon?.outline}
@@ -928,6 +1046,63 @@ function NeedsKey({
928
1046
  );
929
1047
  }
930
1048
 
1049
+ /**
1050
+ * The backend has the turn: the activity line says so and counts, so the
1051
+ * seconds before a tool and before her voice never read as a call that has
1052
+ * stopped. Once a reasoning summary names what the work is, the line says that
1053
+ * instead. It shines where the rest of this screen pulses (the user's pick). The
1054
+ * seconds arrive after the first one, which is most answers.
1055
+ */
1056
+ function Thinking({
1057
+ since,
1058
+ title,
1059
+ running,
1060
+ }: {
1061
+ since: number;
1062
+ title: string | null;
1063
+ running: boolean;
1064
+ }) {
1065
+ const [now, setNow] = useState(() => Date.now());
1066
+ // Stays mounted to fade out; the count stops with it rather than ticking unseen
1067
+ useEffect(() => {
1068
+ if (!running) return;
1069
+ // back from a tool, the first reading is now rather than where it paused
1070
+ const first = setTimeout(() => setNow(Date.now()), 0);
1071
+ const tick = setInterval(() => setNow(Date.now()), 1000);
1072
+ return () => {
1073
+ clearTimeout(first);
1074
+ clearInterval(tick);
1075
+ };
1076
+ }, [running]);
1077
+ const seconds = Math.max(0, Math.floor((now - since) / 1000));
1078
+ const words = title ? `Thinking about ${midSentence(title)}` : "Thinking…";
1079
+ return (
1080
+ <span className="flex max-w-full items-center gap-1.5 text-[13px] leading-5">
1081
+ {/* keyed so a new title fades in rather than replacing the words mid-sweep */}
1082
+ <ShinyText
1083
+ key={words}
1084
+ text={words}
1085
+ className="min-w-0 animate-in truncate fade-in duration-300"
1086
+ />
1087
+ {seconds >= 1 && (
1088
+ <>
1089
+ <span className="shrink-0 text-muted-foreground/40">·</span>
1090
+ <span className="shrink-0 text-muted-foreground tabular-nums">
1091
+ {seconds}s
1092
+ </span>
1093
+ </>
1094
+ )}
1095
+ </span>
1096
+ );
1097
+ }
1098
+
1099
+ /** A title read mid-sentence: "Checking thread status" becomes "checking thread status"; "API limits" stays. */
1100
+ function midSentence(title: string) {
1101
+ return /^\p{Lu}\p{Lu}/u.test(title)
1102
+ ? title
1103
+ : title.charAt(0).toLowerCase() + title.slice(1);
1104
+ }
1105
+
931
1106
  /** Time since the line opened, mm:ss; only this span re-renders each second. */
932
1107
  function Elapsed({ since }: { since: number | null }) {
933
1108
  const [now, setNow] = useState(() => Date.now());
@@ -947,160 +1122,21 @@ function Elapsed({ since }: { since: number | null }) {
947
1122
  );
948
1123
  }
949
1124
 
950
- /** Turns shown at once. */
951
- const VISIBLE = 3;
952
-
953
- /**
954
- * Wheel delta per turn. Deliberately heavy — a turn is a paragraph, and a
955
- * trackpad flick that pages three of them reads as losing the conversation
956
- * rather than moving through it. The give is what says it is not stuck.
957
- */
958
- const STEP = 360;
959
-
960
- /** How far the plates lean while the wheel is short of a turn, px. */
961
- const GIVE = 10;
962
-
963
- /** How long the wheel keeps what it has gathered. Past this the lean springs back and the count starts over, so half a turn never waits around to be completed by the next flick. */
964
- const SETTLE_MS = 220;
965
-
966
- /**
967
- * Where the wheel has wound the turns back to. Lives on the screen rather than
968
- * in the plates: the three plates are small and sit off to the sides, so a
969
- * wheel over the face — where the cursor is — used to reach nothing at all.
970
- */
971
- function useTurnPager(messages: CallMessage[]) {
972
- const [back, setBack] = useState(0);
973
- const [give, setGive] = useState(0);
974
- const drag = useRef(0);
975
- const settle = useRef<ReturnType<typeof setTimeout> | null>(null);
976
- const seen = useRef(messages.length);
977
-
978
- const furthest = Math.max(0, messages.length - VISIBLE);
979
-
980
- // stay on the turn being read when new turns arrive
981
- useEffect(() => {
982
- const grew = messages.length - seen.current;
983
- seen.current = messages.length;
984
- if (grew > 0) {
985
- setBack((was) => (was > 0 ? Math.min(was + grew, furthest) : 0));
986
- }
987
- }, [messages.length, furthest]);
988
-
989
- useEffect(
990
- () => () => {
991
- if (settle.current) clearTimeout(settle.current);
992
- },
993
- [],
994
- );
995
-
996
- const onWheel = (event: React.WheelEvent) => {
997
- // nothing behind the three on screen: no travel, and no lean pretending there is
998
- if (furthest === 0) return;
999
- drag.current += event.deltaY;
1000
- const steps = Math.trunc(drag.current / STEP);
1001
- if (steps !== 0) {
1002
- drag.current -= steps * STEP;
1003
- setBack((was) => Math.min(furthest, Math.max(0, was - steps)));
1004
- }
1005
- setGive(drag.current / STEP);
1006
-
1007
- // spring back so the give reads as resistance, not position
1008
- if (settle.current) clearTimeout(settle.current);
1009
- settle.current = setTimeout(() => {
1010
- drag.current = 0;
1011
- setGive(0);
1012
- }, SETTLE_MS);
1013
- };
1014
-
1015
- const end = messages.length - back;
1016
- return {
1017
- shown: messages.slice(Math.max(0, end - VISIBLE), end),
1018
- give,
1019
- onWheel,
1020
- };
1021
- }
1022
-
1023
- /**
1024
- * Recent turns beside the face: yours on the right, hers on the left, each on
1025
- * a fill with no edge. Anchored outside the face box (`right-full` /
1026
- * `left-full`) so they never cover it. Older turns sit higher, smaller, and
1027
- * their plate recedes.
1028
- */
1029
- function SideCaptions({
1030
- shown,
1031
- give,
1032
- }: {
1033
- shown: CallMessage[];
1034
- /** How far into the next turn the wheel is, -1..1; the plates lean by it. */
1035
- give: number;
1036
- }) {
1037
- return (
1038
- <>
1039
- {shown.map((message, index) => {
1040
- // the speaker fixes the side, so a turn never switches sides as older ones stack up
1041
- const mine = message.role === "user";
1042
- // 18..68 rather than 20..75: a five-line newest turn has to clear the activity line
1043
- const top = 18 + (index * 50) / Math.max(1, shown.length - 1);
1044
- const age = shown.length - 1 - index;
1045
- const size = atAge(CAPTION_SIZES, age);
1046
- const pad = atAge(CAPTION_PADS, age);
1047
- const fill = atAge(mine ? CAPTION_YOURS : CAPTION_HERS, age);
1048
- const ink = atAge(CAPTION_HERS_INK, age);
1049
-
1050
- return (
1051
- <div
1052
- key={message.id}
1053
- style={{
1054
- top: `${top}%`,
1055
- transform: `translateY(calc(-50% + ${-give * GIVE}px))`,
1056
- }}
1057
- className={cn(
1058
- // fixed-width slot, natural-width plate: short lines stay short and hug the face
1059
- "pointer-events-auto absolute flex w-[min(23rem,24vw)] transition-transform duration-100",
1060
- // yours on the right, the side the settings corner already takes
1061
- mine ? "left-full ml-6" : "right-full mr-6 justify-end",
1062
- )}
1063
- >
1064
- {/* Not a `Bubble`: that sets its surface on the content from the
1065
- parent, at a specificity a caption cannot override. One shape,
1066
- two fills — and no edge on either, so the plate is the whole
1067
- surface. The squared corner is the one nearest the face. */}
1068
- <div
1069
- className={cn(
1070
- "w-fit max-w-full animate-in rounded-[18px] fade-in duration-500",
1071
- pad,
1072
- mine
1073
- ? cn(
1074
- "rounded-bl-md text-primary-foreground slide-in-from-left-2",
1075
- fill,
1076
- )
1077
- : cn("rounded-br-md slide-in-from-right-2", fill, ink),
1078
- )}
1079
- >
1080
- {/* One truncation idiom on both sides: speech that runs past the
1081
- frame fades out. An ellipsis mid-sentence reads as an error. */}
1082
- <Flow
1083
- text={message.text}
1084
- lines={CAPTION_TURN_LINES[age] ?? 2}
1085
- className={size}
1086
- />
1087
- </div>
1088
- </div>
1089
- );
1090
- })}
1091
- </>
1092
- );
1093
- }
1094
-
1095
1125
  export function Thursday() {
1096
1126
  const {
1097
1127
  status,
1128
+ failed,
1098
1129
  messages,
1099
1130
  tool,
1131
+ thinkingSince,
1132
+ thinkingTitle,
1133
+ ended,
1134
+ faceWord,
1100
1135
  idleLeft,
1101
1136
  since,
1102
- micOff,
1103
1137
  call,
1138
+ ringing,
1139
+ decline,
1104
1140
  getSpectrum,
1105
1141
  getMicSpectrum,
1106
1142
  wakePhrase,
@@ -1117,24 +1153,29 @@ export function Thursday() {
1117
1153
  * can change during a call (NeedsKey). Unknown counts as not callable.
1118
1154
  */
1119
1155
  const { data: config } = useServerRoute<ConfigStatus[]>(queryKey.config);
1120
- const callable = SPEACH_MODEL_PROVIDER_LIST.some((provider) =>
1121
- isConfigSet(config, provider.apiKeyName),
1122
- );
1156
+ const callable = isConfigSet(config, LIVE_PROVIDER.apiKeyName);
1123
1157
 
1124
1158
  return (
1125
1159
  <>
1126
1160
  {/* the tab shows the call and what is owed while the app is off-screen */}
1127
- <TabState live={status !== "idle"} />
1161
+ <TabState live={status !== "idle"} ringing={ringing !== null} />
1128
1162
  <CallScreen
1129
1163
  status={status}
1164
+ failed={failed}
1130
1165
  messages={messages}
1131
1166
  tool={tool}
1167
+ thinkingSince={thinkingSince}
1168
+ thinkingTitle={thinkingTitle}
1169
+ ended={ended}
1170
+ faceWord={faceWord}
1132
1171
  onTap={call}
1172
+ // without a key the face asks for one, so nothing can answer
1173
+ ringing={callable ? ringing : null}
1174
+ onDecline={decline}
1133
1175
  wakePhrase={wakePhrase}
1134
1176
  hotkeyLabel={hotkeyLabel}
1135
1177
  idleLeft={idleLeft}
1136
1178
  since={since}
1137
- micOff={micOff}
1138
1179
  getSpectrum={getSpectrum}
1139
1180
  getMicSpectrum={getMicSpectrum}
1140
1181
  face={face}