thursday-agent 0.2.0 → 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 (535) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +5 -2
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/prerender-manifest.json +5 -5
  5. package/.next/routes-manifest.json +24 -6
  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/page_client-reference-manifest.js +1 -1
  9. package/.next/server/app/_global-error.html +1 -1
  10. package/.next/server/app/_global-error.rsc +2 -2
  11. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
  13. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next/server/app/_not-found/page.js +2 -2
  15. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  16. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  17. package/.next/server/app/_not-found.html +1 -1
  18. package/.next/server/app/_not-found.rsc +14 -14
  19. package/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
  20. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +12 -12
  21. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  22. package/.next/server/app/api/artifact/route.js +3 -3
  23. package/.next/server/app/api/artifact/route.js.nft.json +1 -1
  24. package/.next/server/app/api/bot/memory/files/route/app-paths-manifest.json +3 -0
  25. package/.next/server/app/api/bot/memory/files/route.js +10 -0
  26. package/.next/server/app/api/bot/memory/files/route.js.nft.json +1 -0
  27. package/.next/server/app/api/bot/memory/files/route_client-reference-manifest.js +3 -0
  28. package/.next/server/app/api/bot/memory/route/app-paths-manifest.json +3 -0
  29. package/.next/server/app/api/bot/memory/route.js +14 -0
  30. package/.next/server/app/api/bot/{notes → memory}/route.js.nft.json +1 -1
  31. package/.next/server/app/api/bot/memory/route_client-reference-manifest.js +3 -0
  32. package/.next/server/app/api/bot/route.js +6 -6
  33. package/.next/server/app/api/bot/route.js.nft.json +1 -1
  34. package/.next/server/app/api/bot/task/[id]/route.js +8 -8
  35. package/.next/server/app/api/bot/task/[id]/route.js.nft.json +1 -1
  36. package/.next/server/app/api/bot/task/route.js +8 -8
  37. package/.next/server/app/api/bot/task/route.js.nft.json +1 -1
  38. package/.next/server/app/api/config/route.js +5 -5
  39. package/.next/server/app/api/config/route.js.nft.json +1 -1
  40. package/.next/server/app/api/events/route.js +4 -5
  41. package/.next/server/app/api/events/route.js.nft.json +1 -1
  42. package/.next/server/app/api/file/[...path]/route.js +3 -3
  43. package/.next/server/app/api/file/[...path]/route.js.nft.json +1 -1
  44. package/.next/server/app/api/llm-model/catalog/route.js +9 -8
  45. package/.next/server/app/api/llm-model/catalog/route.js.nft.json +1 -1
  46. package/.next/server/app/api/llm-model/credits/route/app-paths-manifest.json +3 -0
  47. package/.next/server/app/api/llm-model/credits/route/build-manifest.json +12 -0
  48. package/.next/server/app/api/llm-model/credits/route/server-reference-manifest.json +4 -0
  49. package/.next/server/app/api/llm-model/credits/route.js +16 -0
  50. package/.next/server/app/api/llm-model/credits/route.js.map +5 -0
  51. package/.next/server/app/api/llm-model/credits/route.js.nft.json +1 -0
  52. package/.next/server/app/api/llm-model/credits/route_client-reference-manifest.js +3 -0
  53. package/.next/server/app/api/llm-model/route.js +10 -8
  54. package/.next/server/app/api/llm-model/route.js.nft.json +1 -1
  55. package/.next/server/app/api/llm-model/subscription/route/app-paths-manifest.json +3 -0
  56. package/.next/server/app/api/llm-model/subscription/route/build-manifest.json +12 -0
  57. package/.next/server/app/api/llm-model/subscription/route/server-reference-manifest.json +4 -0
  58. package/.next/server/app/api/llm-model/subscription/route.js +15 -0
  59. package/.next/server/app/api/llm-model/subscription/route.js.map +5 -0
  60. package/.next/server/app/api/llm-model/subscription/route.js.nft.json +1 -0
  61. package/.next/server/app/api/llm-model/subscription/route_client-reference-manifest.js +3 -0
  62. package/.next/server/app/api/mcp/[name]/route.js +4 -4
  63. package/.next/server/app/api/mcp/[name]/route.js.nft.json +1 -1
  64. package/.next/server/app/api/mcp/oauth/callback/route.js +5 -5
  65. package/.next/server/app/api/mcp/oauth/callback/route.js.nft.json +1 -1
  66. package/.next/server/app/api/mcp/route.js +4 -4
  67. package/.next/server/app/api/mcp/route.js.nft.json +1 -1
  68. package/.next/server/app/api/mcp/tools/route.js +4 -4
  69. package/.next/server/app/api/mcp/tools/route.js.nft.json +1 -1
  70. package/.next/server/app/api/memory/edit/route/app-paths-manifest.json +3 -0
  71. package/.next/server/app/api/memory/edit/route/build-manifest.json +12 -0
  72. package/.next/server/app/api/memory/edit/route/server-reference-manifest.json +4 -0
  73. package/.next/server/app/api/memory/edit/route.js +24 -0
  74. package/.next/server/app/api/memory/edit/route.js.map +5 -0
  75. package/.next/server/app/api/memory/edit/route.js.nft.json +1 -0
  76. package/.next/server/app/api/memory/{tidy → edit}/route_client-reference-manifest.js +1 -1
  77. package/.next/server/app/api/memory/route.js +6 -7
  78. package/.next/server/app/api/memory/route.js.nft.json +1 -1
  79. package/.next/server/app/api/skills/[source]/[name]/route.js +3 -3
  80. package/.next/server/app/api/skills/[source]/[name]/route.js.nft.json +1 -1
  81. package/.next/server/app/api/skills/route.js +3 -3
  82. package/.next/server/app/api/skills/route.js.nft.json +1 -1
  83. package/.next/server/app/api/thursday/call/route.js +9 -7
  84. package/.next/server/app/api/thursday/call/route.js.nft.json +1 -1
  85. package/.next/server/app/api/thursday/skills/route.js +9 -7
  86. package/.next/server/app/api/thursday/skills/route.js.nft.json +1 -1
  87. package/.next/server/app/api/thursday/tool-call/route.js +17 -15
  88. package/.next/server/app/api/thursday/tool-call/route.js.nft.json +1 -1
  89. package/.next/server/app/api/workspace/route.js +4 -5
  90. package/.next/server/app/api/workspace/route.js.nft.json +1 -1
  91. package/.next/server/app/artifact/[...path]/page/server-reference-manifest.json +1 -1
  92. package/.next/server/app/artifact/[...path]/page.js +5 -6
  93. package/.next/server/app/artifact/[...path]/page.js.nft.json +1 -1
  94. package/.next/server/app/artifact/[...path]/page_client-reference-manifest.js +1 -1
  95. package/.next/server/app/favicon.ico/route.js +3 -4
  96. package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
  97. package/.next/server/app/favicon.ico.body +0 -0
  98. package/.next/server/app/page/server-reference-manifest.json +57 -90
  99. package/.next/server/app/page.js +10 -10
  100. package/.next/server/app/page.js.nft.json +1 -1
  101. package/.next/server/app/page_client-reference-manifest.js +1 -1
  102. package/.next/server/app-paths-manifest.json +5 -2
  103. package/.next/server/chunks/[externals]__0dzuk6_._.js +3 -0
  104. package/.next/server/chunks/[root-of-the-server]__01dkp4a._.js +3 -0
  105. package/.next/server/chunks/[root-of-the-server]__02va5wz._.js +1 -1
  106. package/.next/server/chunks/[root-of-the-server]__02xjoto._.js +3 -0
  107. package/.next/server/chunks/[root-of-the-server]__04q--0f._.js +8 -0
  108. package/.next/server/chunks/[root-of-the-server]__0bl-e63._.js +3 -0
  109. package/.next/server/chunks/[root-of-the-server]__0ce8nf8._.js +5 -0
  110. package/.next/server/chunks/[root-of-the-server]__0ffbg85._.js +3 -0
  111. package/.next/server/chunks/[root-of-the-server]__0j7z0r3._.js +12 -0
  112. package/.next/server/chunks/[root-of-the-server]__0jq4ohy._.js +8 -0
  113. package/.next/server/chunks/[root-of-the-server]__0ls6_8y._.js +8 -0
  114. package/.next/server/chunks/[root-of-the-server]__0ncs9uu._.js +3 -0
  115. package/.next/server/chunks/[root-of-the-server]__0pa2n68._.js +3 -0
  116. package/.next/server/chunks/[root-of-the-server]__0q0vvt2._.js +5 -0
  117. package/.next/server/chunks/[root-of-the-server]__1-ga0kh._.js +5 -0
  118. package/.next/server/chunks/[root-of-the-server]__11n256_._.js +5 -0
  119. package/.next/server/chunks/[root-of-the-server]__12cabg9._.js +3 -0
  120. package/.next/server/chunks/[root-of-the-server]__1339jnv._.js +8 -0
  121. package/.next/server/chunks/[root-of-the-server]__15bjnu5._.js +5 -0
  122. package/.next/server/chunks/[root-of-the-server]__18_teo8._.js +8 -0
  123. package/.next/server/chunks/[root-of-the-server]__1_04mcf._.js +5 -0
  124. package/.next/server/chunks/[root-of-the-server]__1anc1p9._.js +3 -0
  125. package/.next/server/chunks/[root-of-the-server]__1ccon1w._.js +5 -5
  126. package/.next/server/chunks/[root-of-the-server]__1fizn31._.js +3 -0
  127. package/.next/server/chunks/[root-of-the-server]__1llj3-b._.js +3 -0
  128. package/.next/server/chunks/[root-of-the-server]__1orn-5b._.js +11 -0
  129. package/.next/server/chunks/[root-of-the-server]__1p8m3f4._.js +3 -0
  130. package/.next/server/chunks/[root-of-the-server]__1wa1k7r._.js +8 -0
  131. package/.next/server/chunks/_0-73of8._.js +5 -5
  132. package/.next/server/chunks/_0-fjql4._.js +26 -0
  133. package/.next/server/chunks/_00xh_q0._.js +27 -0
  134. package/.next/server/chunks/{_0pppbb5._.js → _03kp0jp._.js} +163 -36
  135. package/.next/server/chunks/_051qh8u._.js +3 -0
  136. package/.next/server/chunks/{node_modules_next_04q-o58._.js → _05dla15._.js} +2 -2
  137. package/.next/server/chunks/_05h2cii._.js +1 -1
  138. package/.next/server/chunks/_0c9j6rb._.js +49 -0
  139. package/.next/server/chunks/_0fzqt2y._.js +130 -0
  140. package/.next/server/chunks/{_0vnutv9._.js → _0ioz3nd._.js} +1 -1
  141. package/.next/server/chunks/_0iwfby5._.js +3 -0
  142. package/.next/server/chunks/_0mecv0f._.js +26 -0
  143. package/.next/server/chunks/_0mh8j4m._.js +3 -0
  144. package/.next/server/chunks/_0n74zsf._.js +4 -4
  145. package/.next/server/chunks/_0obojvv._.js +3 -0
  146. package/.next/server/chunks/_0recufk._.js +49 -0
  147. package/.next/server/chunks/_0rnf-6n._.js +4 -4
  148. package/.next/server/chunks/_0sb7tlz._.js +3 -0
  149. package/.next/server/chunks/_0u10wmw._.js +5 -5
  150. package/.next/server/chunks/_0u4aiy2._.js +3 -0
  151. package/.next/server/chunks/_0wp4le3._.js +1 -1
  152. package/.next/server/chunks/_0y5fg-x._.js +1 -1
  153. package/.next/server/chunks/_1230wbx._.js +10 -0
  154. package/.next/server/chunks/_12nheja._.js +3 -0
  155. package/.next/server/chunks/_13510xh._.js +3 -0
  156. package/.next/server/chunks/_15hq89o._.js +1 -1
  157. package/.next/server/chunks/_17l5ygt._.js +49 -0
  158. package/.next/server/chunks/_18-1wcc._.js +5 -5
  159. package/.next/server/chunks/_1_5mnq4._.js +4 -4
  160. package/.next/server/chunks/_1_9mjx-._.js +3 -0
  161. package/.next/server/chunks/_1d0j961._.js +6 -0
  162. package/.next/server/chunks/_1d5zmy8._.js +1 -1
  163. package/.next/server/chunks/_1drgfx4._.js +4 -4
  164. package/.next/server/chunks/_1f46eie._.js +3 -0
  165. package/.next/server/chunks/_1h5_8sk._.js +3 -0
  166. package/.next/server/chunks/_1hdnxb7._.js +3 -0
  167. package/.next/server/chunks/_1j8k_j5._.js +3 -0
  168. package/.next/server/chunks/_1krd9rf._.js +3 -0
  169. package/.next/server/chunks/_1nc0eui._.js +11 -0
  170. package/.next/server/chunks/_1nula3v._.js +3 -0
  171. package/.next/server/chunks/_1pf4qe-._.js +3 -0
  172. package/.next/server/chunks/_1php4eq._.js +1 -1
  173. package/.next/server/chunks/_1qxheoa._.js +3 -0
  174. package/.next/server/chunks/_1x6kd1r._.js +3 -0
  175. package/.next/server/chunks/_1yuv4vp._.js +1 -1
  176. package/.next/server/chunks/_1zjrdag._.js +3 -0
  177. package/.next/server/chunks/_next-internal_server_app_api_bot_memory_files_route_actions_1_xd_8f.js +3 -0
  178. package/.next/server/chunks/_next-internal_server_app_api_bot_memory_route_actions_1a8xfr7.js +3 -0
  179. package/.next/server/chunks/_next-internal_server_app_api_llm-model_credits_route_actions_0v424pb.js +3 -0
  180. package/.next/server/chunks/_next-internal_server_app_api_llm-model_subscription_route_actions_0-udcm5.js +3 -0
  181. package/.next/server/chunks/_next-internal_server_app_api_memory_edit_route_actions_02sfnmr.js +3 -0
  182. package/.next/server/chunks/config_ts_1g4sz5o._.js +1 -1
  183. package/.next/server/chunks/features_0rpfxbk._.js +3 -0
  184. package/.next/server/chunks/features_1qf3cei._.js +3 -0
  185. package/.next/server/chunks/features_bot_0zt92oh._.js +3 -0
  186. package/.next/server/chunks/features_bot_bot_query_ts_12mgkl2._.js +3 -0
  187. package/.next/server/chunks/features_memory_1uqvksn._.js +6 -0
  188. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0a1rd09._.js → node_modules_consola_dist_chunks_prompt_mjs_0i7cvml._.js} +1 -1
  189. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_06067w6._.js → node_modules_consola_dist_chunks_prompt_mjs_0icqaqw._.js} +1 -1
  190. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_096ais_._.js → node_modules_consola_dist_chunks_prompt_mjs_0r28-sm._.js} +1 -1
  191. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_02s3hjt._.js → node_modules_consola_dist_chunks_prompt_mjs_0v_7drq._.js} +1 -1
  192. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1xoq2rl._.js +3 -0
  193. package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0tvkfgh._.js → node_modules_consola_dist_chunks_prompt_mjs_1y49572._.js} +1 -1
  194. package/.next/server/chunks/ssr/[root-of-the-server]__09q9med._.js +1 -1
  195. package/.next/server/chunks/ssr/{[root-of-the-server]__1qfqvmg._.js → [root-of-the-server]__0_69wqr._.js} +2 -2
  196. package/.next/server/chunks/ssr/[root-of-the-server]__0hg5gib._.js +1 -1
  197. package/.next/server/chunks/ssr/[root-of-the-server]__0i8ie7f._.js +3 -0
  198. package/.next/server/chunks/ssr/{[root-of-the-server]__0jsmmb-._.js → [root-of-the-server]__0mz33a7._.js} +1 -1
  199. package/.next/server/chunks/ssr/[root-of-the-server]__0tbkdlx._.js +3 -0
  200. package/.next/server/chunks/ssr/[root-of-the-server]__0vnmv-s._.js +3 -0
  201. package/.next/server/chunks/ssr/{[root-of-the-server]__0f6s48x._.js → [root-of-the-server]__0xpcv9w._.js} +3 -3
  202. package/.next/server/chunks/ssr/[root-of-the-server]__0xtssj3._.js +1 -1
  203. package/.next/server/chunks/ssr/[root-of-the-server]__0zkv7z_._.js +3 -0
  204. package/.next/server/chunks/ssr/[root-of-the-server]__10-dz5o._.js +57 -0
  205. package/.next/server/chunks/ssr/[root-of-the-server]__15_uhip._.js +3 -0
  206. package/.next/server/chunks/ssr/[root-of-the-server]__16jc196._.js +1 -1
  207. package/.next/server/chunks/ssr/{[root-of-the-server]__0m1b5ky._.js → [root-of-the-server]__1cyjywu._.js} +1 -1
  208. package/.next/server/chunks/ssr/[root-of-the-server]__1eh7hid._.js +3 -0
  209. package/.next/server/chunks/ssr/[root-of-the-server]__1gtlw6v._.js +43 -0
  210. package/.next/server/chunks/ssr/[root-of-the-server]__1mzi-ju._.js +1 -1
  211. package/.next/server/chunks/ssr/[root-of-the-server]__1ox7hbk._.js +4 -0
  212. package/.next/server/chunks/ssr/[root-of-the-server]__1pduf5n._.js +3 -0
  213. package/.next/server/chunks/ssr/{_1mznrxk._.js → _001_ms1._.js} +3 -3
  214. package/.next/server/chunks/ssr/_01_6m32._.js +3 -0
  215. package/.next/server/chunks/ssr/_01n3o0m._.js +3 -0
  216. package/.next/server/chunks/ssr/_07uchnc._.js +3 -0
  217. package/.next/server/chunks/ssr/_0ap2fzn._.js +3 -0
  218. package/.next/server/chunks/ssr/_0c-ql2m._.js +3 -0
  219. package/.next/server/chunks/ssr/_0h42fze._.js +3 -0
  220. package/.next/server/chunks/ssr/_0h7zdni._.js +1 -1
  221. package/.next/server/chunks/ssr/_0i_6q1w._.js +41 -0
  222. package/.next/server/chunks/ssr/_0kbkmar._.js +3 -0
  223. package/.next/server/chunks/ssr/{_077-g6s._.js → _0mb8zc8._.js} +3 -3
  224. package/.next/server/chunks/ssr/_0mo2w06._.js +3 -0
  225. package/.next/server/chunks/ssr/{_13punpm._.js → _0q0zan6._.js} +2 -2
  226. package/.next/server/chunks/ssr/{_040sk32._.js → _0u2jv_x._.js} +76 -82
  227. package/.next/server/chunks/ssr/_0y0tiht._.js +4 -0
  228. package/.next/server/chunks/ssr/_0zhoc3r._.js +3 -0
  229. package/.next/server/chunks/ssr/_1-teee5._.js +3 -3
  230. package/.next/server/chunks/ssr/_13fke0x._.js +3 -0
  231. package/.next/server/chunks/ssr/{_1lip4th._.js → _13z3f85._.js} +2 -2
  232. package/.next/server/chunks/ssr/_14_lw10._.js +3 -0
  233. package/.next/server/chunks/ssr/_15j2_8f._.js +3 -0
  234. package/.next/server/chunks/ssr/{_09ja_w1._.js → _1eoqg_g._.js} +2 -2
  235. package/.next/server/chunks/ssr/_1sji22n._.js +3 -0
  236. package/.next/server/chunks/ssr/_1spzm8z._.js +3 -0
  237. package/.next/server/chunks/ssr/{_0x_nu5e._.js → _1vi_uo7._.js} +2 -2
  238. package/.next/server/chunks/ssr/_1vyd1ji._.js +1 -1
  239. package/.next/server/chunks/ssr/{_1m3bsfr._.js → _1xvgn77._.js} +2 -2
  240. package/.next/server/chunks/ssr/_20kd_n0._.js +42 -0
  241. package/.next/server/chunks/ssr/features_bot_1s_-2vj._.js +3 -0
  242. package/.next/server/chunks/ssr/features_config_components_config-setting_tsx_0xfwqdg._.js +1 -1
  243. package/.next/server/chunks/ssr/features_connectors_components_mcp-setting_tsx_0jmbgvp._.js +1 -1
  244. package/.next/server/chunks/ssr/lib_date-like_ts_02is0pw._.js +3 -0
  245. package/.next/server/chunks/ssr/{node_modules_18_dk7_._.js → node_modules_063xwpp._.js} +1 -1
  246. package/.next/server/chunks/ssr/{node_modules_01qnv-a._.js → node_modules_0bvraul._.js} +1 -1
  247. package/.next/server/chunks/ssr/{node_modules_0n1kj4p._.js → node_modules_0eu1va6._.js} +1 -1
  248. package/.next/server/chunks/ssr/{node_modules_0kl7-tn._.js → node_modules_0h50b8n._.js} +1 -1
  249. package/.next/server/chunks/ssr/{node_modules_0_n241r._.js → node_modules_0h5hnr1._.js} +1 -1
  250. package/.next/server/chunks/ssr/{node_modules_0lrjk5t._.js → node_modules_0h7u60n._.js} +1 -1
  251. package/.next/server/chunks/ssr/{node_modules_0ndvxub._.js → node_modules_0lm7_vm._.js} +1 -1
  252. package/.next/server/chunks/ssr/{node_modules_13r44jq._.js → node_modules_0mwym0g._.js} +1 -1
  253. package/.next/server/chunks/ssr/{node_modules_17lrt3y._.js → node_modules_0rjg6t1._.js} +1 -1
  254. package/.next/server/chunks/ssr/node_modules_0x3-ply._.js +3 -0
  255. package/.next/server/chunks/ssr/{node_modules_004qlr4._.js → node_modules_0y_cded._.js} +1 -1
  256. package/.next/server/chunks/ssr/{node_modules_0kxz4gt._.js → node_modules_0z5tsu8._.js} +1 -1
  257. package/.next/server/chunks/ssr/{node_modules_0iz1rzd._.js → node_modules_0zp9r92._.js} +1 -1
  258. package/.next/server/chunks/ssr/{node_modules_19dp7ko._.js → node_modules_1-hc6uv._.js} +1 -1
  259. package/.next/server/chunks/ssr/{node_modules_1q0p85z._.js → node_modules_113tz0l._.js} +1 -1
  260. package/.next/server/chunks/ssr/{node_modules_1toafc-._.js → node_modules_124bue5._.js} +1 -1
  261. package/.next/server/chunks/ssr/node_modules_17bwo43._.js +55 -0
  262. package/.next/server/chunks/ssr/{node_modules_08d8bke._.js → node_modules_1_n3ztw._.js} +1 -1
  263. package/.next/server/chunks/ssr/{node_modules_1dwguw_._.js → node_modules_1bc7gu8._.js} +1 -1
  264. package/.next/server/chunks/ssr/{node_modules_20yebtj._.js → node_modules_1bgb6-f._.js} +1 -1
  265. package/.next/server/chunks/ssr/{node_modules_1n5ygqe._.js → node_modules_1h4a4pp._.js} +1 -1
  266. package/.next/server/chunks/ssr/{node_modules_04n7mjs._.js → node_modules_1i7u1ia._.js} +1 -1
  267. package/.next/server/chunks/ssr/{node_modules_1hn8z1f._.js → node_modules_1juq3hs._.js} +1 -1
  268. package/.next/server/chunks/ssr/{node_modules_1xyd7ov._.js → node_modules_1l0qtdu._.js} +1 -1
  269. package/.next/server/chunks/ssr/{node_modules_0m93zeg._.js → node_modules_1t9cyee._.js} +1 -1
  270. package/.next/server/chunks/ssr/{node_modules_09xhur7._.js → node_modules_1uuj9x1._.js} +1 -1
  271. package/.next/server/chunks/ssr/{node_modules_1hd-5tv._.js → node_modules_1vqb6po._.js} +1 -1
  272. package/.next/server/chunks/ssr/{node_modules_1zu988w._.js → node_modules_1xx28h1._.js} +1 -1
  273. package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0m9a4y0._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0d3skpl._.js} +1 -1
  274. package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_13w_wy9._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0i44kk5._.js} +1 -1
  275. package/.next/server/chunks/ssr/node_modules_@vercel_oidc_dist_0qe30a_._.js +3 -0
  276. package/.next/server/chunks/ssr/{node_modules_next_dist_0w6mzq5._.js → node_modules_next_dist_0drixxt._.js} +4 -4
  277. package/.next/server/chunks/ssr/node_modules_next_dist_1n3w9lb._.js +2 -2
  278. package/.next/server/instrumentation.js +1 -1
  279. package/.next/server/middleware-build-manifest.js +3 -3
  280. package/.next/server/pages/404.html +1 -1
  281. package/.next/server/pages/500.html +1 -1
  282. package/.next/server/server-reference-manifest.js +1 -1
  283. package/.next/server/server-reference-manifest.json +58 -91
  284. package/.next/static/chunks/{31ttnjt6g35a0.js → 0ev4hyghq0mbb.js} +1 -1
  285. package/.next/static/chunks/0r5qcctujhxli.js +1 -0
  286. package/.next/static/chunks/0tmgrnlje14-s.js +3 -0
  287. package/.next/static/chunks/0x79tx04dxbpp.js +1 -0
  288. package/.next/static/chunks/0xzgvc0fkdb6t.js +1 -0
  289. package/.next/static/chunks/10teww7wt-f6h.js +1 -0
  290. package/.next/static/chunks/14hgtsjg-kael.css +1 -0
  291. package/.next/static/chunks/17donp69y4do7.js +40 -0
  292. package/.next/static/chunks/{3_h9txt3tq8t8.js → 1dkjg87pxk0n9.js} +1 -1
  293. package/.next/static/chunks/1ekc7ul556kbl.js +1 -0
  294. package/.next/static/chunks/{3ard_noqapnw1.js → 1grhjo7a5a0_-.js} +1 -1
  295. package/.next/static/chunks/1kq8h8wi_e0bc.js +53 -0
  296. package/.next/static/chunks/1okqiww3pnwka.js +1 -0
  297. package/.next/static/chunks/26-pr2c9ro222.js +1 -0
  298. package/.next/static/chunks/{18vhba44l1fc9.js → 280t0_e99i276.js} +1 -1
  299. package/.next/static/chunks/28cizcdaabd4h.js +1 -0
  300. package/.next/static/chunks/2a0k6jlq0_v1u.js +2 -0
  301. package/.next/static/chunks/2fzs9fm8yarvq.js +1 -0
  302. package/.next/static/chunks/2h0sd2vumhb15.js +1 -0
  303. package/.next/static/chunks/2hw2f6zmt7fmt.js +1 -0
  304. package/.next/static/chunks/2t4vhi3m8ahya.js +1 -0
  305. package/.next/static/chunks/2tigdtz3dpcy8.js +1 -0
  306. package/.next/static/chunks/3111pj5wnjgos.js +1 -0
  307. package/.next/static/chunks/{07kl352c-lvcz.js → 3agpgkwaa321p.js} +1 -1
  308. package/.next/static/chunks/3cfi_q6d_wtci.js +1 -0
  309. package/.next/static/chunks/3ok9_x74rip3v.js +1 -0
  310. package/.next/static/chunks/3xk7yqgomx7yq.js +1 -0
  311. package/.next/static/chunks/3xpbkl66ssib5.js +38 -0
  312. package/.next/static/media/favicon.37o1pjldwi2uc.ico +0 -0
  313. package/README.md +6 -4
  314. package/app/api/bot/memory/files/route.ts +14 -0
  315. package/app/api/bot/memory/route.ts +5 -0
  316. package/app/api/events/app-event.ts +3 -5
  317. package/app/api/llm-model/credits/route.ts +11 -0
  318. package/app/api/llm-model/route.ts +6 -1
  319. package/app/api/llm-model/subscription/route.ts +11 -0
  320. package/app/api/memory/edit/route.ts +7 -0
  321. package/app/api/memory/route.ts +21 -0
  322. package/app/api/query-key.ts +19 -4
  323. package/bin/thursday.mjs +3 -0
  324. package/components/ui/shiny-text.tsx +48 -134
  325. package/database/migrations/{20260909060813_optimal_sabretooth → 20260910173049_numerous_spirit}/migration.sql +24 -34
  326. package/database/migrations/{20260909060813_optimal_sabretooth → 20260910173049_numerous_spirit}/snapshot.json +71 -145
  327. package/database/migrations/20260911030320_opposite_jack_power/migration.sql +1 -0
  328. package/database/migrations/{20260909063821_bitter_tenebrous → 20260911030320_opposite_jack_power}/snapshot.json +57 -184
  329. package/features/ai/chatgpt.ts +617 -0
  330. package/features/ai/components/chatgpt-sign-in.tsx +66 -0
  331. package/features/ai/components/model-picker.tsx +17 -1
  332. package/features/ai/components/provider-icon.tsx +1 -0
  333. package/features/ai/load-tools.ts +58 -42
  334. package/features/ai/model.schema.ts +232 -34
  335. package/features/ai/model.ts +169 -24
  336. package/features/ai/prompts/bot.prompt.ts +156 -65
  337. package/features/ai/prompts/memory-edit.prompt.ts +33 -0
  338. package/features/ai/prompts/prompt-helper.ts +45 -26
  339. package/features/ai/prompts/thursday.prompt.ts +40 -17
  340. package/features/ai/tools/bot.tool.ts +35 -52
  341. package/features/ai/tools/connected.ts +17 -4
  342. package/features/ai/tools/memory.tool.ts +133 -72
  343. package/features/ai/tools/search.tool.ts +136 -47
  344. package/features/ai/tools/tool-name.ts +2 -5
  345. package/features/ai/tools/workspace.tool.ts +2 -1
  346. package/features/bot/bot.action.ts +8 -18
  347. package/features/bot/bot.memory.ts +89 -0
  348. package/features/bot/bot.query.ts +37 -57
  349. package/features/bot/bot.run.ts +507 -241
  350. package/features/bot/bot.runner.ts +410 -114
  351. package/features/bot/bot.schema.ts +79 -19
  352. package/features/bot/bot.seed.ts +89 -16
  353. package/features/bot/components/bot-mark.tsx +122 -20
  354. package/features/bot/components/bot-room.tsx +159 -130
  355. package/features/bot/components/bot-setting.tsx +790 -402
  356. package/features/bot/components/bot-tool.tsx +2 -6
  357. package/features/bot/components/mark-palette.tsx +54 -0
  358. package/features/bot/components/path-chips.tsx +1 -1
  359. package/features/bot/components/task-badge.tsx +26 -6
  360. package/features/bot/components/task-reply.tsx +22 -33
  361. package/features/bot/components/task-setting.tsx +20 -17
  362. package/features/bot/task.query.ts +215 -28
  363. package/features/bot/task.store.ts +53 -9
  364. package/features/config/components/config-setting.tsx +265 -34
  365. package/features/config/components/models-badge.tsx +42 -0
  366. package/features/config/config.action.ts +12 -0
  367. package/features/config/config.const.ts +38 -2
  368. package/features/connectors/mcp.manager.ts +10 -2
  369. package/features/intro/components/intro.tsx +47 -46
  370. package/features/memory/components/memory-edit.tsx +338 -0
  371. package/features/memory/components/memory-setting.tsx +75 -77
  372. package/features/memory/memory.action.ts +3 -43
  373. package/features/memory/memory.edit.ts +87 -0
  374. package/features/memory/memory.query.ts +24 -42
  375. package/features/memory/memory.schema.ts +29 -106
  376. package/features/settings/components/settings.tsx +2 -0
  377. package/features/settings/settings.alert.ts +3 -1
  378. package/features/thursday/components/ascii-orb.tsx +106 -90
  379. package/features/thursday/components/boot.tsx +7 -0
  380. package/features/thursday/components/call-log.tsx +219 -92
  381. package/features/thursday/components/face.tsx +6 -4
  382. package/features/thursday/components/tab-state.tsx +97 -0
  383. package/features/thursday/components/thursday-mark.tsx +4 -1
  384. package/features/thursday/components/thursday-setting.tsx +17 -47
  385. package/features/thursday/components/thursday.tsx +208 -95
  386. package/features/thursday/thursday.action.ts +12 -17
  387. package/features/thursday/thursday.query.ts +61 -70
  388. package/features/thursday/thursday.schema.ts +20 -7
  389. package/features/thursday/tool-line.ts +85 -11
  390. package/features/thursday/use-thursday.ts +86 -59
  391. package/features/workspace/components/file-view.tsx +4 -2
  392. package/features/workspace/workspace.ts +110 -26
  393. package/lib/limits.ts +1 -1
  394. package/lib/oauth-page.ts +27 -0
  395. package/lib/realtime/openai.ts +2 -2
  396. package/lib/realtime/realtime.protocol.ts +1 -1
  397. package/lib/realtime/realtime.tap.ts +122 -0
  398. package/lib/sandbox.ts +59 -36
  399. package/package.json +1 -1
  400. package/skills/browser/SKILL.md +55 -34
  401. package/.next/server/app/api/bot/notes/route/app-paths-manifest.json +0 -3
  402. package/.next/server/app/api/bot/notes/route.js +0 -14
  403. package/.next/server/app/api/bot/notes/route_client-reference-manifest.js +0 -3
  404. package/.next/server/app/api/memory/tidy/route/app-paths-manifest.json +0 -3
  405. package/.next/server/app/api/memory/tidy/route.js +0 -22
  406. package/.next/server/app/api/memory/tidy/route.js.nft.json +0 -1
  407. package/.next/server/chunks/0d-b_next_dist_server_app-render_after-task-async-storage_external_0-exn7p.js +0 -3
  408. package/.next/server/chunks/[root-of-the-server]__0-npkh_._.js +0 -3
  409. package/.next/server/chunks/[root-of-the-server]__005var5._.js +0 -3
  410. package/.next/server/chunks/[root-of-the-server]__019dx9f._.js +0 -5
  411. package/.next/server/chunks/[root-of-the-server]__02_2sm-._.js +0 -5
  412. package/.next/server/chunks/[root-of-the-server]__04_8spk._.js +0 -3
  413. package/.next/server/chunks/[root-of-the-server]__05luceg._.js +0 -3
  414. package/.next/server/chunks/[root-of-the-server]__06t77t4._.js +0 -11
  415. package/.next/server/chunks/[root-of-the-server]__0bed6hu._.js +0 -8
  416. package/.next/server/chunks/[root-of-the-server]__0cro2dz._.js +0 -5
  417. package/.next/server/chunks/[root-of-the-server]__0dupqt7._.js +0 -5
  418. package/.next/server/chunks/[root-of-the-server]__0emr6go._.js +0 -3
  419. package/.next/server/chunks/[root-of-the-server]__0le4d-p._.js +0 -3
  420. package/.next/server/chunks/[root-of-the-server]__0m8ywyo._.js +0 -10
  421. package/.next/server/chunks/[root-of-the-server]__0nfuugi._.js +0 -5
  422. package/.next/server/chunks/[root-of-the-server]__0p32jfz._.js +0 -12
  423. package/.next/server/chunks/[root-of-the-server]__0phdgco._.js +0 -8
  424. package/.next/server/chunks/[root-of-the-server]__0wvgrsl._.js +0 -3
  425. package/.next/server/chunks/[root-of-the-server]__10qshki._.js +0 -3
  426. package/.next/server/chunks/[root-of-the-server]__11j52t0._.js +0 -5
  427. package/.next/server/chunks/[root-of-the-server]__1692zrv._.js +0 -3
  428. package/.next/server/chunks/[root-of-the-server]__16thabu._.js +0 -3
  429. package/.next/server/chunks/[root-of-the-server]__173nhwf._.js +0 -8
  430. package/.next/server/chunks/[root-of-the-server]__1_aoyr9._.js +0 -3
  431. package/.next/server/chunks/[root-of-the-server]__1bn73ih._.js +0 -10
  432. package/.next/server/chunks/[root-of-the-server]__1bqhuh9._.js +0 -8
  433. package/.next/server/chunks/[root-of-the-server]__1cqizfq._.js +0 -5
  434. package/.next/server/chunks/[root-of-the-server]__1e-ci13._.js +0 -3
  435. package/.next/server/chunks/[root-of-the-server]__1fboj2j._.js +0 -3
  436. package/.next/server/chunks/[root-of-the-server]__1lr7j9t._.js +0 -5
  437. package/.next/server/chunks/[root-of-the-server]__1oli73c._.js +0 -3
  438. package/.next/server/chunks/[root-of-the-server]__1txedx6._.js +0 -8
  439. package/.next/server/chunks/[root-of-the-server]__1untqv8._.js +0 -5
  440. package/.next/server/chunks/[root-of-the-server]__1zgpz1w._.js +0 -3
  441. package/.next/server/chunks/_00_584r._.js +0 -293
  442. package/.next/server/chunks/_012yhfm._.js +0 -39
  443. package/.next/server/chunks/_06mxlgn._.js +0 -3
  444. package/.next/server/chunks/_07rtlxv._.js +0 -3
  445. package/.next/server/chunks/_0a351g7._.js +0 -6
  446. package/.next/server/chunks/_0kjjmag._.js +0 -3
  447. package/.next/server/chunks/_0nn99o2._.js +0 -3
  448. package/.next/server/chunks/_0zuzmt_._.js +0 -39
  449. package/.next/server/chunks/_106co2o._.js +0 -3
  450. package/.next/server/chunks/_12bbv5i._.js +0 -100
  451. package/.next/server/chunks/_15q1pig._.js +0 -3
  452. package/.next/server/chunks/_19wg5yn._.js +0 -138
  453. package/.next/server/chunks/_1bt95p9._.js +0 -55
  454. package/.next/server/chunks/_1cbk7ms._.js +0 -26
  455. package/.next/server/chunks/_1drv2et._.js +0 -40
  456. package/.next/server/chunks/_1ic3bew._.js +0 -5
  457. package/.next/server/chunks/_1mxn_lb._.js +0 -3
  458. package/.next/server/chunks/_1rysr-r._.js +0 -10
  459. package/.next/server/chunks/_1u3rwbr._.js +0 -3
  460. package/.next/server/chunks/_1x7p319._.js +0 -6
  461. package/.next/server/chunks/_next-internal_server_app_api_bot_notes_route_actions_0qcsyu7.js +0 -3
  462. package/.next/server/chunks/_next-internal_server_app_api_memory_tidy_route_actions_0_cfuom.js +0 -3
  463. package/.next/server/chunks/features_bot_task_query_ts_11xuzl3._.js +0 -3
  464. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_0gzvgfv._.js +0 -3
  465. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_0vorg6w._.js +0 -3
  466. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1tpvamf._.js +0 -3
  467. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1vmaq9f._.js +0 -3
  468. package/.next/server/chunks/node_modules_consola_dist_chunks_prompt_mjs_1y9-wcr._.js +0 -3
  469. package/.next/server/chunks/ssr/[externals]_node_crypto_1kn79-7._.js +0 -3
  470. package/.next/server/chunks/ssr/[root-of-the-server]__0iuoa-r._.js +0 -3
  471. package/.next/server/chunks/ssr/[root-of-the-server]__0un-erv._.js +0 -4
  472. package/.next/server/chunks/ssr/[root-of-the-server]__1-mnhyd._.js +0 -92
  473. package/.next/server/chunks/ssr/[root-of-the-server]__1bww500._.js +0 -3
  474. package/.next/server/chunks/ssr/[root-of-the-server]__1vcdast._.js +0 -3
  475. package/.next/server/chunks/ssr/[root-of-the-server]__1xwnu-h._.js +0 -3
  476. package/.next/server/chunks/ssr/_0-mc7ro._.js +0 -3
  477. package/.next/server/chunks/ssr/_01p5r6-._.js +0 -3
  478. package/.next/server/chunks/ssr/_03webdr._.js +0 -3
  479. package/.next/server/chunks/ssr/_09-0k3z._.js +0 -3
  480. package/.next/server/chunks/ssr/_0f_b_9s._.js +0 -3
  481. package/.next/server/chunks/ssr/_0fafgvl._.js +0 -3
  482. package/.next/server/chunks/ssr/_0sf1isi._.js +0 -3
  483. package/.next/server/chunks/ssr/_0su6_08._.js +0 -3
  484. package/.next/server/chunks/ssr/_0ti51xc._.js +0 -3
  485. package/.next/server/chunks/ssr/_0w5bgak._.js +0 -3
  486. package/.next/server/chunks/ssr/_0y84qo6._.js +0 -3
  487. package/.next/server/chunks/ssr/_1ciyoo3._.js +0 -3
  488. package/.next/server/chunks/ssr/_1e-dgz0._.js +0 -4
  489. package/.next/server/chunks/ssr/_1fq7zgb._.js +0 -3
  490. package/.next/server/chunks/ssr/_1g6l4uj._.js +0 -3
  491. package/.next/server/chunks/ssr/_1ruzehw._.js +0 -3
  492. package/.next/server/chunks/ssr/features_bot_components_bot-setting_tsx_01ajft6._.js +0 -4
  493. package/.next/server/chunks/ssr/features_config_components_14jqgy4._.js +0 -3
  494. package/.next/server/chunks/ssr/features_memory_components_memory-setting_tsx_0oa43sw._.js +0 -4
  495. package/.next/server/chunks/ssr/node_modules_zod_v4_classic_external_15fmmz3.js +0 -55
  496. package/.next/static/chunks/05b6nx_o3_f_n.js +0 -1
  497. package/.next/static/chunks/0dmns-gi0raa4.js +0 -1
  498. package/.next/static/chunks/0f9sq-7q_yxyl.js +0 -1
  499. package/.next/static/chunks/0oc4_xoia77dg.css +0 -1
  500. package/.next/static/chunks/0pheo8m3a-rzi.js +0 -1
  501. package/.next/static/chunks/19i8h2t-5evjx.js +0 -1
  502. package/.next/static/chunks/1c5fcj473025q.js +0 -1
  503. package/.next/static/chunks/1pi_hbmjw-f-z.js +0 -1
  504. package/.next/static/chunks/1twt6k09vg4np.js +0 -1
  505. package/.next/static/chunks/1ytnip8wdng93.js +0 -1
  506. package/.next/static/chunks/28gotuisldmom.js +0 -1
  507. package/.next/static/chunks/2998p0y7k1c8z.js +0 -3
  508. package/.next/static/chunks/29gyb3nubj51j.js +0 -53
  509. package/.next/static/chunks/2l233xfycjpd8.js +0 -1
  510. package/.next/static/chunks/2p5n7pa3c40t-.js +0 -2
  511. package/.next/static/chunks/2ru1q132af7h7.js +0 -1
  512. package/.next/static/chunks/2t8wqycwg3y04.js +0 -2
  513. package/.next/static/chunks/2uxum7qbyeyxd.js +0 -1
  514. package/.next/static/chunks/32z_ypgtchw48.js +0 -1
  515. package/.next/static/chunks/33pyjb3xd1iez.js +0 -1
  516. package/.next/static/chunks/3omud5xunrk1f.js +0 -1
  517. package/.next/static/chunks/42dnrkbph9e-1.js +0 -1
  518. package/.next/static/media/favicon.2vob68tjqpejf.ico +0 -0
  519. package/database/migrations/20260909063821_bitter_tenebrous/migration.sql +0 -1
  520. package/features/ai/prompts/memory-tidy.prompt.ts +0 -129
  521. package/features/ai/tools/tidy.tool.ts +0 -22
  522. package/features/bot/bot.notes.ts +0 -120
  523. package/features/memory/components/memory-tidy.tsx +0 -395
  524. package/features/memory/memory.tidy.ts +0 -332
  525. package/features/memory/tidy.query.ts +0 -135
  526. package/features/thursday/use-call-title.ts +0 -31
  527. /package/.next/server/app/api/bot/{notes → memory/files}/route/build-manifest.json +0 -0
  528. /package/.next/server/app/api/bot/{notes → memory/files}/route/server-reference-manifest.json +0 -0
  529. /package/.next/server/app/api/bot/{notes → memory/files}/route.js.map +0 -0
  530. /package/.next/server/app/api/{memory/tidy → bot/memory}/route/build-manifest.json +0 -0
  531. /package/.next/server/app/api/{memory/tidy → bot/memory}/route/server-reference-manifest.json +0 -0
  532. /package/.next/server/app/api/{memory/tidy → bot/memory}/route.js.map +0 -0
  533. /package/.next/static/{YR4NWcDDuzwLbTTU7U7Vr → x1cCqY0kWpR_VcYx_lWO5}/_buildManifest.js +0 -0
  534. /package/.next/static/{YR4NWcDDuzwLbTTU7U7Vr → x1cCqY0kWpR_VcYx_lWO5}/_clientMiddlewareManifest.js +0 -0
  535. /package/.next/static/{YR4NWcDDuzwLbTTU7U7Vr → x1cCqY0kWpR_VcYx_lWO5}/_ssgManifest.js +0 -0
