thursday-agent 0.10.0 → 0.12.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 (308) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +0 -1
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/prerender-manifest.json +3 -3
  5. package/.next/routes-manifest.json +0 -6
  6. package/.next/server/app/_global-error/page.js.nft.json +1 -1
  7. package/.next/server/app/_global-error.html +1 -1
  8. package/.next/server/app/_global-error.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  12. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  13. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/.next/server/app/_not-found.html +1 -1
  15. package/.next/server/app/_not-found.rsc +2 -2
  16. package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  17. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  18. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  19. package/.next/server/app/api/artifact/route.js.nft.json +1 -1
  20. package/.next/server/app/api/bot/keep-working/route.js.nft.json +1 -1
  21. package/.next/server/app/api/bot/memory/files/route.js.nft.json +1 -1
  22. package/.next/server/app/api/bot/memory/route.js.nft.json +1 -1
  23. package/.next/server/app/api/bot/route.js.nft.json +1 -1
  24. package/.next/server/app/api/bot/thread/[id]/route.js.nft.json +1 -1
  25. package/.next/server/app/api/bot/thread/route.js.nft.json +1 -1
  26. package/.next/server/app/api/config/route.js.nft.json +1 -1
  27. package/.next/server/app/api/events/route.js.nft.json +1 -1
  28. package/.next/server/app/api/favicon/[host]/route.js.nft.json +1 -1
  29. package/.next/server/app/api/file/[...path]/route.js.nft.json +1 -1
  30. package/.next/server/app/api/llm-model/catalog/route.js.nft.json +1 -1
  31. package/.next/server/app/api/llm-model/credits/route.js.nft.json +1 -1
  32. package/.next/server/app/api/llm-model/route.js.nft.json +1 -1
  33. package/.next/server/app/api/llm-model/subscription/route.js.nft.json +1 -1
  34. package/.next/server/app/api/mcp/[name]/route.js.nft.json +1 -1
  35. package/.next/server/app/api/mcp/oauth/callback/route.js.nft.json +1 -1
  36. package/.next/server/app/api/mcp/route.js.nft.json +1 -1
  37. package/.next/server/app/api/mcp/tools/route.js.nft.json +1 -1
  38. package/.next/server/app/api/memory/edit/route.js.nft.json +1 -1
  39. package/.next/server/app/api/memory/route.js.nft.json +1 -1
  40. package/.next/server/app/api/reach/route.js +13 -13
  41. package/.next/server/app/api/reach/route.js.nft.json +1 -1
  42. package/.next/server/app/api/routine/route.js.nft.json +1 -1
  43. package/.next/server/app/api/signins/route.js +1 -1
  44. package/.next/server/app/api/signins/route.js.nft.json +1 -1
  45. package/.next/server/app/api/skills/[source]/[name]/route.js.nft.json +1 -1
  46. package/.next/server/app/api/skills/route.js.nft.json +1 -1
  47. package/.next/server/app/api/thursday/call/route.js.nft.json +1 -1
  48. package/.next/server/app/api/thursday/skills/route.js.nft.json +1 -1
  49. package/.next/server/app/api/thursday/text/route.js.nft.json +1 -1
  50. package/.next/server/app/api/thursday/tool-call/route.js +14 -15
  51. package/.next/server/app/api/thursday/tool-call/route.js.nft.json +1 -1
  52. package/.next/server/app/api/workspace/files/route.js.nft.json +1 -1
  53. package/.next/server/app/api/workspace/route.js.nft.json +1 -1
  54. package/.next/server/app/artifact/[...path]/page/server-reference-manifest.json +1 -1
  55. package/.next/server/app/artifact/[...path]/page.js.nft.json +1 -1
  56. package/.next/server/app/artifact/[...path]/page_client-reference-manifest.js +1 -1
  57. package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
  58. package/.next/server/app/manifest.webmanifest/route.js.nft.json +1 -1
  59. package/.next/server/app/page/server-reference-manifest.json +56 -56
  60. package/.next/server/app/page.js +4 -4
  61. package/.next/server/app/page.js.nft.json +1 -1
  62. package/.next/server/app/page_client-reference-manifest.js +1 -1
  63. package/.next/server/app-paths-manifest.json +0 -1
  64. package/.next/server/chunks/[root-of-the-server]__014ydvq._.js +2 -2
  65. package/.next/server/chunks/[root-of-the-server]__0209ah4._.js +7 -5
  66. package/.next/server/chunks/[root-of-the-server]__04r4h-2._.js +4 -2
  67. package/.next/server/chunks/[root-of-the-server]__04wldw9._.js +1 -1
  68. package/.next/server/chunks/[root-of-the-server]__0542v_d._.js +3 -0
  69. package/.next/server/chunks/[root-of-the-server]__0cfszwn._.js +1 -1
  70. package/.next/server/chunks/[root-of-the-server]__0gewdg9._.js +2 -2
  71. package/.next/server/chunks/[root-of-the-server]__0mrk2it._.js +1 -1
  72. package/.next/server/chunks/[root-of-the-server]__0npn_pn._.js +1 -1
  73. package/.next/server/chunks/[root-of-the-server]__0nr-v8s._.js +1 -1
  74. package/.next/server/chunks/[root-of-the-server]__0rqgb17._.js +1 -1
  75. package/.next/server/chunks/[root-of-the-server]__1-swlkw._.js +2 -2
  76. package/.next/server/chunks/[root-of-the-server]__12iddfq._.js +1 -1
  77. package/.next/server/chunks/[root-of-the-server]__1ai5lme._.js +3 -3
  78. package/.next/server/chunks/[root-of-the-server]__1cjxw0-._.js +2 -2
  79. package/.next/server/chunks/{[root-of-the-server]__1fy2g82._.js → [root-of-the-server]__1k3j52n._.js} +1 -1
  80. package/.next/server/chunks/[root-of-the-server]__1kyjt5m._.js +3 -3
  81. package/.next/server/chunks/[root-of-the-server]__1tg5got._.js +1 -1
  82. package/.next/server/chunks/[root-of-the-server]__1ygplx1._.js +1 -1
  83. package/.next/server/chunks/_05v8k21._.js +3 -1
  84. package/.next/server/chunks/_0awtznl._.js +1 -1
  85. package/.next/server/chunks/_0f6ts7z._.js +1 -1
  86. package/.next/server/chunks/_0na2fj1._.js +1 -1
  87. package/.next/server/chunks/_0x-ej3f._.js +17 -17
  88. package/.next/server/chunks/_10tkr26._.js +1 -1
  89. package/.next/server/chunks/_12-amdu._.js +2 -2
  90. package/.next/server/chunks/_15740dw._.js +60 -0
  91. package/.next/server/chunks/_1dc_d1q._.js +3 -3
  92. package/.next/server/chunks/_1i350l5._.js +3 -1
  93. package/.next/server/chunks/_1jc5g4p._.js +2 -2
  94. package/.next/server/chunks/_1jn-aep._.js +4 -4
  95. package/.next/server/chunks/_1m88lkh._.js +11 -11
  96. package/.next/server/chunks/_1ogqovh._.js +4 -4
  97. package/.next/server/chunks/_1taa4bs._.js +1 -1
  98. package/.next/server/chunks/_1tpr507._.js +1 -1
  99. package/.next/server/chunks/_1uuucy-._.js +1 -1
  100. package/.next/server/chunks/_1zxp-p6._.js +1 -1
  101. package/.next/server/chunks/features_0rfvely._.js +5 -3
  102. package/.next/server/chunks/ssr/[root-of-the-server]__0a967km._.js +3 -0
  103. package/.next/server/chunks/ssr/{[root-of-the-server]__1se25x0._.js → [root-of-the-server]__0hpp3nw._.js} +29 -13
  104. package/.next/server/chunks/ssr/[root-of-the-server]__0tzekbl._.js +24 -60
  105. package/.next/server/chunks/ssr/{[root-of-the-server]__0tvavy9._.js → [root-of-the-server]__1iwyp1e._.js} +1 -1
  106. package/.next/server/chunks/ssr/[root-of-the-server]__1r77-88._.js +3 -0
  107. package/.next/server/chunks/ssr/[root-of-the-server]__1w9hqq8._.js +1 -1
  108. package/.next/server/chunks/ssr/_026qnc9._.js +1 -1
  109. package/.next/server/chunks/ssr/{_173siic._.js → _02c7o30._.js} +2 -2
  110. package/.next/server/chunks/ssr/_097-ljd._.js +1 -1
  111. package/.next/server/chunks/ssr/_0euzzf8._.js +3 -0
  112. package/.next/server/chunks/ssr/{_1u2jw0i._.js → _0fbi6om._.js} +3 -3
  113. package/.next/server/chunks/ssr/_0fqhhwi._.js +1 -1
  114. package/.next/server/chunks/ssr/_0igfcax._.js +20 -0
  115. package/.next/server/chunks/ssr/_0ld9_qq._.js +1 -1
  116. package/.next/server/chunks/ssr/_0nvh1h9._.js +3 -0
  117. package/.next/server/chunks/ssr/_0qmpw35._.js +3 -0
  118. package/.next/server/chunks/ssr/{_1yk6yak._.js → _0rmaf32._.js} +2 -2
  119. package/.next/server/chunks/ssr/_0uv-qs4._.js +3 -0
  120. package/.next/server/chunks/ssr/{_1doidrw._.js → _10f-jib._.js} +2 -2
  121. package/.next/server/chunks/ssr/{_05rxyf9._.js → _10mo72m._.js} +2 -2
  122. package/.next/server/chunks/ssr/_12mijol._.js +11 -11
  123. package/.next/server/chunks/ssr/_132qkfz._.js +1 -1
  124. package/.next/server/chunks/ssr/_15zqt-t._.js +1 -1
  125. package/.next/server/chunks/ssr/_16gfxpn._.js +3 -0
  126. package/.next/server/chunks/ssr/_184327j._.js +1 -1
  127. package/.next/server/chunks/ssr/{_1ng831c._.js → _18ujhhq._.js} +2 -2
  128. package/.next/server/chunks/ssr/_1d423aj._.js +7 -5
  129. package/.next/server/chunks/ssr/_1lx6w_o._.js +67 -0
  130. package/.next/server/chunks/ssr/_1mqn9mc._.js +1 -1
  131. package/.next/server/chunks/ssr/_1neoj22._.js +1 -1
  132. package/.next/server/chunks/ssr/{_19n08g5._.js → _1ntpny5._.js} +2 -2
  133. package/.next/server/chunks/ssr/{_0bp-_fi._.js → _1o1g4uk._.js} +3 -3
  134. package/.next/server/chunks/ssr/_1xoch6f._.js +1 -1
  135. package/.next/server/chunks/ssr/_1yh5bj5._.js +1 -1
  136. package/.next/server/chunks/ssr/{_0tg0vf8._.js → _2008vmi._.js} +8 -8
  137. package/.next/server/chunks/ssr/{features_bot_1yr-_pm._.js → features_bot_1n1s7bh._.js} +2 -2
  138. package/.next/server/chunks/ssr/features_connectors_components_mcp-setting_tsx_0jmbgvp._.js +1 -1
  139. package/.next/server/chunks/ssr/features_memory_components_memory-setting_tsx_0oa43sw._.js +1 -1
  140. package/.next/server/chunks/ssr/features_routine_components_routine-setting_tsx_055fknz._.js +1 -1
  141. package/.next/server/chunks/ssr/features_workspace_00jz2hu._.js +1 -1
  142. package/.next/server/chunks/ssr/{node_modules_1n0h9sf._.js → node_modules_0-x0zxm._.js} +1 -1
  143. package/.next/server/chunks/ssr/{node_modules_1xzo2wn._.js → node_modules_00nrzz_._.js} +1 -1
  144. package/.next/server/chunks/ssr/{node_modules_0511ha-._.js → node_modules_01ed0yi._.js} +1 -1
  145. package/.next/server/chunks/ssr/{node_modules_18woe20._.js → node_modules_01u4ea3._.js} +1 -1
  146. package/.next/server/chunks/ssr/{node_modules_1expacw._.js → node_modules_05b6e99._.js} +1 -1
  147. package/.next/server/chunks/ssr/{node_modules_0eb4rx7._.js → node_modules_07jp6tj._.js} +1 -1
  148. package/.next/server/chunks/ssr/{node_modules_0piws2a._.js → node_modules_0azd56x._.js} +1 -1
  149. package/.next/server/chunks/ssr/{node_modules_0f2k5l0._.js → node_modules_0b0iy01._.js} +1 -1
  150. package/.next/server/chunks/ssr/{node_modules_0wq4-ts._.js → node_modules_0fpnetd._.js} +1 -1
  151. package/.next/server/chunks/ssr/{node_modules_11hbkuj._.js → node_modules_0ky76fz._.js} +1 -1
  152. package/.next/server/chunks/ssr/{node_modules_048wmmj._.js → node_modules_0lautv3._.js} +1 -1
  153. package/.next/server/chunks/ssr/{node_modules_1dcbvfw._.js → node_modules_0m3_5ge._.js} +1 -1
  154. package/.next/server/chunks/ssr/{node_modules_0pk6i-e._.js → node_modules_0operbz._.js} +1 -1
  155. package/.next/server/chunks/ssr/{node_modules_1zkpek5._.js → node_modules_0ou0all._.js} +1 -1
  156. package/.next/server/chunks/ssr/{node_modules_04t200t._.js → node_modules_0ucridu._.js} +1 -1
  157. package/.next/server/chunks/ssr/{node_modules_08g12f-._.js → node_modules_0v_-uht._.js} +1 -1
  158. package/.next/server/chunks/ssr/{node_modules_1m2ho33._.js → node_modules_0yzmxfq._.js} +1 -1
  159. package/.next/server/chunks/ssr/{node_modules_1ksh9gy._.js → node_modules_19b0bir._.js} +1 -1
  160. package/.next/server/chunks/ssr/{node_modules_1qehmwm._.js → node_modules_1c2ikr9._.js} +1 -1
  161. package/.next/server/chunks/ssr/{node_modules_1_afpz7._.js → node_modules_1hv-j1l._.js} +1 -1
  162. package/.next/server/chunks/ssr/{node_modules_0cdu6jq._.js → node_modules_1iau_ea._.js} +1 -1
  163. package/.next/server/chunks/ssr/{node_modules_1zf46lp._.js → node_modules_1ma_y68._.js} +1 -1
  164. package/.next/server/chunks/ssr/{node_modules_0s1lwc9._.js → node_modules_1omg9xr._.js} +1 -1
  165. package/.next/server/chunks/ssr/{node_modules_1cxc8kn._.js → node_modules_1wgnj8q._.js} +1 -1
  166. package/.next/server/chunks/ssr/{node_modules_0zjqwz7._.js → node_modules_1wvf3oo._.js} +1 -1
  167. package/.next/server/chunks/ssr/{node_modules_1_ban4u._.js → node_modules_1z3sh11._.js} +1 -1
  168. package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0avl8-1._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0tmjn9w._.js} +1 -1
  169. package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_14oslro._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_1_e8c3o._.js} +1 -1
  170. package/.next/server/middleware-build-manifest.js +3 -3
  171. package/.next/server/pages/404.html +1 -1
  172. package/.next/server/pages/500.html +1 -1
  173. package/.next/server/server-reference-manifest.js +1 -1
  174. package/.next/server/server-reference-manifest.json +57 -57
  175. package/.next/static/chunks/09pkygbht7ki8.js +18 -0
  176. package/.next/static/chunks/0bm-nhblcmmj4.js +17 -0
  177. package/.next/static/chunks/{22shnca_6zfzs.js → 0hhza87hkbqaz.js} +1 -1
  178. package/.next/static/chunks/{19ojn4lomsk0r.js → 0mscosx0eh3ee.js} +1 -1
  179. package/.next/static/chunks/{3vynnr71ghvom.js → 0p50d-au6-3hx.js} +2 -2
  180. package/.next/static/chunks/{42890jqk049q1.js → 0p9jcyqkpq71f.js} +1 -1
  181. package/.next/static/chunks/{35kwn237o4wdn.js → 0qgn3u86jkbm7.js} +1 -1
  182. package/.next/static/chunks/{2d4z3er53ng8r.js → 0samf80yu2b17.js} +1 -1
  183. package/.next/static/chunks/{1pjygp7hqib6j.js → 1czfknd4luo6t.js} +1 -1
  184. package/.next/static/chunks/{09120tms63y21.js → 1jz8n0mhtdzr8.js} +1 -1
  185. package/.next/static/chunks/206laxm6maiz2.js +1 -0
  186. package/.next/static/chunks/{24dst4mgurbsh.js → 2_c7mu6x1lu49.js} +1 -1
  187. package/.next/static/chunks/{2ky81z-jgvasc.js → 2h-tlhb2ffhkk.js} +1 -1
  188. package/.next/static/chunks/{0oz3rs47yy3p8.js → 2i5_w8olnrvo0.js} +2 -2
  189. package/.next/static/chunks/{1mwee7xvitlp9.js → 2m8iqwc6sykln.js} +1 -1
  190. package/.next/static/chunks/{0o980477kfqez.js → 2r6xnyg7ezyxr.js} +1 -1
  191. package/.next/static/chunks/33fyp-byw48ju.js +65 -0
  192. package/.next/static/chunks/{2ndw4w4u2x51r.js → 35yd_27d9ylra.js} +1 -1
  193. package/.next/static/chunks/{3exjsi8pfdvzj.js → 3ag1i3__yl94f.js} +1 -1
  194. package/.next/static/chunks/{3obf9n3cxj99k.js → 3jvldk2wvwbge.js} +1 -1
  195. package/.next/static/chunks/3m_ejzg-xn21r.css +1 -0
  196. package/.next/static/chunks/{0w20wnnfrscq7.js → 3s-jpd3ky49-n.js} +1 -1
  197. package/.next/static/chunks/40ij3wsn3vt7h.js +1 -0
  198. package/README.md +1 -1
  199. package/app/api/thursday/tool-call/route.ts +1 -15
  200. package/docs/images/demo-still.jpg +0 -0
  201. package/docs/live-calls.md +19 -11
  202. package/features/ai/components/model-picker.tsx +8 -2
  203. package/features/ai/model.schema.ts +26 -0
  204. package/features/ai/prompts/live.prompt.ts +51 -26
  205. package/features/ai/prompts/prompt-helper.ts +1 -9
  206. package/features/ai/prompts/thursday.prompt.ts +8 -2
  207. package/features/bot/bot.run.ts +9 -3
  208. package/features/bot/bot.seed.ts +35 -91
  209. package/features/bot/components/bot-room.tsx +3 -12
  210. package/features/bot/components/room-list.tsx +1 -1
  211. package/features/bot/components/room-pill.tsx +44 -55
  212. package/features/bot/seed-bots.ts +2 -2
  213. package/features/config/components/config-badge.tsx +6 -0
  214. package/features/config/components/config-setting.tsx +20 -1
  215. package/features/config/components/models-badge.tsx +5 -7
  216. package/features/config/components/voice-key.tsx +24 -1
  217. package/features/config/config.const.ts +7 -0
  218. package/features/intro/components/intro.tsx +4 -19
  219. package/features/reach/reach.ts +2 -2
  220. package/features/settings/components/setting-ui.tsx +14 -5
  221. package/features/settings/components/settings.tsx +3 -1
  222. package/features/settings/settings.alert.ts +12 -11
  223. package/features/signins/signins.query.ts +66 -1
  224. package/features/thursday/components/side-captions.tsx +31 -8
  225. package/features/thursday/components/tab-state.tsx +5 -2
  226. package/features/thursday/components/thursday.tsx +158 -68
  227. package/features/thursday/components/write-line.tsx +37 -17
  228. package/features/thursday/thursday.action.ts +4 -20
  229. package/features/thursday/thursday.schema.ts +0 -9
  230. package/features/thursday/thursday.store.ts +11 -15
  231. package/features/thursday/thursday.text.ts +10 -8
  232. package/features/thursday/tool-line.ts +17 -6
  233. package/features/thursday/use-call-ring.ts +1 -5
  234. package/features/thursday/use-text-call.ts +37 -21
  235. package/features/thursday/use-thursday.ts +14 -44
  236. package/features/workspace/workspace.ts +4 -2
  237. package/guide/bots.md +12 -2
  238. package/guide/calls.md +9 -6
  239. package/guide/setup.md +4 -1
  240. package/lib/live/live.session.ts +0 -39
  241. package/package.json +1 -1
  242. package/scripts/live-session.test.mts +19 -26
  243. package/seed-skills/analyst/README.md +3 -4
  244. package/seed-skills/analyst/data-report/SKILL.md +5 -4
  245. package/seed-skills/insta/instagram/SKILL.md +5 -2
  246. package/seed-skills/insta/instagram/references/post.md +2 -2
  247. package/seed-skills/insta/instagram/scripts/compose.mjs +5 -1
  248. package/seed-skills/insta/instagram/scripts/inbox.mjs +5 -1
  249. package/seed-skills/insta/instagram/scripts/lib.mjs +12 -70
  250. package/seed-skills/insta/instagram/scripts/media.mjs +1 -1
  251. package/seed-skills/insta/instagram/scripts/profile.mjs +6 -2
  252. package/seed-skills/insta/instagram/scripts/read.mjs +5 -1
  253. package/seed-skills/insta/instagram/scripts/search.mjs +6 -2
  254. package/seed-skills/insta/instagram/scripts/sheet.mjs +31 -88
  255. package/seed-skills/insta/instagram/templates/carousel.html +1 -1
  256. package/seed-skills/tutor/picture-book/SKILL.md +16 -0
  257. package/seed-skills/tutor/picture-book/book/book.css +181 -0
  258. package/seed-skills/tutor/picture-book/book/book.html +33 -0
  259. package/seed-skills/tutor/picture-book/book/book.js +60 -0
  260. package/seed-skills/tutor/picture-book/references/picture-book.md +104 -0
  261. package/seed-skills/tutor/picture-book/scripts/book-video.mjs +177 -0
  262. package/seed-skills/tutor/picture-book/scripts/book.mjs +98 -0
  263. package/skills/browser/SKILL.md +13 -4
  264. package/skills/browser/references/scripts.md +55 -0
  265. package/{seed-skills/insta/instagram → skills/browser}/scripts/render.mjs +1 -1
  266. package/skills/browser/scripts/session.mjs +105 -0
  267. package/skills/browser/scripts/sheet.mjs +97 -0
  268. package/{seed-skills/insta/instagram → skills/browser}/scripts/webimage.mjs +1 -1
  269. package/skills/interactive-page/SKILL.md +4 -3
  270. package/{seed-skills/analyst/data-report → skills/interactive-page}/scripts/chart.mjs +3 -3
  271. package/skills/interactive-page/scripts/page.mjs +1 -0
  272. package/.next/server/app/api/probe/route/app-paths-manifest.json +0 -3
  273. package/.next/server/app/api/probe/route/build-manifest.json +0 -12
  274. package/.next/server/app/api/probe/route/server-reference-manifest.json +0 -4
  275. package/.next/server/app/api/probe/route.js +0 -10
  276. package/.next/server/app/api/probe/route.js.map +0 -5
  277. package/.next/server/app/api/probe/route.js.nft.json +0 -1
  278. package/.next/server/app/api/probe/route_client-reference-manifest.js +0 -3
  279. package/.next/server/chunks/[root-of-the-server]__012s2o8._.js +0 -3
  280. package/.next/server/chunks/[root-of-the-server]__0z0hc1f._.js +0 -4
  281. package/.next/server/chunks/[root-of-the-server]__1v7-zke._.js +0 -5
  282. package/.next/server/chunks/_0whlpyi._.js +0 -4
  283. package/.next/server/chunks/_next-internal_server_app_api_probe_route_actions_0cqd30j.js +0 -3
  284. package/.next/server/chunks/ssr/[root-of-the-server]__0cxqzfc._.js +0 -103
  285. package/.next/server/chunks/ssr/[root-of-the-server]__1-d2tgh._.js +0 -3
  286. package/.next/server/chunks/ssr/_070nv1_._.js +0 -3
  287. package/.next/server/chunks/ssr/_0hlxij-._.js +0 -3
  288. package/.next/server/chunks/ssr/_0p9pmxe._.js +0 -3
  289. package/.next/server/chunks/ssr/_0s41irp._.js +0 -3
  290. package/.next/server/chunks/ssr/_157yfrk._.js +0 -3
  291. package/.next/server/chunks/ssr/_17v-33v._.js +0 -20
  292. package/.next/server/chunks/ssr/_1flsjeo._.js +0 -3
  293. package/.next/static/chunks/03kgfyrli_0gz.js +0 -1
  294. package/.next/static/chunks/1p764d75_0lhv.js +0 -18
  295. package/.next/static/chunks/25zq8gzhbdsr7.js +0 -1
  296. package/.next/static/chunks/2qod5tqlpz8b7.js +0 -17
  297. package/.next/static/chunks/30skcjexa57zn.js +0 -101
  298. package/.next/static/chunks/3qq4o8w2rv_e9.css +0 -1
  299. package/lib/probe.server.ts +0 -17
  300. package/lib/probe.ts +0 -55
  301. package/seed-skills/mail/mail/SKILL.md +0 -37
  302. package/seed-skills/mail/mail/recipes/gmail.mjs +0 -448
  303. package/seed-skills/mail/mail/references/recipes.md +0 -55
  304. package/seed-skills/mail/mail/scripts/mail.mjs +0 -202
  305. /package/.next/static/{Ar5V4kMdL4CE1trx8anrH → QSe0rTdXU5kh5BBzr2XBB}/_buildManifest.js +0 -0
  306. /package/.next/static/{Ar5V4kMdL4CE1trx8anrH → QSe0rTdXU5kh5BBzr2XBB}/_clientMiddlewareManifest.js +0 -0
  307. /package/.next/static/{Ar5V4kMdL4CE1trx8anrH → QSe0rTdXU5kh5BBzr2XBB}/_ssgManifest.js +0 -0
  308. /package/{seed-skills/insta/instagram → skills/browser}/scripts/image-size.mjs +0 -0
