thursday-agent 0.1.2 → 0.3.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 (573) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +5 -1
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/prerender-manifest.json +5 -5
  5. package/.next/routes-manifest.json +27 -3
  6. package/.next/server/app/_global-error/page.js +2 -2
  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 +2 -2
  10. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
  12. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/server/app/_not-found/page.js +2 -2
  14. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  15. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/.next/server/app/_not-found.html +1 -1
  17. package/.next/server/app/_not-found.rsc +14 -14
  18. package/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
  19. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +12 -12
  20. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  21. package/.next/server/app/api/artifact/route.js +3 -3
  22. package/.next/server/app/api/artifact/route.js.nft.json +1 -1
  23. package/.next/server/app/api/bot/memory/files/route/app-paths-manifest.json +3 -0
  24. package/.next/server/app/api/bot/memory/files/route.js +10 -0
  25. package/.next/server/app/api/bot/memory/files/route.js.nft.json +1 -0
  26. package/.next/server/app/api/bot/memory/files/route_client-reference-manifest.js +3 -0
  27. package/.next/server/app/api/bot/memory/route/app-paths-manifest.json +3 -0
  28. package/.next/server/app/api/bot/memory/route/build-manifest.json +12 -0
  29. package/.next/server/app/api/bot/memory/route/server-reference-manifest.json +4 -0
  30. package/.next/server/app/api/bot/memory/route.js +14 -0
  31. package/.next/server/app/api/bot/memory/route.js.map +5 -0
  32. package/.next/server/app/api/bot/memory/route.js.nft.json +1 -0
  33. package/.next/server/app/api/bot/memory/route_client-reference-manifest.js +3 -0
  34. package/.next/server/app/api/bot/route.js +7 -7
  35. package/.next/server/app/api/bot/route.js.nft.json +1 -1
  36. package/.next/server/app/api/bot/task/[id]/route.js +9 -9
  37. package/.next/server/app/api/bot/task/[id]/route.js.nft.json +1 -1
  38. package/.next/server/app/api/bot/task/route.js +8 -8
  39. package/.next/server/app/api/bot/task/route.js.nft.json +1 -1
  40. package/.next/server/app/api/config/route.js +6 -6
  41. package/.next/server/app/api/config/route.js.nft.json +1 -1
  42. package/.next/server/app/api/events/route.js +4 -5
  43. package/.next/server/app/api/events/route.js.nft.json +1 -1
  44. package/.next/server/app/api/file/[...path]/route.js +3 -3
  45. package/.next/server/app/api/file/[...path]/route.js.nft.json +1 -1
  46. package/.next/server/app/api/llm-model/catalog/route.js +10 -9
  47. package/.next/server/app/api/llm-model/catalog/route.js.nft.json +1 -1
  48. package/.next/server/app/api/llm-model/credits/route/app-paths-manifest.json +3 -0
  49. package/.next/server/app/api/llm-model/credits/route/build-manifest.json +12 -0
  50. package/.next/server/app/api/llm-model/credits/route/server-reference-manifest.json +4 -0
  51. package/.next/server/app/api/llm-model/credits/route.js +16 -0
  52. package/.next/server/app/api/llm-model/credits/route.js.map +5 -0
  53. package/.next/server/app/api/llm-model/credits/route.js.nft.json +1 -0
  54. package/.next/server/app/api/llm-model/credits/route_client-reference-manifest.js +3 -0
  55. package/.next/server/app/api/llm-model/route.js +10 -8
  56. package/.next/server/app/api/llm-model/route.js.nft.json +1 -1
  57. package/.next/server/app/api/llm-model/subscription/route/app-paths-manifest.json +3 -0
  58. package/.next/server/app/api/llm-model/subscription/route/build-manifest.json +12 -0
  59. package/.next/server/app/api/llm-model/subscription/route/server-reference-manifest.json +4 -0
  60. package/.next/server/app/api/llm-model/subscription/route.js +15 -0
  61. package/.next/server/app/api/llm-model/subscription/route.js.map +5 -0
  62. package/.next/server/app/api/llm-model/subscription/route.js.nft.json +1 -0
  63. package/.next/server/app/api/llm-model/subscription/route_client-reference-manifest.js +3 -0
  64. package/.next/server/app/api/mcp/[name]/route.js +5 -5
  65. package/.next/server/app/api/mcp/[name]/route.js.nft.json +1 -1
  66. package/.next/server/app/api/mcp/oauth/callback/route.js +6 -6
  67. package/.next/server/app/api/mcp/oauth/callback/route.js.nft.json +1 -1
  68. package/.next/server/app/api/mcp/route.js +5 -5
  69. package/.next/server/app/api/mcp/route.js.nft.json +1 -1
  70. package/.next/server/app/api/mcp/tools/route.js +5 -5
  71. package/.next/server/app/api/mcp/tools/route.js.nft.json +1 -1
  72. package/.next/server/app/api/memory/edit/route/app-paths-manifest.json +3 -0
  73. package/.next/server/app/api/memory/edit/route/build-manifest.json +12 -0
  74. package/.next/server/app/api/memory/edit/route/server-reference-manifest.json +4 -0
  75. package/.next/server/app/api/memory/edit/route.js +24 -0
  76. package/.next/server/app/api/memory/edit/route.js.map +5 -0
  77. package/.next/server/app/api/memory/edit/route.js.nft.json +1 -0
  78. package/.next/server/app/api/memory/{tidy → edit}/route_client-reference-manifest.js +1 -1
  79. package/.next/server/app/api/memory/route.js +7 -8
  80. package/.next/server/app/api/memory/route.js.nft.json +1 -1
  81. package/.next/server/app/api/skills/[source]/[name]/route.js +3 -3
  82. package/.next/server/app/api/skills/[source]/[name]/route.js.nft.json +1 -1
  83. package/.next/server/app/api/skills/route.js +3 -3
  84. package/.next/server/app/api/skills/route.js.nft.json +1 -1
  85. package/.next/server/app/api/thursday/call/route.js +9 -7
  86. package/.next/server/app/api/thursday/call/route.js.nft.json +1 -1
  87. package/.next/server/app/api/thursday/skills/route.js +9 -7
  88. package/.next/server/app/api/thursday/skills/route.js.nft.json +1 -1
  89. package/.next/server/app/api/thursday/tool-call/route.js +16 -15
  90. package/.next/server/app/api/thursday/tool-call/route.js.nft.json +1 -1
  91. package/.next/server/app/api/workspace/route.js +4 -5
  92. package/.next/server/app/api/workspace/route.js.nft.json +1 -1
  93. package/.next/server/app/artifact/[...path]/page/server-reference-manifest.json +1 -1
  94. package/.next/server/app/artifact/[...path]/page.js +5 -6
  95. package/.next/server/app/artifact/[...path]/page.js.nft.json +1 -1
  96. package/.next/server/app/artifact/[...path]/page_client-reference-manifest.js +1 -1
  97. package/.next/server/app/favicon.ico/route.js +3 -4
  98. package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
  99. package/.next/server/app/favicon.ico.body +0 -0
  100. package/.next/server/app/page/server-reference-manifest.json +66 -77
  101. package/.next/server/app/page.js +11 -11
  102. package/.next/server/app/page.js.nft.json +1 -1
  103. package/.next/server/app/page_client-reference-manifest.js +1 -1
  104. package/.next/server/app-paths-manifest.json +5 -1
  105. package/.next/server/chunks/[externals]__0dzuk6_._.js +3 -0
  106. package/.next/server/chunks/[root-of-the-server]__01dkp4a._.js +3 -0
  107. package/.next/server/chunks/[root-of-the-server]__02va5wz._.js +3 -0
  108. package/.next/server/chunks/[root-of-the-server]__02xjoto._.js +3 -0
  109. package/.next/server/chunks/[root-of-the-server]__04q--0f._.js +8 -0
  110. package/.next/server/chunks/[root-of-the-server]__0bl-e63._.js +3 -0
  111. package/.next/server/chunks/[root-of-the-server]__0ce8nf8._.js +5 -0
  112. package/.next/server/chunks/[root-of-the-server]__0ffbg85._.js +3 -0
  113. package/.next/server/chunks/[root-of-the-server]__0j7z0r3._.js +12 -0
  114. package/.next/server/chunks/[root-of-the-server]__0jq4ohy._.js +8 -0
  115. package/.next/server/chunks/[root-of-the-server]__0ls6_8y._.js +8 -0
  116. package/.next/server/chunks/[root-of-the-server]__0ncs9uu._.js +3 -0
  117. package/.next/server/chunks/[root-of-the-server]__0pa2n68._.js +3 -0
  118. package/.next/server/chunks/[root-of-the-server]__0q0vvt2._.js +5 -0
  119. package/.next/server/chunks/[root-of-the-server]__1-ga0kh._.js +5 -0
  120. package/.next/server/chunks/[root-of-the-server]__11n256_._.js +5 -0
  121. package/.next/server/chunks/[root-of-the-server]__12cabg9._.js +3 -0
  122. package/.next/server/chunks/[root-of-the-server]__1339jnv._.js +8 -0
  123. package/.next/server/chunks/[root-of-the-server]__15bjnu5._.js +5 -0
  124. package/.next/server/chunks/[root-of-the-server]__18_teo8._.js +8 -0
  125. package/.next/server/chunks/[root-of-the-server]__1_04mcf._.js +5 -0
  126. package/.next/server/chunks/[root-of-the-server]__1anc1p9._.js +3 -0
  127. package/.next/server/chunks/[root-of-the-server]__1ccon1w._.js +10 -0
  128. package/.next/server/chunks/[root-of-the-server]__1fizn31._.js +3 -0
  129. package/.next/server/chunks/[root-of-the-server]__1llj3-b._.js +3 -0
  130. package/.next/server/chunks/[root-of-the-server]__1orn-5b._.js +11 -0
  131. package/.next/server/chunks/[root-of-the-server]__1p8m3f4._.js +3 -0
  132. package/.next/server/chunks/[root-of-the-server]__1wa1k7r._.js +8 -0
  133. package/.next/server/chunks/{_1hzrsvg._.js → _0-73of8._.js} +6 -6
  134. package/.next/server/chunks/{_1cbk7ms._.js → _0-fjql4._.js} +10 -10
  135. package/.next/server/chunks/_00xh_q0._.js +27 -0
  136. package/.next/server/chunks/{_0y8is-s._.js → _03a0897._.js} +1 -1
  137. package/.next/server/chunks/{_0pppbb5._.js → _03kp0jp._.js} +166 -35
  138. package/.next/server/chunks/_051qh8u._.js +3 -0
  139. package/.next/server/chunks/{node_modules_next_04q-o58._.js → _05dla15._.js} +2 -2
  140. package/.next/server/chunks/_05h2cii._.js +1 -1
  141. package/.next/server/chunks/_0c9j6rb._.js +49 -0
  142. package/.next/server/chunks/_0fzqt2y._.js +74 -44
  143. package/.next/server/chunks/{_0kl37zl._.js → _0ioz3nd._.js} +1 -1
  144. package/.next/server/chunks/_0iwfby5._.js +3 -0
  145. package/.next/server/chunks/{_0hin38_._.js → _0khihjk._.js} +5 -5
  146. package/.next/server/chunks/_0mecv0f._.js +26 -0
  147. package/.next/server/chunks/_0mh8j4m._.js +3 -0
  148. package/.next/server/chunks/{_1icgbi8._.js → _0n74zsf._.js} +2 -2
  149. package/.next/server/chunks/_0obojvv._.js +3 -0
  150. package/.next/server/chunks/_0recufk._.js +49 -0
  151. package/.next/server/chunks/{_1t8fv7b._.js → _0rnf-6n._.js} +2 -2
  152. package/.next/server/chunks/_0sb7tlz._.js +3 -0
  153. package/.next/server/chunks/{_0jgm3b8._.js → _0u10wmw._.js} +6 -6
  154. package/.next/server/chunks/{_12nr4xc._.js → _0u4aiy2._.js} +2 -2
  155. package/.next/server/chunks/_0wp4le3._.js +1 -1
  156. package/.next/server/chunks/_0y5fg-x._.js +1 -1
  157. package/.next/server/chunks/_1230wbx._.js +10 -0
  158. package/.next/server/chunks/_12nheja._.js +3 -0
  159. package/.next/server/chunks/_13510xh._.js +3 -0
  160. package/.next/server/chunks/_15hq89o._.js +1 -1
  161. package/.next/server/chunks/_17l5ygt._.js +49 -0
  162. package/.next/server/chunks/{_0ks6_sg._.js → _18-1wcc._.js} +6 -6
  163. package/.next/server/chunks/_1_5mnq4._.js +18 -0
  164. package/.next/server/chunks/{_0tzfesa._.js → _1_9mjx-._.js} +1 -1
  165. package/.next/server/chunks/_1d0j961._.js +6 -0
  166. package/.next/server/chunks/_1d5zmy8._.js +1 -1
  167. package/.next/server/chunks/_1drgfx4._.js +18 -0
  168. package/.next/server/chunks/{_1skgy7b._.js → _1e48czp._.js} +5 -5
  169. package/.next/server/chunks/_1f46eie._.js +3 -0
  170. package/.next/server/chunks/_1h5_8sk._.js +3 -0
  171. package/.next/server/chunks/_1hdnxb7._.js +3 -0
  172. package/.next/server/chunks/_1j8k_j5._.js +3 -0
  173. package/.next/server/chunks/{_10qam6o._.js → _1kg70wc._.js} +5 -5
  174. package/.next/server/chunks/_1krd9rf._.js +3 -0
  175. package/.next/server/chunks/_1nc0eui._.js +11 -0
  176. package/.next/server/chunks/_1nula3v._.js +3 -0
  177. package/.next/server/chunks/_1pf4qe-._.js +3 -0
  178. package/.next/server/chunks/_1php4eq._.js +1 -1
  179. package/.next/server/chunks/_1qxheoa._.js +3 -0
  180. package/.next/server/chunks/_1x6kd1r._.js +3 -0
  181. package/.next/server/chunks/_1yuv4vp._.js +1 -1
  182. package/.next/server/chunks/_1zjrdag._.js +3 -0
  183. package/.next/server/chunks/_next-internal_server_app_api_bot_memory_files_route_actions_1_xd_8f.js +3 -0
  184. package/.next/server/chunks/_next-internal_server_app_api_bot_memory_route_actions_1a8xfr7.js +3 -0
  185. package/.next/server/chunks/_next-internal_server_app_api_llm-model_credits_route_actions_0v424pb.js +3 -0
  186. package/.next/server/chunks/_next-internal_server_app_api_llm-model_subscription_route_actions_0-udcm5.js +3 -0
  187. package/.next/server/chunks/_next-internal_server_app_api_memory_edit_route_actions_02sfnmr.js +3 -0
  188. package/.next/server/chunks/config_ts_1g4sz5o._.js +1 -1
  189. package/.next/server/chunks/features_0rpfxbk._.js +3 -0
  190. package/.next/server/chunks/features_1qf3cei._.js +3 -0
  191. package/.next/server/chunks/features_bot_0zt92oh._.js +3 -0
  192. package/.next/server/chunks/features_bot_bot_query_ts_12mgkl2._.js +1 -1
  193. package/.next/server/chunks/features_memory_1uqvksn._.js +6 -0
  194. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0tf290y._.js → node_modules_consola_dist_chunks_prompt_mjs_0i7cvml._.js} +1 -1
  195. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0228f43._.js → node_modules_consola_dist_chunks_prompt_mjs_0icqaqw._.js} +1 -1
  196. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0ir-vo2._.js → node_modules_consola_dist_chunks_prompt_mjs_0j61-ma._.js} +1 -1
  197. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0otw8ef._.js → node_modules_consola_dist_chunks_prompt_mjs_0r28-sm._.js} +1 -1
  198. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_0v_7drq._.js +3 -0
  199. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_11zjo-1._.js +3 -0
  200. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1pycqsq._.js +3 -0
  201. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1xoq2rl._.js +3 -0
  202. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1y49572._.js +3 -0
  203. package/.next/server/chunks/ssr/[externals]__0dizgek._.js +3 -0
  204. package/.next/server/chunks/ssr/[root-of-the-server]__09q9med._.js +1 -1
  205. package/.next/server/chunks/ssr/[root-of-the-server]__0_69wqr._.js +3 -0
  206. package/.next/server/chunks/ssr/[root-of-the-server]__0hg5gib._.js +1 -1
  207. package/.next/server/chunks/ssr/[root-of-the-server]__0i8ie7f._.js +3 -0
  208. package/.next/server/chunks/ssr/{[root-of-the-server]__0jsmmb-._.js → [root-of-the-server]__0mz33a7._.js} +1 -1
  209. package/.next/server/chunks/ssr/[root-of-the-server]__0tbkdlx._.js +3 -0
  210. package/.next/server/chunks/ssr/[root-of-the-server]__0vnmv-s._.js +3 -0
  211. package/.next/server/chunks/ssr/{[root-of-the-server]__0f6s48x._.js → [root-of-the-server]__0xpcv9w._.js} +3 -3
  212. package/.next/server/chunks/ssr/[root-of-the-server]__0xtssj3._.js +1 -1
  213. package/.next/server/chunks/ssr/[root-of-the-server]__0zkv7z_._.js +3 -0
  214. package/.next/server/chunks/ssr/[root-of-the-server]__10-dz5o._.js +57 -0
  215. package/.next/server/chunks/ssr/[root-of-the-server]__15_uhip._.js +3 -0
  216. package/.next/server/chunks/ssr/[root-of-the-server]__16jc196._.js +1 -1
  217. package/.next/server/chunks/ssr/[root-of-the-server]__1cyjywu._.js +3 -0
  218. package/.next/server/chunks/ssr/[root-of-the-server]__1eh7hid._.js +3 -0
  219. package/.next/server/chunks/ssr/[root-of-the-server]__1gtlw6v._.js +43 -0
  220. package/.next/server/chunks/ssr/[root-of-the-server]__1mzi-ju._.js +1 -1
  221. package/.next/server/chunks/ssr/[root-of-the-server]__1ox7hbk._.js +4 -0
  222. package/.next/server/chunks/ssr/[root-of-the-server]__1pduf5n._.js +3 -0
  223. package/.next/server/chunks/ssr/{_1mznrxk._.js → _001_ms1._.js} +3 -3
  224. package/.next/server/chunks/ssr/_01_6m32._.js +3 -0
  225. package/.next/server/chunks/ssr/_01n3o0m._.js +3 -0
  226. package/.next/server/chunks/ssr/_07uchnc._.js +3 -0
  227. package/.next/server/chunks/ssr/_0ap2fzn._.js +3 -0
  228. package/.next/server/chunks/ssr/_0c-ql2m._.js +3 -0
  229. package/.next/server/chunks/ssr/_0h42fze._.js +3 -0
  230. package/.next/server/chunks/ssr/_0h7zdni._.js +1 -1
  231. package/.next/server/chunks/ssr/_0i_6q1w._.js +41 -0
  232. package/.next/server/chunks/ssr/_0kbkmar._.js +3 -0
  233. package/.next/server/chunks/ssr/_0lu1n_x._.js +3 -0
  234. package/.next/server/chunks/ssr/{_077-g6s._.js → _0mb8zc8._.js} +3 -3
  235. package/.next/server/chunks/ssr/_0mo2w06._.js +3 -0
  236. package/.next/server/chunks/ssr/{_13punpm._.js → _0q0zan6._.js} +2 -2
  237. package/.next/server/chunks/ssr/{_0gd4vaa._.js → _0u2jv_x._.js} +94 -58
  238. package/.next/server/chunks/ssr/_0y0tiht._.js +4 -0
  239. package/.next/server/chunks/ssr/_0zhoc3r._.js +3 -0
  240. package/.next/server/chunks/ssr/_1-teee5._.js +3 -3
  241. package/.next/server/chunks/ssr/_13fke0x._.js +3 -0
  242. package/.next/server/chunks/ssr/{_0q_2f2x._.js → _13z3f85._.js} +2 -2
  243. package/.next/server/chunks/ssr/_14_lw10._.js +3 -0
  244. package/.next/server/chunks/ssr/_15j2_8f._.js +3 -0
  245. package/.next/server/chunks/ssr/{_0lc_7c8._.js → _1eoqg_g._.js} +2 -2
  246. package/.next/server/chunks/ssr/_1sji22n._.js +3 -0
  247. package/.next/server/chunks/ssr/_1spzm8z._.js +3 -0
  248. package/.next/server/chunks/ssr/{_19_o9d-._.js → _1vi_uo7._.js} +2 -2
  249. package/.next/server/chunks/ssr/_1vyd1ji._.js +3 -0
  250. package/.next/server/chunks/ssr/_1xvgn77._.js +3 -0
  251. package/.next/server/chunks/ssr/_20kd_n0._.js +42 -0
  252. package/.next/server/chunks/ssr/features_bot_1s_-2vj._.js +3 -0
  253. package/.next/server/chunks/ssr/features_config_components_config-setting_tsx_0xfwqdg._.js +1 -1
  254. package/.next/server/chunks/ssr/features_connectors_components_mcp-setting_tsx_0jmbgvp._.js +1 -1
  255. package/.next/server/chunks/ssr/lib_date-like_ts_02is0pw._.js +3 -0
  256. package/.next/server/chunks/ssr/lib_realtime_12wg_dd._.js +27 -0
  257. package/.next/server/chunks/ssr/lib_realtime_15h_v1v._.js +27 -0
  258. package/.next/server/chunks/ssr/{node_modules_1d-is1o._.js → node_modules_063xwpp._.js} +1 -1
  259. package/.next/server/chunks/ssr/{node_modules_1ub40gk._.js → node_modules_0bvraul._.js} +1 -1
  260. package/.next/server/chunks/ssr/{node_modules_1nhw9qg._.js → node_modules_0eu1va6._.js} +1 -1
  261. package/.next/server/chunks/ssr/{node_modules_0xwx4u-._.js → node_modules_0h50b8n._.js} +1 -1
  262. package/.next/server/chunks/ssr/{node_modules_043-236._.js → node_modules_0h5hnr1._.js} +1 -1
  263. package/.next/server/chunks/ssr/{node_modules_1t0y5jt._.js → node_modules_0h7u60n._.js} +1 -1
  264. package/.next/server/chunks/ssr/{node_modules_1k29j1j._.js → node_modules_0lm7_vm._.js} +1 -1
  265. package/.next/server/chunks/ssr/{node_modules_16i4o_3._.js → node_modules_0mwym0g._.js} +1 -1
  266. package/.next/server/chunks/ssr/{node_modules_1y8_3vg._.js → node_modules_0rjg6t1._.js} +1 -1
  267. package/.next/server/chunks/ssr/node_modules_0x3-ply._.js +3 -0
  268. package/.next/server/chunks/ssr/{node_modules_108wd2o._.js → node_modules_0y_cded._.js} +1 -1
  269. package/.next/server/chunks/ssr/{node_modules_1jatz7w._.js → node_modules_0z5tsu8._.js} +1 -1
  270. package/.next/server/chunks/ssr/{node_modules_0ls96je._.js → node_modules_0zp9r92._.js} +1 -1
  271. package/.next/server/chunks/ssr/{node_modules_06kvvuo._.js → node_modules_1-hc6uv._.js} +1 -1
  272. package/.next/server/chunks/ssr/{node_modules_1_588_4._.js → node_modules_113tz0l._.js} +1 -1
  273. package/.next/server/chunks/ssr/{node_modules_0ipn1ho._.js → node_modules_124bue5._.js} +1 -1
  274. package/.next/server/chunks/ssr/node_modules_17bwo43._.js +55 -0
  275. package/.next/server/chunks/ssr/{node_modules_0_j7-g4._.js → node_modules_1_n3ztw._.js} +1 -1
  276. package/.next/server/chunks/ssr/{node_modules_20ynonm._.js → node_modules_1bc7gu8._.js} +1 -1
  277. package/.next/server/chunks/ssr/{node_modules_1hn_sd5._.js → node_modules_1bgb6-f._.js} +1 -1
  278. package/.next/server/chunks/ssr/{node_modules_0bue6gy._.js → node_modules_1h4a4pp._.js} +1 -1
  279. package/.next/server/chunks/ssr/{node_modules_0fbw5-u._.js → node_modules_1i7u1ia._.js} +1 -1
  280. package/.next/server/chunks/ssr/{node_modules_1ee8gav._.js → node_modules_1juq3hs._.js} +1 -1
  281. package/.next/server/chunks/ssr/{node_modules_0_fvxnt._.js → node_modules_1l0qtdu._.js} +1 -1
  282. package/.next/server/chunks/ssr/{node_modules_13f2md6._.js → node_modules_1t9cyee._.js} +1 -1
  283. package/.next/server/chunks/ssr/{node_modules_0y2-av4._.js → node_modules_1uuj9x1._.js} +1 -1
  284. package/.next/server/chunks/ssr/{node_modules_00d6675._.js → node_modules_1vqb6po._.js} +1 -1
  285. package/.next/server/chunks/ssr/{node_modules_1v0077l._.js → node_modules_1xx28h1._.js} +1 -1
  286. package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_092s8nx._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0d3skpl._.js} +1 -1
  287. package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_1nvexgu._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0i44kk5._.js} +1 -1
  288. package/.next/server/chunks/ssr/node_modules_@vercel_oidc_dist_0qe30a_._.js +3 -0
  289. package/.next/server/chunks/ssr/node_modules_consola_dist_chunks_prompt_mjs_05cwqv2._.js +47 -0
  290. package/.next/server/chunks/ssr/{node_modules_next_dist_0w6mzq5._.js → node_modules_next_dist_0drixxt._.js} +4 -4
  291. package/.next/server/chunks/ssr/node_modules_next_dist_1n3w9lb._.js +2 -2
  292. package/.next/server/instrumentation.js +1 -1
  293. package/.next/server/middleware-build-manifest.js +3 -3
  294. package/.next/server/pages/404.html +1 -1
  295. package/.next/server/pages/500.html +1 -1
  296. package/.next/server/server-reference-manifest.js +1 -1
  297. package/.next/server/server-reference-manifest.json +67 -78
  298. package/.next/static/chunks/{31ttnjt6g35a0.js → 0ev4hyghq0mbb.js} +1 -1
  299. package/.next/static/chunks/0r5qcctujhxli.js +1 -0
  300. package/.next/static/chunks/0tmgrnlje14-s.js +3 -0
  301. package/.next/static/chunks/0x79tx04dxbpp.js +1 -0
  302. package/.next/static/chunks/0xzgvc0fkdb6t.js +1 -0
  303. package/.next/static/chunks/10teww7wt-f6h.js +1 -0
  304. package/.next/static/chunks/14hgtsjg-kael.css +1 -0
  305. package/.next/static/chunks/17donp69y4do7.js +40 -0
  306. package/.next/static/chunks/1dkjg87pxk0n9.js +28 -0
  307. package/.next/static/chunks/1ekc7ul556kbl.js +1 -0
  308. package/.next/static/chunks/{1khakdu_iuedc.js → 1grhjo7a5a0_-.js} +1 -1
  309. package/.next/static/chunks/1kq8h8wi_e0bc.js +53 -0
  310. package/.next/static/chunks/1okqiww3pnwka.js +1 -0
  311. package/.next/static/chunks/26-pr2c9ro222.js +1 -0
  312. package/.next/static/chunks/{2ihkf6l2jowo9.js → 280t0_e99i276.js} +3 -3
  313. package/.next/static/chunks/28cizcdaabd4h.js +1 -0
  314. package/.next/static/chunks/2a0k6jlq0_v1u.js +2 -0
  315. package/.next/static/chunks/2fzs9fm8yarvq.js +1 -0
  316. package/.next/static/chunks/2h0sd2vumhb15.js +1 -0
  317. package/.next/static/chunks/2hw2f6zmt7fmt.js +1 -0
  318. package/.next/static/chunks/2t4vhi3m8ahya.js +1 -0
  319. package/.next/static/chunks/2tigdtz3dpcy8.js +1 -0
  320. package/.next/static/chunks/3111pj5wnjgos.js +1 -0
  321. package/.next/static/chunks/3786h5vayt80-.js +28 -0
  322. package/.next/static/chunks/{3aquirz5h71da.js → 3agpgkwaa321p.js} +1 -1
  323. package/.next/static/chunks/3cfi_q6d_wtci.js +1 -0
  324. package/.next/static/chunks/3ok9_x74rip3v.js +1 -0
  325. package/.next/static/chunks/3xk7yqgomx7yq.js +1 -0
  326. package/.next/static/chunks/3xpbkl66ssib5.js +38 -0
  327. package/.next/static/media/favicon.37o1pjldwi2uc.ico +0 -0
  328. package/README.md +6 -4
  329. package/app/api/bot/memory/files/route.ts +14 -0
  330. package/app/api/bot/memory/route.ts +5 -0
  331. package/app/api/events/app-event.client.ts +24 -0
  332. package/app/api/events/app-event.server.ts +45 -0
  333. package/app/api/events/app-event.ts +42 -0
  334. package/app/api/llm-model/catalog/route.ts +12 -0
  335. package/app/api/llm-model/credits/route.ts +11 -0
  336. package/app/api/llm-model/route.ts +26 -0
  337. package/app/api/llm-model/subscription/route.ts +11 -0
  338. package/app/api/memory/edit/route.ts +7 -0
  339. package/app/api/memory/route.ts +21 -0
  340. package/app/api/query-key.ts +159 -0
  341. package/app/api/thursday/tool-call/route.ts +67 -0
  342. package/bin/thursday.mjs +3 -0
  343. package/components/ui/button-group.tsx +45 -0
  344. package/components/ui/custom-icon.tsx +259 -0
  345. package/components/ui/dropdown-menu.tsx +271 -0
  346. package/components/ui/folded-text.tsx +68 -0
  347. package/components/ui/input-group.tsx +157 -0
  348. package/components/ui/json-view.tsx +240 -0
  349. package/components/ui/shiny-text.tsx +55 -0
  350. package/components/ui/text-type.tsx +213 -0
  351. package/components/ui/theme-sync.tsx +9 -0
  352. package/database/migrations/{20260908030742_rainy_marvel_zombies → 20260910173049_numerous_spirit}/migration.sql +30 -33
  353. package/database/migrations/{20260908030742_rainy_marvel_zombies → 20260910173049_numerous_spirit}/snapshot.json +124 -145
  354. package/database/migrations/20260911030320_opposite_jack_power/migration.sql +1 -0
  355. package/database/migrations/20260911030320_opposite_jack_power/snapshot.json +1158 -0
  356. package/docs/how-it-works.md +59 -0
  357. package/docs/images/keeps-going.png +0 -0
  358. package/docs/images/task-thread.png +0 -0
  359. package/docs/images/two-minds.png +0 -0
  360. package/features/ai/chatgpt.ts +617 -0
  361. package/features/ai/components/chatgpt-sign-in.tsx +66 -0
  362. package/features/ai/components/model-picker.tsx +17 -1
  363. package/features/ai/components/provider-icon.tsx +1 -0
  364. package/features/ai/load-tools.ts +105 -39
  365. package/features/ai/model.schema.ts +232 -34
  366. package/features/ai/model.ts +169 -24
  367. package/features/ai/prompts/bot.prompt.ts +245 -59
  368. package/features/ai/prompts/memory-edit.prompt.ts +33 -0
  369. package/features/ai/prompts/prompt-helper.ts +75 -30
  370. package/features/ai/prompts/thursday.prompt.ts +42 -17
  371. package/features/ai/tools/bot.tool.ts +32 -30
  372. package/features/ai/tools/connected.ts +17 -4
  373. package/features/ai/tools/mcp.tool.ts +5 -5
  374. package/features/ai/tools/memory.tool.ts +142 -74
  375. package/features/ai/tools/search.tool.ts +136 -47
  376. package/features/ai/tools/studio.tool.ts +5 -5
  377. package/features/ai/tools/tool-name.ts +2 -3
  378. package/features/ai/tools/workspace.tool.ts +33 -4
  379. package/features/bot/bot.action.ts +24 -7
  380. package/features/bot/bot.memory.ts +89 -0
  381. package/features/bot/bot.query.ts +41 -6
  382. package/features/bot/bot.run.ts +516 -210
  383. package/features/bot/bot.runner.ts +463 -93
  384. package/features/bot/bot.schema.ts +84 -7
  385. package/features/bot/bot.seed.ts +89 -16
  386. package/features/bot/components/bot-mark.tsx +122 -20
  387. package/features/bot/components/bot-room.tsx +519 -391
  388. package/features/bot/components/bot-setting.tsx +832 -341
  389. package/features/bot/components/bot-tool.tsx +2 -6
  390. package/features/bot/components/mark-palette.tsx +54 -0
  391. package/features/bot/components/path-chips.tsx +1 -1
  392. package/features/bot/components/task-badge.tsx +26 -6
  393. package/features/bot/components/task-reply.tsx +22 -33
  394. package/features/bot/components/task-setting.tsx +20 -17
  395. package/features/bot/task.query.ts +233 -33
  396. package/features/bot/task.store.ts +53 -9
  397. package/features/config/components/config-setting.tsx +265 -34
  398. package/features/config/components/models-badge.tsx +42 -0
  399. package/features/config/config.action.ts +12 -0
  400. package/features/config/config.const.ts +38 -2
  401. package/features/connectors/mcp.manager.ts +10 -2
  402. package/features/intro/components/intro.tsx +47 -46
  403. package/features/memory/components/memory-edit.tsx +338 -0
  404. package/features/memory/components/memory-setting.tsx +86 -79
  405. package/features/memory/memory.action.ts +3 -43
  406. package/features/memory/memory.edit.ts +87 -0
  407. package/features/memory/memory.query.ts +33 -44
  408. package/features/memory/memory.schema.ts +50 -97
  409. package/features/settings/components/settings.tsx +2 -0
  410. package/features/settings/settings.alert.ts +3 -1
  411. package/features/skills/components/skills-setting.tsx +9 -1
  412. package/features/thursday/components/ascii-orb.tsx +106 -90
  413. package/features/thursday/components/boot.tsx +7 -0
  414. package/features/thursday/components/call-log.tsx +219 -92
  415. package/features/thursday/components/face.tsx +6 -4
  416. package/features/thursday/components/tab-state.tsx +97 -0
  417. package/features/thursday/components/thursday-mark.tsx +4 -1
  418. package/features/thursday/components/thursday-setting.tsx +17 -47
  419. package/features/thursday/components/thursday.tsx +208 -95
  420. package/features/thursday/thursday.action.ts +12 -17
  421. package/features/thursday/thursday.query.ts +61 -70
  422. package/features/thursday/thursday.schema.ts +20 -7
  423. package/features/thursday/tool-line.ts +85 -11
  424. package/features/thursday/use-thursday.ts +86 -59
  425. package/features/workspace/components/file-view.tsx +4 -2
  426. package/features/workspace/workspace.ts +230 -13
  427. package/hooks/use-draft.ts +37 -0
  428. package/hooks/use-hotkey.ts +105 -0
  429. package/hooks/use-object-state.ts +51 -0
  430. package/hooks/use-on-visible.ts +72 -0
  431. package/hooks/use-theme.ts +106 -0
  432. package/hooks/use-wake-word.ts +262 -0
  433. package/lib/limits.ts +1 -1
  434. package/lib/oauth-page.ts +27 -0
  435. package/lib/realtime/openai.ts +2 -2
  436. package/lib/realtime/realtime.driver.ts +34 -1
  437. package/lib/realtime/realtime.protocol.ts +7 -1
  438. package/lib/realtime/realtime.tap.ts +122 -0
  439. package/lib/sandbox.ts +60 -29
  440. package/lib/tokens.ts +19 -0
  441. package/package.json +1 -1
  442. package/skills/browser/SKILL.md +55 -34
  443. package/.next/server/app/api/memory/tidy/route/app-paths-manifest.json +0 -3
  444. package/.next/server/app/api/memory/tidy/route.js +0 -23
  445. package/.next/server/app/api/memory/tidy/route.js.nft.json +0 -1
  446. package/.next/server/chunks/[root-of-the-server]__019dx9f._.js +0 -5
  447. package/.next/server/chunks/[root-of-the-server]__02_2sm-._.js +0 -5
  448. package/.next/server/chunks/[root-of-the-server]__02kt8yn._.js +0 -10
  449. package/.next/server/chunks/[root-of-the-server]__05luceg._.js +0 -3
  450. package/.next/server/chunks/[root-of-the-server]__06t77t4._.js +0 -11
  451. package/.next/server/chunks/[root-of-the-server]__0bed6hu._.js +0 -8
  452. package/.next/server/chunks/[root-of-the-server]__0dupqt7._.js +0 -5
  453. package/.next/server/chunks/[root-of-the-server]__0h59di-._.js +0 -5
  454. package/.next/server/chunks/[root-of-the-server]__0j_n5l3._.js +0 -3
  455. package/.next/server/chunks/[root-of-the-server]__0le4d-p._.js +0 -3
  456. package/.next/server/chunks/[root-of-the-server]__0m8ywyo._.js +0 -10
  457. package/.next/server/chunks/[root-of-the-server]__0nfuugi._.js +0 -5
  458. package/.next/server/chunks/[root-of-the-server]__0phdgco._.js +0 -8
  459. package/.next/server/chunks/[root-of-the-server]__0qjqtwo._.js +0 -5
  460. package/.next/server/chunks/[root-of-the-server]__0t_dqaj._.js +0 -3
  461. package/.next/server/chunks/[root-of-the-server]__0wvgrsl._.js +0 -3
  462. package/.next/server/chunks/[root-of-the-server]__0yk6s3w._.js +0 -5
  463. package/.next/server/chunks/[root-of-the-server]__0z3gelv._.js +0 -3
  464. package/.next/server/chunks/[root-of-the-server]__10qshki._.js +0 -3
  465. package/.next/server/chunks/[root-of-the-server]__113fqg5._.js +0 -12
  466. package/.next/server/chunks/[root-of-the-server]__11w_7_3._.js +0 -3
  467. package/.next/server/chunks/[root-of-the-server]__16thabu._.js +0 -3
  468. package/.next/server/chunks/[root-of-the-server]__173nhwf._.js +0 -8
  469. package/.next/server/chunks/[root-of-the-server]__1_0q_9w._.js +0 -3
  470. package/.next/server/chunks/[root-of-the-server]__1_aoyr9._.js +0 -3
  471. package/.next/server/chunks/[root-of-the-server]__1bqhuh9._.js +0 -8
  472. package/.next/server/chunks/[root-of-the-server]__1cqizfq._.js +0 -5
  473. package/.next/server/chunks/[root-of-the-server]__1dow0bz._.js +0 -3
  474. package/.next/server/chunks/[root-of-the-server]__1e-ci13._.js +0 -3
  475. package/.next/server/chunks/[root-of-the-server]__1gmn1cs._.js +0 -10
  476. package/.next/server/chunks/[root-of-the-server]__1lr7j9t._.js +0 -5
  477. package/.next/server/chunks/[root-of-the-server]__1mzp_el._.js +0 -3
  478. package/.next/server/chunks/[root-of-the-server]__1txedx6._.js +0 -8
  479. package/.next/server/chunks/[root-of-the-server]__1u_qvc7._.js +0 -3
  480. package/.next/server/chunks/[root-of-the-server]__1zgpz1w._.js +0 -3
  481. package/.next/server/chunks/_012yhfm._.js +0 -39
  482. package/.next/server/chunks/_06mxlgn._.js +0 -3
  483. package/.next/server/chunks/_07rtlxv._.js +0 -3
  484. package/.next/server/chunks/_0a351g7._.js +0 -6
  485. package/.next/server/chunks/_0kjjmag._.js +0 -3
  486. package/.next/server/chunks/_0zmestz._.js +0 -55
  487. package/.next/server/chunks/_0zuzmt_._.js +0 -39
  488. package/.next/server/chunks/_12bbv5i._.js +0 -96
  489. package/.next/server/chunks/_15q1pig._.js +0 -3
  490. package/.next/server/chunks/_1drv2et._.js +0 -36
  491. package/.next/server/chunks/_1ic3bew._.js +0 -5
  492. package/.next/server/chunks/_1mxn_lb._.js +0 -3
  493. package/.next/server/chunks/_1uj4_io._.js +0 -10
  494. package/.next/server/chunks/_1x7p319._.js +0 -6
  495. package/.next/server/chunks/_1yldgo4._.js +0 -55
  496. package/.next/server/chunks/_2050gwn._.js +0 -255
  497. package/.next/server/chunks/_next-internal_server_app_api_memory_tidy_route_actions_0_cfuom.js +0 -3
  498. package/.next/server/chunks/features_0scmr4i._.js +0 -3
  499. package/.next/server/chunks/features_bot_task_query_ts_11xuzl3._.js +0 -3
  500. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_0vorg6w._.js +0 -3
  501. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_0ykklj0._.js +0 -3
  502. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1ryg3wh._.js +0 -3
  503. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1vmaq9f._.js +0 -3
  504. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1vwbnq_._.js +0 -3
  505. package/.next/server/chunks/ssr/[externals]_node_crypto_1kn79-7._.js +0 -3
  506. package/.next/server/chunks/ssr/[root-of-the-server]__01id-v3._.js +0 -3
  507. package/.next/server/chunks/ssr/[root-of-the-server]__0b-0i32._.js +0 -3
  508. package/.next/server/chunks/ssr/[root-of-the-server]__0dfwvr4._.js +0 -4
  509. package/.next/server/chunks/ssr/[root-of-the-server]__0iuoa-r._.js +0 -3
  510. package/.next/server/chunks/ssr/[root-of-the-server]__1-mnhyd._.js +0 -90
  511. package/.next/server/chunks/ssr/[root-of-the-server]__185grso._.js +0 -3
  512. package/.next/server/chunks/ssr/[root-of-the-server]__1minz8_._.js +0 -3
  513. package/.next/server/chunks/ssr/_0-mc7ro._.js +0 -3
  514. package/.next/server/chunks/ssr/_01p5r6-._.js +0 -3
  515. package/.next/server/chunks/ssr/_09-0k3z._.js +0 -3
  516. package/.next/server/chunks/ssr/_0ay6b01._.js +0 -3
  517. package/.next/server/chunks/ssr/_0cvjbuz._.js +0 -3
  518. package/.next/server/chunks/ssr/_0fafgvl._.js +0 -3
  519. package/.next/server/chunks/ssr/_0j4v8of._.js +0 -3
  520. package/.next/server/chunks/ssr/_0l81bzy._.js +0 -3
  521. package/.next/server/chunks/ssr/_0nso8tp._.js +0 -3
  522. package/.next/server/chunks/ssr/_0sf1isi._.js +0 -3
  523. package/.next/server/chunks/ssr/_0ti51xc._.js +0 -3
  524. package/.next/server/chunks/ssr/_0y84qo6._.js +0 -3
  525. package/.next/server/chunks/ssr/_10uvw3l._.js +0 -3
  526. package/.next/server/chunks/ssr/_1dcxzqe._.js +0 -3
  527. package/.next/server/chunks/ssr/_1e-dgz0._.js +0 -4
  528. package/.next/server/chunks/ssr/_1fq7zgb._.js +0 -3
  529. package/.next/server/chunks/ssr/_1jbv7m1._.js +0 -3
  530. package/.next/server/chunks/ssr/features_bot_components_bot-setting_tsx_01ajft6._.js +0 -4
  531. package/.next/server/chunks/ssr/features_config_components_14jqgy4._.js +0 -3
  532. package/.next/server/chunks/ssr/features_memory_components_memory-setting_tsx_0oa43sw._.js +0 -4
  533. package/.next/server/chunks/ssr/lib_realtime_0aetuyp._.js +0 -24
  534. package/.next/server/chunks/ssr/lib_realtime_18k_aj-._.js +0 -24
  535. package/.next/server/chunks/ssr/node_modules_zod_v4_classic_errors_05xv7vp.js +0 -3
  536. package/.next/server/chunks/ssr/node_modules_zod_v4_classic_external_15fmmz3.js +0 -55
  537. package/.next/static/chunks/0f9sq-7q_yxyl.js +0 -1
  538. package/.next/static/chunks/0tydnn2kybibk.js +0 -1
  539. package/.next/static/chunks/0yysv1gzin17t.js +0 -1
  540. package/.next/static/chunks/1fr5m75pwtyic.js +0 -1
  541. package/.next/static/chunks/1gy3bbv7usnjo.js +0 -1
  542. package/.next/static/chunks/1keib_gt066gc.js +0 -1
  543. package/.next/static/chunks/1ytnip8wdng93.js +0 -1
  544. package/.next/static/chunks/21rk8w7bda-su.js +0 -1
  545. package/.next/static/chunks/247d-r95j8xs0.js +0 -1
  546. package/.next/static/chunks/28g42tw8tsvhd.js +0 -1
  547. package/.next/static/chunks/29gyb3nubj51j.js +0 -53
  548. package/.next/static/chunks/2cwjgkt_hoyhj.js +0 -22
  549. package/.next/static/chunks/2fq_86jvq9ymq.js +0 -2
  550. package/.next/static/chunks/2rbf1fpd8n80-.js +0 -22
  551. package/.next/static/chunks/2ukj8k69y89um.js +0 -1
  552. package/.next/static/chunks/2uxum7qbyeyxd.js +0 -1
  553. package/.next/static/chunks/2wngeetfnzxkp.js +0 -2
  554. package/.next/static/chunks/388-t2v72jh39.css +0 -1
  555. package/.next/static/chunks/3fqvc0cg-atq_.js +0 -1
  556. package/.next/static/chunks/3p4egvfjxpatb.js +0 -1
  557. package/.next/static/chunks/3qe1w1ei-huhl.js +0 -1
  558. package/.next/static/chunks/3xxa2j56-f3nm.js +0 -3
  559. package/.next/static/chunks/3y40riycdip-i.js +0 -1
  560. package/.next/static/chunks/45a6zbtkelph9.js +0 -1
  561. package/.next/static/media/favicon.2vob68tjqpejf.ico +0 -0
  562. package/features/ai/prompts/memory-tidy.prompt.ts +0 -123
  563. package/features/ai/tools/tidy.tool.ts +0 -22
  564. package/features/memory/components/memory-tidy.tsx +0 -395
  565. package/features/memory/memory.tidy.ts +0 -332
  566. package/features/memory/tidy.query.ts +0 -135
  567. package/features/thursday/use-call-title.ts +0 -31
  568. /package/.next/server/app/api/{memory/tidy → bot/memory/files}/route/build-manifest.json +0 -0
  569. /package/.next/server/app/api/{memory/tidy → bot/memory/files}/route/server-reference-manifest.json +0 -0
  570. /package/.next/server/app/api/{memory/tidy → bot/memory/files}/route.js.map +0 -0
  571. /package/.next/static/{X34BUd9bfdH27ripuwwBm → x1cCqY0kWpR_VcYx_lWO5}/_buildManifest.js +0 -0
  572. /package/.next/static/{X34BUd9bfdH27ripuwwBm → x1cCqY0kWpR_VcYx_lWO5}/_clientMiddlewareManifest.js +0 -0
  573. /package/.next/static/{X34BUd9bfdH27ripuwwBm → x1cCqY0kWpR_VcYx_lWO5}/_ssgManifest.js +0 -0