@@ -1,11 +1,9 @@
1
- import { BOT_NOTES, BOT_RUN, PATHS } from "@/config";
1
+ import type { ModelMessage } from "ai";
2
+ import { PATHS, PROMPT_LINE, WORKSPACE_KEEP } from "@/config";
2
3
  import { TOOL_NAMES } from "@/features/ai/tools/tool-name";
3
- import {
4
- listJobBots,
5
- readBotNote,
6
- readBotNotesOn,
7
- } from "@/features/bot/bot.query";
8
- import type { JobBot } from "@/features/bot/bot.schema";
4
+ import { botMemoryFolder, listBotMemory } from "@/features/bot/bot.memory";
5
+ import { listJobBots, readBotMemoryOn } from "@/features/bot/bot.query";
6
+ import type { BotMemory, JobBot } from "@/features/bot/bot.schema";
9
7
  import { findPinnedTools } from "@/features/connectors/mcp.query";
10
8
  import type { McpToolRef } from "@/features/connectors/mcp.schema";
11
9
  import {
@@ -43,21 +41,22 @@ import {
43
41
  * Each chapter decides for itself whether it is included. Assembled on every run, never cached,
44
42
  * so a skill or server added a minute ago is in the next job's prompt.
45
43
  * Two seats share this prompt: the bot holding the job, and one borrowed for a part of it
46
- * (`ask_bot`). Only identity, roster, asking and finishing differ between them.
44
+ * (`ask_bot`). Only identity, roster, asking and answering differ between them.
47
45
  *
48
46
  * @param self This run's bot name; used only to drop itself from the roster.
49
47
  * @param persona Owner's instruction for this bot from settings.
50
- * @param askedBy Name of the borrowing bot in the borrowed seat; null for the bot holding the job.
48
+ * @param seat The borrowed seat who handed it this part, every bot above it on the job, whether it may borrow in turn; null for the bot holding the job.
51
49
  */
52
50
  export async function loadBotPrompt(
53
51
  self: string,
54
52
  persona?: string | null,
55
- askedBy?: string | null,
53
+ seat?: { askedBy: string; above: string[]; canBorrow: boolean } | null,
56
54
  /** The two folders that are this run's rather than the user's (bot.run). */
57
55
  folders?: { scratch: string | null; own: string },
58
56
  ): Promise<LoadedPrompt> {
59
57
  const sandbox = await openWorkspace();
60
58
  const name = self.trim();
59
+ const askedBy = seat?.askedBy ?? null;
61
60
  const [
62
61
  skills,
63
62
  index,
@@ -65,8 +64,8 @@ export async function loadBotPrompt(
65
64
  mcpTools,
66
65
  pinned,
67
66
  allBots,
68
- ownNote,
69
- notesOn,
67
+ kept,
68
+ memoryOn,
70
69
  machine,
71
70
  ] = await Promise.all([
72
71
  loadSkills(sandbox),
@@ -77,26 +76,32 @@ export async function loadBotPrompt(
77
76
  // MCP tools this bot already holds; dropped from the listing below
78
77
  findPinnedTools(name),
79
78
  listJobBots(),
80
- // What this bot left itself; kept by the pass that runs after a job (bot.notes)
81
- readBotNote(name),
82
- readBotNotesOn(),
79
+ // What this bot kept on earlier jobs, read off its own folder (bot.memory)
80
+ listBotMemory(name),
81
+ readBotMemoryOn(),
83
82
  // One `command -v` sweep; what is here decides the first command (environment)
84
83
  readMachineTools(sandbox),
85
84
  ]);
86
85
 
87
- // Drop self so a bot cannot call itself; a borrowed bot has no roster at all (bot.run MAX_DEPTH)
88
- const peers = askedBy ? [] : allBots.filter((bot) => bot.name !== name);
86
+ // Drop itself and every bot above it on this job, which are blocked waiting on
87
+ // it; the last seat the depth allows has no roster at all (config BOT_RUN.depth)
88
+ const peers =
89
+ seat && !seat.canBorrow
90
+ ? []
91
+ : allBots.filter(
92
+ (bot) => bot.name !== name && !seat?.above.includes(bot.name),
93
+ );
89
94
 
90
95
  const text = [
91
96
  askedBy ? borrowedIdentity(name, askedBy) : identity(name),
92
97
  memory(index, carried),
93
- notesOn ? notes(ownNote) : "",
98
+ memoryOn ? ownMemory(botMemoryFolder(name), kept) : "",
94
99
  connectedTools(mcpTools, pinned),
95
100
  methods(skills),
96
101
  environment(sandbox.cwd, machine, folders),
97
102
  roster(peers),
98
103
  askedBy ? askingBack(askedBy) : ASKING,
99
- askedBy ? handingUp(askedBy) : FINISHING,
104
+ askedBy ? handingUp(askedBy) : ANSWERING,
100
105
  // Last, so it is the closest thing to the work and outranks the rest
101
106
  ownerInstruction(persona),
102
107
  ]
@@ -117,7 +122,7 @@ export async function loadBotPrompt(
117
122
  function identity(name: string): string {
118
123
  return [
119
124
  // Named, because the owner's prompt may not name it and its own instructions are addressed to it
120
- `You are ${name}, a worker. Thursday handed you a job while she keeps talking to the user — they speak to her and to nobody else, and what she reads them out of your report is the only part of this that reaches them. You are not in that conversation and never address the user. This thread is drawn on their screen while you work, though: not written to them, but not private either. ${nowLine()}`,
125
+ `You are ${name}, a worker. Thursday handed you a job while she keeps talking to the user — they speak to her and to nobody else, and what she says out of your answer is the only part of this that reaches them. You are not in that conversation and never address the user. This thread is drawn on their screen while you work, though: not written to them, but not private either. ${nowLine()}`,
121
126
  MACHINE,
122
127
  NO_GUESSING,
123
128
  ].join("\n\n");
@@ -126,7 +131,7 @@ function identity(name: string): string {
126
131
  /** The borrowed seat: above it is the borrowing bot, not Thursday. */
127
132
  function borrowedIdentity(name: string, askedBy: string): string {
128
133
  return [
129
- `You are ${name}, a worker. ${askedBy} is holding a job Thursday handed them and has handed one part of it to you. The user speaks to Thursday and to nobody else; you never address them, and what you hand back goes into ${askedBy}'s report as-is. This thread is drawn on their screen while you work: not written to them, but not private either. ${nowLine()}`,
134
+ `You are ${name}, a worker. ${askedBy} has handed you one part of a job Thursday handed over; your first message says who holds it and how it reached you. The user speaks to Thursday and to nobody else; you never address them, and what you hand back goes into ${askedBy}'s answer as-is. This thread is drawn on their screen while you work: not written to them, but not private either. ${nowLine()}`,
130
135
  MACHINE,
131
136
  NO_GUESSING,
132
137
  ].join("\n\n");
@@ -154,16 +159,14 @@ Written by the person this bot works for. Where these and anything above disagre
154
159
  ${persona.trim()}`
155
160
  : "";
156
161
 
157
- /** A bot reads memory and adds to it; revising and naming are the call's (load-tools), so the chapter is that small. */
162
+ /** A bot only reads memory; every write, revising and the names the user says are the call's (load-tools), so the chapter is that small. */
158
163
  function memory(
159
164
  index: MemoryIndexEntry[],
160
165
  carried: MemoryAlwaysLoaded[],
161
166
  ): string {
162
- const head = `## Memory
163
-
164
- What the user's assistant knows about them. Thursday keeps it as she talks with them, and everyone reads it.
167
+ const head = `## The user's memory
165
168
 
166
- Yours to read, and to add to when the work turns up something about *them* that outlives the job; what the job itself turned up goes in your report. It describes the user, not you: how they are named and spoken to is hers to use with them, not yours to borrow. What you learn about this machine is not memory — that is your own instructions, below.`;
169
+ What Thursday knows about the user, kept as she talks with them. Every bot reads it; none writes to it. Open a note with \`${TOOL_NAMES.memory_recall}\` when the job needs something about them the address they sign in with, the name to book under — and put what you find out about them in your answer; she decides what to keep. It describes them, not you: how they are named and spoken to is hers to use. What you learn about the work goes in your own memory, below.`;
167
170
 
168
171
  const alreadyKnown = carried.length
169
172
  ? `Already known:
@@ -181,21 +184,31 @@ A topic not listed is one nobody knows anything about.`;
181
184
  }
182
185
 
183
186
  /**
184
- * The bot's own notes, carried between jobs (database bot_note). Always drawn, empty or not,
185
- * so a first job knows it has them. What belongs in them is on `report`, the only place a
186
- * change can be asked for (tools/bot.tool); the chapter is what they are and that they last.
187
- * The line about what goes in first shows only while they are empty — a standing instruction
188
- * to write something turns every job into a note, and there is nothing to nudge once a bot
189
- * has one. The count is here for the same reason: the room left is a fact, not a target.
187
+ * The bot's own memory (features/bot/bot.memory), listed by each file's first line so what a
188
+ * file holds costs one line until a job opens it. Always drawn, so a first job knows it has one.
189
+ * Nothing says what usually goes in first: a line like that is what a first job writes, whether
190
+ * or not the job taught it anything.
190
191
  */
191
- function notes(own: string | null): string {
192
- return `## Your own instructions
193
-
194
- What you wrote to yourself on earlier jobs here, to get better at this machine. Only you read it, and it is the one thing that reaches your next job. What belongs in it is what working here is like — the command that turns out to be the right one, the flag it needs, the tool this platform does not have. Not what the user is like: that is Memory, above, and everyone reads that one.
195
-
196
- ${own ?? "Empty. What usually goes in first is whatever you had to find out before you could start."}
197
-
198
- (${own?.length ?? 0}/${BOT_NOTES.chars} characters)`;
192
+ function ownMemory(folder: string, kept: BotMemory): string {
193
+ const listing = kept.entries.length
194
+ ? kept.entries
195
+ .map(({ file, line }) =>
196
+ line
197
+ ? `- ${file} ${clip(line, PROMPT_LINE.botMemory)}`
198
+ : `- ${file}`,
199
+ )
200
+ .join("\n")
201
+ : "Empty.";
202
+ const rest =
203
+ kept.total > kept.entries.length
204
+ ? `\n\nThe newest ${kept.entries.length} of ${kept.total}; \`ls ${folder}\` for the rest.`
205
+ : "";
206
+
207
+ return `## Your memory
208
+
209
+ What you kept from your own earlier jobs, in \`${folder}/\`: one topic per file, its first line saying what it holds, read by no other bot. Keep what a later job would otherwise have to find out again — how a site signs in, the way through its screens, a command that turned out right — and fix or delete a file that proved wrong. No passwords, keys or codes.
210
+
211
+ ${listing}${rest}`;
199
212
  }
200
213
 
201
214
  /**
@@ -256,8 +269,8 @@ Your workspace — \`${TOOL_NAMES.bash}\` runs here. Everything you write goes i
256
269
 
257
270
  - \`${PATHS.artifacts}/\` — finished work the user opens, one entry per result. Theirs, and it stays.
258
271
  - \`${PATHS.projects}/\` — code you build, one folder each. It outlives this job, and a project's dependencies install inside it, never at the workspace root.
259
- - \`${folders?.scratch ?? PATHS.scratch}/\` — this job's working material. It goes when the job does, so nothing here is worth keeping.
260
- - \`${folders?.own ?? PATHS.bots}/\` — yours, across every job you run here. A script you wrote once and will want again, a table you built. Your own instructions can name what is in it.
272
+ - \`${folders?.scratch ?? PATHS.scratch}/\` — this job's working material, one folder for every bot on the job: what another bot on it wrote is here too. It is cleared ${Math.round(WORKSPACE_KEEP.forMs / 86_400_000)} days after the job ends, so nothing here is worth keeping.
273
+ - \`${folders?.own ?? PATHS.bots}/\` — yours, across every job you run here: your memory, a script you wrote once and will want again, a table you built.
261
274
 
262
275
  Never the directory above — that is the app you run in; outside the workspace, only where the user pointed you.`;
263
276
 
@@ -271,64 +284,142 @@ ${peers.map((bot) => `- **${bot.name}** — ${bot.description}`).join("\n")}
271
284
 
272
285
  These lines were written for the user, who reads them on their own screen: where one says "you" it means them, not you.
273
286
 
274
- A part of your job another bot is for goes to \`${TOOL_NAMES.ask_bot}\`, because their tools and their practice are the reason they exist. What you build is made of what they bring back, so send for it before you build. A part you could finish in a couple of commands is yours; one that would take a run of its own is theirs. The job stays yours and you report; they see only your brief not the call, not this thread.`;
287
+ A part of your job another bot is for goes to \`${TOOL_NAMES.ask_bot}\`, because their tools and their practice are the reason they exist. What you build is made of what they bring back, so send for it before you build. A part you could finish in a couple of commands is yours; one that would take a run of its own is theirs. What you were handed stays yours and you answer for it. They get the job, the call it came from and what you write them — not this thread.`;
275
288
  }
276
289
 
277
- /** The only place a run can stop. The user's request to buy, pay or top up is itself the go-ahead. */
290
+ /** The only place a run can stop. */
278
291
  const ASKING = `## Asking
279
292
 
280
293
  What you cannot get for yourself — a decision between real options, a detail only the conversation has, a go-ahead before deleting or sending — goes to \`${TOOL_NAMES.ask_thursday}\`, once, with everything in it. Ask before you start, not after an hour; never ask what one command would tell you.
281
294
 
282
295
  A job that arrives without its shape — several ways to do it, no telling what it is for, a scale nobody named — is planned before it is built: put the plan up in two or three lines with what you need decided. A job asked for as one thing is built, not proposed.
283
296
 
284
- A request that says buy, pay, top up is the go-ahead — press the button. Only what is theirs alone stops the job: a password, a one-time code, a passkey, a permission on their machine. Set it up one action away, ask, and carry on when the answer comes back.`;
297
+ Only what is theirs alone stops the job: a password, a one-time code, a passkey, a permission on their machine. Set it up one action away, ask, and carry on when the answer comes back.`;
285
298
 
286
299
  const askingBack = (askedBy: string) => `## Asking
287
300
 
288
301
  What you cannot get for yourself goes to \`${TOOL_NAMES.ask_back}\`: ${askedBy} handed you this part and answers from what they have. They cannot take a question to the user, so for what is the user's alone take the safer reading and say so in what you hand back. Ask before you start, once, whole.`;
289
302
 
290
- const STEP_CAP = `Your steps are capped at ${BOT_RUN.steps}; running long, report what you have with \`complete: false\` while the summary is still yours to write.`;
291
-
292
303
  /**
293
- * The job ends in the thing that was asked for; no prescribed document shape.
304
+ * An answer is sized by the request, not by the shape of a document: this is where
305
+ * the only upper bound on a run is stated, and the one place that says the spoken
306
+ * line is hers to compose. Nothing here asks for a script — a bot that writes to be
307
+ * heard writes for a length rather than for a question, and the padding it adds to
308
+ * reach that length is steps, not words.
309
+ * The step cap is stated nowhere a bot reads: told the number, runs work to it. It is
310
+ * enforced where it cannot be argued with (bot.run `stepCountIs`, and `lastStep`).
294
311
  * Images in `.md` need absolute routes because a document opens from two places and relative paths resolve differently.
295
312
  */
296
- const FINISHING = `## Finishing
313
+ const ANSWERING = `## Answering
297
314
 
298
- Every job ends with \`${TOOL_NAMES.report}\`, in the user's language, written to be heard she reads it out loud. **The job ends in the thing that was asked for**: an action with the action done, photos in a page with the photos in it, a comparison in a table. Anything that does not fit in a few lines is a file under \`${PATHS.artifacts}/\`, and the report names its path it becomes a link on their screen. In a \`.md\`, images only by absolute route (\`/api/file/${PATHS.artifacts}/…\`).
315
+ Every job ends with \`${TOOL_NAMES.answer}\`. **Answer what was asked, at the size it was asked**: a question ends in its answer one number is one line and a thing to make comes back made, an action done, photos in a page, a comparison in a table. Once you have what they asked for the job is done; the next thing you would go and check is theirs to ask for.
299
316
 
300
- \`complete: false\` when part of the request is genuinely undone say where you got to and the user decides. ${STEP_CAP}`;
317
+ You are answering Thursday, not the user: she is on the call and picks what to say out of this. Write it plainly, in the user's language — the thread is on their screen as you write it. Anything past a few lines is a file under \`${PATHS.artifacts}/\`, and the answer names its path — it becomes a link on their screen. In a \`.md\`, images only by absolute route (\`/api/file/${PATHS.artifacts}/…\`).`;
301
318
 
302
- const handingUp = (askedBy: string) => `## Finishing
319
+ const handingUp = (askedBy: string) => `## Answering
303
320
 
304
- Every part ends with \`${TOOL_NAMES.report}\`; what you hand back goes into ${askedBy}'s own report. Hand back what the brief asked for, in the shape it asked for, with everything you learned that bears on it — exact values, the paths of files you wrote, what did not work. They cannot see your thread, so long is right here. \`complete: false\` when part of the brief is undone, with where you got to. ${STEP_CAP}`;
321
+ Every part ends with \`${TOOL_NAMES.answer}\`; what you hand back goes into ${askedBy}'s own answer. Hand back what the brief asked for, in the shape it asked for, with everything you learned that bears on it — exact values, the paths of files you wrote, what did not work. They cannot see your thread, so long is right here.`;
305
322
 
306
323
  /** How many turns of the call travel with the job: enough for one missed detail, not enough to bury the request. */
307
324
  export const OPENING_TURNS = 10;
308
- const OPENING_TURN_CHARS = 160;
325
+
326
+ /** A user message's content; every seat's first message is two text parts (buildTaskOpening). */
327
+ export type OpeningContent = Extract<ModelMessage, { role: "user" }>["content"];
309
328
 
310
329
  /**
311
- * The first message a bot reads: the request plus the last turns of the call, verbatim.
312
- * The request is one sentence the realtime model produced; a detail the user said may only be here.
313
- * Stored as the thread's first row (seq 0) so it survives a resume (bot.runner).
330
+ * The first message of the bot holding a job, stored as the thread's first row
331
+ * (seq 0, bot.runner startTask) and read whole on every resume and after every
332
+ * compaction. Two text parts: who is who on this job, then the chain — the job
333
+ * as Thursday handed it and the call it came from, verbatim, because the request
334
+ * is one sentence the realtime model produced and a detail the user said may
335
+ * only be in the call. The chain is what a borrowed bot inherits (buildHandoff),
336
+ * so its headings name people and never say "you".
314
337
  */
315
338
  export function buildTaskOpening(input: {
339
+ bot: string;
316
340
  request: string;
317
341
  conversation: { role: "user" | "assistant"; text: string }[];
318
- }): string {
342
+ }): OpeningContent {
319
343
  const turns = input.conversation.filter((turn) => turn.text.trim());
320
- if (!turns.length) return input.request;
344
+ const job = `## Thursday → ${input.bot}: the job\n\n${input.request.trim()}`;
345
+ const call = turns.length
346
+ ? `## The call the job came from — its last ${turns.length} turns, verbatim
347
+
348
+ A detail missing from the job may be here — a name, a number, which of two. Where the two disagree the job wins, unless they asked out loud for something to be done and the job only asks about it: then the doing is the job.
321
349
 
322
- const lines = turns.map(
350
+ ${turns
351
+ .map(
323
352
  (turn) =>
324
- `${turn.role === "user" ? "user" : "thursday"}: ${clip(turn.text, OPENING_TURN_CHARS)}`,
325
- );
326
- return `${input.request}
353
+ `${turn.role === "user" ? "user" : "thursday"}: ${clip(turn.text, PROMPT_LINE.callTurn)}`,
354
+ )
355
+ .join("\n")}`
356
+ : "";
357
+ return [
358
+ { type: "text", text: whoIsWho([input.bot]) },
359
+ { type: "text", text: [job, call].filter(Boolean).join("\n\n") },
360
+ ];
361
+ }
327
362
 
328
- ## The call, just before this reached you
363
+ /**
364
+ * The first message of a borrowed bot, built by the bot that borrows it (bot.run
365
+ * `ask_bot`) and never stored: the chain it inherits, copied as it is, then what
366
+ * the borrowing bot has done and the part it hands over. `bots` runs from the
367
+ * bot holding the job down to this one, so each hand-off adds one name and two
368
+ * sections and every seat reads the same shape.
369
+ */
370
+ export function buildHandoff(input: {
371
+ chain: string;
372
+ bots: string[];
373
+ did: string | null;
374
+ part: string;
375
+ }): OpeningContent {
376
+ const to = input.bots.at(-1) ?? "";
377
+ const from = input.bots.at(-2) ?? "";
378
+ const did = input.did?.trim()
379
+ ? `## ${from} → ${to}: what ${from} has done, and why this part is ${to}'s\n\n${input.did.trim()}`
380
+ : "";
381
+ const part = `## ${from} → ${to}: the part\n\n${input.part.trim()}`;
382
+ return [
383
+ { type: "text", text: whoIsWho(input.bots) },
384
+ {
385
+ type: "text",
386
+ text: [input.chain, did, part].filter(Boolean).join("\n\n"),
387
+ },
388
+ ];
389
+ }
329
390
 
330
- A detail that is not in the request may be here — a name, a number, which one of two. The request wins where they disagree, with one exception: when they asked out loud for something to be *done* and the request only asks about it, the doing is the job.
391
+ /**
392
+ * The chain out of a seat's first message, for the next hand-off. A thread
393
+ * opened before the two-part shape has its opening as one string, which is the
394
+ * whole chain there was.
395
+ */
396
+ export function chainOf(first: ModelMessage | undefined): string {
397
+ if (first?.role !== "user") return "";
398
+ if (typeof first.content === "string") return first.content;
399
+ const texts = first.content.flatMap((part) =>
400
+ part.type === "text" ? [part.text] : [],
401
+ );
402
+ return texts.at(-1) ?? "";
403
+ }
331
404
 
405
+ /** Who is who on this job; the last of `bots` is the one reading it. */
406
+ function whoIsWho(bots: string[]): string {
407
+ const you = bots.length - 1;
408
+ const lines = bots.map((bot, index) => {
409
+ if (index === 0) {
410
+ return index === you
411
+ ? `- **${bot} (you)** — Thursday handed you this job. You answer to Thursday, and your answer is the only part of it that reaches the user.`
412
+ : `- **${bot}** — holds the job Thursday handed over, and answers for it.`;
413
+ }
414
+ const from = bots[index - 1];
415
+ return index === you
416
+ ? `- **${bot} (you)** — ${from} handed you one part of it: the last section below. You answer to ${from}; nobody else hears from you.`
417
+ : `- **${bot}** — ${from} handed them one part of it.`;
418
+ });
419
+ return `## Who is who on this job
420
+
421
+ - **The user** — talks only to Thursday, on a voice call. No bot addresses them.
422
+ - **Thursday** — the user's assistant on that call. Hands jobs to bots and tells the user what comes back.
332
423
  ${lines.join("\n")}`;
333
424
  }
334
425
 
@@ -0,0 +1,33 @@
1
+ import { TOOL_NAMES } from "@/features/ai/tools/tool-name";
2
+ import { listNoteIndex } from "@/features/memory/memory.query";
3
+ import { MEMORY_PATHS } from "@/features/memory/memory.schema";
4
+ import { logPromptSize, noteLines, nowLine } from "./prompt-helper";
5
+
6
+ /**
7
+ * Everything an edit from the memory screen hears (features/memory/memory.edit):
8
+ * who it is working for, and memory as a listing — one line a note, with how
9
+ * many facts it holds — the way the call sees it. Facts and their ids come from
10
+ * opening a note, so the prompt grows with the number of notes, not of facts.
11
+ * Assembled once per request.
12
+ */
13
+ export async function loadMemoryEditPrompt(): Promise<string> {
14
+ const text = [identity(), memory(await listNoteIndex())].join("\n\n");
15
+ logPromptSize("memory-edit", text);
16
+ return text;
17
+ }
18
+
19
+ function identity(): string {
20
+ return `You are the user's personal assistant, and this is their memory. Change it the way their message asks: add what is new, replace what stopped being true, remove what they want gone. Make every change with \`${TOOL_NAMES.memory_remember}\` and \`${TOOL_NAMES.memory_forget}\`; when nothing is left to change, stop. ${nowLine()}`;
21
+ }
22
+
23
+ function memory(index: Awaited<ReturnType<typeof listNoteIndex>>): string {
24
+ return `## Memory
25
+
26
+ path — what is under it (facts) "what the user calls it"
27
+
28
+ ${noteLines(index)}
29
+
30
+ A note is listed, not shown: open it with \`${TOOL_NAMES.memory_recall}\` for its facts and their ids before replacing or removing any of them. Adding to a note needs no opening.
31
+
32
+ ${MEMORY_PATHS.map((entry) => `- ${entry.path} — ${entry.of}`).join("\n")}`;
33
+ }
@@ -1,13 +1,13 @@
1
1
  import { format, formatDistanceToNowStrict } from "date-fns";
2
- import { MEMORY_LISTING_TOKENS, PROMPT_BUDGET, PROMPT_LINE } from "@/config";
2
+ import { MEMORY_LIMITS, PROMPT_BUDGET, PROMPT_LINE } from "@/config";
3
3
  import { TOOL_NAMES } from "@/features/ai/tools/tool-name";
4
4
  import type { McpToolRef } from "@/features/connectors/mcp.schema";
5
5
  import type {
6
6
  MemoryAlwaysLoaded,
7
7
  MemoryIndexEntry,
8
8
  } from "@/features/memory/memory.schema";
9
- import { MANY_FACTS } from "@/features/memory/memory.schema";
10
9
  import type { SkillMetadata } from "@/features/skills/skills.discover";
10
+ import { delegatedLabel } from "@/features/thursday/tool-line";
11
11
  import { toDate } from "@/lib/date-like";
12
12
  import { logger } from "@/lib/logger";
13
13
  import { estimateTokens, sectionTokens } from "@/lib/tokens";
@@ -58,6 +58,36 @@ export const clockNow = (now = new Date()) =>
58
58
  /** `**Now**: 2026-09-02 (Wed) 15:41 Asia/Seoul` */
59
59
  export const nowLine = (now = new Date()) => `**Now**: ${clockNow(now)}`;
60
60
 
61
+ /**
62
+ * When a call happened, the one way every prompt and tool says it: local, the
63
+ * same shape as `Now` (clockNow), with how long ago. A UTC stamp beside a local
64
+ * `Now` put two clocks nine hours apart in one prompt, and a call from last
65
+ * night read as one from this afternoon.
66
+ */
67
+ export const callStamp = (at: Date): string =>
68
+ `${format(at, "yyyy-MM-dd (EEE) HH:mm")} (${formatDistanceToNowStrict(at, { addSuffix: true })})`;
69
+
70
+ /** When a fact was said, for the fact itself: the date and time without the distance. */
71
+ export const saidStamp = (at: Date): string =>
72
+ format(at, "yyyy-MM-dd (EEE) HH:mm");
73
+
74
+ /**
75
+ * A call as a conversation: who said what, and which tool was used — never its
76
+ * arguments or its result. What was asked for and what was said back is the
77
+ * conversation; the arguments are already in memory and the results are not
78
+ * what anyone said.
79
+ */
80
+ export const conversationLines = (
81
+ turns: { role: string; tool: string | null; text: string }[],
82
+ ): string =>
83
+ turns
84
+ .map((turn) =>
85
+ turn.role === "tool"
86
+ ? `you → ${turn.tool ?? "tool"}`
87
+ : `${turn.role === "user" ? "user" : "you"}: ${turn.text}`,
88
+ )
89
+ .join("\n");
90
+
61
91
  /** `4mo`, `12d`: short enough to read aloud. */
62
92
  function sinceLast(at: MemoryIndexEntry["lastSeenAt"]): string {
63
93
  const days = Math.floor((Date.now() - toDate(at).getTime()) / 86_400_000);
@@ -85,13 +115,15 @@ export const carriedLines = (loaded: MemoryAlwaysLoaded[]): string =>
85
115
  loaded.map((fact) => `- ${fact.text} · ${fact.path} #${fact.id}`).join("\n");
86
116
 
87
117
  /**
88
- * Whether it is time to tidy, and why: a crowded listing points at the coldest notes,
89
- * a heavy note at itself. load-tools also reads this to decide whether `memory_show` is attached.
118
+ * Whether it is time to tidy, and why: too much held in all (the coldest notes
119
+ * are what to drop), or one note too long to hold in one piece (it names itself).
120
+ * Both counted in facts, the unit the user sees on their own screen and the one
121
+ * every write hands back — a token estimate is nobody's unit and cannot be acted on.
90
122
  */
91
123
  export const tidying = (index: MemoryIndexEntry[]) => ({
92
- // The age column is only shown when picking what to drop
93
- crowded: estimateTokens(noteLines(index)) > MEMORY_LISTING_TOKENS,
94
- heavy: index.filter((note) => note.factCount > MANY_FACTS),
124
+ crowded:
125
+ index.reduce((sum, note) => sum + note.factCount, 0) > MEMORY_LIMITS.facts,
126
+ heavy: index.filter((note) => note.factCount > MEMORY_LIMITS.factsPerNote),
95
127
  });
96
128
 
97
129
  /** A bot gets the whole description; `short` gives the voice prompt the first sentence only. */
@@ -158,9 +190,6 @@ export type RecentCall = {
158
190
  }[];
159
191
  };
160
192
 
161
- /** How much of a past job's report is worth carrying; the rest is asked for with `task`. */
162
- const JOB_OUTCOME = 160;
163
-
164
193
  /**
165
194
  * One turn as the transcript carries it. A `delegate` line takes what became of
166
195
  * the job: the handle to pick it back up, and how it ended — arguments alone say
@@ -173,25 +202,20 @@ function turnLine(turn: RecentCall["turns"][number], call: RecentCall): string {
173
202
  const args = `you → ${turn.tool ?? "tool"} ${clip(turn.text, PROMPT_LINE.toolArgs)}`;
174
203
  if (turn.tool !== TOOL_NAMES.delegate) return args;
175
204
 
176
- const label = ((): string | null => {
177
- try {
178
- const said = JSON.parse(turn.text) as { label?: unknown };
179
- return typeof said.label === "string" ? said.label : null;
180
- } catch {
181
- return null;
182
- }
183
- })();
205
+ const label = delegatedLabel(turn.tool, turn.text);
184
206
  const job = label ? call.jobs?.find((one) => one.label === label) : undefined;
185
207
  if (!job) return args;
186
208
 
187
- const said = job.outcome ? `: ${clip(job.outcome, JOB_OUTCOME)}` : "";
209
+ const said = job.outcome
210
+ ? `: ${clip(job.outcome, PROMPT_LINE.jobOutcome)}`
211
+ : "";
188
212
  return `you → ${turn.tool} "${job.label}" (${job.id}) — ${job.status}${said}`;
189
213
  }
190
214
 
191
215
  /** The last calls as stored, newest last, cut to a token budget. A tool turn carries the call, not the result. */
192
216
  export function recentCallLines(calls: RecentCall[], budget: number): string {
193
217
  // Newest line backwards until the budget is spent, then put it back in order.
194
- // Measured on the rendered line: a job's report is on it and its arguments are not
218
+ // Measured on the rendered line: a job's answer is on it and its arguments are not
195
219
  const kept: { call: RecentCall; line: string }[] = [];
196
220
  let spent = 0;
197
221
  outer: for (let c = calls.length - 1; c >= 0; c--) {
@@ -211,12 +235,7 @@ export function recentCallLines(calls: RecentCall[], budget: number): string {
211
235
  for (const { call, line } of kept) {
212
236
  if (call !== open) {
213
237
  open = call;
214
- const when = formatDistanceToNowStrict(call.startedAt, {
215
- addSuffix: true,
216
- });
217
- lines.push(
218
- `### ${call.startedAt.toISOString().slice(0, 16).replace("T", " ")} (${when})`,
219
- );
238
+ lines.push(`### ${callStamp(call.startedAt)}`);
220
239
  }
221
240
  lines.push(line);
222
241
  }
@@ -1,4 +1,4 @@
1
- import { RECENT_CALL } from "@/config";
1
+ import { MEMORY_LIMITS, RECENT_CALL } from "@/config";
2
2
  import { TOOL_NAMES } from "@/features/ai/tools/tool-name";
3
3
  import { listJobBots } from "@/features/bot/bot.query";
4
4
  import type { JobBot } from "@/features/bot/bot.schema";
@@ -159,27 +159,46 @@ ${carriedLines(carried)}`
159
159
 
160
160
  ${noteLines(index, crowded)}
161
161
 
162
- Open a note before answering out of it; a topic not listed is one you know nothing about.
162
+ Open a note before answering out of it; a topic not listed is one you know nothing about. A fact marked \`said\` came from a call; open that call with \`${TOOL_NAMES.memory_conversation}\` only when the line itself cannot answer — exactly what they said, or why it was saved.
163
163
 
164
164
  Anything worth knowing next time goes in the moment it comes up, without asking — one fact per line, dates as dates. A rule they lay down for you — their language, how long an answer runs, when to hang up — is \`alwaysLoad\`; unsaved, it dies with the call. What stopped being true is corrected with \`replaces\`, not left standing beside it. Never claim to remember what you did not save.
165
165
 
166
166
  ${MEMORY_PATHS.map((entry) => `- ${entry.path} — ${entry.of}`).join("\n")}`;
167
167
 
168
- // Tidying rules only when there is something to tidy
169
- const why = heavy.length
170
- ? `Too long to hold in one piece: ${heavy
168
+ // Only when there is something to tidy, and it names a few notes rather than
169
+ // every one it caught: a warning that lists everything is a second listing,
170
+ // and what it is warning about is that the first one is already too long.
171
+ const full = heavy.length
172
+ ? `${[...heavy]
173
+ .sort((a, b) => b.factCount - a.factCount)
174
+ .slice(0, 3)
171
175
  .map((note) => `${note.path} (${note.factCount})`)
172
- .join(", ")}.`
173
- : `This listing is past its size. Coldest: ${index
176
+ .join(", ")} ${heavy.length > 1 ? "have" : "has"} grown past ${
177
+ MEMORY_LIMITS.factsPerNote
178
+ } facts, more than one note holds well. Put ${
179
+ heavy.length > 1 ? "each" : "it"
180
+ } on their screen with \`${TOOL_NAMES.memory_show}\`, read back what is stale and forget only what they name.`
181
+ : "";
182
+ const many = crowded
183
+ ? `Memory is past ${MEMORY_LIMITS.facts} facts in all. The coldest notes are ${index
174
184
  .slice(-4)
175
185
  .map((note) => note.path)
176
- .join(", ")}.`;
177
- const tidy =
178
- crowded || heavy.length
179
- ? `${why} In a lull, put one on their screen with \`${TOOL_NAMES.memory_show}\` and forget only what they name.`
180
- : "";
181
-
182
- return [head, alreadyKnown, listing, tidy].filter(Boolean).join("\n\n");
186
+ .join(
187
+ ", ",
188
+ )} ask whether any of them is still worth keeping, and delete the note if not.`
189
+ : "";
190
+ // Ahead of anything else she would raise, never ahead of what they came with
191
+ const tidy = [full, many].filter(Boolean).join(" ");
192
+
193
+ return [
194
+ head,
195
+ alreadyKnown,
196
+ listing,
197
+ tidy &&
198
+ `${tidy} Get this settled before anything else you would bring up yourself — not before what they came to you with.`,
199
+ ]
200
+ .filter(Boolean)
201
+ .join("\n\n");
183
202
  }
184
203
 
185
204
  /** Last calls verbatim, marked as past so the model does not answer as if just asked. Absent on the first call. */
@@ -244,7 +263,7 @@ Who you hand work to — the names \`${TOOL_NAMES.delegate}\` takes.
244
263
 
245
264
  ${input.roster.map((bot) => `- **${bot.name}** — ${bot.description}`).join("\n")}`;
246
265
 
247
- const machine = `**A bot has the whole machine** — this computer, a real browser, the web, files, a shell to build whatever is missing — and far more time than you. It signs in where it has to, with the window in front of them, and carries a job to the end: the thing bought, the account made, the page built. How, and whether, is its call; yours is what they want — so a thing you have no idea how to do is a job, not a no. Bots borrow each other, so a job that spans several things is still one job for one bot.`;
266
+ const machine = `**A bot has the whole machine** — this computer, a real browser, the web, files, a shell to build whatever is missing — and far more time than you. It signs in where it has to, with the window in front of them, and carries a job to the end: the account made, the page built. How, and whether, is its call; yours is what they want — so a thing you have no idea how to do is a job, not a no. Bots borrow each other, so a job that spans several things is still one job for one bot.`;
248
267
 
249
268
  const hands = [
250
269
  input.skills.length
@@ -265,9 +284,13 @@ ${hands.join("\n\n")}`
265
284
 
266
285
  const handingOver = `**Anything that takes more than a few seconds is a bot's** — one note or one file is yours. \`${TOOL_NAMES.delegate}\` answers at once: say who has it and keep talking. Put the request in their own words, with what it stands on and nothing they did not say; if something only they can say is missing — how much, which one, by when — ask that first.
267
286
 
268
- A job you handed over is followed with \`${TOOL_NAMES.task}\`to answer what it asks, change its course, or continue it never with a second \`${TOOL_NAMES.delegate}\`; with no job named it lists what is open.`;
287
+ **The conversation belongs to the job, not to the bot** hand the same bot a second job and it starts from nothing, knowing neither what was asked nor what it found. So more about a job you already handed over goes to \`${TOOL_NAMES.task}\` by its name, and the bot wakes with that job's own thread; with no name given it takes the one that moved last.`;
269
288
 
270
- const comingBack = `A job comes back as a system note, not the user speaking: tell them in one sentence, in your own words. A file it names opens on their screen by itself — say what it holds, not the path. A question arrives the same way: answer it yourself if you can, else read them the options and send back what they say.`;
289
+ // An answer is written to her, and the whole of it is already drawn on the
290
+ // user's screen (bot-room). Saying so is what keeps its length from deciding
291
+ // hers: a bot that answers a one-line question in one line and a bot that
292
+ // hands back a table both end in one spoken sentence.
293
+ const comingBack = `A job comes back as a system note, not the user speaking. **It was written to you, not to them** — its length is not how much to say, and the whole of it is already on their screen. Give them the part that answers what they asked, in one sentence, in your own words. A file it names opens there by itself — say what it holds, not the path. A question arrives the same way: answer it yourself if you can, else read them the options and send back what they say.`;
271
294
 
272
295
  return [list, machine, catalogue, handingOver, comingBack]
273
296
  .filter(Boolean)