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
@@ -0,0 +1,971 @@
1
+ import type { AssistantContent, ModelMessage, ToolContent } from "ai";
2
+ import { and, desc, eq, inArray, lt, max, sql } from "drizzle-orm";
3
+ import { appEvents } from "@/app/api/events/app-event.server";
4
+ import { INBOX_FINISHED, PAGE_SIZE, THREAD_STATUS_LIMIT } from "@/config";
5
+ import { database } from "@/database/db";
6
+ import {
7
+ threadDeliveryTable,
8
+ threadMessageTable,
9
+ threadRelayTable,
10
+ threadTable,
11
+ threadWorkTable,
12
+ } from "@/database/tables";
13
+ import { TOOL_NAMES } from "@/features/ai/tools/tool-name";
14
+ import { clip } from "@/lib/utils";
15
+ import {
16
+ type ResultPart,
17
+ type Thread,
18
+ type ThreadLine,
19
+ type ThreadPending,
20
+ type ThreadStatus,
21
+ type TokenUsage,
22
+ untagSpeaker,
23
+ } from "./bot.schema";
24
+ import { RESUME_CHECK, ROOM_THURSDAY } from "./room.schema";
25
+
26
+ // Threads and their messages. Bots themselves (roster, pinned tools) are bot.query.
27
+
28
+ /** Every write in this file ends with this; the screen re-reads the list on it. */
29
+ const changed = () => appEvents.emit({ type: "threads" });
30
+
31
+ /** Thread state without its messages. `lines` is filled by `withLines` from thread_message; `pending` folds into `ask`. */
32
+ const threadView = {
33
+ id: threadTable.id,
34
+ bot: threadTable.bot,
35
+ label: threadTable.label,
36
+ request: threadTable.request,
37
+ status: threadTable.status,
38
+ outcome: threadTable.outcome,
39
+ pending: threadTable.pending,
40
+ seen: threadTable.seen,
41
+ inputTokens: threadTable.inputTokens,
42
+ outputTokens: threadTable.outputTokens,
43
+ contextTokens: threadTable.contextTokens,
44
+ contextBudget: threadTable.contextBudget,
45
+ createdAt: threadTable.createdAt,
46
+ updatedAt: threadTable.updatedAt,
47
+ };
48
+
49
+ type ThreadRow = {
50
+ id: string;
51
+ bot: string;
52
+ label: string;
53
+ request: string;
54
+ status: ThreadStatus;
55
+ outcome: string | null;
56
+ pending: ThreadPending | null;
57
+ seen: boolean;
58
+ inputTokens: number;
59
+ outputTokens: number;
60
+ contextTokens: number;
61
+ contextBudget: number;
62
+ createdAt: Date;
63
+ updatedAt: Date;
64
+ };
65
+
66
+ /**
67
+ * One row, the thread's first message and the coordinator's first exchange, in one
68
+ * transaction: a thread is a room from its first row. `request` is what the screen
69
+ * shows; `opening` is what the model reads (who handed it over, and the job). The
70
+ * screen skips seq 0 and shows `request` instead (linesOf).
71
+ */
72
+ export async function insertThread(input: {
73
+ bot: string;
74
+ label: string;
75
+ request: string;
76
+ callId?: string | null;
77
+ opening: Extract<ModelMessage, { role: "user" }>["content"];
78
+ }) {
79
+ const { opening, ...row } = input;
80
+ const thread = await database.transaction(async (tx) => {
81
+ const [created] = await tx
82
+ .insert(threadTable)
83
+ .values({ id: crypto.randomUUID(), status: "running", ...row })
84
+ .returning();
85
+ await tx.insert(threadMessageTable).values({
86
+ threadId: created.id,
87
+ seq: 0,
88
+ bot: null,
89
+ parent: null,
90
+ role: "user",
91
+ content: opening,
92
+ compact: false,
93
+ note: false,
94
+ });
95
+ await tx.insert(threadWorkTable).values({
96
+ id: crypto.randomUUID(),
97
+ threadId: created.id,
98
+ bot: created.bot,
99
+ caller: ROOM_THURSDAY,
100
+ state: "queued",
101
+ });
102
+ return created;
103
+ });
104
+ changed();
105
+ return thread;
106
+ }
107
+
108
+ export async function updateThread(
109
+ id: string,
110
+ patch: Partial<{
111
+ status: ThreadStatus;
112
+ outcome: string | null;
113
+ pending: ThreadPending | null;
114
+ seen: boolean;
115
+ endedAt: Date | null;
116
+ }>,
117
+ ) {
118
+ await database
119
+ .update(threadTable)
120
+ .set({ ...patch, updatedAt: new Date() })
121
+ .where(eq(threadTable.id, id));
122
+ changed();
123
+ }
124
+
125
+ /**
126
+ * Bypasses `updateThread` on purpose: being seen is not movement and must not
127
+ * reorder the inbox by updatedAt. What was read on screen, or told enough on a
128
+ * call, has nothing left to relay, so its relays are settled with it.
129
+ */
130
+ export async function markSeen(ids: string[]) {
131
+ if (ids.length === 0) return;
132
+ await database.transaction(async (tx) => {
133
+ await tx
134
+ .update(threadTable)
135
+ .set({ seen: true })
136
+ .where(inArray(threadTable.id, ids));
137
+ await tx
138
+ .update(threadRelayTable)
139
+ .set({ accepted: true })
140
+ .where(inArray(threadRelayTable.threadId, ids));
141
+ });
142
+ changed();
143
+ }
144
+
145
+ /**
146
+ * Adds one step's usage (added, not set: participants' steps land on the same
147
+ * thread in parallel) without touching updatedAt. `context` is overwritten: it is
148
+ * the current window fill and threshold, not a sum. null leaves it as is.
149
+ */
150
+ export async function addThreadUsage(
151
+ id: string,
152
+ usage: TokenUsage,
153
+ context: { tokens: number; budget: number } | null = null,
154
+ ) {
155
+ if (!usage.input && !usage.output && context === null) return;
156
+ await database
157
+ .update(threadTable)
158
+ .set({
159
+ inputTokens: sql`${threadTable.inputTokens} + ${usage.input}`,
160
+ outputTokens: sql`${threadTable.outputTokens} + ${usage.output}`,
161
+ ...(context === null
162
+ ? {}
163
+ : { contextTokens: context.tokens, contextBudget: context.budget }),
164
+ })
165
+ .where(eq(threadTable.id, id));
166
+ changed();
167
+ }
168
+
169
+ /** One thread as the screen sees it, messages included. null if missing. */
170
+ export async function findThreadView(id: string): Promise<Thread | null> {
171
+ const [row] = await database
172
+ .select(threadView)
173
+ .from(threadTable)
174
+ .where(eq(threadTable.id, id));
175
+ if (!row) return null;
176
+ const [thread] = await withLines([row]);
177
+ return thread ?? null;
178
+ }
179
+
180
+ export async function findThread(id: string) {
181
+ const [thread] = await database
182
+ .select()
183
+ .from(threadTable)
184
+ .where(eq(threadTable.id, id));
185
+ return thread ?? null;
186
+ }
187
+
188
+ /** Keep open work, unread endings and unrelayed messages alongside recent read endings. */
189
+ export async function listInboxThreads(): Promise<Thread[]> {
190
+ const [open, finished, unread, unrelayed] = await Promise.all([
191
+ database
192
+ .select(threadView)
193
+ .from(threadTable)
194
+ .where(inArray(threadTable.status, ["running", "waiting"]))
195
+ .orderBy(desc(threadTable.updatedAt)),
196
+ database
197
+ .select(threadView)
198
+ .from(threadTable)
199
+ .where(inArray(threadTable.status, ["done", "cancelled"]))
200
+ .orderBy(desc(threadTable.updatedAt))
201
+ .limit(INBOX_FINISHED),
202
+ database
203
+ .select(threadView)
204
+ .from(threadTable)
205
+ .where(
206
+ and(
207
+ inArray(threadTable.status, ["done", "cancelled"]),
208
+ eq(threadTable.seen, false),
209
+ ),
210
+ ),
211
+ database
212
+ .select(threadView)
213
+ .from(threadTable)
214
+ .where(
215
+ inArray(
216
+ threadTable.id,
217
+ database
218
+ .select({ id: threadRelayTable.threadId })
219
+ .from(threadRelayTable)
220
+ .where(eq(threadRelayTable.accepted, false)),
221
+ ),
222
+ ),
223
+ ]);
224
+ const rows = [
225
+ ...new Map(
226
+ [...open, ...finished, ...unread, ...unrelayed].map((row) => [
227
+ row.id,
228
+ row,
229
+ ]),
230
+ ).values(),
231
+ ].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
232
+ return withLines(rows);
233
+ }
234
+
235
+ /** The call's bounded overview includes older open work before recent endings. */
236
+ export async function listThreadOverview(): Promise<Thread[]> {
237
+ const rows = await database
238
+ .select(threadView)
239
+ .from(threadTable)
240
+ .orderBy(
241
+ sql`case when ${threadTable.status} in ('running', 'waiting') then 0 else 1 end`,
242
+ desc(threadTable.updatedAt),
243
+ desc(threadTable.id),
244
+ )
245
+ .limit(THREAD_STATUS_LIMIT);
246
+ return withLines(rows);
247
+ }
248
+
249
+ /** All threads, newest first, one page at a time. `before` is a cursor (last page's updatedAt), not an offset, because rows move in between. */
250
+ export async function listThreadHistory(
251
+ options: { before?: Date | null; limit?: number } = {},
252
+ ): Promise<Thread[]> {
253
+ const rows = await database
254
+ .select(threadView)
255
+ .from(threadTable)
256
+ .where(
257
+ options.before ? lt(threadTable.updatedAt, options.before) : undefined,
258
+ )
259
+ .orderBy(desc(threadTable.updatedAt))
260
+ .limit(options.limit ?? PAGE_SIZE);
261
+ return withLines(rows);
262
+ }
263
+
264
+ /**
265
+ * What each of these calls handed over, for the transcript the next call reads
266
+ * (ai/prompts/thursday.prompt). Not the inbox: a job belongs to the call it was
267
+ * opened from, and this is how it is still there once that call is over.
268
+ */
269
+ export async function listCallJobs(callIds: string[]) {
270
+ if (!callIds.length) return [];
271
+ return await database
272
+ .select({
273
+ id: threadTable.id,
274
+ callId: threadTable.callId,
275
+ label: threadTable.label,
276
+ status: threadTable.status,
277
+ outcome: threadTable.outcome,
278
+ })
279
+ .from(threadTable)
280
+ .where(inArray(threadTable.callId, callIds))
281
+ .orderBy(threadTable.createdAt);
282
+ }
283
+
284
+ export type CallJob = Awaited<ReturnType<typeof listCallJobs>>[number];
285
+
286
+ /** Its messages go with it (cascade). The runner stops a live one first. */
287
+ export async function deleteThread(id: string) {
288
+ const removed = await database
289
+ .delete(threadTable)
290
+ .where(eq(threadTable.id, id))
291
+ .returning({ id: threadTable.id });
292
+ if (removed.length) changed();
293
+ return removed.length > 0;
294
+ }
295
+
296
+ /** Every job, newest first. The caller stops each one before deleting it (bot.runner removeThread). */
297
+ export async function listAllThreadIds(): Promise<string[]> {
298
+ const rows = await database
299
+ .select({ id: threadTable.id })
300
+ .from(threadTable)
301
+ .orderBy(desc(threadTable.createdAt));
302
+ return rows.map((row) => row.id);
303
+ }
304
+
305
+ /** Rows a previous server left as running; the runner decides which still are. */
306
+ export async function listRunningThreadIds() {
307
+ const rows = await database
308
+ .select({ id: threadTable.id })
309
+ .from(threadTable)
310
+ .where(eq(threadTable.status, "running"));
311
+ return rows.map((row) => row.id);
312
+ }
313
+
314
+ /** Every job with what names its folder and whether it still keeps it (bot.runner sweepJobFiles). */
315
+ export async function listThreadFolders() {
316
+ return await database
317
+ .select({
318
+ id: threadTable.id,
319
+ label: threadTable.label,
320
+ status: threadTable.status,
321
+ endedAt: threadTable.endedAt,
322
+ updatedAt: threadTable.updatedAt,
323
+ })
324
+ .from(threadTable);
325
+ }
326
+
327
+ /** Jobs paused for browser absence, which pick themselves back up. */
328
+ export async function listAutoStoppedThreads(): Promise<string[]> {
329
+ const rows = await database
330
+ .select({ id: threadTable.id, pending: threadTable.pending })
331
+ .from(threadTable)
332
+ .where(eq(threadTable.status, "waiting"));
333
+ return rows.flatMap((row) => (row.pending?.auto ? [row.id] : []));
334
+ }
335
+
336
+ /**
337
+ * Paths a list of message contents gave `write_file`, oldest first and each once; a
338
+ * path written again moves to the end. A refused write is here too, so whoever shows
339
+ * them checks the disk (workspace.ts filesOnDisk).
340
+ */
341
+ export function writtenPathsIn(contents: unknown[]): string[] {
342
+ const paths = new Set<string>();
343
+ for (const content of contents) {
344
+ if (!Array.isArray(content)) continue;
345
+ for (const part of content as {
346
+ type?: unknown;
347
+ toolName?: unknown;
348
+ input?: unknown;
349
+ }[]) {
350
+ if (
351
+ part.type !== "tool-call" ||
352
+ part.toolName !== TOOL_NAMES.write_file
353
+ ) {
354
+ continue;
355
+ }
356
+ const path = (part.input as { path?: unknown } | null)?.path;
357
+ if (typeof path !== "string" || !path.trim()) continue;
358
+ paths.delete(path.trim());
359
+ paths.add(path.trim());
360
+ }
361
+ }
362
+ return [...paths];
363
+ }
364
+
365
+ /** Every path this job gave `write_file`, from every participant (writtenPathsIn). */
366
+ export async function listWrittenPaths(threadId: string): Promise<string[]> {
367
+ const rows = await database
368
+ .select({ content: threadMessageTable.content })
369
+ .from(threadMessageTable)
370
+ .where(
371
+ and(
372
+ eq(threadMessageTable.threadId, threadId),
373
+ eq(threadMessageTable.role, "assistant"),
374
+ ),
375
+ )
376
+ .orderBy(threadMessageTable.seq);
377
+ return writtenPathsIn(rows.map((row) => row.content));
378
+ }
379
+
380
+ /**
381
+ * Finds a thread by id or by exact label (case-insensitive). No looser matching:
382
+ * a miss returns null and the caller lists recent threads instead.
383
+ */
384
+ export async function resolveThread(ref: string) {
385
+ const term = ref.trim();
386
+ if (!term) return null;
387
+ const whole = await findThread(term);
388
+ if (whole) return whole;
389
+
390
+ const lower = term.toLowerCase();
391
+ const recent = await database
392
+ .select()
393
+ .from(threadTable)
394
+ .orderBy(desc(threadTable.updatedAt))
395
+ .limit(20);
396
+ return recent.find((thread) => thread.label.toLowerCase() === lower) ?? null;
397
+ }
398
+
399
+ export type ThreadMessageInput = {
400
+ bot: string | null;
401
+ parent: string | null;
402
+ role: ModelMessage["role"];
403
+ content: ModelMessage["content"];
404
+ /** Compaction summary row; `listParticipantTranscript` re-reads from here. */
405
+ compact?: boolean;
406
+ /** Drawn as the app's own line, not as something a person or a bot said. */
407
+ note?: boolean;
408
+ };
409
+
410
+ /**
411
+ * Writes by seat, not append: a step's messages are written while streaming and
412
+ * rewritten whole when it ends, and the second write must replace the first.
413
+ */
414
+ export async function upsertMessage(
415
+ threadId: string,
416
+ seq: number,
417
+ message: ThreadMessageInput,
418
+ ) {
419
+ await database
420
+ .insert(threadMessageTable)
421
+ .values({
422
+ threadId,
423
+ seq,
424
+ ...message,
425
+ compact: message.compact ?? false,
426
+ note: message.note ?? false,
427
+ })
428
+ .onConflictDoUpdate({
429
+ target: [threadMessageTable.threadId, threadMessageTable.seq],
430
+ set: {
431
+ role: message.role,
432
+ content: message.content,
433
+ compact: message.compact ?? false,
434
+ note: message.note ?? false,
435
+ },
436
+ });
437
+ changed();
438
+ }
439
+
440
+ /** Frees seats claimed while streaming but unused when the step ended. Left in, they put unmatched tool results in the transcript the model re-reads. */
441
+ export async function deleteMessages(threadId: string, seqs: number[]) {
442
+ if (!seqs.length) return;
443
+ await database
444
+ .delete(threadMessageTable)
445
+ .where(
446
+ and(
447
+ eq(threadMessageTable.threadId, threadId),
448
+ inArray(threadMessageTable.seq, seqs),
449
+ ),
450
+ );
451
+ changed();
452
+ }
453
+
454
+ /** Highest seq taken so far; -1 for an empty thread. */
455
+ export async function lastSeq(threadId: string): Promise<number> {
456
+ const [row] = await database
457
+ .select({ seq: max(threadMessageTable.seq) })
458
+ .from(threadMessageTable)
459
+ .where(eq(threadMessageTable.threadId, threadId));
460
+ return row?.seq ?? -1;
461
+ }
462
+
463
+ /** One row in the screen's shape; `pending` folds into `ask`. */
464
+ function viewOf(row: ThreadRow, lines: ThreadLine[]): Omit<Thread, "room"> {
465
+ // contextTokens and contextBudget pass through in `rest`
466
+ const { pending, inputTokens, outputTokens, ...rest } = row;
467
+ return {
468
+ ...rest,
469
+ ask:
470
+ row.status === "waiting"
471
+ ? {
472
+ question: row.outcome ?? "",
473
+ options: pending?.options ?? [],
474
+ auto: pending?.auto === true,
475
+ messageId: pending?.messageId,
476
+ bot: pending?.bot,
477
+ }
478
+ : null,
479
+ tokens: { input: inputTokens, output: outputTokens },
480
+ lines,
481
+ };
482
+ }
483
+
484
+ /** Reduces every thread's messages to lines and attaches them to the rows. */
485
+ async function withLines(rows: ThreadRow[]): Promise<Thread[]> {
486
+ if (rows.length === 0) return [];
487
+ const messages = await database
488
+ .select()
489
+ .from(threadMessageTable)
490
+ .where(
491
+ inArray(
492
+ threadMessageTable.threadId,
493
+ rows.map((row) => row.id),
494
+ ),
495
+ )
496
+ .orderBy(threadMessageTable.seq);
497
+
498
+ const threadIds = rows.map((row) => row.id);
499
+ const [works, deliveries, relays] = await Promise.all([
500
+ database
501
+ .select()
502
+ .from(threadWorkTable)
503
+ .where(inArray(threadWorkTable.threadId, threadIds)),
504
+ database
505
+ .select()
506
+ .from(threadDeliveryTable)
507
+ .where(
508
+ and(
509
+ inArray(threadDeliveryTable.threadId, threadIds),
510
+ eq(threadDeliveryTable.visible, true),
511
+ ),
512
+ ),
513
+ database
514
+ .select()
515
+ .from(threadRelayTable)
516
+ .where(
517
+ and(
518
+ inArray(threadRelayTable.threadId, threadIds),
519
+ eq(threadRelayTable.accepted, false),
520
+ ),
521
+ ),
522
+ ]);
523
+ // A line is addressed to whoever opened the exchange it was written under
524
+ const owners = new Map(rows.map((row) => [row.id, row.bot]));
525
+ const callers = new Map(works.map((item) => [item.id, item.caller]));
526
+ const byThread = new Map<string, ThreadLine[]>();
527
+ for (const message of messages) {
528
+ const list = byThread.get(message.threadId) ?? [];
529
+ list.push(
530
+ ...linesOf(
531
+ message,
532
+ (message.parent ? callers.get(message.parent) : null) ??
533
+ owners.get(message.threadId) ??
534
+ "",
535
+ ),
536
+ );
537
+ byThread.set(message.threadId, list);
538
+ }
539
+ return rows.map((row) => {
540
+ const own = works.filter((item) => item.threadId === row.id);
541
+ const rank = [
542
+ "running",
543
+ "queued",
544
+ "paused",
545
+ "waiting",
546
+ "done",
547
+ "cancelled",
548
+ ];
549
+ const participants = [...new Set(own.map((item) => item.bot))]
550
+ .filter((bot) => bot !== ROOM_THURSDAY)
551
+ .map((bot) => ({
552
+ bot,
553
+ state: own
554
+ .filter((item) => item.bot === bot)
555
+ .sort((a, b) => rank.indexOf(a.state) - rank.indexOf(b.state))[0]
556
+ .state,
557
+ }));
558
+ return {
559
+ ...viewOf(row, byThread.get(row.id) ?? []),
560
+ room: {
561
+ participants,
562
+ questions: own
563
+ .filter((item) => item.state === "external")
564
+ .map((item) => ({
565
+ id: item.id,
566
+ bot: item.caller,
567
+ text: item.result ?? "",
568
+ options: item.options,
569
+ })),
570
+ deliveries: deliveries
571
+ .filter((item) => item.threadId === row.id)
572
+ .map((item) => ({
573
+ id: item.key,
574
+ bot: own.find((w) => w.id === item.workId)?.bot ?? row.bot,
575
+ text: item.text,
576
+ delivered: item.consumed,
577
+ })),
578
+ relays: relays.filter((item) => item.threadId === row.id),
579
+ },
580
+ };
581
+ });
582
+ }
583
+
584
+ /** One-line cap: room for a path, small enough for a row. */
585
+ const LINE_MAX = 120;
586
+
587
+ /** Argument names likely to carry the point, in reading order. */
588
+ const TELLING = [
589
+ "query",
590
+ "command",
591
+ "prompt",
592
+ "url",
593
+ "path",
594
+ "pattern",
595
+ "request",
596
+ "question",
597
+ "title",
598
+ "name",
599
+ "thread",
600
+ "text",
601
+ ] as const;
602
+
603
+ const stringAt = (args: Record<string, unknown>, key: string) => {
604
+ const value = args[key];
605
+ return typeof value === "string" && value.trim() ? value : null;
606
+ };
607
+
608
+ /** Model-written label on bash. null when absent; the command is then the label. */
609
+ function labelOf(args: Record<string, unknown>): string | null {
610
+ const note = stringAt(args, "description");
611
+ return note ? clip(note, LINE_MAX) : null;
612
+ }
613
+
614
+ /**
615
+ * One tool call as one line, for the screen. No per-tool summary table: the
616
+ * label is model-written (bash `description`), other tools use the first
617
+ * telling argument, else the argument shape.
618
+ */
619
+ function argumentLine(name: string, input: unknown): string {
620
+ const args = (input ?? {}) as Record<string, unknown>;
621
+ if (typeof args !== "object" || Array.isArray(args)) {
622
+ return clip(String(input ?? ""), LINE_MAX);
623
+ }
624
+ if (name === TOOL_NAMES.bash) {
625
+ return labelOf(args) ?? clip(String(args.command ?? ""), LINE_MAX);
626
+ }
627
+ if (name === TOOL_NAMES.tool_call || name === TOOL_NAMES.tool_search) {
628
+ // For MCP the call is inside the args. Server name alone would collapse
629
+ // twenty different calls into one line, and a resumed bot would repeat them
630
+ const called = Array.isArray(args.tools)
631
+ ? args.tools.map(String).join(", ")
632
+ : String(args.tool ?? "");
633
+ const passed =
634
+ args.args && typeof args.args === "object"
635
+ ? JSON.stringify(args.args)
636
+ : "";
637
+ return clip(
638
+ [String(args.server ?? ""), called, passed].filter(Boolean).join(" "),
639
+ LINE_MAX,
640
+ );
641
+ }
642
+ for (const key of TELLING) {
643
+ const value = stringAt(args, key);
644
+ if (value) return clip(value, LINE_MAX);
645
+ }
646
+ for (const key of Object.keys(args)) {
647
+ const value = stringAt(args, key);
648
+ if (value) return clip(value, LINE_MAX);
649
+ }
650
+ const keys = Object.keys(args);
651
+ return keys.length ? clip(JSON.stringify(args), LINE_MAX) : "";
652
+ }
653
+
654
+ /** Result lines carried in the list, enough for a glance. */
655
+ const RESULT_LINES = 4;
656
+
657
+ /**
658
+ * Characters kept of each glance line. The list carries every thread's results
659
+ * on every read, so a long line (a skill's body, a JSON blob) would ride along
660
+ * whole; the output opens in full on demand. Long enough for a file path.
661
+ */
662
+ const RESULT_LINE_MAX = 200;
663
+
664
+ /** Cap for the full result, so one log file cannot flatten the browser. */
665
+ const FULL_RESULT_LINES = 400;
666
+
667
+ /** Everything one tool returned, as written; only when the screen asks for it. The list carries a clipped glance (resultLine). */
668
+ export async function readToolResult(
669
+ threadId: string,
670
+ toolCallId: string,
671
+ ): Promise<ResultPart[] | null> {
672
+ // Usually a tool row, but results of provider-run tools (search) sit inside
673
+ // an assistant row, so both are scanned
674
+ const rows = await database
675
+ .select({
676
+ role: threadMessageTable.role,
677
+ content: threadMessageTable.content,
678
+ })
679
+ .from(threadMessageTable)
680
+ .where(
681
+ and(
682
+ eq(threadMessageTable.threadId, threadId),
683
+ inArray(threadMessageTable.role, ["tool", "assistant"]),
684
+ ),
685
+ );
686
+ for (const row of rows) {
687
+ if (typeof row.content === "string") continue;
688
+ for (const part of row.content as ToolContent) {
689
+ if (part.type === "tool-result" && part.toolCallId === toolCallId) {
690
+ return resultParts(part.output, FULL_RESULT_LINES, true);
691
+ }
692
+ }
693
+ }
694
+ return null;
695
+ }
696
+
697
+ /** Who a message call names, with Thursday spelled the room's way whatever case the model wrote. */
698
+ function addresseeOf(args: Record<string, unknown>): string {
699
+ const to = String(args.to ?? "").trim();
700
+ return to.toLowerCase() === ROOM_THURSDAY.toLowerCase() ? ROOM_THURSDAY : to;
701
+ }
702
+
703
+ type StoredMessage = typeof threadMessageTable.$inferSelect;
704
+
705
+ /**
706
+ * One stored message as screen lines. seq 0 is the request and is drawn from the
707
+ * row instead, so it yields nothing. A message call draws as speech. `addressee`
708
+ * is who opened the exchange the message was written under.
709
+ */
710
+ function linesOf(message: StoredMessage, addressee: string): ThreadLine[] {
711
+ if (message.hidden) return [];
712
+ const base = {
713
+ seq: message.seq,
714
+ bot: message.bot,
715
+ parent: message.parent,
716
+ to: message.role === "user" ? message.bot : addressee,
717
+ at: message.createdAt,
718
+ };
719
+ const id = (index: number) => `${message.id}-${index}`;
720
+ const content = message.content;
721
+
722
+ if (message.role === "user") {
723
+ if (message.seq === 0) return [];
724
+ const text = typeof content === "string" ? content : textOf(content);
725
+ if (!text) return [];
726
+ // The app's own lines, never the user's words
727
+ if (message.compact) return [{ ...base, id: id(0), kind: "note", text }];
728
+ if (message.note) {
729
+ const why = text.endsWith(RESUME_CHECK)
730
+ ? text.slice(0, -RESUME_CHECK.length).trimEnd()
731
+ : text;
732
+ return [{ ...base, id: id(0), kind: "stop", text: why }];
733
+ }
734
+ // Tagged with who said it for the bot (bot.schema tagSpeaker); the screen draws the words
735
+ return [{ ...base, id: id(0), kind: "user", text: untagSpeaker(text) }];
736
+ }
737
+
738
+ if (message.role === "assistant") {
739
+ const content = message.content as AssistantContent;
740
+ if (typeof content === "string") {
741
+ return content.trim()
742
+ ? [{ ...base, id: id(0), kind: "text", text: content.trim() }]
743
+ : [];
744
+ }
745
+ const lines: ThreadLine[] = [];
746
+ content.forEach((part, index) => {
747
+ if (part.type === "text") {
748
+ const text = part.text.trim();
749
+ if (text) lines.push({ ...base, id: id(index), kind: "text", text });
750
+ return;
751
+ }
752
+ // Result of a provider-run tool (search) arrives inside the same message
753
+ if (part.type === "tool-result") {
754
+ lines.push(resultLine(base, id(index), part));
755
+ return;
756
+ }
757
+ if (part.type !== "tool-call") return;
758
+ const args = (part.input ?? {}) as Record<string, unknown>;
759
+ if (part.toolName === TOOL_NAMES.send_message) {
760
+ lines.push({
761
+ ...base,
762
+ id: id(index),
763
+ kind: "ask",
764
+ callId: part.toolCallId,
765
+ to: addresseeOf(args),
766
+ text: String(args.text ?? ""),
767
+ // No kind is a message (room.schema RoomMessageSchema), never a question
768
+ question: args.kind === "question",
769
+ });
770
+ } else if (part.toolName === TOOL_NAMES.bash) {
771
+ // Shell line: the command is the body, the model-written label sits above it
772
+ lines.push({
773
+ ...base,
774
+ id: id(index),
775
+ kind: "tool",
776
+ callId: part.toolCallId,
777
+ name: part.toolName,
778
+ input: clip(String(args.command ?? ""), LINE_MAX),
779
+ note: labelOf(args),
780
+ });
781
+ } else {
782
+ lines.push({
783
+ ...base,
784
+ id: id(index),
785
+ kind: "tool",
786
+ callId: part.toolCallId,
787
+ name: part.toolName,
788
+ input: argumentLine(part.toolName, args),
789
+ // Any tool's model-written `description` is its label
790
+ note: labelOf(args),
791
+ // File tools open from the screen, so the path also travels unclipped
792
+ path:
793
+ part.toolName === TOOL_NAMES.write_file
794
+ ? (stringAt(args, "path") ?? null)
795
+ : null,
796
+ });
797
+ }
798
+ });
799
+ return lines;
800
+ }
801
+
802
+ if (message.role === "tool") {
803
+ const lines: ThreadLine[] = [];
804
+ (message.content as ToolContent).forEach((part, index) => {
805
+ if (part.type !== "tool-result") return;
806
+ // A delivered message's receipt is not a step; a refused one is (thread.store)
807
+ if (
808
+ part.toolName === TOOL_NAMES.send_message &&
809
+ part.output.type !== "error-text"
810
+ )
811
+ return;
812
+ lines.push(resultLine(base, id(index), part));
813
+ });
814
+ return lines;
815
+ }
816
+
817
+ return [];
818
+ }
819
+
820
+ /**
821
+ * List line for one tool result: a glance of text lines, each clipped, and whether
822
+ * the output holds more. Asks for one line past the glance to know. Called for tool
823
+ * rows and, for provider-run tools, assistant rows.
824
+ */
825
+ function resultLine(
826
+ base: Omit<ThreadLine, "id" | "kind">,
827
+ id: string,
828
+ part: { toolCallId: string; toolName: string; output: unknown },
829
+ ): ThreadLine {
830
+ const peek = resultParts(part.output, RESULT_LINES + 1);
831
+ const lines = peek.flatMap((result) =>
832
+ result.type === "text" ? [result.text] : [],
833
+ );
834
+ const glance = lines.slice(0, RESULT_LINES);
835
+ return {
836
+ ...base,
837
+ id,
838
+ kind: "tool-result",
839
+ callId: part.toolCallId,
840
+ name: part.toolName,
841
+ // Images stay out of the list: a screenshot is fetched with the full output
842
+ results: glance.map((text) => ({
843
+ type: "text" as const,
844
+ text: clip(text, RESULT_LINE_MAX),
845
+ })),
846
+ more:
847
+ lines.length > glance.length ||
848
+ peek.length > lines.length ||
849
+ glance.some((text) => text.length > RESULT_LINE_MAX),
850
+ };
851
+ }
852
+
853
+ function textOf(parts: Array<{ type: string; text?: string }>): string {
854
+ return parts
855
+ .flatMap((part) => (part.type === "text" && part.text ? [part.text] : []))
856
+ .join("\n")
857
+ .trim();
858
+ }
859
+
860
+ /**
861
+ * Tool output as parts: text and images. A glance trims lines and drops blank ones;
862
+ * `full` keeps the text as written and lays objects out to be read. ai-sdk output
863
+ * shapes first, then our tools' shapes, then raw JSON.
864
+ */
865
+ function resultParts(
866
+ output: unknown,
867
+ limit: number,
868
+ full = false,
869
+ ): ResultPart[] {
870
+ if (output == null) return [];
871
+ if (typeof output === "string") return textParts(output, limit, full);
872
+ if (typeof output !== "object") return textParts(String(output), limit, full);
873
+
874
+ const wrapped = output as { type?: unknown; value?: unknown };
875
+ switch (wrapped.type) {
876
+ case "text":
877
+ case "error-text":
878
+ return textParts(String(wrapped.value ?? ""), limit, full);
879
+ case "json":
880
+ case "error-json":
881
+ return resultParts(wrapped.value, limit, full);
882
+ case "execution-denied":
883
+ return textParts("Denied.", limit, full);
884
+ case "content": {
885
+ if (!Array.isArray(wrapped.value)) return [];
886
+ const parts: ResultPart[] = [];
887
+ for (const block of wrapped.value as Array<Record<string, unknown>>) {
888
+ if (block.type === "text" && typeof block.text === "string") {
889
+ parts.push(...textParts(block.text, limit, full));
890
+ continue;
891
+ }
892
+ const image = imageSrc(block);
893
+ if (image) parts.push({ type: "image", src: image });
894
+ }
895
+ return parts;
896
+ }
897
+ default:
898
+ break;
899
+ }
900
+
901
+ const record = output as Record<string, unknown>;
902
+ if (Array.isArray(record.sources)) {
903
+ return (
904
+ record.sources
905
+ .map((source) => String((source as { title?: string })?.title ?? ""))
906
+ // Untitled results would be blank rows; filter after map so slice still fills
907
+ .filter(Boolean)
908
+ .slice(0, limit)
909
+ .map((text) => ({ type: "text" as const, text }))
910
+ );
911
+ }
912
+ if (typeof record.stdout === "string") {
913
+ return textParts(record.stdout || String(record.stderr ?? ""), limit, full);
914
+ }
915
+ if (typeof record.answer === "string")
916
+ return textParts(record.answer, limit, full);
917
+ return textParts(
918
+ full ? readable(output) : JSON.stringify(output),
919
+ limit,
920
+ full,
921
+ );
922
+ }
923
+
924
+ /** An object read whole: a multi-line string (a skill's body) as the text it is, anything else as indented JSON. */
925
+ function readable(value: object): string {
926
+ if (Array.isArray(value)) return JSON.stringify(value, null, 2);
927
+ return Object.entries(value)
928
+ .map(([key, field]) =>
929
+ typeof field === "string" && field.includes("\n")
930
+ ? `${key}:\n${field}`
931
+ : `${key}: ${JSON.stringify(field, null, 2)}`,
932
+ )
933
+ .join("\n");
934
+ }
935
+
936
+ /** A file block holding an image, as something <img> can show. base64 becomes a data url, urls pass through, anything else is dropped. */
937
+ function imageSrc(block: Record<string, unknown>): string | null {
938
+ const mediaType = String(block.mediaType ?? "");
939
+ if (!mediaType.startsWith("image/")) return null;
940
+ const data = block.data as
941
+ | string
942
+ | { type?: string; data?: unknown; url?: string }
943
+ | undefined;
944
+ if (typeof data === "string") return `data:${mediaType};base64,${data}`;
945
+ if (data?.type === "url" && data.url) return data.url;
946
+ if (data?.type === "data" && typeof data.data === "string") {
947
+ return `data:${mediaType};base64,${data.data}`;
948
+ }
949
+ return null;
950
+ }
951
+
952
+ /**
953
+ * Text as parts. A glance is one part per non-blank trimmed line, up to `limit`;
954
+ * `full` is the text as written in one part, cut after `limit` lines with a count
955
+ * of what was left.
956
+ */
957
+ function textParts(text: string, limit: number, full = false): ResultPart[] {
958
+ if (full) {
959
+ const lines = text.split("\n");
960
+ const kept = lines.slice(0, limit);
961
+ if (lines.length > limit) kept.push(`… ${lines.length - limit} more lines`);
962
+ const whole = kept.join("\n").trimEnd();
963
+ return whole ? [{ type: "text", text: whole }] : [];
964
+ }
965
+ return text
966
+ .split("\n")
967
+ .map((line) => line.trim())
968
+ .filter(Boolean)
969
+ .slice(0, limit)
970
+ .map((line) => ({ type: "text" as const, text: line }));
971
+ }