@@ -0,0 +1,59 @@
1
+ # How it works
2
+
3
+ The README says what Thursday is. This says what each part may touch, so you can guess what will happen before you say it.
4
+
5
+ ## The call
6
+
7
+ A call is a session with a realtime speech model — OpenAI Realtime or xAI Grok Voice, whichever key you gave. Audio in, audio out, interruptions and all. The model hears a prompt assembled the moment the line opens: who she is, what she remembers about you, which bots exist, what was said on the last calls.
8
+
9
+ She holds three things and nothing else:
10
+
11
+ - **Memory.** A folder of notes about you. She reads a note before answering out of it and writes the moment something worth keeping comes up — one fact per line, dates as dates. A rule you lay down ("answer in Korean", "keep it short") is carried into every call.
12
+ - **One command.** A shell, for the things that take a second: open a file, play something, look at what is in a folder. One command is something she does, not a job she hands over.
13
+ - **The roster.** The bots, by name, and what each is for.
14
+
15
+ Everything else — anything that takes more than a few seconds — she hands to a bot with `delegate` and keeps talking. That is deliberate: the call must never go silent, and a speech model that runs a browser would be silent for minutes.
16
+
17
+ When a job comes back, it arrives as a system note, not as you speaking. She tells you in one sentence, in her own words. A file the job made opens on your screen by itself; a question the job asks becomes buttons on screen and words in your ear.
18
+
19
+ ## Bots
20
+
21
+ A bot is a text model — any of OpenAI, Anthropic, Google, xAI, or whatever the Vercel AI Gateway carries — with the whole machine:
22
+
23
+ - a **shell** in a workspace folder, with no pager and no secrets in its environment
24
+ - a **real browser** (Chromium through Playwright), headless until a sign-in or something you should see, and able to attach to the Chrome you already use
25
+ - the **filesystem**, read anywhere, written only inside the workspace and where you pointed it
26
+ - **skills** — written-down methods it reads before starting: the browser, this Mac, building a page, finding and writing skills
27
+ - **MCP servers** you connected, searched by name and called by schema, with OAuth handled
28
+ - a **studio** of image, video, speech and transcription models
29
+ - **each other**: a bot hands a part of its job to the bot that exists for it, and folds the answer into its own report
30
+
31
+ A job ends in the thing that was asked for — the account made, the page built, the comparison in a table — and a report, written to be heard, because she reads it out loud. Anything that does not fit in a few lines is a file under `artifacts/`, and the report names it.
32
+
33
+ What only you can give — a password, a one-time code, a passkey, a decision between two real options — stops the job at that point. The bot sets it up one action away, asks, and continues when the answer comes.
34
+
35
+ Three bots come with the app, split by temperament: **Jarvis** plans a job, hands parts to whoever fits and does the rest; **Navigator** goes and looks; **Scribe** writes. Make more in settings: a name, a sentence about what it is for, optionally a model and a few pinned tools. The sentence is what she reads when deciding who gets the job.
36
+
37
+ ## Jobs outlive the call
38
+
39
+ A job runs on the server. It is not in the tab, and it does not stop when you hang up. Every step — the request, each tool call, what came back, the report — is written as a row, and the screen is a projection of those rows. When you come back, the jobs that finished are waiting, and she tells you about them on the next call, or rings you if you turned that on.
40
+
41
+ A job that stopped to ask, or ran out of its step budget, is resumed from where it stopped: the same thread, with your answer appended. A long thread is compacted — the model summarizes what happened so far and continues from the summary.
42
+
43
+ ## Memory is a folder
44
+
45
+ Memory is plain notes on your disk, in the data folder (`~/.thursday` when installed with `npx`, the checkout otherwise). Each note is a topic — `profile`, `preferences`, `people/…`, and whatever she creates. A few are `alwaysLoad` and travel into every call: your name, your language, how you like to be addressed, the rules you gave her.
46
+
47
+ You can open any note from settings and read exactly what she knows. When the listing gets long she says so in a lull, puts a note on your screen and forgets only what you name.
48
+
49
+ Writing mid-conversation catches what she noticed; it misses what only reads back as important afterwards. So when a call ends, a text model re-reads the most recent turns and reconciles the notes with them — correcting what changed, adding what was said in passing. Calls are stamped as they are read, so a backlog of short calls collapses into one pass rather than one per call.
50
+
51
+ Bots read memory but do not write it. What a bot learns goes into its report; whether it is worth keeping is her call, with you.
52
+
53
+ ## What is not here
54
+
55
+ - **No cloud.** The server is the one on your machine; keys are stored locally and sent only to the provider you chose.
56
+ - **No account.** There is nothing to sign up for. There is also no login, which is why the server binds to `127.0.0.1` — see [SECURITY.md](../SECURITY.md).
57
+ - **No prompt engineering on your side.** The prompts are assembled from your data on every session; the one thing you can add is an instruction to her, and one to each bot, in settings.
58
+
59
+ For the code — which file owns what, and why — read [CLAUDE.md](../CLAUDE.md).
Binary file
Binary file
Binary file
@@ -0,0 +1,617 @@
1
+ import { createHash, randomBytes, randomUUID } from "node:crypto";
2
+ import { createServer, type Server } from "node:http";
3
+ import { createOpenAI } from "@ai-sdk/openai";
4
+ import { type LanguageModel, wrapLanguageModel } from "ai";
5
+ import { formatDistanceToNowStrict } from "date-fns";
6
+ import { z } from "zod";
7
+ import { appEvents } from "@/app/api/events/app-event.server";
8
+ import { CHATGPT_SIGN_IN, CHATGPT_USAGE_HIGH } from "@/config";
9
+ import { readConfig, writeConfig } from "@/features/config/config.query";
10
+ import { logger } from "@/lib/logger";
11
+ import { oauthPage } from "@/lib/oauth-page";
12
+ import { publicError } from "@/lib/public-error";
13
+ import { createKeyedLock } from "@/lib/queue";
14
+ import { errorToString } from "@/lib/utils";
15
+ import { type SubscriptionUsage, TEXT_MODEL_PROVIDERS } from "./model.schema";
16
+
17
+ /**
18
+ * ChatGPT as a text provider: the Codex usage a ChatGPT plan carries, in place of an API key.
19
+ * Signing in is OpenAI's OAuth for the Codex CLI, the way other agent apps reach a plan. The
20
+ * model is the Responses API behind chatgpt.com, which takes less than api.openai.com does;
21
+ * `codexFetch` is the difference.
22
+ */
23
+
24
+ /** The Codex CLI's public OAuth client. */
25
+ const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
26
+ const AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize";
27
+ const TOKEN_URL = "https://auth.openai.com/oauth/token";
28
+ /** The only redirect registered for that client, so the answer comes back to this port and no other. */
29
+ const CALLBACK_PORT = 1455;
30
+ const REDIRECT_URI = `http://localhost:${CALLBACK_PORT}/auth/callback`;
31
+ const SCOPE = "openid profile email offline_access";
32
+ const CODEX_URL = "https://chatgpt.com/backend-api/codex";
33
+ /** The access token's claim naming the account and its plan. */
34
+ const AUTH_CLAIM = "https://api.openai.com/auth";
35
+ /** How this app names itself to OpenAI. */
36
+ const ORIGINATOR = "thursday";
37
+
38
+ const { apiKeyName: SIGN_IN_KEY, label: LABEL } = TEXT_MODEL_PROVIDERS.chatgpt;
39
+ const NOT_SIGNED_IN = `${LABEL} is not signed in — sign in from Config.`;
40
+
41
+ /** What a sign-in leaves in config. Tokens: it never reaches the browser. */
42
+ const SignInSchema = z.object({
43
+ access: z.string().min(1),
44
+ refresh: z.string().min(1),
45
+ /** When the access token runs out, epoch ms. */
46
+ expires: z.number(),
47
+ accountId: z.string().min(1),
48
+ /** The plan as the token names it ("free", "plus", "pro"); null when it does not say. */
49
+ plan: z.string().nullable(),
50
+ });
51
+ type SignIn = z.infer<typeof SignInSchema>;
52
+
53
+ type Pinned = {
54
+ __chatgptListener?: Server | null;
55
+ __chatgptRenewal?: ReturnType<typeof createKeyedLock>;
56
+ };
57
+
58
+ /** Pinned to globalThis: next dev reloads this module, and the listener holding the port and the renewal lane must be the originals. */
59
+ const pinned = globalThis as Pinned;
60
+
61
+ /**
62
+ * One renewal at a time. A refresh token is spent by using it, so two requests renewing at
63
+ * once leave one holding a token OpenAI has already retired — which signs the app out.
64
+ */
65
+ const renewal = (pinned.__chatgptRenewal ??= createKeyedLock());
66
+
67
+ /**
68
+ * Starts a sign-in and returns the page to send the browser to. The answer comes back to the
69
+ * loopback port, so the app listens there until it arrives or `CHATGPT_SIGN_IN.waitMs` passes;
70
+ * starting again replaces a sign-in still waiting.
71
+ */
72
+ export async function startChatGptSignIn(): Promise<string> {
73
+ stopListening();
74
+ const verifier = randomBytes(32).toString("base64url");
75
+ const state = randomBytes(16).toString("hex");
76
+
77
+ const server = createServer((request, response) => {
78
+ void answerCallback(request.url ?? "/", { state, verifier }).then(
79
+ (page) => {
80
+ response.writeHead(page.status, {
81
+ "content-type": "text/html; charset=utf-8",
82
+ });
83
+ response.end(
84
+ oauthPage(page.title, page.detail, {
85
+ autoClose: page.status === 200,
86
+ }),
87
+ () => {
88
+ if (page.last && pinned.__chatgptListener === server) {
89
+ stopListening();
90
+ }
91
+ },
92
+ );
93
+ },
94
+ );
95
+ });
96
+
97
+ await new Promise<void>((resolve, reject) => {
98
+ server.once("error", reject);
99
+ server.listen(CALLBACK_PORT, "127.0.0.1", () => resolve());
100
+ }).catch((cause: NodeJS.ErrnoException) => {
101
+ publicError(
102
+ cause.code === "EADDRINUSE"
103
+ ? `Port ${CALLBACK_PORT} is in use — another ChatGPT or Codex sign-in is waiting. Finish or close it, then try again.`
104
+ : `Could not wait for the sign-in: ${errorToString(cause)}`,
105
+ );
106
+ });
107
+ pinned.__chatgptListener = server;
108
+ // A page nobody finishes must not hold the port for the life of the process
109
+ setTimeout(() => {
110
+ if (pinned.__chatgptListener === server) stopListening();
111
+ }, CHATGPT_SIGN_IN.waitMs).unref();
112
+
113
+ const url = new URL(AUTHORIZE_URL);
114
+ url.search = new URLSearchParams({
115
+ response_type: "code",
116
+ client_id: CLIENT_ID,
117
+ redirect_uri: REDIRECT_URI,
118
+ scope: SCOPE,
119
+ code_challenge: createHash("sha256").update(verifier).digest("base64url"),
120
+ code_challenge_method: "S256",
121
+ state,
122
+ // What the Codex CLI asks for: the account's organizations in the token, and the short consent page
123
+ id_token_add_organizations: "true",
124
+ codex_cli_simplified_flow: "true",
125
+ originator: ORIGINATOR,
126
+ }).toString();
127
+ return url.toString();
128
+ }
129
+
130
+ function stopListening() {
131
+ const server = pinned.__chatgptListener;
132
+ pinned.__chatgptListener = null;
133
+ server?.close();
134
+ server?.closeIdleConnections();
135
+ }
136
+
137
+ type CallbackPage = {
138
+ status: number;
139
+ title: string;
140
+ detail: string;
141
+ /** The sign-in is over, however it went, so the port is let go. */
142
+ last: boolean;
143
+ };
144
+
145
+ async function answerCallback(
146
+ path: string,
147
+ flow: { state: string; verifier: string },
148
+ ): Promise<CallbackPage> {
149
+ const url = new URL(path, REDIRECT_URI);
150
+ // A browser asks the same port for a favicon; that is not the answer
151
+ if (url.pathname !== "/auth/callback") {
152
+ return { status: 404, title: "Not found", detail: "", last: false };
153
+ }
154
+
155
+ const error = url.searchParams.get("error");
156
+ if (error) {
157
+ const description = url.searchParams.get("error_description");
158
+ return {
159
+ status: 400,
160
+ title: "Sign-in failed",
161
+ detail: description ? `${error}: ${description}` : error,
162
+ last: true,
163
+ };
164
+ }
165
+ const code = url.searchParams.get("code");
166
+ if (!code || url.searchParams.get("state") !== flow.state) {
167
+ return {
168
+ status: 400,
169
+ title: "Sign-in failed",
170
+ detail:
171
+ "This is not the sign-in that was started. Start again from Config.",
172
+ last: false,
173
+ };
174
+ }
175
+
176
+ try {
177
+ const signIn = await requestToken({
178
+ grant_type: "authorization_code",
179
+ code,
180
+ code_verifier: flow.verifier,
181
+ redirect_uri: REDIRECT_URI,
182
+ });
183
+ await writeConfig(SIGN_IN_KEY, JSON.stringify(signIn));
184
+ // The screen that opened this page is waiting on it
185
+ appEvents.emit({ type: "config" });
186
+ return {
187
+ status: 200,
188
+ title: "Signed in to ChatGPT",
189
+ detail: "Bots can run on your plan now. This window closes itself.",
190
+ last: true,
191
+ };
192
+ } catch (cause) {
193
+ logger.warn({ cause }, "chatgpt sign-in failed");
194
+ return {
195
+ status: 502,
196
+ title: "Sign-in failed",
197
+ detail: errorToString(cause),
198
+ last: true,
199
+ };
200
+ }
201
+ }
202
+
203
+ /** The account, plan and expiry an access token carries. Read, never verified: it came from OpenAI over TLS. */
204
+ function claimsOf(access: string) {
205
+ try {
206
+ const payload = JSON.parse(
207
+ Buffer.from(access.split(".")[1] ?? "", "base64url").toString("utf8"),
208
+ ) as Record<string, unknown>;
209
+ const auth = (payload[AUTH_CLAIM] ?? {}) as Record<string, unknown>;
210
+ return {
211
+ accountId:
212
+ typeof auth.chatgpt_account_id === "string"
213
+ ? auth.chatgpt_account_id
214
+ : null,
215
+ plan:
216
+ typeof auth.chatgpt_plan_type === "string"
217
+ ? auth.chatgpt_plan_type
218
+ : null,
219
+ expires: typeof payload.exp === "number" ? payload.exp * 1000 : null,
220
+ };
221
+ } catch {
222
+ return { accountId: null, plan: null, expires: null };
223
+ }
224
+ }
225
+
226
+ /** One call to OpenAI's token endpoint — the first exchange, or a renewal. A refusal comes back in its own words. */
227
+ async function requestToken(fields: Record<string, string>): Promise<SignIn> {
228
+ const response = await fetch(TOKEN_URL, {
229
+ method: "POST",
230
+ headers: {
231
+ accept: "application/json",
232
+ "content-type": "application/x-www-form-urlencoded",
233
+ },
234
+ body: new URLSearchParams({ client_id: CLIENT_ID, ...fields }),
235
+ }).catch((cause: unknown) => {
236
+ logger.warn({ cause }, "chatgpt token endpoint unreachable");
237
+ publicError("Could not reach ChatGPT's sign-in");
238
+ });
239
+ const body = (await response.json().catch(() => null)) as {
240
+ access_token?: string;
241
+ refresh_token?: string;
242
+ expires_in?: number;
243
+ error?: string | { message?: string; code?: string };
244
+ error_description?: string;
245
+ } | null;
246
+
247
+ const access = body?.access_token;
248
+ if (!response.ok || !access) {
249
+ const error = body?.error;
250
+ const said =
251
+ typeof error === "string"
252
+ ? [error, body?.error_description].filter(Boolean).join(": ")
253
+ : (error?.message ?? error?.code ?? "");
254
+ publicError(
255
+ `ChatGPT's sign-in answered ${response.status}${said ? `: ${said}` : ""}`,
256
+ );
257
+ }
258
+
259
+ const claims = claimsOf(access);
260
+ const refresh = body?.refresh_token ?? fields.refresh_token;
261
+ if (!claims.accountId || !refresh) {
262
+ publicError("ChatGPT's sign-in came back without an account");
263
+ }
264
+ const expiresIn = body?.expires_in;
265
+ return {
266
+ access,
267
+ refresh,
268
+ expires:
269
+ typeof expiresIn === "number"
270
+ ? Date.now() + expiresIn * 1000
271
+ : (claims.expires ?? Date.now()),
272
+ accountId: claims.accountId,
273
+ plan: claims.plan,
274
+ };
275
+ }
276
+
277
+ async function readSignIn(): Promise<SignIn | null> {
278
+ const stored = await readConfig(SIGN_IN_KEY);
279
+ if (!stored) return null;
280
+ try {
281
+ return SignInSchema.parse(JSON.parse(stored));
282
+ } catch {
283
+ return null;
284
+ }
285
+ }
286
+
287
+ const expiring = (signIn: SignIn) =>
288
+ signIn.expires - CHATGPT_SIGN_IN.renewBeforeMs <= Date.now();
289
+
290
+ /** The sign-in to send a request with, renewed first when its access token is about to run out. */
291
+ async function currentSignIn(): Promise<SignIn> {
292
+ const stored = await readSignIn();
293
+ if (!stored) publicError(NOT_SIGNED_IN);
294
+ if (!expiring(stored)) return stored;
295
+
296
+ return renewal(SIGN_IN_KEY, async () => {
297
+ // Another request may have renewed it while this one waited its turn
298
+ const latest = await readSignIn();
299
+ if (!latest) publicError(NOT_SIGNED_IN);
300
+ if (!expiring(latest)) return latest;
301
+ try {
302
+ const renewed = await requestToken({
303
+ grant_type: "refresh_token",
304
+ refresh_token: latest.refresh,
305
+ });
306
+ await writeConfig(SIGN_IN_KEY, JSON.stringify(renewed));
307
+ return renewed;
308
+ } catch (cause) {
309
+ publicError(
310
+ `Could not renew the ${LABEL} sign-in (${errorToString(cause)}). If it keeps failing, sign in again from Config.`,
311
+ );
312
+ }
313
+ });
314
+ }
315
+
316
+ /** The plan the signed-in account is on, for the settings screen; null when nobody is signed in. */
317
+ export async function readChatGptPlan(): Promise<string | null> {
318
+ return (await readSignIn())?.plan ?? null;
319
+ }
320
+
321
+ /** Answers the sign-in's own token, so no second key is needed to say what is left. */
322
+ const USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
323
+
324
+ type UsageWindow = {
325
+ used_percent?: number;
326
+ /** Epoch seconds. */
327
+ reset_at?: number;
328
+ reset_after_seconds?: number;
329
+ };
330
+
331
+ /**
332
+ * How much of the plan the signed-in account has used, by its tightest window — Free has one of
333
+ * 30 days, a paid plan a short one and a weekly one — and when that window frees up; null when
334
+ * nobody is signed in. A sign-in the plan turns away is a state of the sign-in, not a failed
335
+ * read, so it comes back as `refused` in OpenAI's words.
336
+ */
337
+ export async function readChatGptUsage(): Promise<SubscriptionUsage | null> {
338
+ if (!(await readSignIn())) return null;
339
+ let signIn: SignIn;
340
+ try {
341
+ signIn = await currentSignIn();
342
+ } catch (cause) {
343
+ return { refused: errorToString(cause) };
344
+ }
345
+
346
+ const response = await fetch(USAGE_URL, {
347
+ headers: {
348
+ accept: "application/json",
349
+ authorization: `Bearer ${signIn.access}`,
350
+ "chatgpt-account-id": signIn.accountId,
351
+ originator: ORIGINATOR,
352
+ },
353
+ }).catch((cause: unknown) => {
354
+ logger.warn({ cause }, "chatgpt usage unreachable");
355
+ publicError("Could not reach ChatGPT");
356
+ });
357
+ const body = (await response.json().catch(() => null)) as {
358
+ plan_type?: string;
359
+ rate_limit?: {
360
+ limit_reached?: boolean;
361
+ primary_window?: UsageWindow | null;
362
+ secondary_window?: UsageWindow | null;
363
+ };
364
+ detail?: string;
365
+ error?: { message?: string };
366
+ } | null;
367
+
368
+ if (response.status === 401 || response.status === 403) {
369
+ return {
370
+ refused:
371
+ body?.error?.message ??
372
+ body?.detail ??
373
+ `ChatGPT answered ${response.status}`,
374
+ };
375
+ }
376
+ if (!response.ok) publicError(`ChatGPT answered ${response.status}`);
377
+
378
+ const tightest = [
379
+ body?.rate_limit?.primary_window,
380
+ body?.rate_limit?.secondary_window,
381
+ ]
382
+ .filter(
383
+ (window): window is UsageWindow =>
384
+ typeof window?.used_percent === "number",
385
+ )
386
+ .sort((a, b) => (b.used_percent ?? 0) - (a.used_percent ?? 0))[0];
387
+ if (!tightest)
388
+ publicError("ChatGPT did not say how much of the plan is used");
389
+
390
+ const usedPercent = Math.round(tightest.used_percent ?? 0);
391
+ const spent = Boolean(body?.rate_limit?.limit_reached);
392
+ const resetsAt = tightest.reset_at
393
+ ? tightest.reset_at * 1000
394
+ : tightest.reset_after_seconds
395
+ ? Date.now() + tightest.reset_after_seconds * 1000
396
+ : null;
397
+ return {
398
+ plan: body?.plan_type ?? signIn.plan,
399
+ usedPercent,
400
+ resetsAt: resetsAt ? new Date(resetsAt).toISOString() : null,
401
+ spent,
402
+ high: spent || usedPercent >= CHATGPT_USAGE_HIGH,
403
+ };
404
+ }
405
+
406
+ /** A Codex model on the signed-in plan. */
407
+ export function chatGptModel(modelId: string): LanguageModel {
408
+ const chatgpt = createOpenAI({
409
+ baseURL: CODEX_URL,
410
+ // The sdk builds no request without a key; codexFetch signs every one with the sign-in instead
411
+ apiKey: "signed-in",
412
+ headers: { "OpenAI-Beta": "responses=experimental" },
413
+ fetch: codexFetch,
414
+ });
415
+ // One key for the model's life — a run, its compactions, its answers — so its calls share a prompt cache
416
+ const promptCacheKey = randomUUID();
417
+ return wrapLanguageModel({
418
+ model: chatgpt.responses(modelId),
419
+ middleware: {
420
+ // Unstored: the sdk then sends whole items rather than ids the backend never kept, and asks
421
+ // for the encrypted reasoning a stored thread needs to be replayed
422
+ transformParams: async ({ params }) => ({
423
+ ...params,
424
+ providerOptions: {
425
+ ...params.providerOptions,
426
+ openai: {
427
+ promptCacheKey,
428
+ ...params.providerOptions?.openai,
429
+ store: false,
430
+ },
431
+ },
432
+ }),
433
+ },
434
+ });
435
+ }
436
+
437
+ type CodexBody = {
438
+ input?: { role?: string; content?: string | { text?: string }[] }[];
439
+ instructions?: string;
440
+ stream?: boolean;
441
+ store?: boolean;
442
+ max_output_tokens?: number;
443
+ };
444
+
445
+ /**
446
+ * What the ChatGPT backend asks of a Responses call that api.openai.com does not, applied to
447
+ * every request the sdk makes: signed with the current sign-in, so a job that outlives a token
448
+ * renews it; always streamed and never stored; the system prompt as `instructions`, which it
449
+ * refuses a call without; no output cap.
450
+ */
451
+ async function codexFetch(
452
+ input: RequestInfo | URL,
453
+ init?: RequestInit,
454
+ ): Promise<Response> {
455
+ const signIn = await currentSignIn();
456
+ const headers = new Headers(init?.headers);
457
+ headers.set("authorization", `Bearer ${signIn.access}`);
458
+ headers.set("chatgpt-account-id", signIn.accountId);
459
+ headers.set("originator", ORIGINATOR);
460
+
461
+ if (
462
+ init?.method !== "POST" ||
463
+ typeof init.body !== "string" ||
464
+ !String(input).endsWith("/responses")
465
+ ) {
466
+ return fetch(input, { ...init, headers });
467
+ }
468
+
469
+ const body = JSON.parse(init.body) as CodexBody;
470
+ const streamed = body.stream === true;
471
+ const system: string[] = [];
472
+ body.input = (body.input ?? []).filter((item) => {
473
+ if (item.role !== "system" && item.role !== "developer") return true;
474
+ system.push(
475
+ typeof item.content === "string"
476
+ ? item.content
477
+ : (item.content ?? []).map((part) => part.text ?? "").join("\n"),
478
+ );
479
+ return false;
480
+ });
481
+ body.instructions =
482
+ [body.instructions, ...system].filter(Boolean).join("\n\n") ||
483
+ "You are a helpful assistant.";
484
+ body.store = false;
485
+ body.stream = true;
486
+ body.max_output_tokens = undefined;
487
+
488
+ headers.set("accept", "text/event-stream");
489
+ const response = await fetch(input, {
490
+ ...init,
491
+ headers,
492
+ body: JSON.stringify(body),
493
+ });
494
+ if (response.status === 429) return usageLimitOf(response);
495
+ if (streamed || !response.ok) return response;
496
+ return finishedOf(response);
497
+ }
498
+
499
+ /**
500
+ * A 429 is either a rate limit a moment fixes or the plan's usage spent until its window resets
501
+ * — hours, or on Free a month. The second becomes a 402 in the backend's words: the sdk does not
502
+ * retry it, and a job fails with it (model.ts modelFailureOf) instead of waking every few
503
+ * minutes to be refused again.
504
+ */
505
+ async function usageLimitOf(response: Response): Promise<Response> {
506
+ const text = await response.text();
507
+ let said: {
508
+ code?: string;
509
+ type?: string;
510
+ plan_type?: string;
511
+ resets_at?: number;
512
+ resets_in_seconds?: number;
513
+ } = {};
514
+ try {
515
+ said = (JSON.parse(text) as { error?: typeof said }).error ?? {};
516
+ } catch {}
517
+
518
+ if (
519
+ !/usage_limit_reached|usage_not_included/.test(`${said.code} ${said.type}`)
520
+ ) {
521
+ const retryAfter = response.headers.get("retry-after");
522
+ return new Response(text, {
523
+ status: 429,
524
+ headers: {
525
+ "content-type": "application/json",
526
+ ...(retryAfter ? { "retry-after": retryAfter } : {}),
527
+ },
528
+ });
529
+ }
530
+
531
+ const resets = said.resets_at
532
+ ? said.resets_at * 1000
533
+ : said.resets_in_seconds
534
+ ? Date.now() + said.resets_in_seconds * 1000
535
+ : null;
536
+ const plan = said.plan_type ? ` on the ${said.plan_type} plan` : "";
537
+ const when = resets
538
+ ? ` It resets in ${formatDistanceToNowStrict(resets)}.`
539
+ : "";
540
+ return Response.json(
541
+ {
542
+ error: {
543
+ message: `${LABEL} usage limit reached${plan}.${when}`,
544
+ code: said.code ?? said.type,
545
+ },
546
+ },
547
+ { status: 402 },
548
+ );
549
+ }
550
+
551
+ type StreamEvent = {
552
+ type?: string;
553
+ item?: unknown;
554
+ response?: {
555
+ output?: unknown[];
556
+ error?: { code?: string; message?: string } | null;
557
+ };
558
+ code?: string;
559
+ message?: string;
560
+ };
561
+
562
+ /**
563
+ * The one JSON body a caller that did not stream expects, read off the stream the backend
564
+ * insists on. The finished event's `output` arrives empty, so items are gathered as each
565
+ * completes. A failed response becomes an error in its own words: a 400 when it was the
566
+ * context, which a compaction fixes, a 500 otherwise.
567
+ */
568
+ async function finishedOf(stream: Response): Promise<Response> {
569
+ const items: unknown[] = [];
570
+ let finished: { output?: unknown[] } | null = null;
571
+ let failure: { code?: string; message?: string } | null = null;
572
+
573
+ const blocks = (await stream.text()).replaceAll("\r\n", "\n").split("\n\n");
574
+ for (const block of blocks) {
575
+ const data = block
576
+ .split("\n")
577
+ .filter((line) => line.startsWith("data:"))
578
+ .map((line) => line.slice(5).trimStart())
579
+ .join("\n");
580
+ if (!data || data === "[DONE]") continue;
581
+ let event: StreamEvent;
582
+ try {
583
+ event = JSON.parse(data) as StreamEvent;
584
+ } catch {
585
+ continue;
586
+ }
587
+ if (event.type === "response.output_item.done") {
588
+ items.push(event.item);
589
+ } else if (
590
+ event.type === "response.completed" ||
591
+ event.type === "response.incomplete"
592
+ ) {
593
+ finished = event.response ?? {};
594
+ } else if (event.type === "response.failed") {
595
+ failure = event.response?.error ?? { message: "The response failed" };
596
+ } else if (event.type === "error") {
597
+ failure = { code: event.code, message: event.message };
598
+ }
599
+ }
600
+
601
+ if (failure || !finished) {
602
+ return Response.json(
603
+ {
604
+ error: {
605
+ message:
606
+ failure?.message ?? "The stream ended before the response finished",
607
+ code: failure?.code,
608
+ },
609
+ },
610
+ { status: failure?.code === "context_length_exceeded" ? 400 : 500 },
611
+ );
612
+ }
613
+ return Response.json({
614
+ ...finished,
615
+ output: finished.output?.length ? finished.output : items,
616
+ });
617
+ }