@@ -13,7 +13,11 @@ import {
13
13
  } from "ai";
14
14
  import { ZodError, z } from "zod";
15
15
  import { TEXT_CALL } from "@/config";
16
- import { LIVE_PROVIDER } from "@/features/ai/live.schema";
16
+ import {
17
+ LIVE_PROVIDER,
18
+ type LiveSettings,
19
+ LiveSettingsSchema,
20
+ } from "@/features/ai/live.schema";
17
21
  import { loadTools } from "@/features/ai/load-tools";
18
22
  import { getTextModel, modelErrorToString } from "@/features/ai/model";
19
23
  import {
@@ -38,8 +42,6 @@ import {
38
42
  TEXT_CALL_PROVIDERS,
39
43
  type TextCallHandshake,
40
44
  type TextCallProvider,
41
- type ThursdaySettings,
42
- ThursdaySettingsSchema,
43
45
  textCallRunsOn,
44
46
  } from "./thursday.schema";
45
47
  import { toolLine } from "./tool-line";
@@ -70,7 +72,7 @@ export async function readTextCallProvider(): Promise<TextCallProvider | null> {
70
72
  * in, else the OpenAI key, on the call's own backend model.
71
73
  */
72
74
  async function runsOnOf(
73
- settings: ThursdaySettings,
75
+ settings: LiveSettings,
74
76
  picked: TextModelRef | null | undefined,
75
77
  ): Promise<TextModelRef> {
76
78
  if (picked) return picked;
@@ -81,7 +83,7 @@ async function runsOnOf(
81
83
 
82
84
  /** The row a call in writing is kept under, and what stood open as it began. */
83
85
  export async function openTextCall(
84
- settings: ThursdaySettings,
86
+ settings: LiveSettings,
85
87
  picked?: TextModelRef | null,
86
88
  ): Promise<TextCallHandshake> {
87
89
  const ref = await runsOnOf(settings, picked);
@@ -98,7 +100,7 @@ export async function openTextCall(
98
100
 
99
101
  const BodySchema = z.object({
100
102
  callId: z.string().min(1),
101
- settings: ThursdaySettingsSchema,
103
+ settings: LiveSettingsSchema,
102
104
  /** What stood open as the call opened (ai/prompts/call-standing), as the page was handed it. */
103
105
  standing: z.string().nullish(),
104
106
  /** The model picked on the write line; absent, the rule decides (runsOnOf). */
@@ -149,7 +151,7 @@ export async function streamTextCall(
149
151
  */
150
152
  export async function answerInWriting(input: {
151
153
  callId: string;
152
- settings: ThursdaySettings;
154
+ settings: LiveSettings;
153
155
  standing: string | null;
154
156
  messages: ModelMessage[];
155
157
  said: string | null;
@@ -246,7 +248,7 @@ const standingHead = (standing: string | null | undefined): ModelMessage[] =>
246
248
  /** What a turn runs on, whoever holds the conversation: the model, her prompt, her tools. */
247
249
  async function loadRun(
248
250
  callId: string,
249
- settings: ThursdaySettings,
251
+ settings: LiveSettings,
250
252
  picked?: TextModelRef | null,
251
253
  ) {
252
254
  const ref = await runsOnOf(settings, picked);
@@ -172,13 +172,15 @@ const ON_A_THREAD = new Set<string>([
172
172
  * The bot a call's tool reaches, so its row wears that bot's face rather than a glyph —
173
173
  * who work went to is a face everywhere else in the app. The one work is handed to, else
174
174
  * the bot of the thread the tool names, found as the server finds a thread (thread.query
175
- * resolveThread): its id, else its label. An answer goes to the bot that asked; "all", and
176
- * a thread the page does not hold, reach nobody in particular.
175
+ * resolveThread): its id, else its label. An answer goes to the bot that asked; "all"
176
+ * reaches nobody in particular. A thread the page does not hold — stopped and read, or
177
+ * finished longer ago than the inbox keeps — is named by the tool's answer once it is back.
177
178
  */
178
179
  export function toolBot(
179
180
  name: string,
180
181
  args?: string,
181
182
  threads?: Thread[],
183
+ output?: string,
182
184
  ): string | null {
183
185
  const parsed = parseArgs(args);
184
186
  if (!parsed) return null;
@@ -186,19 +188,28 @@ export function toolBot(
186
188
  return typeof parsed.bot === "string" && parsed.bot.trim()
187
189
  ? parsed.bot.trim()
188
190
  : null;
189
- if (!ON_A_THREAD.has(name) || !threads) return null;
191
+ if (!ON_A_THREAD.has(name)) return null;
190
192
  const ref = typeof parsed.thread === "string" ? parsed.thread.trim() : "";
191
193
  if (!ref || ref.toLowerCase() === "all") return null;
192
194
  const lower = ref.toLowerCase();
193
195
  const thread =
194
- threads.find((one) => one.id === ref) ??
195
- threads.find((one) => one.label.toLowerCase() === lower);
196
- if (!thread) return null;
196
+ threads?.find((one) => one.id === ref) ??
197
+ threads?.find((one) => one.label.toLowerCase() === lower);
198
+ if (!thread) return answeredBy(output);
197
199
  return name === TOOL_NAMES.thread_answer
198
200
  ? (thread.room.questions[0]?.bot ?? thread.bot)
199
201
  : thread.bot;
200
202
  }
201
203
 
204
+ /** Whom a thread tool's answer says it reached (load-tools): `bot`, or the bot that took it as its answer. */
205
+ function answeredBy(output?: string): string | null {
206
+ const said = parseArgs(output);
207
+ if (!said) return null;
208
+ if (typeof said.bot === "string" && said.bot.trim()) return said.bot.trim();
209
+ const answered = said.answered as { bot?: unknown } | null | undefined;
210
+ return typeof answered?.bot === "string" ? answered.bot : null;
211
+ }
212
+
202
213
  /**
203
214
  * The label a thread was started under, or null for any other turn. It is
204
215
  * how a job is found again from the line that opened it — in her prompt and in
@@ -6,7 +6,6 @@ import type { Thread } from "@/features/bot/bot.schema";
6
6
  import { ringingThreads } from "@/features/bot/thread.store";
7
7
  import { toDate } from "@/lib/date-like";
8
8
  import { createRing } from "@/lib/live/ring";
9
- import { createProbe } from "@/lib/probe";
10
9
  import type { CallBack } from "./thursday.schema";
11
10
  import { useThursdayStore } from "./thursday.store";
12
11
 
@@ -51,7 +50,6 @@ export function useCallRing({
51
50
  /** A call in writing holds the screen (use-text-call): nothing rings meanwhile. */
52
51
  writing: boolean;
53
52
  }) {
54
- const probe = useRef(createProbe("page")).current;
55
53
  /**
56
54
  * The call-back rings only for threads that changed after this: the last call's
57
55
  * end, the last ring, or the page opening.
@@ -84,7 +82,6 @@ export function useCallRing({
84
82
  );
85
83
  if (!fresh.length) return;
86
84
 
87
- probe("ring", { threads: fresh.map((thread) => thread.id) });
88
85
  ringAfter.current = Date.now();
89
86
  // Work that is new to it rings again, even if it had rung out
90
87
  setRangOutAt(null);
@@ -92,7 +89,7 @@ export function useCallRing({
92
89
  ...ids,
93
90
  ...fresh.map((thread) => thread.id).filter((id) => !ids.includes(id)),
94
91
  ]);
95
- }, [threads, callBack, resting, writing, probe]);
92
+ }, [threads, callBack, resting, writing]);
96
93
 
97
94
  // Writing to her answers a ring as calling her does, and what came up while they wrote
98
95
  // was that call's to tell: nothing from before its end rings afterwards
@@ -110,7 +107,6 @@ export function useCallRing({
110
107
  * A thread added to a ring already going does not restart the clock.
111
108
  */
112
109
  const decline = useCallback(() => {
113
- probe("ring.declined");
114
110
  setRingingFor([]);
115
111
  setRangOutAt(null);
116
112
  }, []);
@@ -267,34 +267,50 @@ export function useTextCall(): TextCall {
267
267
  // pages a search read stay through her answer, until the next words are sent, as on a
268
268
  // spoken call
269
269
  const lastTool = [...parts].reverse().find(isToolUIPart);
270
- const used = useMemo((): ActivityLine | null => {
271
- if (!lastTool) return null;
272
- const name = getToolName(lastTool);
273
- // A provider's own search names what it looked for in its answer, not in what it was asked
274
- const asked = (lastTool.output as { action?: { query?: unknown } } | null)
275
- ?.action?.query;
276
- const args = JSON.stringify({
277
- ...(typeof asked === "string" ? { query: asked } : {}),
278
- ...(lastTool.input ?? {}),
279
- });
280
- const done =
281
- lastTool.state === "output-available" ||
282
- lastTool.state === "output-error";
283
- const sources =
270
+ // The part is copied with every piece of the stream, and its arguments arrive a few
271
+ // characters a piece: the line is a new object only when what it draws changes, or
272
+ // every piece redraws the call screen several times over
273
+ const id = lastTool?.toolCallId ?? null;
274
+ const name = lastTool ? getToolName(lastTool) : null;
275
+ const done =
276
+ lastTool?.state === "output-available" ||
277
+ lastTool?.state === "output-error";
278
+ const output = lastTool?.output;
279
+ const sources = useMemo(
280
+ () =>
284
281
  done && name === TOOL_NAMES.web_search
285
- ? searchSourcesOf(JSON.stringify(lastTool.output ?? null))
286
- : [];
287
- if (last?.type === "text" && !sources.length) return null;
288
- const bot = toolBot(name, args, inbox.current);
282
+ ? searchSourcesOf(JSON.stringify(output ?? null))
283
+ : [],
284
+ [done, name, output],
285
+ );
286
+ // A provider's own search names what it looked for in its answer, not in what it was asked
287
+ const asked = (output as { action?: { query?: unknown } } | null | undefined)
288
+ ?.action?.query;
289
+ const args = JSON.stringify({
290
+ ...(typeof asked === "string" ? { query: asked } : {}),
291
+ ...(lastTool?.input ?? {}),
292
+ });
293
+ const bot = name
294
+ ? toolBot(
295
+ name,
296
+ args,
297
+ inbox.current,
298
+ output === undefined ? undefined : JSON.stringify(output),
299
+ )
300
+ : null;
301
+ const drawn = name ? toolLine(name, args, bot) : null;
302
+ const answered = last?.type === "text";
303
+ const used = useMemo((): ActivityLine | null => {
304
+ if (!id || !name || (answered && !sources.length)) return null;
289
305
  return {
290
- id: lastTool.toolCallId,
306
+ id,
291
307
  name,
292
- line: toolLine(name, args, bot),
308
+ line: drawn,
293
309
  bot,
294
310
  done,
295
311
  ...(sources.length ? { sources } : {}),
296
312
  };
297
- }, [lastTool, last?.type]);
313
+ }, [id, name, drawn, bot, done, sources, answered]);
298
314
  const [lingered, setLingered] = useState<string | null>(null);
299
315
  const usedId = used?.done && !used.sources ? used.id : null;
300
316
  useEffect(() => {
@@ -4,13 +4,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
4
4
  import { useAppEvent } from "@/app/api/events/app-event.client";
5
5
  import { queryKey } from "@/app/api/query-key";
6
6
  import { toast } from "@/components/ui/toast";
7
- import {
8
- CALL_END,
9
- CALL_ENDED_MS,
10
- CALL_IDLE,
11
- CALL_NUDGE,
12
- CALL_RELAY,
13
- } from "@/config";
7
+ import { CALL_END, CALL_ENDED_MS, CALL_IDLE, CALL_RELAY } from "@/config";
14
8
  import { TOOL_NAMES } from "@/features/ai/tools/tool-name";
15
9
  import { acceptThreadRelaysAction } from "@/features/bot/bot.action";
16
10
  import type { Bot, Thread } from "@/features/bot/bot.schema";
@@ -32,7 +26,6 @@ import {
32
26
  createAudioTap,
33
27
  SPECTRUM_BANDS,
34
28
  } from "@/lib/live/live.tap";
35
- import { createProbe } from "@/lib/probe";
36
29
  import { type Result, unwrapResult } from "@/lib/protocol/result";
37
30
  import {
38
31
  revalidate,
@@ -216,8 +209,6 @@ export function useThursday(
216
209
  const onLine = useRef<string[]>([]);
217
210
  /** That update's relay rows, accepted once she has voiced it. */
218
211
  const onLineRows = useRef<number[]>([]);
219
- /** The experiment's timer (config CALL_NUDGE): a backend turn the page starts itself. */
220
- const nudge = useRef<ReturnType<typeof setTimeout> | null>(null);
221
212
  /** When her voice was last heard, for letting a goodbye finish (CALL_END). */
222
213
  const voiced = useRef(0);
223
214
  /** The hang-up waiting on her goodbye once the backend called end_call. */
@@ -267,12 +258,22 @@ export function useThursday(
267
258
  });
268
259
  }, []);
269
260
 
261
+ /** The tool's answer is back: a thread the page does not hold is named by it (toolBot). */
262
+ const nameTool = useCallback((call: LiveToolCall, output: string) => {
263
+ const bot = toolBot(call.name, call.arguments, latest.current, output);
264
+ if (!bot) return;
265
+ setTool((open) =>
266
+ open?.id === call.id && open.bot !== bot
267
+ ? { ...open, bot, line: toolLine(call.name, call.arguments, bot) }
268
+ : open,
269
+ );
270
+ }, []);
271
+
270
272
  /**
271
273
  * A relay takes the tool line and runs there like a tool does: while it goes in,
272
274
  * and while she voices it. Her voice finishing it is what stops the motion
273
275
  * (doneReading); after that it stays long enough to read.
274
276
  */
275
- const probe = useRef(createProbe("page")).current;
276
277
  const showRelay = useCallback((run: ActivityLine) => {
277
278
  if (linger.current) clearTimeout(linger.current);
278
279
  relayOpen.current = true;
@@ -295,7 +296,6 @@ export function useThursday(
295
296
  /** The relayed update was voiced, or waited on long enough: the next can go in. */
296
297
  const doneReading = useCallback(() => {
297
298
  if (reading.current.giveUp) clearTimeout(reading.current.giveUp);
298
- probe("read.done", { spoke: reading.current.spoke, keys: onLine.current });
299
299
  // Her voice started and stopped on it: carried, and only now are its rows accepted.
300
300
  // Live's acknowledgement says the text arrived, not that anyone heard it
301
301
  if (reading.current.spoke) {
@@ -419,13 +419,6 @@ export function useThursday(
419
419
  }
420
420
  // Finished work is good news before she says it (an item's key opens with what it is)
421
421
  if (first.key.startsWith("done:")) setFaceWord(finished());
422
- probe("relay.out", {
423
- kind: first.kind,
424
- keys: due.map((item) => item.key),
425
- waiting: open.length - due.length,
426
- quietMs: Date.now() - heard.current,
427
- rung,
428
- });
429
422
  readAloud();
430
423
  onLine.current = due.map((item) => item.key);
431
424
  onLineRows.current = due.flatMap((item) => item.relayIds);
@@ -439,7 +432,6 @@ export function useThursday(
439
432
  : `[${lines.length} updates.]\n\n${lines.join("\n\n")}`,
440
433
  )
441
434
  .then((delivered) => {
442
- probe("relay.acked", { delivered });
443
435
  // Refused: nothing to wait for her voice on
444
436
  if (!delivered) {
445
437
  onLineRows.current = [];
@@ -572,12 +564,6 @@ export function useThursday(
572
564
  async (why: CallEnd | null = null) => {
573
565
  if (ending.current) return;
574
566
  ending.current = true;
575
- probe("hangup", {
576
- why,
577
- call: callId.current,
578
- unvoiced: [...unvoiced.current],
579
- reading: reading.current.on,
580
- });
581
567
  if (leaving.current) clearInterval(leaving.current);
582
568
  leaving.current = null;
583
569
  attempt.current += 1;
@@ -665,7 +651,6 @@ export function useThursday(
665
651
  const leave = useCallback(() => {
666
652
  if (leaving.current) return;
667
653
  const asked = Date.now();
668
- probe("end_call", { sinceVoiceMs: asked - voiced.current });
669
654
  leaving.current = setInterval(() => {
670
655
  const now = Date.now();
671
656
  // A goodbye heard just before the backend ended the call counts as said
@@ -703,7 +688,6 @@ export function useThursday(
703
688
  const call = useCallback(async () => {
704
689
  // Every way in answers a ringing call-back: the face, the wake word, the hotkey
705
690
  const calledBack = ring.isRinging;
706
- probe("call", { calledBack, ringingFor: ring.ringingFor });
707
691
  // Guard with a ref, not `status`: three entry points (face, wake word,
708
692
  // hotkey) can fire in one frame and both see a stale "idle", opening two sessions
709
693
  if (opening.current || ending.current) return;
@@ -831,8 +815,6 @@ export function useThursday(
831
815
  if (word) setFaceWord({ text: word, at: Date.now() });
832
816
  return reply;
833
817
  }
834
- const began = Date.now();
835
- probe("tool.run", { name: call.name, id: call.id });
836
818
  showTool(call);
837
819
  // Exa's search, while its key is set (load-tools): its pages come back with
838
820
  // the answer and stay on the line the way the hosted search's do
@@ -846,6 +828,7 @@ export function useThursday(
846
828
  settings.webSearch,
847
829
  stop.signal,
848
830
  );
831
+ nameTool(call, output);
849
832
  if (searching)
850
833
  kept = keepSearch({
851
834
  id: call.id,
@@ -857,7 +840,6 @@ export function useThursday(
857
840
  });
858
841
  return output;
859
842
  } finally {
860
- probe("tool.done", { id: call.id, ms: Date.now() - began });
861
843
  if (!kept) hideTool(call.id);
862
844
  }
863
845
  },
@@ -946,19 +928,6 @@ export function useThursday(
946
928
  .slice(-KEEP_MESSAGES),
947
929
  );
948
930
  }
949
- // The experiment (config CALL_NUDGE): their words have settled, and if the voice
950
- // hands nothing over in a moment the page starts the backend turn itself
951
- if (CALL_NUDGE.on && turn.done && turn.role === "user" && words) {
952
- if (nudge.current) clearTimeout(nudge.current);
953
- const spoke = stirred.current;
954
- nudge.current = setTimeout(() => {
955
- nudge.current = null;
956
- // they went on talking, or the backend has the turn already
957
- if (stirred.current !== spoke || thinking.current !== null)
958
- return;
959
- probe("nudge", { sent: session.current?.nudge() ?? false });
960
- }, CALL_NUDGE.waitMs);
961
- }
962
931
  if (turn.done) {
963
932
  persist(saving, () =>
964
933
  saveTurnsAction(line.callId, [
@@ -1076,6 +1045,7 @@ export function useThursday(
1076
1045
  status,
1077
1046
  hangUp,
1078
1047
  showTool,
1048
+ nameTool,
1079
1049
  hideTool,
1080
1050
  showFace,
1081
1051
  wearFace,
@@ -207,11 +207,13 @@ export const jobShellEnv = (
207
207
  });
208
208
 
209
209
  /**
210
- * A bot's artifacts folder in its shell, for a script that delivers a file
211
- * rather than a model typing the path (skills/interactive-page scripts/page.mjs).
210
+ * What a bot's scripts find in its shell rather than a model typing the path.
212
211
  */
213
212
  export const botShellEnv = (bot: string): Record<string, string> => ({
213
+ // Where a script delivers a file (skills/interactive-page scripts/page.mjs)
214
214
  THURSDAY_ARTIFACTS: botArtifacts(bot),
215
+ // The shipped skills: a kit script in a bot's folder imports the shared ones from here
216
+ THURSDAY_SKILLS: join(APP_DIR, PATHS.skills.default),
215
217
  });
216
218
 
217
219
  /** A participant's browser belongs to the job and its canonical bot name, across every caller. */
package/guide/bots.md CHANGED
@@ -10,8 +10,11 @@ switched on; any of them can be switched off there, and added later from the sam
10
10
 
11
11
  Every bot has the same kit: a shell on this computer, a real browser of its own or the one
12
12
  the user is already signed into, their files, the web, whatever skills and connected services
13
- are installed, and the other bots. A ready-made bot may also arrive with written methods of
14
- its own for its subject, which no other bot sees.
13
+ are installed, and the other bots. A ready-made bot may also arrive with written methods and
14
+ scripts of its own for its subject, which no other bot sees: Insta studies accounts that do well
15
+ in a niche, builds posts the same way and reads messages without opening them (an opened thread
16
+ shows as seen); Analyst fetches published series and draws them; Marketer keeps one brief per
17
+ product in its memory and works from it.
15
18
 
16
19
  ## A job is a thread
17
20
 
@@ -75,6 +78,13 @@ of itself. Asking her on a call to show a result opens that file on the screen.
75
78
  stay under **Settings › Files**, one shelf per bot. Its second tab, **All files**, is the
76
79
  whole folder, for looking around rather than for the results.
77
80
 
81
+ Asked to explain something simply, **Tutor** makes a picture book: a picture and a line
82
+ or two a page, turned by swiping, the arrow keys, or tapping the right or left of the page. The
83
+ same book can be a PDF, or a video that reads itself aloud (landscape, or portrait when asked);
84
+ when the request does not say which, it asks first. It remembers what the user already
85
+ knows and how they liked being taught. The video needs a speech model in **Settings › Models**, and a drawn
86
+ picture an image model; without one it asks for it and stops.
87
+
78
88
  A bot keeps its own memory — what a job taught it, how the user asked it to work — in its own
79
89
  folder, listed on its page in Settings › Bots.
80
90
 
package/guide/calls.md CHANGED
@@ -11,7 +11,8 @@ Three ways in, and any of them also answers a call she placed:
11
11
 
12
12
  She speaks first. From the second call on she is told, as the call opens, when the last one
13
13
  was and how it ended, so she can greet as someone who remembers it; a call she places herself
14
- opens on why she called instead.
14
+ opens on why she called instead. She speaks the language the user speaks and switches when they
15
+ do; the browser's language plays no part, so the very first greeting is in whatever she picks.
15
16
 
16
17
  The wake phrase and the shortcut are in **Settings › Thursday › Starting a call**, where
17
18
  either can be switched on or off, or rewritten. The first-run setup offers the wake phrase
@@ -42,14 +43,16 @@ in **Settings › Thursday**. With nothing to run on it says so and sends nothin
42
43
  **Settings › API keys**. **Esc** ends it — saying goodbye does not, since there is no
43
44
  line for her to drop — and so does starting a spoken call: tapping her face in the middle of
44
45
  one ends the written call and places a spoken one. When a turn breaks — a plan's limit, a
45
- refused key — her face says ERROR and the provider's own words show in red under the line;
46
- writing again tries again, and Esc still ends it. When an OpenAI key is set and was not what
47
- broke, a blue button sends the same words again on that key; it never switches by itself,
48
- since what a turn costs changes with what it runs on. It is kept with
46
+ refused key — her face says ERROR and the provider's own words show in red under the line,
47
+ over a blue **Send it again** and the way to **API keys**; writing again tries again too, and
48
+ Esc still ends it. When an OpenAI key is set and was not what broke, the button sends the same
49
+ words again on that key instead; it never switches by itself, since what a turn costs changes
50
+ with what it runs on. It is kept with
49
51
  the other calls, marked *in writing*, and she reads it back on the next call like any other.
50
52
  During a spoken call the line writes to bots only — but a file put down then (dropped on the
51
53
  window, pasted, or picked with the paperclip) is one she is told about as it lands: its chip
52
- says *she knows it is here*, and what to do with it can simply be said.
54
+ says *she knows it is here*, and what to do with it can simply be said. A line left open with
55
+ nothing in it closes when a spoken call picks up; one holding words or files stays.
53
56
 
54
57
  ## What she does on the line, and what goes to a bot
55
58
 
package/guide/setup.md CHANGED
@@ -17,7 +17,10 @@ said about it (a mistyped key, one that was deleted). Bots can run on that same
17
17
  key. The screen puts the two easy ways next: a GPT Subscription sign-in (bots run on the
18
18
  ChatGPT plan, no key), and the Vercel AI Gateway, one key for every model and the one the app
19
19
  recommends. Under them every other provider is a mark to tap and paste a key into: Anthropic,
20
- Google, xAI, Mistral, DeepSeek, Groq, Cerebras, Together, Fireworks, DeepInfra, Cohere.
20
+ Google, xAI, Mistral, DeepSeek, Groq, Cerebras, Together, Fireworks, DeepInfra, Cohere. The box
21
+ a key goes in shows how that provider's keys begin (`sk-ant-…` for Anthropic) where it is known,
22
+ with a link to the page that makes one; **Remove**, on a key already set, is the red word at the
23
+ far left.
21
24
 
22
25
  A search key (Exa) is optional. With it, calls and bots both search through Exa, which is
23
26
  cheaper per search and comes with free credits every month. Without it, a call uses OpenAI's
@@ -1,6 +1,5 @@
1
1
  import { LIVE_CALL } from "@/config";
2
2
  import { logger } from "@/lib/logger";
3
- import { briefLiveEvent, createProbe } from "@/lib/probe";
4
3
  import { errorToString } from "@/lib/utils";
5
4
  import {
6
5
  LIVE_HOSTED_TOOLS,
@@ -9,8 +8,6 @@ import {
9
8
  } from "./live.schema";
10
9
  import { createWebRtcTransport } from "./live.transport";
11
10
 
12
- const probe = createProbe("live");
13
-
14
11
  /** A revisable display group, independent of audio playback and backend responses. */
15
12
  export type LiveTurn = {
16
13
  id: string;
@@ -313,12 +310,6 @@ export const createLiveSession = ({ initialize, audio, on }: LiveOptions) => {
313
310
  return;
314
311
  const [next] = appends;
315
312
  pendingAppend = crypto.randomUUID();
316
- probe("out", {
317
- type: `session.${next.kind}.append`,
318
- client: pendingAppend,
319
- head: next.chunks[0].slice(0, 80),
320
- chunksLeft: next.chunks.length,
321
- });
322
313
  transport.send({
323
314
  type: `session.${next.kind}.append`,
324
315
  event_id: pendingAppend,
@@ -405,7 +396,6 @@ export const createLiveSession = ({ initialize, audio, on }: LiveOptions) => {
405
396
  return;
406
397
  response.continued = true;
407
398
  await Promise.all(response.calls.values());
408
- probe("out", { type: "response.create", closing, closed });
409
399
  if (!closing && !closed) {
410
400
  continuedAt = performance.now();
411
401
  transport.send({
@@ -422,8 +412,6 @@ export const createLiveSession = ({ initialize, audio, on }: LiveOptions) => {
422
412
  events.add(event.event_id);
423
413
  }
424
414
  timeline = Math.max(timeline, event.offset_ms ?? 0, event.end_ms ?? 0);
425
- if (!event.type.endsWith(".delta") && !event.event?.type.endsWith(".delta"))
426
- probe("in", briefLiveEvent(event));
427
415
  switch (event.type) {
428
416
  case "session.started":
429
417
  started = true;
@@ -576,12 +564,6 @@ export const createLiveSession = ({ initialize, audio, on }: LiveOptions) => {
576
564
  .catch((cause) => `Error: ${errorToString(cause)}`)
577
565
  .then((output) => {
578
566
  tools.delete(item.call_id);
579
- probe("out", {
580
- type: "function_call_output",
581
- name: item.name,
582
- call: item.call_id,
583
- size: output.length,
584
- });
585
567
  if (!closed && !closing)
586
568
  transport.send({
587
569
  type: "response.item.create",
@@ -690,7 +672,6 @@ export const createLiveSession = ({ initialize, audio, on }: LiveOptions) => {
690
672
  cleanup();
691
673
  }, LIVE_CALL.closeMs);
692
674
  audio.element.muted = true;
693
- probe("out", { type: "session.close" });
694
675
  transport.send({ type: "session.close", event_id: crypto.randomUUID() });
695
676
  return closePromise;
696
677
  },
@@ -708,25 +689,5 @@ export const createLiveSession = ({ initialize, audio, on }: LiveOptions) => {
708
689
  appendNext();
709
690
  });
710
691
  },
711
- /**
712
- * Starts a backend turn the voice did not hand over: the same `response.create` that
713
- * continues delegated work, sent with nothing pending. Does nothing while the backend
714
- * already holds the turn, or once the call is closing. False when it was not sent.
715
- */
716
- nudge(): boolean {
717
- const busy =
718
- [...responses.values()].some((response) => !response.terminal) ||
719
- tools.size > 0 ||
720
- performance.now() - continuedAt < CONTINUE_GAP_MS;
721
- if (closed || closing || busy) return false;
722
- probe("out", { type: "response.create", nudge: true });
723
- continuedAt = performance.now();
724
- transport.send({
725
- type: "response.create",
726
- event_id: crypto.randomUUID(),
727
- });
728
- activity();
729
- return true;
730
- },
731
692
  };
732
693
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thursday-agent",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "A voice agent that runs on your own machine — talk to her, she hands the slow work to bots with a shell, a browser and skills.",
5
5
  "license": "MIT",
6
6
  "keywords": [