@withbuddi/buddi 0.1.0-pre.18 → 0.1.0-pre.20

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 (635) hide show
  1. package/README.md +10 -4
  2. package/extension/background.js +38 -7
  3. package/node_modules/@buddi/cli/dist/.tsbuildinfo +1 -1
  4. package/node_modules/@buddi/cli/dist/args.d.ts +7 -0
  5. package/node_modules/@buddi/cli/dist/args.d.ts.map +1 -1
  6. package/node_modules/@buddi/cli/dist/args.js +14 -0
  7. package/node_modules/@buddi/cli/dist/args.js.map +1 -1
  8. package/node_modules/@buddi/cli/dist/commands.d.ts.map +1 -1
  9. package/node_modules/@buddi/cli/dist/commands.js +37 -2
  10. package/node_modules/@buddi/cli/dist/commands.js.map +1 -1
  11. package/node_modules/@buddi/cli/dist/dashboard-app.d.ts +6 -0
  12. package/node_modules/@buddi/cli/dist/dashboard-app.d.ts.map +1 -1
  13. package/node_modules/@buddi/cli/dist/dashboard-app.js +16 -1
  14. package/node_modules/@buddi/cli/dist/dashboard-app.js.map +1 -1
  15. package/node_modules/@buddi/cli/dist/doctor-probes.d.ts.map +1 -1
  16. package/node_modules/@buddi/cli/dist/doctor-probes.js +5 -2
  17. package/node_modules/@buddi/cli/dist/doctor-probes.js.map +1 -1
  18. package/node_modules/@buddi/cli/dist/doctor.d.ts +16 -0
  19. package/node_modules/@buddi/cli/dist/doctor.d.ts.map +1 -1
  20. package/node_modules/@buddi/cli/dist/doctor.js +18 -0
  21. package/node_modules/@buddi/cli/dist/doctor.js.map +1 -1
  22. package/node_modules/@buddi/cli/dist/index.d.ts +1 -0
  23. package/node_modules/@buddi/cli/dist/index.d.ts.map +1 -1
  24. package/node_modules/@buddi/cli/dist/index.js +1 -0
  25. package/node_modules/@buddi/cli/dist/index.js.map +1 -1
  26. package/node_modules/@buddi/cli/dist/main.d.ts.map +1 -1
  27. package/node_modules/@buddi/cli/dist/main.js +22 -2
  28. package/node_modules/@buddi/cli/dist/main.js.map +1 -1
  29. package/node_modules/@buddi/cli/dist/status.d.ts +10 -0
  30. package/node_modules/@buddi/cli/dist/status.d.ts.map +1 -1
  31. package/node_modules/@buddi/cli/dist/status.js +4 -0
  32. package/node_modules/@buddi/cli/dist/status.js.map +1 -1
  33. package/node_modules/@buddi/cli/dist/uninstall-cmd.d.ts +42 -0
  34. package/node_modules/@buddi/cli/dist/uninstall-cmd.d.ts.map +1 -0
  35. package/node_modules/@buddi/cli/dist/uninstall-cmd.js +208 -0
  36. package/node_modules/@buddi/cli/dist/uninstall-cmd.js.map +1 -0
  37. package/node_modules/@buddi/core/dist/.tsbuildinfo +1 -1
  38. package/node_modules/@buddi/core/dist/agents/catalog.d.ts.map +1 -1
  39. package/node_modules/@buddi/core/dist/agents/catalog.js +7 -1
  40. package/node_modules/@buddi/core/dist/agents/catalog.js.map +1 -1
  41. package/node_modules/@buddi/core/dist/agents/models.d.ts.map +1 -1
  42. package/node_modules/@buddi/core/dist/agents/models.js +4 -3
  43. package/node_modules/@buddi/core/dist/agents/models.js.map +1 -1
  44. package/node_modules/@buddi/core/dist/agents/skills.d.ts +2 -2
  45. package/node_modules/@buddi/core/dist/goals/frequency.d.ts +51 -0
  46. package/node_modules/@buddi/core/dist/goals/frequency.d.ts.map +1 -0
  47. package/node_modules/@buddi/core/dist/goals/frequency.js +113 -0
  48. package/node_modules/@buddi/core/dist/goals/frequency.js.map +1 -0
  49. package/node_modules/@buddi/core/dist/goals/index.d.ts +2 -0
  50. package/node_modules/@buddi/core/dist/goals/index.d.ts.map +1 -1
  51. package/node_modules/@buddi/core/dist/goals/index.js +2 -0
  52. package/node_modules/@buddi/core/dist/goals/index.js.map +1 -1
  53. package/node_modules/@buddi/core/dist/goals/math.d.ts +8 -2
  54. package/node_modules/@buddi/core/dist/goals/math.d.ts.map +1 -1
  55. package/node_modules/@buddi/core/dist/goals/math.js +17 -4
  56. package/node_modules/@buddi/core/dist/goals/math.js.map +1 -1
  57. package/node_modules/@buddi/core/dist/goals/owner.d.ts +129 -0
  58. package/node_modules/@buddi/core/dist/goals/owner.d.ts.map +1 -0
  59. package/node_modules/@buddi/core/dist/goals/owner.js +239 -0
  60. package/node_modules/@buddi/core/dist/goals/owner.js.map +1 -0
  61. package/node_modules/@buddi/core/dist/goals/store.d.ts.map +1 -1
  62. package/node_modules/@buddi/core/dist/goals/store.js +11 -4
  63. package/node_modules/@buddi/core/dist/goals/store.js.map +1 -1
  64. package/node_modules/@buddi/core/dist/goals/types.d.ts +17 -3
  65. package/node_modules/@buddi/core/dist/goals/types.d.ts.map +1 -1
  66. package/node_modules/@buddi/core/dist/goals/types.js +8 -2
  67. package/node_modules/@buddi/core/dist/goals/types.js.map +1 -1
  68. package/node_modules/@buddi/core/dist/learning/announce.d.ts +29 -0
  69. package/node_modules/@buddi/core/dist/learning/announce.d.ts.map +1 -0
  70. package/node_modules/@buddi/core/dist/learning/announce.js +49 -0
  71. package/node_modules/@buddi/core/dist/learning/announce.js.map +1 -0
  72. package/node_modules/@buddi/core/dist/learning/index.d.ts +1 -0
  73. package/node_modules/@buddi/core/dist/learning/index.d.ts.map +1 -1
  74. package/node_modules/@buddi/core/dist/learning/index.js +1 -0
  75. package/node_modules/@buddi/core/dist/learning/index.js.map +1 -1
  76. package/node_modules/@buddi/core/dist/learning/store.d.ts.map +1 -1
  77. package/node_modules/@buddi/core/dist/learning/store.js +15 -4
  78. package/node_modules/@buddi/core/dist/learning/store.js.map +1 -1
  79. package/node_modules/@buddi/core/dist/notifications/notify.d.ts.map +1 -1
  80. package/node_modules/@buddi/core/dist/notifications/notify.js +1 -0
  81. package/node_modules/@buddi/core/dist/notifications/notify.js.map +1 -1
  82. package/node_modules/@buddi/core/dist/notifications/store.d.ts +2 -0
  83. package/node_modules/@buddi/core/dist/notifications/store.d.ts.map +1 -1
  84. package/node_modules/@buddi/core/dist/notifications/store.js +9 -0
  85. package/node_modules/@buddi/core/dist/notifications/store.js.map +1 -1
  86. package/node_modules/@buddi/core/dist/notifications/types.d.ts +6 -0
  87. package/node_modules/@buddi/core/dist/notifications/types.d.ts.map +1 -1
  88. package/node_modules/@buddi/core/dist/pages.d.ts +2 -2
  89. package/node_modules/@buddi/core/dist/provider-accounts.d.ts +7 -1
  90. package/node_modules/@buddi/core/dist/provider-accounts.d.ts.map +1 -1
  91. package/node_modules/@buddi/core/dist/provider-accounts.js +14 -1
  92. package/node_modules/@buddi/core/dist/provider-accounts.js.map +1 -1
  93. package/node_modules/@buddi/core/dist/provider.d.ts +6 -0
  94. package/node_modules/@buddi/core/dist/provider.d.ts.map +1 -1
  95. package/node_modules/@buddi/core/dist/provider.js.map +1 -1
  96. package/node_modules/@buddi/core/dist/sentinels/run.d.ts.map +1 -1
  97. package/node_modules/@buddi/core/dist/sentinels/run.js +4 -1
  98. package/node_modules/@buddi/core/dist/sentinels/run.js.map +1 -1
  99. package/node_modules/@buddi/core/dist/sentinels/types.d.ts +20 -0
  100. package/node_modules/@buddi/core/dist/sentinels/types.d.ts.map +1 -1
  101. package/node_modules/@buddi/core/dist/sentinels/types.js.map +1 -1
  102. package/node_modules/@buddi/core/dist/surfaces.d.ts +2 -0
  103. package/node_modules/@buddi/core/dist/surfaces.d.ts.map +1 -1
  104. package/node_modules/@buddi/core/dist/surfaces.js +7 -0
  105. package/node_modules/@buddi/core/dist/surfaces.js.map +1 -1
  106. package/node_modules/@buddi/core/dist/uninstall.d.ts +79 -0
  107. package/node_modules/@buddi/core/dist/uninstall.d.ts.map +1 -0
  108. package/node_modules/@buddi/core/dist/uninstall.js +109 -0
  109. package/node_modules/@buddi/core/dist/uninstall.js.map +1 -0
  110. package/node_modules/@buddi/core/dist/views.d.ts +66 -66
  111. package/node_modules/@buddi/core/migrations/044_owner_metrics.sql +45 -0
  112. package/node_modules/@buddi/core/migrations/045_frequency_goals.sql +19 -0
  113. package/node_modules/@buddi/core/migrations/046_ollama_device_key.sql +7 -0
  114. package/node_modules/@buddi/core/migrations/047_reported_context_windows.sql +5 -0
  115. package/node_modules/@buddi/core/package.json +4 -0
  116. package/node_modules/@buddi/gateway/dist/.tsbuildinfo +1 -1
  117. package/node_modules/@buddi/gateway/dist/chat/usage.d.ts.map +1 -1
  118. package/node_modules/@buddi/gateway/dist/chat/usage.js +1 -0
  119. package/node_modules/@buddi/gateway/dist/chat/usage.js.map +1 -1
  120. package/node_modules/@buddi/gateway/dist/codex-accounts.d.ts +24 -11
  121. package/node_modules/@buddi/gateway/dist/codex-accounts.d.ts.map +1 -1
  122. package/node_modules/@buddi/gateway/dist/codex-accounts.js +151 -215
  123. package/node_modules/@buddi/gateway/dist/codex-accounts.js.map +1 -1
  124. package/node_modules/@buddi/gateway/dist/index.d.ts +2 -0
  125. package/node_modules/@buddi/gateway/dist/index.d.ts.map +1 -1
  126. package/node_modules/@buddi/gateway/dist/index.js +2 -0
  127. package/node_modules/@buddi/gateway/dist/index.js.map +1 -1
  128. package/node_modules/@buddi/gateway/dist/missions/agent-run.d.ts.map +1 -1
  129. package/node_modules/@buddi/gateway/dist/missions/agent-run.js +1 -0
  130. package/node_modules/@buddi/gateway/dist/missions/agent-run.js.map +1 -1
  131. package/node_modules/@buddi/gateway/dist/missions/execute.d.ts +7 -0
  132. package/node_modules/@buddi/gateway/dist/missions/execute.d.ts.map +1 -1
  133. package/node_modules/@buddi/gateway/dist/missions/execute.js +3 -1
  134. package/node_modules/@buddi/gateway/dist/missions/execute.js.map +1 -1
  135. package/node_modules/@buddi/gateway/dist/missions/goals-format.d.ts +32 -5
  136. package/node_modules/@buddi/gateway/dist/missions/goals-format.d.ts.map +1 -1
  137. package/node_modules/@buddi/gateway/dist/missions/goals-format.js +59 -2
  138. package/node_modules/@buddi/gateway/dist/missions/goals-format.js.map +1 -1
  139. package/node_modules/@buddi/gateway/dist/missions/goals-page.d.ts +60 -5
  140. package/node_modules/@buddi/gateway/dist/missions/goals-page.d.ts.map +1 -1
  141. package/node_modules/@buddi/gateway/dist/missions/goals-page.js +355 -8
  142. package/node_modules/@buddi/gateway/dist/missions/goals-page.js.map +1 -1
  143. package/node_modules/@buddi/gateway/dist/missions/goals.d.ts +64 -5
  144. package/node_modules/@buddi/gateway/dist/missions/goals.d.ts.map +1 -1
  145. package/node_modules/@buddi/gateway/dist/missions/goals.js +834 -116
  146. package/node_modules/@buddi/gateway/dist/missions/goals.js.map +1 -1
  147. package/node_modules/@buddi/gateway/dist/missions/sentinel-wake.d.ts +5 -0
  148. package/node_modules/@buddi/gateway/dist/missions/sentinel-wake.d.ts.map +1 -1
  149. package/node_modules/@buddi/gateway/dist/missions/sentinel-wake.js +12 -0
  150. package/node_modules/@buddi/gateway/dist/missions/sentinel-wake.js.map +1 -1
  151. package/node_modules/@buddi/gateway/dist/ollama-accounts.d.ts +87 -0
  152. package/node_modules/@buddi/gateway/dist/ollama-accounts.d.ts.map +1 -0
  153. package/node_modules/@buddi/gateway/dist/ollama-accounts.js +179 -0
  154. package/node_modules/@buddi/gateway/dist/ollama-accounts.js.map +1 -0
  155. package/node_modules/@buddi/gateway/dist/owner-notify.d.ts +2 -1
  156. package/node_modules/@buddi/gateway/dist/owner-notify.d.ts.map +1 -1
  157. package/node_modules/@buddi/gateway/dist/owner-notify.js +6 -2
  158. package/node_modules/@buddi/gateway/dist/owner-notify.js.map +1 -1
  159. package/node_modules/@buddi/gateway/dist/provider-accounts.d.ts +47 -1
  160. package/node_modules/@buddi/gateway/dist/provider-accounts.d.ts.map +1 -1
  161. package/node_modules/@buddi/gateway/dist/provider-accounts.js +204 -38
  162. package/node_modules/@buddi/gateway/dist/provider-accounts.js.map +1 -1
  163. package/node_modules/@buddi/gateway/dist/serve.d.ts.map +1 -1
  164. package/node_modules/@buddi/gateway/dist/serve.js +1 -0
  165. package/node_modules/@buddi/gateway/dist/serve.js.map +1 -1
  166. package/node_modules/@buddi/gateway/dist/subscription-signins.d.ts +28 -0
  167. package/node_modules/@buddi/gateway/dist/subscription-signins.d.ts.map +1 -0
  168. package/node_modules/@buddi/gateway/dist/subscription-signins.js +30 -0
  169. package/node_modules/@buddi/gateway/dist/subscription-signins.js.map +1 -0
  170. package/node_modules/@buddi/gateway/dist/surfaces/context-budget.d.ts.map +1 -1
  171. package/node_modules/@buddi/gateway/dist/surfaces/context-budget.js +10 -3
  172. package/node_modules/@buddi/gateway/dist/surfaces/context-budget.js.map +1 -1
  173. package/node_modules/@buddi/gateway/dist/telegram/api.d.ts +43 -9
  174. package/node_modules/@buddi/gateway/dist/telegram/api.d.ts.map +1 -1
  175. package/node_modules/@buddi/gateway/dist/telegram/api.js +80 -21
  176. package/node_modules/@buddi/gateway/dist/telegram/api.js.map +1 -1
  177. package/node_modules/@buddi/gateway/dist/telegram/attachments.d.ts +6 -0
  178. package/node_modules/@buddi/gateway/dist/telegram/attachments.d.ts.map +1 -1
  179. package/node_modules/@buddi/gateway/dist/telegram/attachments.js +1 -0
  180. package/node_modules/@buddi/gateway/dist/telegram/attachments.js.map +1 -1
  181. package/node_modules/@buddi/gateway/dist/telegram/browser-view.d.ts.map +1 -1
  182. package/node_modules/@buddi/gateway/dist/telegram/browser-view.js +2 -1
  183. package/node_modules/@buddi/gateway/dist/telegram/browser-view.js.map +1 -1
  184. package/node_modules/@buddi/gateway/dist/telegram/channel.d.ts +7 -1
  185. package/node_modules/@buddi/gateway/dist/telegram/channel.d.ts.map +1 -1
  186. package/node_modules/@buddi/gateway/dist/telegram/channel.js +24 -2
  187. package/node_modules/@buddi/gateway/dist/telegram/channel.js.map +1 -1
  188. package/node_modules/@buddi/gateway/dist/telegram/commands.d.ts +28 -0
  189. package/node_modules/@buddi/gateway/dist/telegram/commands.d.ts.map +1 -0
  190. package/node_modules/@buddi/gateway/dist/telegram/commands.js +74 -0
  191. package/node_modules/@buddi/gateway/dist/telegram/commands.js.map +1 -0
  192. package/node_modules/@buddi/gateway/dist/telegram/main.d.ts +3 -0
  193. package/node_modules/@buddi/gateway/dist/telegram/main.d.ts.map +1 -1
  194. package/node_modules/@buddi/gateway/dist/telegram/main.js +71 -2
  195. package/node_modules/@buddi/gateway/dist/telegram/main.js.map +1 -1
  196. package/node_modules/@buddi/gateway/dist/telegram/outbound.d.ts +191 -0
  197. package/node_modules/@buddi/gateway/dist/telegram/outbound.d.ts.map +1 -0
  198. package/node_modules/@buddi/gateway/dist/telegram/outbound.js +671 -0
  199. package/node_modules/@buddi/gateway/dist/telegram/outbound.js.map +1 -0
  200. package/node_modules/@buddi/gateway/dist/telegram/proposals.d.ts +65 -0
  201. package/node_modules/@buddi/gateway/dist/telegram/proposals.d.ts.map +1 -0
  202. package/node_modules/@buddi/gateway/dist/telegram/proposals.js +183 -0
  203. package/node_modules/@buddi/gateway/dist/telegram/proposals.js.map +1 -0
  204. package/node_modules/@buddi/gateway/dist/telegram/surface.d.ts +47 -47
  205. package/node_modules/@buddi/gateway/dist/telegram/surface.d.ts.map +1 -1
  206. package/node_modules/@buddi/gateway/dist/telegram/surface.js +99 -290
  207. package/node_modules/@buddi/gateway/dist/telegram/surface.js.map +1 -1
  208. package/node_modules/@buddi/gateway/dist/web/onboarding.d.ts.map +1 -1
  209. package/node_modules/@buddi/gateway/dist/web/onboarding.js +4 -0
  210. package/node_modules/@buddi/gateway/dist/web/onboarding.js.map +1 -1
  211. package/node_modules/@buddi/gateway/dist/web/secrets.d.ts.map +1 -1
  212. package/node_modules/@buddi/gateway/dist/web/secrets.js +3 -2
  213. package/node_modules/@buddi/gateway/dist/web/secrets.js.map +1 -1
  214. package/node_modules/@buddi/gateway/dist/web/server.d.ts.map +1 -1
  215. package/node_modules/@buddi/gateway/dist/web/server.js +36 -14
  216. package/node_modules/@buddi/gateway/dist/web/server.js.map +1 -1
  217. package/node_modules/@buddi/gateway/dist/web/telegram.d.ts +39 -0
  218. package/node_modules/@buddi/gateway/dist/web/telegram.d.ts.map +1 -1
  219. package/node_modules/@buddi/gateway/dist/web/telegram.js +53 -1
  220. package/node_modules/@buddi/gateway/dist/web/telegram.js.map +1 -1
  221. package/node_modules/@buddi/install/dist/.tsbuildinfo +1 -1
  222. package/node_modules/@buddi/install/dist/launcher.d.ts.map +1 -1
  223. package/node_modules/@buddi/install/dist/launcher.js +135 -10
  224. package/node_modules/@buddi/install/dist/launcher.js.map +1 -1
  225. package/node_modules/@buddi/install/dist/uninstall.d.ts +59 -0
  226. package/node_modules/@buddi/install/dist/uninstall.d.ts.map +1 -0
  227. package/node_modules/@buddi/install/dist/uninstall.js +260 -0
  228. package/node_modules/@buddi/install/dist/uninstall.js.map +1 -0
  229. package/node_modules/@buddi/runtime/dist/.tsbuildinfo +1 -1
  230. package/node_modules/@buddi/runtime/dist/codex-direct.d.ts +55 -0
  231. package/node_modules/@buddi/runtime/dist/codex-direct.d.ts.map +1 -0
  232. package/node_modules/@buddi/runtime/dist/codex-direct.js +334 -0
  233. package/node_modules/@buddi/runtime/dist/codex-direct.js.map +1 -0
  234. package/node_modules/@buddi/runtime/dist/codex-oauth.d.ts +66 -0
  235. package/node_modules/@buddi/runtime/dist/codex-oauth.d.ts.map +1 -0
  236. package/node_modules/@buddi/runtime/dist/codex-oauth.js +210 -0
  237. package/node_modules/@buddi/runtime/dist/codex-oauth.js.map +1 -0
  238. package/node_modules/@buddi/runtime/dist/codex-profile.d.ts +19 -0
  239. package/node_modules/@buddi/runtime/dist/codex-profile.d.ts.map +1 -0
  240. package/node_modules/@buddi/runtime/dist/codex-profile.js +99 -0
  241. package/node_modules/@buddi/runtime/dist/codex-profile.js.map +1 -0
  242. package/node_modules/@buddi/runtime/dist/context-window.d.ts.map +1 -1
  243. package/node_modules/@buddi/runtime/dist/context-window.js +9 -2
  244. package/node_modules/@buddi/runtime/dist/context-window.js.map +1 -1
  245. package/node_modules/@buddi/runtime/dist/delegate.d.ts +24 -0
  246. package/node_modules/@buddi/runtime/dist/delegate.d.ts.map +1 -1
  247. package/node_modules/@buddi/runtime/dist/delegate.js +87 -11
  248. package/node_modules/@buddi/runtime/dist/delegate.js.map +1 -1
  249. package/node_modules/@buddi/runtime/dist/index.d.ts +4 -4
  250. package/node_modules/@buddi/runtime/dist/index.d.ts.map +1 -1
  251. package/node_modules/@buddi/runtime/dist/index.js +5 -5
  252. package/node_modules/@buddi/runtime/dist/index.js.map +1 -1
  253. package/node_modules/@buddi/runtime/dist/loop.d.ts +7 -0
  254. package/node_modules/@buddi/runtime/dist/loop.d.ts.map +1 -1
  255. package/node_modules/@buddi/runtime/dist/loop.js +48 -8
  256. package/node_modules/@buddi/runtime/dist/loop.js.map +1 -1
  257. package/node_modules/@buddi/runtime/dist/ollama-signing.d.ts +52 -0
  258. package/node_modules/@buddi/runtime/dist/ollama-signing.d.ts.map +1 -0
  259. package/node_modules/@buddi/runtime/dist/ollama-signing.js +154 -0
  260. package/node_modules/@buddi/runtime/dist/ollama-signing.js.map +1 -0
  261. package/node_modules/@buddi/runtime/dist/openai.d.ts.map +1 -1
  262. package/node_modules/@buddi/runtime/dist/openai.js +38 -9
  263. package/node_modules/@buddi/runtime/dist/openai.js.map +1 -1
  264. package/node_modules/@buddi/runtime/dist/provider-models.d.ts +2 -0
  265. package/node_modules/@buddi/runtime/dist/provider-models.d.ts.map +1 -1
  266. package/node_modules/@buddi/runtime/dist/provider-models.js +8 -3
  267. package/node_modules/@buddi/runtime/dist/provider-models.js.map +1 -1
  268. package/node_modules/@buddi/tool-artifacts/dist/.tsbuildinfo +1 -1
  269. package/node_modules/@buddi/tool-browser/dist/.tsbuildinfo +1 -1
  270. package/node_modules/@buddi/tool-browser/dist/index.d.ts +1 -1
  271. package/node_modules/@buddi/tool-browser/dist/index.d.ts.map +1 -1
  272. package/node_modules/@buddi/tool-browser/dist/index.js +12 -4
  273. package/node_modules/@buddi/tool-browser/dist/index.js.map +1 -1
  274. package/node_modules/@buddi/tool-browser/dist/origin-pattern.d.ts +31 -0
  275. package/node_modules/@buddi/tool-browser/dist/origin-pattern.d.ts.map +1 -0
  276. package/node_modules/@buddi/tool-browser/dist/origin-pattern.js +61 -0
  277. package/node_modules/@buddi/tool-browser/dist/origin-pattern.js.map +1 -0
  278. package/node_modules/@buddi/tool-browser/dist/public-suffixes.d.ts +24 -0
  279. package/node_modules/@buddi/tool-browser/dist/public-suffixes.d.ts.map +1 -0
  280. package/node_modules/@buddi/tool-browser/dist/public-suffixes.js +153 -0
  281. package/node_modules/@buddi/tool-browser/dist/public-suffixes.js.map +1 -0
  282. package/node_modules/@buddi/tool-browser/dist/secrets.d.ts +45 -9
  283. package/node_modules/@buddi/tool-browser/dist/secrets.d.ts.map +1 -1
  284. package/node_modules/@buddi/tool-browser/dist/secrets.js +77 -20
  285. package/node_modules/@buddi/tool-browser/dist/secrets.js.map +1 -1
  286. package/node_modules/@buddi/tool-browser/dist/service.d.ts +10 -0
  287. package/node_modules/@buddi/tool-browser/dist/service.d.ts.map +1 -1
  288. package/node_modules/@buddi/tool-browser/dist/service.js +84 -20
  289. package/node_modules/@buddi/tool-browser/dist/service.js.map +1 -1
  290. package/node_modules/@buddi/tool-browser/dist/types.d.ts +7 -1
  291. package/node_modules/@buddi/tool-browser/dist/types.d.ts.map +1 -1
  292. package/node_modules/@buddi/tool-browser/dist/types.js +7 -1
  293. package/node_modules/@buddi/tool-browser/dist/types.js.map +1 -1
  294. package/node_modules/@buddi/tool-email/dist/.tsbuildinfo +1 -1
  295. package/node_modules/@buddi/tool-email/dist/tools/read.js +1 -1
  296. package/node_modules/@buddi/tool-email/dist/tools/read.js.map +1 -1
  297. package/node_modules/@buddi/tool-host/dist/.tsbuildinfo +1 -1
  298. package/node_modules/@buddi/tool-memory/dist/.tsbuildinfo +1 -1
  299. package/node_modules/@buddi/tool-web/dist/.tsbuildinfo +1 -1
  300. package/node_modules/ws/lib/websocket.js +17 -3
  301. package/node_modules/ws/package.json +1 -1
  302. package/package.json +2 -2
  303. package/packages/cli/dist/.tsbuildinfo +1 -1
  304. package/packages/cli/dist/args.d.ts +7 -0
  305. package/packages/cli/dist/args.d.ts.map +1 -1
  306. package/packages/cli/dist/args.js +14 -0
  307. package/packages/cli/dist/args.js.map +1 -1
  308. package/packages/cli/dist/commands.d.ts.map +1 -1
  309. package/packages/cli/dist/commands.js +37 -2
  310. package/packages/cli/dist/commands.js.map +1 -1
  311. package/packages/cli/dist/dashboard-app.d.ts +6 -0
  312. package/packages/cli/dist/dashboard-app.d.ts.map +1 -1
  313. package/packages/cli/dist/dashboard-app.js +16 -1
  314. package/packages/cli/dist/dashboard-app.js.map +1 -1
  315. package/packages/cli/dist/doctor-probes.d.ts.map +1 -1
  316. package/packages/cli/dist/doctor-probes.js +5 -2
  317. package/packages/cli/dist/doctor-probes.js.map +1 -1
  318. package/packages/cli/dist/doctor.d.ts +16 -0
  319. package/packages/cli/dist/doctor.d.ts.map +1 -1
  320. package/packages/cli/dist/doctor.js +18 -0
  321. package/packages/cli/dist/doctor.js.map +1 -1
  322. package/packages/cli/dist/index.d.ts +1 -0
  323. package/packages/cli/dist/index.d.ts.map +1 -1
  324. package/packages/cli/dist/index.js +1 -0
  325. package/packages/cli/dist/index.js.map +1 -1
  326. package/packages/cli/dist/main.d.ts.map +1 -1
  327. package/packages/cli/dist/main.js +22 -2
  328. package/packages/cli/dist/main.js.map +1 -1
  329. package/packages/cli/dist/status.d.ts +10 -0
  330. package/packages/cli/dist/status.d.ts.map +1 -1
  331. package/packages/cli/dist/status.js +4 -0
  332. package/packages/cli/dist/status.js.map +1 -1
  333. package/packages/cli/dist/uninstall-cmd.d.ts +42 -0
  334. package/packages/cli/dist/uninstall-cmd.d.ts.map +1 -0
  335. package/packages/cli/dist/uninstall-cmd.js +208 -0
  336. package/packages/cli/dist/uninstall-cmd.js.map +1 -0
  337. package/packages/core/dist/.tsbuildinfo +1 -1
  338. package/packages/core/dist/agents/catalog.d.ts.map +1 -1
  339. package/packages/core/dist/agents/catalog.js +7 -1
  340. package/packages/core/dist/agents/catalog.js.map +1 -1
  341. package/packages/core/dist/agents/models.d.ts.map +1 -1
  342. package/packages/core/dist/agents/models.js +4 -3
  343. package/packages/core/dist/agents/models.js.map +1 -1
  344. package/packages/core/dist/agents/skills.d.ts +2 -2
  345. package/packages/core/dist/goals/frequency.d.ts +51 -0
  346. package/packages/core/dist/goals/frequency.d.ts.map +1 -0
  347. package/packages/core/dist/goals/frequency.js +113 -0
  348. package/packages/core/dist/goals/frequency.js.map +1 -0
  349. package/packages/core/dist/goals/index.d.ts +2 -0
  350. package/packages/core/dist/goals/index.d.ts.map +1 -1
  351. package/packages/core/dist/goals/index.js +2 -0
  352. package/packages/core/dist/goals/index.js.map +1 -1
  353. package/packages/core/dist/goals/math.d.ts +8 -2
  354. package/packages/core/dist/goals/math.d.ts.map +1 -1
  355. package/packages/core/dist/goals/math.js +17 -4
  356. package/packages/core/dist/goals/math.js.map +1 -1
  357. package/packages/core/dist/goals/owner.d.ts +129 -0
  358. package/packages/core/dist/goals/owner.d.ts.map +1 -0
  359. package/packages/core/dist/goals/owner.js +239 -0
  360. package/packages/core/dist/goals/owner.js.map +1 -0
  361. package/packages/core/dist/goals/store.d.ts.map +1 -1
  362. package/packages/core/dist/goals/store.js +11 -4
  363. package/packages/core/dist/goals/store.js.map +1 -1
  364. package/packages/core/dist/goals/types.d.ts +17 -3
  365. package/packages/core/dist/goals/types.d.ts.map +1 -1
  366. package/packages/core/dist/goals/types.js +8 -2
  367. package/packages/core/dist/goals/types.js.map +1 -1
  368. package/packages/core/dist/learning/announce.d.ts +29 -0
  369. package/packages/core/dist/learning/announce.d.ts.map +1 -0
  370. package/packages/core/dist/learning/announce.js +49 -0
  371. package/packages/core/dist/learning/announce.js.map +1 -0
  372. package/packages/core/dist/learning/index.d.ts +1 -0
  373. package/packages/core/dist/learning/index.d.ts.map +1 -1
  374. package/packages/core/dist/learning/index.js +1 -0
  375. package/packages/core/dist/learning/index.js.map +1 -1
  376. package/packages/core/dist/learning/store.d.ts.map +1 -1
  377. package/packages/core/dist/learning/store.js +15 -4
  378. package/packages/core/dist/learning/store.js.map +1 -1
  379. package/packages/core/dist/notifications/notify.d.ts.map +1 -1
  380. package/packages/core/dist/notifications/notify.js +1 -0
  381. package/packages/core/dist/notifications/notify.js.map +1 -1
  382. package/packages/core/dist/notifications/store.d.ts +2 -0
  383. package/packages/core/dist/notifications/store.d.ts.map +1 -1
  384. package/packages/core/dist/notifications/store.js +9 -0
  385. package/packages/core/dist/notifications/store.js.map +1 -1
  386. package/packages/core/dist/notifications/types.d.ts +6 -0
  387. package/packages/core/dist/notifications/types.d.ts.map +1 -1
  388. package/packages/core/dist/pages.d.ts +2 -2
  389. package/packages/core/dist/provider-accounts.d.ts +7 -1
  390. package/packages/core/dist/provider-accounts.d.ts.map +1 -1
  391. package/packages/core/dist/provider-accounts.js +14 -1
  392. package/packages/core/dist/provider-accounts.js.map +1 -1
  393. package/packages/core/dist/provider.d.ts +6 -0
  394. package/packages/core/dist/provider.d.ts.map +1 -1
  395. package/packages/core/dist/provider.js.map +1 -1
  396. package/packages/core/dist/sentinels/run.d.ts.map +1 -1
  397. package/packages/core/dist/sentinels/run.js +4 -1
  398. package/packages/core/dist/sentinels/run.js.map +1 -1
  399. package/packages/core/dist/sentinels/types.d.ts +20 -0
  400. package/packages/core/dist/sentinels/types.d.ts.map +1 -1
  401. package/packages/core/dist/sentinels/types.js.map +1 -1
  402. package/packages/core/dist/surfaces.d.ts +2 -0
  403. package/packages/core/dist/surfaces.d.ts.map +1 -1
  404. package/packages/core/dist/surfaces.js +7 -0
  405. package/packages/core/dist/surfaces.js.map +1 -1
  406. package/packages/core/dist/uninstall.d.ts +79 -0
  407. package/packages/core/dist/uninstall.d.ts.map +1 -0
  408. package/packages/core/dist/uninstall.js +109 -0
  409. package/packages/core/dist/uninstall.js.map +1 -0
  410. package/packages/core/dist/views.d.ts +66 -66
  411. package/packages/core/migrations/044_owner_metrics.sql +45 -0
  412. package/packages/core/migrations/045_frequency_goals.sql +19 -0
  413. package/packages/core/migrations/046_ollama_device_key.sql +7 -0
  414. package/packages/core/migrations/047_reported_context_windows.sql +5 -0
  415. package/packages/core/package.json +4 -0
  416. package/packages/gateway/dist/.tsbuildinfo +1 -1
  417. package/packages/gateway/dist/chat/usage.d.ts.map +1 -1
  418. package/packages/gateway/dist/chat/usage.js +1 -0
  419. package/packages/gateway/dist/chat/usage.js.map +1 -1
  420. package/packages/gateway/dist/codex-accounts.d.ts +24 -11
  421. package/packages/gateway/dist/codex-accounts.d.ts.map +1 -1
  422. package/packages/gateway/dist/codex-accounts.js +151 -215
  423. package/packages/gateway/dist/codex-accounts.js.map +1 -1
  424. package/packages/gateway/dist/index.d.ts +2 -0
  425. package/packages/gateway/dist/index.d.ts.map +1 -1
  426. package/packages/gateway/dist/index.js +2 -0
  427. package/packages/gateway/dist/index.js.map +1 -1
  428. package/packages/gateway/dist/missions/agent-run.d.ts.map +1 -1
  429. package/packages/gateway/dist/missions/agent-run.js +1 -0
  430. package/packages/gateway/dist/missions/agent-run.js.map +1 -1
  431. package/packages/gateway/dist/missions/execute.d.ts +7 -0
  432. package/packages/gateway/dist/missions/execute.d.ts.map +1 -1
  433. package/packages/gateway/dist/missions/execute.js +3 -1
  434. package/packages/gateway/dist/missions/execute.js.map +1 -1
  435. package/packages/gateway/dist/missions/goals-format.d.ts +32 -5
  436. package/packages/gateway/dist/missions/goals-format.d.ts.map +1 -1
  437. package/packages/gateway/dist/missions/goals-format.js +59 -2
  438. package/packages/gateway/dist/missions/goals-format.js.map +1 -1
  439. package/packages/gateway/dist/missions/goals-page.d.ts +60 -5
  440. package/packages/gateway/dist/missions/goals-page.d.ts.map +1 -1
  441. package/packages/gateway/dist/missions/goals-page.js +355 -8
  442. package/packages/gateway/dist/missions/goals-page.js.map +1 -1
  443. package/packages/gateway/dist/missions/goals.d.ts +64 -5
  444. package/packages/gateway/dist/missions/goals.d.ts.map +1 -1
  445. package/packages/gateway/dist/missions/goals.js +834 -116
  446. package/packages/gateway/dist/missions/goals.js.map +1 -1
  447. package/packages/gateway/dist/missions/sentinel-wake.d.ts +5 -0
  448. package/packages/gateway/dist/missions/sentinel-wake.d.ts.map +1 -1
  449. package/packages/gateway/dist/missions/sentinel-wake.js +12 -0
  450. package/packages/gateway/dist/missions/sentinel-wake.js.map +1 -1
  451. package/packages/gateway/dist/ollama-accounts.d.ts +87 -0
  452. package/packages/gateway/dist/ollama-accounts.d.ts.map +1 -0
  453. package/packages/gateway/dist/ollama-accounts.js +179 -0
  454. package/packages/gateway/dist/ollama-accounts.js.map +1 -0
  455. package/packages/gateway/dist/owner-notify.d.ts +2 -1
  456. package/packages/gateway/dist/owner-notify.d.ts.map +1 -1
  457. package/packages/gateway/dist/owner-notify.js +6 -2
  458. package/packages/gateway/dist/owner-notify.js.map +1 -1
  459. package/packages/gateway/dist/provider-accounts.d.ts +47 -1
  460. package/packages/gateway/dist/provider-accounts.d.ts.map +1 -1
  461. package/packages/gateway/dist/provider-accounts.js +204 -38
  462. package/packages/gateway/dist/provider-accounts.js.map +1 -1
  463. package/packages/gateway/dist/serve.d.ts.map +1 -1
  464. package/packages/gateway/dist/serve.js +1 -0
  465. package/packages/gateway/dist/serve.js.map +1 -1
  466. package/packages/gateway/dist/subscription-signins.d.ts +28 -0
  467. package/packages/gateway/dist/subscription-signins.d.ts.map +1 -0
  468. package/packages/gateway/dist/subscription-signins.js +30 -0
  469. package/packages/gateway/dist/subscription-signins.js.map +1 -0
  470. package/packages/gateway/dist/surfaces/context-budget.d.ts.map +1 -1
  471. package/packages/gateway/dist/surfaces/context-budget.js +10 -3
  472. package/packages/gateway/dist/surfaces/context-budget.js.map +1 -1
  473. package/packages/gateway/dist/telegram/api.d.ts +43 -9
  474. package/packages/gateway/dist/telegram/api.d.ts.map +1 -1
  475. package/packages/gateway/dist/telegram/api.js +80 -21
  476. package/packages/gateway/dist/telegram/api.js.map +1 -1
  477. package/packages/gateway/dist/telegram/attachments.d.ts +6 -0
  478. package/packages/gateway/dist/telegram/attachments.d.ts.map +1 -1
  479. package/packages/gateway/dist/telegram/attachments.js +1 -0
  480. package/packages/gateway/dist/telegram/attachments.js.map +1 -1
  481. package/packages/gateway/dist/telegram/browser-view.d.ts.map +1 -1
  482. package/packages/gateway/dist/telegram/browser-view.js +2 -1
  483. package/packages/gateway/dist/telegram/browser-view.js.map +1 -1
  484. package/packages/gateway/dist/telegram/channel.d.ts +7 -1
  485. package/packages/gateway/dist/telegram/channel.d.ts.map +1 -1
  486. package/packages/gateway/dist/telegram/channel.js +24 -2
  487. package/packages/gateway/dist/telegram/channel.js.map +1 -1
  488. package/packages/gateway/dist/telegram/commands.d.ts +28 -0
  489. package/packages/gateway/dist/telegram/commands.d.ts.map +1 -0
  490. package/packages/gateway/dist/telegram/commands.js +74 -0
  491. package/packages/gateway/dist/telegram/commands.js.map +1 -0
  492. package/packages/gateway/dist/telegram/main.d.ts +3 -0
  493. package/packages/gateway/dist/telegram/main.d.ts.map +1 -1
  494. package/packages/gateway/dist/telegram/main.js +71 -2
  495. package/packages/gateway/dist/telegram/main.js.map +1 -1
  496. package/packages/gateway/dist/telegram/outbound.d.ts +191 -0
  497. package/packages/gateway/dist/telegram/outbound.d.ts.map +1 -0
  498. package/packages/gateway/dist/telegram/outbound.js +671 -0
  499. package/packages/gateway/dist/telegram/outbound.js.map +1 -0
  500. package/packages/gateway/dist/telegram/proposals.d.ts +65 -0
  501. package/packages/gateway/dist/telegram/proposals.d.ts.map +1 -0
  502. package/packages/gateway/dist/telegram/proposals.js +183 -0
  503. package/packages/gateway/dist/telegram/proposals.js.map +1 -0
  504. package/packages/gateway/dist/telegram/surface.d.ts +47 -47
  505. package/packages/gateway/dist/telegram/surface.d.ts.map +1 -1
  506. package/packages/gateway/dist/telegram/surface.js +99 -290
  507. package/packages/gateway/dist/telegram/surface.js.map +1 -1
  508. package/packages/gateway/dist/web/onboarding.d.ts.map +1 -1
  509. package/packages/gateway/dist/web/onboarding.js +4 -0
  510. package/packages/gateway/dist/web/onboarding.js.map +1 -1
  511. package/packages/gateway/dist/web/secrets.d.ts.map +1 -1
  512. package/packages/gateway/dist/web/secrets.js +3 -2
  513. package/packages/gateway/dist/web/secrets.js.map +1 -1
  514. package/packages/gateway/dist/web/server.d.ts.map +1 -1
  515. package/packages/gateway/dist/web/server.js +36 -14
  516. package/packages/gateway/dist/web/server.js.map +1 -1
  517. package/packages/gateway/dist/web/telegram.d.ts +39 -0
  518. package/packages/gateway/dist/web/telegram.d.ts.map +1 -1
  519. package/packages/gateway/dist/web/telegram.js +53 -1
  520. package/packages/gateway/dist/web/telegram.js.map +1 -1
  521. package/packages/install/dist/.tsbuildinfo +1 -1
  522. package/packages/install/dist/launcher.d.ts.map +1 -1
  523. package/packages/install/dist/launcher.js +135 -10
  524. package/packages/install/dist/launcher.js.map +1 -1
  525. package/packages/install/dist/uninstall.d.ts +59 -0
  526. package/packages/install/dist/uninstall.d.ts.map +1 -0
  527. package/packages/install/dist/uninstall.js +260 -0
  528. package/packages/install/dist/uninstall.js.map +1 -0
  529. package/packages/runtime/dist/.tsbuildinfo +1 -1
  530. package/packages/runtime/dist/codex-direct.d.ts +55 -0
  531. package/packages/runtime/dist/codex-direct.d.ts.map +1 -0
  532. package/packages/runtime/dist/codex-direct.js +334 -0
  533. package/packages/runtime/dist/codex-direct.js.map +1 -0
  534. package/packages/runtime/dist/codex-oauth.d.ts +66 -0
  535. package/packages/runtime/dist/codex-oauth.d.ts.map +1 -0
  536. package/packages/runtime/dist/codex-oauth.js +210 -0
  537. package/packages/runtime/dist/codex-oauth.js.map +1 -0
  538. package/packages/runtime/dist/codex-profile.d.ts +19 -0
  539. package/packages/runtime/dist/codex-profile.d.ts.map +1 -0
  540. package/packages/runtime/dist/codex-profile.js +99 -0
  541. package/packages/runtime/dist/codex-profile.js.map +1 -0
  542. package/packages/runtime/dist/context-window.d.ts.map +1 -1
  543. package/packages/runtime/dist/context-window.js +9 -2
  544. package/packages/runtime/dist/context-window.js.map +1 -1
  545. package/packages/runtime/dist/delegate.d.ts +24 -0
  546. package/packages/runtime/dist/delegate.d.ts.map +1 -1
  547. package/packages/runtime/dist/delegate.js +87 -11
  548. package/packages/runtime/dist/delegate.js.map +1 -1
  549. package/packages/runtime/dist/index.d.ts +4 -4
  550. package/packages/runtime/dist/index.d.ts.map +1 -1
  551. package/packages/runtime/dist/index.js +5 -5
  552. package/packages/runtime/dist/index.js.map +1 -1
  553. package/packages/runtime/dist/loop.d.ts +7 -0
  554. package/packages/runtime/dist/loop.d.ts.map +1 -1
  555. package/packages/runtime/dist/loop.js +48 -8
  556. package/packages/runtime/dist/loop.js.map +1 -1
  557. package/packages/runtime/dist/ollama-signing.d.ts +52 -0
  558. package/packages/runtime/dist/ollama-signing.d.ts.map +1 -0
  559. package/packages/runtime/dist/ollama-signing.js +154 -0
  560. package/packages/runtime/dist/ollama-signing.js.map +1 -0
  561. package/packages/runtime/dist/openai.d.ts.map +1 -1
  562. package/packages/runtime/dist/openai.js +38 -9
  563. package/packages/runtime/dist/openai.js.map +1 -1
  564. package/packages/runtime/dist/provider-models.d.ts +2 -0
  565. package/packages/runtime/dist/provider-models.d.ts.map +1 -1
  566. package/packages/runtime/dist/provider-models.js +8 -3
  567. package/packages/runtime/dist/provider-models.js.map +1 -1
  568. package/packages/tools/artifacts/dist/.tsbuildinfo +1 -1
  569. package/packages/tools/browser/dist/.tsbuildinfo +1 -1
  570. package/packages/tools/browser/dist/index.d.ts +1 -1
  571. package/packages/tools/browser/dist/index.d.ts.map +1 -1
  572. package/packages/tools/browser/dist/index.js +12 -4
  573. package/packages/tools/browser/dist/index.js.map +1 -1
  574. package/packages/tools/browser/dist/origin-pattern.d.ts +31 -0
  575. package/packages/tools/browser/dist/origin-pattern.d.ts.map +1 -0
  576. package/packages/tools/browser/dist/origin-pattern.js +61 -0
  577. package/packages/tools/browser/dist/origin-pattern.js.map +1 -0
  578. package/packages/tools/browser/dist/public-suffixes.d.ts +24 -0
  579. package/packages/tools/browser/dist/public-suffixes.d.ts.map +1 -0
  580. package/packages/tools/browser/dist/public-suffixes.js +153 -0
  581. package/packages/tools/browser/dist/public-suffixes.js.map +1 -0
  582. package/packages/tools/browser/dist/secrets.d.ts +45 -9
  583. package/packages/tools/browser/dist/secrets.d.ts.map +1 -1
  584. package/packages/tools/browser/dist/secrets.js +77 -20
  585. package/packages/tools/browser/dist/secrets.js.map +1 -1
  586. package/packages/tools/browser/dist/service.d.ts +10 -0
  587. package/packages/tools/browser/dist/service.d.ts.map +1 -1
  588. package/packages/tools/browser/dist/service.js +84 -20
  589. package/packages/tools/browser/dist/service.js.map +1 -1
  590. package/packages/tools/browser/dist/types.d.ts +7 -1
  591. package/packages/tools/browser/dist/types.d.ts.map +1 -1
  592. package/packages/tools/browser/dist/types.js +7 -1
  593. package/packages/tools/browser/dist/types.js.map +1 -1
  594. package/packages/tools/email/dist/.tsbuildinfo +1 -1
  595. package/packages/tools/email/dist/tools/read.js +1 -1
  596. package/packages/tools/email/dist/tools/read.js.map +1 -1
  597. package/packages/tools/host/dist/.tsbuildinfo +1 -1
  598. package/packages/tools/memory/dist/.tsbuildinfo +1 -1
  599. package/packages/tools/web/dist/.tsbuildinfo +1 -1
  600. package/packages/web/dist/assets/index-BRc5auXm.js +123 -0
  601. package/packages/web/dist/assets/{index-DCn0vN58.css → index-DwUpotTR.css} +1 -1
  602. package/packages/web/dist/index.html +2 -2
  603. package/node_modules/@buddi/runtime/dist/codex-app-server.d.ts +0 -20
  604. package/node_modules/@buddi/runtime/dist/codex-app-server.d.ts.map +0 -1
  605. package/node_modules/@buddi/runtime/dist/codex-app-server.js +0 -172
  606. package/node_modules/@buddi/runtime/dist/codex-app-server.js.map +0 -1
  607. package/node_modules/@buddi/runtime/dist/codex-policy.d.ts +0 -6
  608. package/node_modules/@buddi/runtime/dist/codex-policy.d.ts.map +0 -1
  609. package/node_modules/@buddi/runtime/dist/codex-policy.js +0 -79
  610. package/node_modules/@buddi/runtime/dist/codex-policy.js.map +0 -1
  611. package/node_modules/@buddi/runtime/dist/codex-rpc.d.ts +0 -34
  612. package/node_modules/@buddi/runtime/dist/codex-rpc.d.ts.map +0 -1
  613. package/node_modules/@buddi/runtime/dist/codex-rpc.js +0 -152
  614. package/node_modules/@buddi/runtime/dist/codex-rpc.js.map +0 -1
  615. package/node_modules/@buddi/runtime/dist/codex-session.d.ts +0 -30
  616. package/node_modules/@buddi/runtime/dist/codex-session.d.ts.map +0 -1
  617. package/node_modules/@buddi/runtime/dist/codex-session.js +0 -169
  618. package/node_modules/@buddi/runtime/dist/codex-session.js.map +0 -1
  619. package/packages/runtime/dist/codex-app-server.d.ts +0 -20
  620. package/packages/runtime/dist/codex-app-server.d.ts.map +0 -1
  621. package/packages/runtime/dist/codex-app-server.js +0 -172
  622. package/packages/runtime/dist/codex-app-server.js.map +0 -1
  623. package/packages/runtime/dist/codex-policy.d.ts +0 -6
  624. package/packages/runtime/dist/codex-policy.d.ts.map +0 -1
  625. package/packages/runtime/dist/codex-policy.js +0 -79
  626. package/packages/runtime/dist/codex-policy.js.map +0 -1
  627. package/packages/runtime/dist/codex-rpc.d.ts +0 -34
  628. package/packages/runtime/dist/codex-rpc.d.ts.map +0 -1
  629. package/packages/runtime/dist/codex-rpc.js +0 -152
  630. package/packages/runtime/dist/codex-rpc.js.map +0 -1
  631. package/packages/runtime/dist/codex-session.d.ts +0 -30
  632. package/packages/runtime/dist/codex-session.d.ts.map +0 -1
  633. package/packages/runtime/dist/codex-session.js +0 -169
  634. package/packages/runtime/dist/codex-session.js.map +0 -1
  635. package/packages/web/dist/assets/index-BRkIoT-q.js +0 -123
@@ -28,10 +28,10 @@
28
28
  * `goal.status`; only the holder may update or close, and a non-holder is
29
29
  * told `not-yours` in as many words, the way `reminder.cancel` does it.
30
30
  */
31
- import { GOAL_CADENCES, MAX_GOAL_TITLE, MAX_MILESTONES, MAX_OPEN_GOALS, assertApprovedEffect, closeGoal, createGoal, getGoal, checkMetricParams, STANDING_CHECKS, lastCheck, lastMeasuredCheck, listGoals, localDateString, measureMetricResult, measuredChecks, metricParamsSchema, milestoneValue, milestonesCrossed, paceNeeded, parseReminderWhen, reached, recentChecks, recordCheck, settleGoal, standingOf, targetValue, updateGoal, } from '@buddi/core';
31
+ import { GOAL_CADENCES, MAX_GOAL_TITLE, MAX_MILESTONES, MAX_OPEN_GOALS, assertApprovedEffect, closeGoal, createGoal, getGoal, checkMetricParams, STANDING_CHECKS, lastCheck, lastMeasuredCheck, listGoals, localDateString, measureMetricResult, measuredChecks, metricParamsSchema, milestoneValue, milestonesCrossed, paceNeeded, parseReminderWhen, reached, recentChecks, recordCheck, settleGoal, standingOf, targetValue, updateGoal, FREQUENCY_PERS, MAX_FREQUENCY_COUNT, frequencySettles, frequencyWords, METRIC_DIRECTIONS, METRIC_UNITS, MAX_OWNER_LABEL, MAX_OWNER_UNIT_LABEL, OWNER_SOURCE, createOwnerMetric, cadenceMs, getOwnerMetric, isOwnerMetric, latestOwnerValue, ownerMetricId, ownerSlugOf, recordOwnerValue, refuseOutsideBand, refuseOwnerSlug, } from '@buddi/core';
32
32
  import { z } from 'zod';
33
- import { GOALS_SENTINEL_ID, checkLines, formatPace, formatValue, goalKey, goalLine } from './goals-format.js';
34
- import { CHART_CHECKS, chartOf, createGoalHome, createGoalOwnerClose, createGoalQueries, goalViews, goalsPage, } from './goals-page.js';
33
+ import { GOALS_SENTINEL_ID, asOwnerSource, frequencyNow, baseUnit, checkLines, formatPace, formatValue, goalKey, goalLine, unitLabelOf, valueUnitOf, } from './goals-format.js';
34
+ import { CHART_CHECKS, chartOf, createGoalHome, createGoalOwnerClose, createGoalQueries, frequencyOf, goalOwnerValues, goalViews, goalsPage, } from './goals-page.js';
35
35
  /** Plugin family name for the goal tools. The rows live in core's schema. */
36
36
  export const GOAL_PLUGIN = 'goal';
37
37
  /**
@@ -77,6 +77,16 @@ export const GOAL_WAKE_INSTRUCTION = 'Verify with your own tools, then report; t
77
77
  * they and this one import is what keeps those two out of a cycle.
78
78
  * ------------------------------------------------------------------ */
79
79
  export { checkLines, formatPace, formatValue, goalLine } from './goals-format.js';
80
+ export { asOwnerSource } from './goals-format.js';
81
+ /** Where a value was said, from the surface the run answers on. */
82
+ export function valueSourceOf(ctx) {
83
+ const surface = ctx.surface?.id;
84
+ if (surface === 'telegram')
85
+ return 'telegram';
86
+ if (surface === 'web' || surface === 'cli')
87
+ return 'chat';
88
+ return 'api';
89
+ }
80
90
  /**
81
91
  * The sentence the owner approves.
82
92
  *
@@ -105,6 +115,30 @@ export function renderGoalSet(envelope, unit, direction, timezone) {
105
115
  const stale = readingAsOf !== null && setAt.getTime() - readingAsOf.getTime() > 24 * 60 * 60_000
106
116
  ? ` (reading as of ${localDateString(readingAsOf, timezone)})`
107
117
  : '';
118
+ const measuredBy = envelope.owner !== undefined
119
+ ? `Measured by you, when you tell buddi${envelope.owner.isNew ? ` (a new metric, ${envelope.metric})` : ''}, until ${localDateString(asGoal.deadline, timezone)}.`
120
+ : `Measured by ${envelope.metric}${Object.keys(envelope.params).length === 0 ? '' : ` ${JSON.stringify(envelope.params)}`}, every ${envelope.cadence === 'daily' ? 'day' : 'week'}, until ${localDateString(asGoal.deadline, timezone)}.`;
121
+ if (envelope.target.kind === 'frequency') {
122
+ /*
123
+ * "3 times a week until 2026-12-31": a count per window has no "from"
124
+ * and no pace per week — the window is the pace. Milestones are streaks.
125
+ */
126
+ const per = envelope.target.per;
127
+ const lines = [
128
+ `${envelope.title}`,
129
+ '',
130
+ `${frequencyWords(envelope.target)} until ${localDateString(asGoal.deadline, timezone)}, ` +
131
+ `checked ${envelope.cadence}, held by @${envelope.agentId}`,
132
+ '',
133
+ measuredBy,
134
+ ];
135
+ if (envelope.milestones.length > 0) {
136
+ lines.push(`Milestones you will hear about, once each: ${envelope.milestones
137
+ .map((m) => `${m} ${per}s in a row`)
138
+ .join(', ')}.`);
139
+ }
140
+ return lines.join('\n');
141
+ }
108
142
  const lines = [
109
143
  `${envelope.title}`,
110
144
  '',
@@ -112,7 +146,7 @@ export function renderGoalSet(envelope, unit, direction, timezone) {
112
146
  `${formatValue(targetValue(asGoal), unit, currency)} by ${localDateString(asGoal.deadline, timezone)}: ` +
113
147
  `${formatPace(pace, unit, direction, currency)}, checked ${envelope.cadence}, held by @${envelope.agentId}`,
114
148
  '',
115
- `Measured by ${envelope.metric}${Object.keys(envelope.params).length === 0 ? '' : ` ${JSON.stringify(envelope.params)}`}, every ${envelope.cadence === 'daily' ? 'day' : 'week'}, until ${localDateString(asGoal.deadline, timezone)}.`,
149
+ measuredBy,
116
150
  ];
117
151
  if (envelope.milestones.length > 0) {
118
152
  lines.push(`Milestones you will hear about, once each: ${envelope.milestones
@@ -123,10 +157,12 @@ export function renderGoalSet(envelope, unit, direction, timezone) {
123
157
  }
124
158
  /** The card: one line per field that actually changes, before → after. */
125
159
  export function renderGoalUpdate(envelope, unit, timezone, baseline, currency = null) {
126
- const value = (target) => formatValue(targetValue({ target, baseline: { value: baseline, asOf: new Date(0) } }), unit, currency);
160
+ const value = (target) => target.kind === 'frequency'
161
+ ? frequencyWords(target)
162
+ : formatValue(targetValue({ target, baseline: { value: baseline, asOf: new Date(0) } }), unit, currency);
127
163
  const { before, after } = envelope;
128
164
  const rows = [];
129
- if (before.target.kind !== after.target.kind || before.target.value !== after.target.value) {
165
+ if (JSON.stringify(before.target) !== JSON.stringify(after.target)) {
130
166
  rows.push(`Target: ${value(before.target)} → ${value(after.target)}`);
131
167
  }
132
168
  if (before.deadline !== after.deadline) {
@@ -146,7 +182,7 @@ export function renderGoalUpdate(envelope, unit, timezone, baseline, currency =
146
182
  /* ------------------------------------------------------------------ *
147
183
  * Inputs
148
184
  * ------------------------------------------------------------------ */
149
- const targetInput = z
185
+ const levelTargetInput = z
150
186
  .object({
151
187
  kind: z
152
188
  .enum(['absolute', 'delta'])
@@ -158,6 +194,42 @@ const targetInput = z
158
194
  .describe('The number. For a delta it carries its own sign: -40000 to come down by 40,000.'),
159
195
  })
160
196
  .strict();
197
+ const frequencyTargetInput = z
198
+ .object({
199
+ kind: z
200
+ .literal('frequency')
201
+ .describe('"run three times a week": how many values the owner reports in every week or month.'),
202
+ count: z.number().int().min(1).max(MAX_FREQUENCY_COUNT).describe('How many times in each window: 3.'),
203
+ per: z.enum(FREQUENCY_PERS).describe('The window: a week (Monday to Sunday) or a calendar month.'),
204
+ })
205
+ .strict();
206
+ /**
207
+ * A level target, or a frequency. A frequency goal counts the values of a
208
+ * metric the owner reports; "finish X by Friday" is neither — that is a
209
+ * reminder or a mission.
210
+ */
211
+ const targetInput = z.union([levelTargetInput, frequencyTargetInput]);
212
+ /** A metric the owner will report, named the first time a goal needs it. */
213
+ const ownerMetricInput = z
214
+ .object({
215
+ slug: z
216
+ .string()
217
+ .min(1)
218
+ .max(40)
219
+ .describe('A short kebab name, unique here: "weight", "resting-heart-rate". The metric becomes owner.<slug>.'),
220
+ label: z.string().min(1).max(MAX_OWNER_LABEL).describe('What it is, in the owner\'s words: "Weight".'),
221
+ unit: z
222
+ .enum(METRIC_UNITS)
223
+ .describe('How the number is formatted. Weight is "number" with a unitLabel; a count of runs is "count".'),
224
+ direction: z.enum(METRIC_DIRECTIONS).describe('Which way is better: "down" for weight to lose, "up" for runs.'),
225
+ unitLabel: z
226
+ .string()
227
+ .min(1)
228
+ .max(MAX_OWNER_UNIT_LABEL)
229
+ .optional()
230
+ .describe('The word after the number, when there is one: "lb", "kg", "km".'),
231
+ })
232
+ .strict();
161
233
  const setInput = z
162
234
  .object({
163
235
  title: z
@@ -166,9 +238,16 @@ const setInput = z
166
238
  .max(MAX_GOAL_TITLE)
167
239
  .describe('A short name the owner will recognise on a card months from now: "Debt down by 40k".'),
168
240
  metric: z
169
- .string()
170
- .min(1)
171
- .describe('The namespaced metric to watch, exactly as goal.metrics lists it: "finance.total_debt".'),
241
+ .union([z.string().min(1), z.object({ owner: ownerMetricInput }).strict()])
242
+ .describe('The metric to watch: an id exactly as goal.metrics lists it ("finance.total_debt", "owner.weight"), ' +
243
+ 'or, when no metric measures what the owner named, {"owner": {slug, label, unit, direction, unitLabel?}} ' +
244
+ 'to create one the owner reports. Never create an owner metric for something a plugin already measures.'),
245
+ baseline: z
246
+ .object({ value: z.number().finite().describe('The number the owner said: "I\'m 288" is 288.') })
247
+ .strict()
248
+ .optional()
249
+ .describe('Only for a metric the owner reports: where they are today, from the sentence that set the goal, so ' +
250
+ 'the goal starts measured. A plugin metric is measured instead.'),
172
251
  params: z
173
252
  .record(z.unknown())
174
253
  .optional()
@@ -188,7 +267,8 @@ const setInput = z
188
267
  .max(MAX_MILESTONES)
189
268
  .optional()
190
269
  .describe('Numbers on the same scale as the target — deltas when the target is a delta — that you want to say ' +
191
- 'something about when they are crossed. Each fires once, ever.'),
270
+ 'something about when they are crossed. For a frequency goal they are streaks: [4, 8] is four and ' +
271
+ 'eight windows met in a row. Each fires once, ever.'),
192
272
  })
193
273
  .strict();
194
274
  const updateInput = z
@@ -223,7 +303,52 @@ const statusInput = z
223
303
  .describe('One goal, by id — any goal, whoever holds it. Leave it out for all of your own.'),
224
304
  })
225
305
  .strict();
306
+ const recordInput = z
307
+ .object({
308
+ goal: z
309
+ .string()
310
+ .min(1)
311
+ .optional()
312
+ .describe('The goal the number is for, by id. Name this or metric, not both.'),
313
+ metric: z
314
+ .string()
315
+ .min(1)
316
+ .optional()
317
+ .describe('Or the metric the owner reports: "owner.weight", or just "weight".'),
318
+ value: z.number().finite().describe('The number the owner said. An occurrence ("ran today") is 1.'),
319
+ asOf: z
320
+ .string()
321
+ .min(1)
322
+ .optional()
323
+ .describe("When it was true, in the owner's timezone: 2026-09-26 or 2026-09-26T07:30. Leave it out for now."),
324
+ note: z.string().max(280).optional().describe('A few words the owner added: "after the holidays".'),
325
+ confirmed: z
326
+ .boolean()
327
+ .optional()
328
+ .describe('Set only after the owner confirmed a value this tool refused as far from the last one.'),
329
+ })
330
+ .strict();
331
+ /** A conversation id is a uuid; anything else is not provenance worth keeping. */
332
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
226
333
  const refusal = (reason, message) => ({ ok: false, reason, message });
334
+ /**
335
+ * A frequency goal's milestones are streaks — windows met in a row — so they
336
+ * are whole numbers from 1 up, listed in the order they will be reached.
337
+ */
338
+ function refuseStreaks(milestones) {
339
+ let previous = 0;
340
+ for (const milestone of milestones) {
341
+ if (!Number.isInteger(milestone) || milestone <= previous) {
342
+ return refusal('wrong-direction', `the milestone ${milestone} is not a streak: a frequency goal's milestones are windows met in a row, ` +
343
+ 'whole numbers from 1 up, listed from the smallest ([4, 8]).');
344
+ }
345
+ previous = milestone;
346
+ }
347
+ return null;
348
+ }
349
+ /** How many owner values and frequency windows `goal.status` prints. */
350
+ export const STATUS_VALUES = 10;
351
+ export const STATUS_WINDOWS = 12;
227
352
  /* ------------------------------------------------------------------ *
228
353
  * The manifest
229
354
  * ------------------------------------------------------------------ */
@@ -234,9 +359,15 @@ const refusal = (reason, message) => ({ ok: false, reason, message });
234
359
  * `MetricSource` so a suite can hand it one in-memory metric and exercise the
235
360
  * whole surface without building a plugin.
236
361
  */
237
- export function createGoalManifest(source) {
362
+ export function createGoalManifest(base) {
363
+ /*
364
+ * The plugin metrics and the owner's, behind one `metric(id)`. Every tool
365
+ * refreshes its owner half first — one small `select` — so a metric another
366
+ * conversation created a minute ago is known here.
367
+ */
368
+ const source = asOwnerSource(base);
238
369
  /** The unit a goal's numbers are rendered in, or a safe default. */
239
- const unitOf = (metric) => source.metric(metric)?.unit ?? 'number';
370
+ const unitOf = (metric) => valueUnitOf(source, metric);
240
371
  /** Which way is better, for a goal whose plugin may have been uninstalled. */
241
372
  const directionOf = (metric) => source.metric(metric)?.direction ?? 'down';
242
373
  /**
@@ -280,15 +411,96 @@ export function createGoalManifest(source) {
280
411
  }
281
412
  return null;
282
413
  }
414
+ function definitionOf(metric) {
415
+ return {
416
+ slug: metric.slug,
417
+ label: metric.label,
418
+ unit: metric.unit,
419
+ direction: metric.direction,
420
+ unitLabel: metric.unitLabel,
421
+ };
422
+ }
423
+ /**
424
+ * Which metric this is, or why there is none.
425
+ *
426
+ * A string names an installed metric (a plugin's, or an owner metric that
427
+ * already exists). An `{ owner }` definition names one to create — or one
428
+ * that exists already, in which case it has to agree about the unit and the
429
+ * direction: two goals reading one series in opposite directions is a
430
+ * contradiction, not a second opinion.
431
+ */
432
+ async function resolveMetric(input, ctx) {
433
+ if (typeof input.metric === 'string') {
434
+ const found = source.metric(input.metric);
435
+ if (found === undefined) {
436
+ return refusal('unknown-metric', `no metric "${input.metric}" is installed here. Call goal.metrics and name one of those, or pass ` +
437
+ '{"owner": {...}} for a number the owner will report; a goal without a metric is a reminder.');
438
+ }
439
+ if (isOwnerMetric(found)) {
440
+ const slug = ownerSlugOf(found.id);
441
+ return {
442
+ ok: true,
443
+ metric: {
444
+ id: found.id,
445
+ unit: valueUnitOf(source, found.id),
446
+ direction: found.direction,
447
+ owner: {
448
+ metric: { slug, label: found.label, unit: found.unit, direction: found.direction, unitLabel: found.unitLabel },
449
+ isNew: false,
450
+ },
451
+ },
452
+ };
453
+ }
454
+ return { ok: true, metric: { id: found.id, unit: found.unit, direction: found.direction, owner: null } };
455
+ }
456
+ const def = input.metric.owner;
457
+ const badSlug = refuseOwnerSlug(def.slug);
458
+ if (badSlug !== null)
459
+ return refusal('invalid-metric', badSlug);
460
+ const wanted = {
461
+ slug: def.slug,
462
+ label: def.label.trim(),
463
+ unit: def.unit,
464
+ direction: def.direction,
465
+ unitLabel: def.unitLabel?.trim() || null,
466
+ };
467
+ const existing = await getOwnerMetric(ctx.db, def.slug);
468
+ if (existing !== null) {
469
+ if (existing.unit !== wanted.unit ||
470
+ existing.direction !== wanted.direction ||
471
+ (existing.unitLabel ?? null) !== wanted.unitLabel) {
472
+ return refusal('metric-differs', `owner.${def.slug} already exists as ${existing.unit}${existing.unitLabel ? ` (${existing.unitLabel})` : ''}, better ${existing.direction}. Name it as "owner.${def.slug}" to use it, or choose another slug.`);
473
+ }
474
+ return {
475
+ ok: true,
476
+ metric: {
477
+ id: existing.id,
478
+ unit: existing.unitLabel ? { unit: existing.unit, label: existing.unitLabel } : existing.unit,
479
+ direction: existing.direction,
480
+ owner: { metric: definitionOf(existing), isNew: false },
481
+ },
482
+ };
483
+ }
484
+ return {
485
+ ok: true,
486
+ metric: {
487
+ id: ownerMetricId(def.slug),
488
+ unit: wanted.unitLabel ? { unit: wanted.unit, label: wanted.unitLabel } : wanted.unit,
489
+ direction: wanted.direction,
490
+ owner: { metric: wanted, isNew: true },
491
+ },
492
+ };
493
+ }
283
494
  /**
284
495
  * Everything `goal.set` refuses before an approval exists, in one place.
285
496
  *
286
497
  * Read twice on purpose: by `describe`, so the call never becomes a card,
287
498
  * and again by `execute`, because the approval was recorded minutes or hours
288
499
  * ago and "a delegate may not" is a fact about the run that is executing. It
289
- * only reads the arguments and the context, so both readings agree.
500
+ * only reads the arguments, the context and the metric definitions, so both
501
+ * readings agree.
290
502
  */
291
- function refuseSet(input, ctx) {
503
+ async function refuseSet(input, ctx) {
292
504
  if (!ctx.agentId) {
293
505
  return refusal('no-agent', 'a goal belongs to the agent that holds it, and this run has no agent id, so there is nobody to hold it');
294
506
  }
@@ -296,14 +508,41 @@ export function createGoalManifest(source) {
296
508
  return refusal('delegate', 'a delegate cannot set a goal: a goal outlives this run by months and belongs to the agent that will ' +
297
509
  'be woken about it. Report back instead, and let the agent that asked you propose it.');
298
510
  }
299
- const metric = source.metric(input.metric);
300
- if (metric === undefined) {
301
- return refusal('unknown-metric', `no metric "${input.metric}" is installed here. Call goal.metrics and name one of those; a goal ` +
302
- 'without a metric is a reminder.');
511
+ const resolved = await resolveMetric(input, ctx);
512
+ if (!resolved.ok)
513
+ return resolved;
514
+ const metric = resolved.metric;
515
+ let params = {};
516
+ if (metric.owner !== null) {
517
+ if (input.params !== undefined && Object.keys(input.params).length > 0) {
518
+ return refusal('invalid-params', `${metric.id} is a number the owner reports; it takes no parameters.`);
519
+ }
520
+ }
521
+ else {
522
+ if (input.baseline !== undefined) {
523
+ return refusal('baseline-measured', `${metric.id} is measured by its plugin, so the baseline is measured too; leave baseline out.`);
524
+ }
525
+ const checked = checkMetricParams(source.metric(metric.id), input.params);
526
+ if (!checked.ok)
527
+ return refusal('invalid-params', checked.message);
528
+ params = checked.params;
529
+ }
530
+ if (input.target.kind === 'frequency') {
531
+ /*
532
+ * A frequency goal counts values, and only the owner's metrics have
533
+ * values to count: a plugin's number is a level, read when buddi looks.
534
+ */
535
+ if (metric.owner === null) {
536
+ return refusal('frequency-needs-owner', `a frequency goal counts the times the owner tells buddi, and ${metric.id} is measured by a plugin. ` +
537
+ 'Use an owner metric ({"owner": {...}}) for something the owner does, or a level target for this one.');
538
+ }
539
+ if (input.baseline !== undefined) {
540
+ return refusal('baseline-frequency', 'a frequency goal starts with nothing counted; leave baseline out.');
541
+ }
542
+ const streaks = refuseStreaks(input.milestones ?? []);
543
+ if (streaks !== null)
544
+ return streaks;
303
545
  }
304
- const params = checkMetricParams(metric, input.params);
305
- if (!params.ok)
306
- return refusal('invalid-params', params.message);
307
546
  const when = parseReminderWhen(input.deadline, ctx.timezone);
308
547
  if (!when.ok)
309
548
  return refusal('invalid-deadline', when.message);
@@ -316,26 +555,35 @@ export function createGoalManifest(source) {
316
555
  return refusal('deadline-too-far', `a goal can run at most ${MAX_GOAL_HORIZON_DAYS} days; that deadline is further out than anyone can ` +
317
556
  'hold a plan to.');
318
557
  }
319
- return null;
558
+ return { ok: true, metric, params };
320
559
  }
321
- /** The envelope, from the arguments and one measurement. */
322
- function envelopeOf(input, ctx, baseline) {
560
+ /** The envelope, from the arguments, the resolved metric and one measurement. */
561
+ function envelopeOf(input, ctx, metric, params, baseline) {
323
562
  const when = parseReminderWhen(input.deadline, ctx.timezone);
324
- const metric = source.metric(input.metric);
325
- const params = metric === undefined ? { ok: false } : checkMetricParams(metric, input.params);
326
563
  return {
327
564
  tool: 'goal.set',
328
565
  agentId: ctx.agentId ?? '',
329
566
  title: input.title.trim(),
330
- metric: input.metric,
567
+ metric: metric.id,
331
568
  // What the schema *made of* the input — defaults applied, unknown keys
332
569
  // already refused — so the goal row and every later measurement agree.
333
- params: params.ok ? params.params : (input.params ?? {}),
334
- target: { kind: input.target.kind, value: input.target.value },
570
+ params,
571
+ target: input.target.kind === 'frequency'
572
+ ? { kind: 'frequency', count: input.target.count, per: input.target.per }
573
+ : { kind: input.target.kind, value: input.target.value },
335
574
  baseline,
336
575
  deadline: (when.ok ? when.at : new Date(0)).toISOString(),
337
576
  cadence: input.cadence,
338
577
  milestones: input.milestones ?? [],
578
+ ...(metric.owner === null
579
+ ? {}
580
+ : {
581
+ owner: {
582
+ metric: metric.owner.metric,
583
+ isNew: metric.owner.isNew,
584
+ baselineTold: input.baseline !== undefined,
585
+ },
586
+ }),
339
587
  };
340
588
  }
341
589
  /** The baseline an approval already carries, when it carries one. */
@@ -351,23 +599,50 @@ export function createGoalManifest(source) {
351
599
  readingAsOf: baseline.readingAsOf ?? null,
352
600
  };
353
601
  }
602
+ /**
603
+ * The approved envelope's word on whether the owner metric was new.
604
+ *
605
+ * "New" is a fact about the moment the card was drawn: by the time the
606
+ * executor re-describes, another approval may have created the slug, and a
607
+ * card that said "a new metric" must not be voided for that — the definition
608
+ * already had to agree for the second goal to get this far.
609
+ */
610
+ function approvedIsNew(ctx, metric) {
611
+ const envelope = (ctx.approvedEffect?.envelope ?? null);
612
+ if (metric.owner === null || envelope?.owner === undefined)
613
+ return metric;
614
+ return { ...metric, owner: { ...metric.owner, isNew: envelope.owner.isNew === true } };
615
+ }
354
616
  const metrics = {
355
617
  name: 'goal.metrics',
356
618
  description: 'Every number this installation can actually measure, with what it means, which way is better and the ' +
357
- 'narrowing it takes. A goal watches one of these — name one from here when you propose a goal, and if ' +
358
- 'the list is empty then nothing installed here can be measured and a goal is the wrong tool.',
619
+ 'narrowing it takes. A goal watches one of these — name one from here when you propose a goal. Source ' +
620
+ '"plugin" is measured by a plugin; source "owner" is a number the owner reports with goal.record. When ' +
621
+ 'nothing here measures what the owner named, goal.set can create an owner metric.',
359
622
  tier: 'auto',
360
623
  input: z.object({}).strict(),
361
- async execute() {
624
+ async execute(_input, ctx) {
625
+ await source.refresh(ctx.db);
362
626
  return {
363
- metrics: source.metrics().map((metric) => ({
364
- id: metric.id,
365
- plugin: metric.plugin,
366
- description: metric.description,
367
- unit: metric.unit,
368
- direction: metric.direction,
369
- ...(metricParamsSchema(metric) === undefined ? {} : { params: metricParamsSchema(metric) }),
370
- })),
627
+ metrics: source.metrics().map((metric) => isOwnerMetric(metric)
628
+ ? {
629
+ id: metric.id,
630
+ source: OWNER_SOURCE,
631
+ description: metric.description,
632
+ label: metric.label,
633
+ unit: metric.unit,
634
+ ...(metric.unitLabel === null ? {} : { unitLabel: metric.unitLabel }),
635
+ direction: metric.direction,
636
+ }
637
+ : {
638
+ id: metric.id,
639
+ source: 'plugin',
640
+ plugin: metric.plugin,
641
+ description: metric.description,
642
+ unit: metric.unit,
643
+ direction: metric.direction,
644
+ ...(metricParamsSchema(metric) === undefined ? {} : { params: metricParamsSchema(metric) }),
645
+ }),
371
646
  };
372
647
  },
373
648
  };
@@ -379,7 +654,8 @@ export function createGoalManifest(source) {
379
654
  'deadline gets close. The owner sees the whole shape before they say yes: where the number is today, ' +
380
655
  'where it has to get to, by when, what that is per week, and that it is you who will be speaking about ' +
381
656
  `it. At most ${MAX_OPEN_GOALS} open goals in an installation. Use it for something measurable that ` +
382
- 'runs for weeks; for one future nudge use reminder.set.',
657
+ 'runs for weeks; for one future nudge use reminder.set. When no plugin measures the number (a weight, ' +
658
+ 'a time), create an owner metric with {"owner": {...}} and pass the baseline the owner said.',
383
659
  tier: 'gated',
384
660
  input: setInput,
385
661
  /*
@@ -392,9 +668,11 @@ export function createGoalManifest(source) {
392
668
  * the owner is never shown a card for a goal that could not exist.
393
669
  */
394
670
  async describe(input, ctx) {
395
- const no = refuseSet(input, ctx);
396
- if (no !== null)
397
- throw new Error(no.message);
671
+ await source.refresh(ctx.db);
672
+ const checked = await refuseSet(input, ctx);
673
+ if (!checked.ok)
674
+ throw new Error(checked.message);
675
+ const metric = approvedIsNew(ctx, checked.metric);
398
676
  /*
399
677
  * Measure **once**, on the first description. The executor re-describes
400
678
  * before it dispatches and compares envelope hashes, so a second
@@ -403,43 +681,57 @@ export function createGoalManifest(source) {
403
681
  * hopeless for an unread count, and `email.inbox_unread` is one of the
404
682
  * metrics this exists for. At re-description the approved envelope is on
405
683
  * the context, so the number the owner saw is the number that executes.
684
+ *
685
+ * A number the owner just said is the baseline as it is: there is
686
+ * nothing to measure, and it is the one sentence the goal came from.
406
687
  */
407
688
  const baseline = approvedBaseline(ctx) ??
408
- (await (async () => {
409
- const measured = await measureMetricResult(source, input.metric, input.params ?? {}, ctx);
410
- if (!measured.ok) {
411
- /*
412
- * The card is never shown for a metric nobody can read. Approving
413
- * "from ? today to 47,400" is approving nothing, and the baseline
414
- * is the one number the whole goal is relative to.
415
- */
416
- throw new Error(`${input.metric} cannot be measured right now, so there is no baseline to set a goal against: ` +
417
- `${measured.note ?? 'no reason given'}. Fix that first — a goal needs a number to start from.`);
418
- }
419
- return {
420
- value: measured.reading.value,
421
- currency: measured.reading.currency ?? null,
422
- asOf: ctx.now().toISOString(),
423
- readingAsOf: measured.reading.asOf?.toISOString() ?? null,
424
- };
425
- })());
426
- const envelope = envelopeOf(input, ctx, baseline);
427
- const direction = directionOf(input.metric);
428
- const wrongWay = refuseDirection({ target: envelope.target, baseline: { value: baseline.value, asOf: new Date(baseline.asOf) } }, envelope.milestones, direction, unitOf(input.metric), baseline.currency);
689
+ // A frequency goal starts with nothing counted: there is no level to measure.
690
+ (input.target.kind === 'frequency'
691
+ ? { value: 0, currency: null, asOf: ctx.now().toISOString(), readingAsOf: null }
692
+ : input.baseline !== undefined && metric.owner !== null
693
+ ? { value: input.baseline.value, currency: null, asOf: ctx.now().toISOString(), readingAsOf: null }
694
+ : await (async () => {
695
+ const measured = metric.owner !== null && metric.owner.isNew
696
+ ? { ok: false, note: 'the owner has told buddi no value yet' }
697
+ : await measureMetricResult(source, metric.id, metric.owner !== null ? { cadence: input.cadence } : checked.params, ctx);
698
+ if (!measured.ok) {
699
+ /*
700
+ * The card is never shown for a metric nobody can read.
701
+ * Approving "from ? today to 47,400" is approving nothing, and
702
+ * the baseline is the one number the whole goal is relative to.
703
+ */
704
+ throw new Error(metric.owner !== null
705
+ ? `${metric.id} has no recent value, so there is no baseline to set a goal against. Ask the ` +
706
+ 'owner where they are today and pass it as baseline: {"value": ...}.'
707
+ : `${metric.id} cannot be measured right now, so there is no baseline to set a goal against: ` +
708
+ `${measured.note ?? 'no reason given'}. Fix that first — a goal needs a number to start from.`);
709
+ }
710
+ return {
711
+ value: measured.reading.value,
712
+ currency: measured.reading.currency ?? null,
713
+ asOf: ctx.now().toISOString(),
714
+ readingAsOf: measured.reading.asOf?.toISOString() ?? null,
715
+ };
716
+ })());
717
+ const envelope = envelopeOf(input, ctx, metric, checked.params, baseline);
718
+ const wrongWay = envelope.target.kind === 'frequency' ? null : refuseDirection({ target: envelope.target, baseline: { value: baseline.value, asOf: new Date(baseline.asOf) } }, envelope.milestones, metric.direction, metric.unit, baseline.currency);
429
719
  if (wrongWay !== null)
430
720
  throw new Error(wrongWay.message);
431
721
  return {
432
722
  envelope,
433
- preview: renderGoalSet(envelope, unitOf(input.metric), direction, ctx.timezone),
723
+ preview: renderGoalSet(envelope, metric.unit, metric.direction, ctx.timezone),
434
724
  };
435
725
  },
436
726
  async execute(input, ctx) {
437
727
  // Only `executeApproved` reaches this. The checks run again anyway: the
438
728
  // approval was recorded minutes or hours ago, and "a delegate may not"
439
729
  // is a fact about the run that is executing, not about the one that asked.
440
- const no = refuseSet(input, ctx);
441
- if (no !== null)
442
- throw new Error(no.message);
730
+ await source.refresh(ctx.db);
731
+ const checked = await refuseSet(input, ctx);
732
+ if (!checked.ok)
733
+ throw new Error(checked.message);
734
+ const metric = approvedIsNew(ctx, checked.metric);
443
735
  /*
444
736
  * The baseline is the approval's: it is the number the owner saw, and a
445
737
  * goal measured against a different one is a different goal. Everything
@@ -451,7 +743,7 @@ export function createGoalManifest(source) {
451
743
  if (baseline === null) {
452
744
  throw new Error('the effect no longer matches the approved preview; propose it again');
453
745
  }
454
- const envelope = envelopeOf(input, ctx, baseline);
746
+ const envelope = envelopeOf(input, ctx, metric, checked.params, baseline);
455
747
  assertApprovedEffect(ctx, envelope);
456
748
  const created = await createGoal(ctx.db, {
457
749
  title: envelope.title,
@@ -467,6 +759,26 @@ export function createGoalManifest(source) {
467
759
  });
468
760
  if (!created.ok)
469
761
  return { ok: false, reason: created.reason, message: created.message };
762
+ /*
763
+ * The owner metric is created only now that the goal exists — a goal the
764
+ * budget refused leaves no definition behind — and a baseline the owner
765
+ * said is the metric's first value, so the next thing they tell buddi is
766
+ * measured against it.
767
+ */
768
+ if (envelope.owner !== undefined) {
769
+ const owned = await createOwnerMetric(ctx.db, envelope.owner.metric);
770
+ source.remember(owned);
771
+ if (envelope.owner.baselineTold) {
772
+ await recordOwnerValue(ctx.db, {
773
+ slug: owned.slug,
774
+ value: envelope.baseline.value,
775
+ at: new Date(envelope.baseline.asOf),
776
+ note: 'baseline, said when the goal was set',
777
+ source: valueSourceOf(ctx),
778
+ conversationId: ctx.conversationId ?? null,
779
+ });
780
+ }
781
+ }
470
782
  // The baseline is also the first check: a goal's history starts where
471
783
  // the owner was told it starts, not at the first sentinel tick.
472
784
  await recordCheck(ctx.db, {
@@ -475,12 +787,16 @@ export function createGoalManifest(source) {
475
787
  asOf: envelope.baseline.readingAsOf === null ? null : new Date(envelope.baseline.readingAsOf),
476
788
  value: envelope.baseline.value,
477
789
  currency: envelope.baseline.currency,
478
- note: 'baseline, measured when the goal was set',
479
- paceNeeded: paceNeeded(created.goal, envelope.baseline.value, ctx.now()),
790
+ note: envelope.target.kind === 'frequency'
791
+ ? 'set; nothing counted yet'
792
+ : envelope.owner?.baselineTold === true
793
+ ? 'baseline, said when the goal was set'
794
+ : 'baseline, measured when the goal was set',
795
+ paceNeeded: envelope.target.kind === 'frequency' ? null : paceNeeded(created.goal, envelope.baseline.value, ctx.now()),
480
796
  });
481
797
  return {
482
798
  ok: true,
483
- goal: renderGoal(created.goal, unitOf(envelope.metric), ctx.timezone, unchecked(created.goal, ctx.now()), [], ctx.now()),
799
+ goal: renderGoal(created.goal, metric.unit, ctx.timezone, unchecked(created.goal, ctx.now()), [], ctx.now()),
484
800
  };
485
801
  },
486
802
  };
@@ -540,6 +856,18 @@ export function createGoalManifest(source) {
540
856
  }
541
857
  }
542
858
  const after = afterOf(input, goal, ctx);
859
+ /*
860
+ * A goal keeps its shape. A level goal's baseline is a number and a
861
+ * frequency goal's is nothing counted; turning one into the other would
862
+ * judge months of history against a baseline that never meant that.
863
+ */
864
+ if ((goal.target.kind === 'frequency') !== (after.target.kind === 'frequency')) {
865
+ return refusal('shape', goal.target.kind === 'frequency'
866
+ ? 'this is a frequency goal and stays one; a level target is a new goal.'
867
+ : 'this goal aims at a level and stays one; a frequency is a new goal on an owner metric.');
868
+ }
869
+ if (after.target.kind === 'frequency')
870
+ return refuseStreaks(after.milestones);
543
871
  // Same rule as `goal.set`: an update that puts the target on the wrong
544
872
  // side of the baseline would settle the goal `met` on the next tick.
545
873
  return refuseDirection({ target: after.target, baseline: goal.baseline }, after.milestones, directionOf(goal.metric), unitOf(goal.metric), goal.currency);
@@ -556,6 +884,7 @@ export function createGoalManifest(source) {
556
884
  // any action exists, and the owner is never asked about a change nobody
557
885
  // is allowed to make.
558
886
  async describe(input, ctx) {
887
+ await source.refresh(ctx.db);
559
888
  const mine = await holderOf(input.id, ctx);
560
889
  if (!mine.ok)
561
890
  throw new Error(mine.message);
@@ -570,6 +899,7 @@ export function createGoalManifest(source) {
570
899
  };
571
900
  },
572
901
  async execute(input, ctx) {
902
+ await source.refresh(ctx.db);
573
903
  const mine = await holderOf(input.id, ctx);
574
904
  if (!mine.ok)
575
905
  throw new Error(mine.message);
@@ -610,6 +940,7 @@ export function createGoalManifest(source) {
610
940
  tier: 'auto',
611
941
  input: closeInput,
612
942
  async execute(input, ctx) {
943
+ await source.refresh(ctx.db);
613
944
  const mine = await holderOf(input.id, ctx);
614
945
  if (!mine.ok)
615
946
  return mine;
@@ -648,19 +979,63 @@ export function createGoalManifest(source) {
648
979
  */
649
980
  function renderGoal(goal, unit, timezone, standing, checks,
650
981
  /** The run's clock. Never `new Date()`: `goal.status` is read under one. */
651
- now) {
982
+ now,
983
+ /** For a goal on an owner metric: its newest values, and a frequency goal's windows. */
984
+ owner = null) {
652
985
  const currency = goal.currency;
653
986
  const latest = standing.latest;
654
987
  const value = latest?.value ?? null;
655
988
  const projected = standing.projected;
656
989
  void now;
990
+ const ownerFields = owner === null
991
+ ? {}
992
+ : {
993
+ values: owner.values.slice(0, STATUS_VALUES).map((v) => ({
994
+ asOf: v.asOf.toISOString(),
995
+ value: v.value,
996
+ valueFormatted: formatValue(v.value, unit, null),
997
+ note: v.note,
998
+ source: v.source,
999
+ })),
1000
+ };
1001
+ const frequency = owner?.frequency ?? null;
1002
+ if (goal.target.kind === 'frequency' && frequency !== null) {
1003
+ /*
1004
+ * A frequency goal's numbers are windows, not a level: the value is the
1005
+ * count so far in this window, the pace is what is left to do in it,
1006
+ * and the verdict is the last window that closed.
1007
+ */
1008
+ const last = frequency.lastClosed;
1009
+ const onTrackNow = last === null || last.state === 'partial' ? null : last.state === 'met';
1010
+ return {
1011
+ ...renderGoal({ ...goal }, unit, timezone, standing, checks, now),
1012
+ ...ownerFields,
1013
+ value: frequency.current?.count ?? null,
1014
+ valueFormatted: frequencyNow(goal, frequency),
1015
+ progress: null,
1016
+ paceNeeded: frequency.toGo,
1017
+ paceNeededInWords: frequency.toGo === null ? null : frequency.toGo === 0 ? 'done this ' + goal.target.per : `${frequency.toGo} more this ${goal.target.per}`,
1018
+ projected: null,
1019
+ onTrack: onTrackNow,
1020
+ verdict: onTrackNow === null ? 'no-projection' : onTrackNow ? 'on-track' : 'off-track',
1021
+ streak: frequency.streak,
1022
+ windows: frequency.windows.slice(-STATUS_WINDOWS).map((w) => ({
1023
+ start: w.start,
1024
+ end: w.end,
1025
+ count: w.count,
1026
+ state: w.state,
1027
+ })),
1028
+ };
1029
+ }
657
1030
  return {
1031
+ ...ownerFields,
658
1032
  id: goal.id,
659
1033
  title: goal.title,
660
1034
  agentId: goal.agentId,
661
1035
  metric: goal.metric,
662
1036
  params: goal.params,
663
- unit,
1037
+ unit: baseUnit(unit),
1038
+ unitLabel: unitLabelOf(unit),
664
1039
  target: goal.target,
665
1040
  targetValue: targetValue(goal),
666
1041
  baseline: { value: goal.baseline.value, asOf: goal.baseline.asOf.toISOString() },
@@ -705,6 +1080,7 @@ export function createGoalManifest(source) {
705
1080
  tier: 'auto',
706
1081
  input: statusInput,
707
1082
  async execute(input, ctx) {
1083
+ await source.refresh(ctx.db);
708
1084
  const goals = input.id === undefined
709
1085
  ? await listGoals(ctx.db, { agentId: ctx.agentId ?? '', limit: 50 })
710
1086
  : await (async () => {
@@ -735,7 +1111,13 @@ export function createGoalManifest(source) {
735
1111
  */
736
1112
  const printed = await recentChecks(ctx.db, goal.id, 4);
737
1113
  const standing = standingOf(goal, directionOf(goal.metric), await measuredChecks(ctx.db, goal.id, STANDING_CHECKS), ctx.now());
738
- out.push(renderGoal(goal, unitOf(goal.metric), ctx.timezone, standing, printed, ctx.now()));
1114
+ const ownerInfo = ownerSlugOf(goal.metric) !== null
1115
+ ? {
1116
+ values: await goalOwnerValues(ctx.db, goal),
1117
+ frequency: await frequencyOf(ctx.db, goal, ctx.now(), ctx.timezone),
1118
+ }
1119
+ : null;
1120
+ out.push(renderGoal(goal, unitOf(goal.metric), ctx.timezone, standing, printed, ctx.now(), ownerInfo));
739
1121
  if (goals.length === 1) {
740
1122
  /*
741
1123
  * The chart is the goal's *history*, not its last four looks: a
@@ -743,12 +1125,147 @@ export function createGoalManifest(source) {
743
1125
  * milestone crossed in week two would have no event on it. Its own
744
1126
  * read, so the printed rows stay four.
745
1127
  */
746
- chart = chartOf(goal, unitOf(goal.metric), directionOf(goal.metric), await recentChecks(ctx.db, goal.id, CHART_CHECKS), ctx.timezone);
1128
+ chart = chartOf(goal, unitOf(goal.metric), directionOf(goal.metric), await recentChecks(ctx.db, goal.id, CHART_CHECKS), ctx.timezone, ownerInfo);
747
1129
  }
748
1130
  }
749
1131
  return chart === null ? { goals: out } : { goals: out, chart };
750
1132
  },
751
1133
  };
1134
+ /**
1135
+ * Where a goal stands, in one sentence: what `goal.record` answers with.
1136
+ *
1137
+ * Read off `standingOf` like every other surface, with the value just said
1138
+ * stood in front of the measured checks — it is not a check yet, and the
1139
+ * owner wants to know what it means now, not after the next tick.
1140
+ */
1141
+ function paceSentence(goal, standing, timezone) {
1142
+ const unit = unitOf(goal.metric);
1143
+ const direction = directionOf(goal.metric);
1144
+ const value = standing.latest?.value ?? null;
1145
+ const target = formatValue(targetValue(goal), unit, goal.currency);
1146
+ const by = localDateString(goal.deadline, timezone);
1147
+ const pct = standing.progress === null ? '' : `, ${Math.round(standing.progress * 100)}% of the way`;
1148
+ const pace = standing.paceNeeded === null
1149
+ ? 'the deadline has passed'
1150
+ : `${formatPace(standing.paceNeeded, unit, direction, goal.currency)} from here reaches ${target} by ${by}`;
1151
+ return `${goal.title}: ${formatValue(value, unit, goal.currency)} now${pct}; ${pace}.`;
1152
+ }
1153
+ const record = {
1154
+ name: 'goal.record',
1155
+ description: 'Write down a number the owner just told you for a metric they report ("285 this morning", "ran today" ' +
1156
+ 'is 1). Name the goal or the metric, not whose goal it is — any agent may record, so the owner can say it ' +
1157
+ 'to whoever is listening. Answers with where the goal stands now, in one sentence you can repeat. A value ' +
1158
+ 'far from the last one is refused with a sentence: ask the owner, then record again with confirmed: true.',
1159
+ tier: 'auto',
1160
+ input: recordInput,
1161
+ async execute(input, ctx) {
1162
+ await source.refresh(ctx.db);
1163
+ if ((input.goal === undefined) === (input.metric === undefined)) {
1164
+ return refusal('name-one', 'name the goal or the metric, exactly one of them');
1165
+ }
1166
+ /*
1167
+ * Which series. A goal names its metric; a metric may be named as
1168
+ * `owner.weight` or just `weight`. Either way it has to be one the owner
1169
+ * reports: a plugin's number is measured, and typing over it would be a
1170
+ * second, disagreeing history.
1171
+ */
1172
+ let goals = [];
1173
+ let metricId;
1174
+ if (input.goal !== undefined) {
1175
+ const goal = await getGoal(ctx.db, input.goal).catch(() => null);
1176
+ if (goal === null)
1177
+ return refusal('not-found', `no goal ${input.goal}`);
1178
+ metricId = goal.metric;
1179
+ goals = [goal];
1180
+ }
1181
+ else {
1182
+ const raw = input.metric.trim();
1183
+ metricId = raw.includes('.') ? raw : ownerMetricId(raw);
1184
+ }
1185
+ const metric = source.metric(metricId);
1186
+ if (!isOwnerMetric(metric)) {
1187
+ return refusal('not-owner-metric', metric === undefined
1188
+ ? `no metric "${metricId}" is kept here. goal.metrics lists the ones the owner reports (source "owner").`
1189
+ : `${metricId} is measured by ${metric.plugin}; there is nothing to record by hand.`);
1190
+ }
1191
+ const slug = ownerSlugOf(metric.id);
1192
+ if (input.goal === undefined) {
1193
+ goals = (await listGoals(ctx.db, { openOnly: true, limit: MAX_OPEN_GOALS })).filter((goal) => goal.metric === metric.id);
1194
+ }
1195
+ const now = ctx.now();
1196
+ let asOf = now;
1197
+ if (input.asOf !== undefined) {
1198
+ const when = parseReminderWhen(input.asOf, ctx.timezone);
1199
+ if (!when.ok)
1200
+ return refusal('invalid-as-of', when.message);
1201
+ asOf = when.at;
1202
+ // "This morning" written as today's date lands at 09:00; before nine
1203
+ // that is a few hours ahead of the clock, and it still means today.
1204
+ if (asOf.getTime() > now.getTime()) {
1205
+ if (localDateString(asOf, ctx.timezone) !== localDateString(now, ctx.timezone)) {
1206
+ return refusal('as-of-future', `${input.asOf} has not happened yet; a value is true of now or of the past`);
1207
+ }
1208
+ asOf = now;
1209
+ }
1210
+ }
1211
+ const unit = unitOf(metric.id);
1212
+ if (input.confirmed !== true) {
1213
+ const last = await latestOwnerValue(ctx.db, slug);
1214
+ const outside = refuseOutsideBand(last?.value ?? null, input.value, (n) => formatValue(n, unit, null));
1215
+ if (outside !== null)
1216
+ return refusal('confirm', outside);
1217
+ }
1218
+ const conversationId = ctx.conversationId !== undefined && UUID.test(ctx.conversationId) ? ctx.conversationId : null;
1219
+ const saved = await recordOwnerValue(ctx.db, {
1220
+ slug,
1221
+ value: input.value,
1222
+ at: now,
1223
+ asOf,
1224
+ note: input.note ?? null,
1225
+ source: valueSourceOf(ctx),
1226
+ conversationId,
1227
+ });
1228
+ const lines = [];
1229
+ for (const goal of goals) {
1230
+ if (goal.state !== 'open')
1231
+ continue;
1232
+ const frequency = await frequencyOf(ctx.db, goal, now, ctx.timezone);
1233
+ if (frequency !== null) {
1234
+ // The value is saved, so the windows already count it.
1235
+ lines.push(`${goal.title}: ${frequencyNow(goal, frequency)}.`);
1236
+ continue;
1237
+ }
1238
+ const pending = {
1239
+ id: '',
1240
+ goalId: goal.id,
1241
+ at: now,
1242
+ asOf,
1243
+ value: input.value,
1244
+ currency: null,
1245
+ note: null,
1246
+ onTrack: null,
1247
+ paceNeeded: null,
1248
+ projected: null,
1249
+ };
1250
+ const standing = standingOf(goal, directionOf(goal.metric), [pending, ...(await measuredChecks(ctx.db, goal.id, STANDING_CHECKS))], now);
1251
+ lines.push(paceSentence(goal, standing, ctx.timezone));
1252
+ }
1253
+ return {
1254
+ ok: true,
1255
+ recorded: {
1256
+ metric: metric.id,
1257
+ value: saved.value,
1258
+ valueFormatted: formatValue(saved.value, unit, null),
1259
+ asOf: saved.asOf.toISOString(),
1260
+ source: saved.source,
1261
+ },
1262
+ // One sentence per open goal on this metric; usually exactly one.
1263
+ pace: lines.length === 0
1264
+ ? `Recorded ${formatValue(saved.value, unit, null)}; no open goal watches ${metric.id}.`
1265
+ : lines.join(' '),
1266
+ };
1267
+ },
1268
+ };
752
1269
  const list = {
753
1270
  name: 'goal.list',
754
1271
  description: 'Every open goal in this installation, whoever holds it, one line each. Reading is open to any agent — ' +
@@ -757,6 +1274,7 @@ export function createGoalManifest(source) {
757
1274
  tier: 'auto',
758
1275
  input: z.object({}).strict(),
759
1276
  async execute(_input, ctx) {
1277
+ await source.refresh(ctx.db);
760
1278
  const goals = await listGoals(ctx.db, { openOnly: true, limit: MAX_OPEN_GOALS });
761
1279
  const out = [];
762
1280
  for (const goal of goals) {
@@ -780,7 +1298,7 @@ export function createGoalManifest(source) {
780
1298
  // core concept and this manifest only exposes it to a model.
781
1299
  schema: 'core',
782
1300
  migrationsDir: '',
783
- tools: [metrics, set, update, close, status, list, createGoalOwnerClose()],
1301
+ tools: [metrics, set, update, close, status, list, record, createGoalOwnerClose()],
784
1302
  sentinels: [createGoalsSentinel(source)],
785
1303
  // Where a goal shows (§7): a block on Home, a rail page, and one way of
786
1304
  // drawing `goal.status` on the canvas. All three are in `goals-page.ts`;
@@ -794,12 +1312,37 @@ export function createGoalManifest(source) {
794
1312
  /* ------------------------------------------------------------------ *
795
1313
  * The watcher
796
1314
  * ------------------------------------------------------------------ */
1315
+ /**
1316
+ * How long an off-track goal stays quiet after it woke its holder: one
1317
+ * cadence. Whole days, not the four-hour-early "due" margin — the margin keeps
1318
+ * checks from drifting; this keeps the same news from arriving twice a week.
1319
+ */
1320
+ export function offTrackCooldownMs(cadence) {
1321
+ return (cadence === 'daily' ? 1 : 7) * 24 * 60 * 60_000;
1322
+ }
797
1323
  /** Is this goal's cadence due again? See `DAILY_DUE_MS` for the margin. */
798
1324
  export function cadenceDue(cadence, lastAt, now) {
799
1325
  if (lastAt === null)
800
1326
  return true;
801
1327
  return now.getTime() - lastAt.getTime() >= (cadence === 'daily' ? DAILY_DUE_MS : WEEKLY_DUE_MS);
802
1328
  }
1329
+ /**
1330
+ * Which cadence window an owner metric has gone quiet into, or null.
1331
+ *
1332
+ * Windows are counted from `since` — the newest value, or when the goal was
1333
+ * set — one cadence at a time: a weekly goal last told on a Monday morning is
1334
+ * stale from the next Monday morning, and again from the one after. The label
1335
+ * is the owner's day the window began, which is what the finding key and the
1336
+ * notification's dedupe key carry, so each window is said at most once.
1337
+ */
1338
+ export function staleWindow(since, now, cadence, timezone) {
1339
+ const length = cadenceMs(cadence);
1340
+ const elapsed = now.getTime() - since.getTime();
1341
+ if (elapsed < length)
1342
+ return null;
1343
+ const start = new Date(since.getTime() + Math.floor(elapsed / length) * length);
1344
+ return localDateString(start, timezone);
1345
+ }
803
1346
  /**
804
1347
  * The `core.goals` watcher.
805
1348
  *
@@ -813,7 +1356,123 @@ export function cadenceDue(cadence, lastAt, now) {
813
1356
  * and a settled goal is no longer open, so every key under it stops being
814
1357
  * returned on the next tick and resolves. The wake it raised has already gone.
815
1358
  */
816
- export function createGoalsSentinel(source) {
1359
+ export function createGoalsSentinel(base) {
1360
+ const source = asOwnerSource(base);
1361
+ /**
1362
+ * One tick of a frequency goal: its windows, a check on its cadence, and
1363
+ * the three things worth saying — a window that closed short, a streak
1364
+ * milestone, the deadline.
1365
+ *
1366
+ * Each is keyed so it is said once. The short window's key carries the
1367
+ * window, and it is returned only while that window is the newest closed
1368
+ * one; a streak milestone is returned while the window that reached it is
1369
+ * the newest closed one, and never again once raised for another. There is
1370
+ * no staleness line: a week with nothing said is a short week, not silence.
1371
+ */
1372
+ async function frequencyTick(goal, ctx, now) {
1373
+ if (goal.target.kind !== 'frequency')
1374
+ return [];
1375
+ const { count, per } = goal.target;
1376
+ const unit = valueUnitOf(source, goal.metric);
1377
+ const standing = (await frequencyOf(ctx.db, goal, now, ctx.timezone));
1378
+ const lastClosed = standing.lastClosed;
1379
+ const verdictOf = (w) => w === null || w.state === 'partial' ? null : w.state === 'met';
1380
+ let checks = await recentChecks(ctx.db, goal.id, 4);
1381
+ const lastAt = checks[0]?.at ?? null;
1382
+ const pastDeadline = now.getTime() >= goal.deadline.getTime();
1383
+ const deadlineUnmeasured = pastDeadline && (lastAt === null || lastAt.getTime() < goal.deadline.getTime());
1384
+ if (deadlineUnmeasured || cadenceDue(goal.cadence, lastAt, now)) {
1385
+ // The record of the tally as it stood: the count this window, the
1386
+ // verdict of the last closed one, what is left to do.
1387
+ await recordCheck(ctx.db, {
1388
+ goalId: goal.id,
1389
+ at: now,
1390
+ value: standing.current?.count ?? lastClosed?.count ?? 0,
1391
+ note: frequencyNow(goal, standing),
1392
+ onTrack: verdictOf(lastClosed),
1393
+ paceNeeded: standing.toGo,
1394
+ });
1395
+ checks = await recentChecks(ctx.db, goal.id, 4);
1396
+ }
1397
+ const recent = standing.windows.slice(-6);
1398
+ const detail = (lead) => [
1399
+ lead,
1400
+ '',
1401
+ goalLine(goal, unit, checks[0] ?? null, ctx.timezone),
1402
+ '',
1403
+ `The last ${recent.length} ${per}s:`,
1404
+ ...recent.map((w) => ` ${per} of ${w.start}: ${w.count} of ${count} · ${w.state}`),
1405
+ '',
1406
+ GOAL_WAKE_INSTRUCTION,
1407
+ ].join('\n');
1408
+ const data = {
1409
+ goalId: goal.id,
1410
+ metric: goal.metric,
1411
+ target: { count, per },
1412
+ deadline: goal.deadline.toISOString(),
1413
+ streak: standing.streak,
1414
+ windows: recent.map((w) => ({ start: w.start, count: w.count, state: w.state })),
1415
+ };
1416
+ const base = { agentId: goal.agentId, data };
1417
+ const out = [];
1418
+ // The deadline: settled by the windows, met when more met than fell short.
1419
+ if (pastDeadline) {
1420
+ const verdict = frequencySettles(standing);
1421
+ await settleGoal(ctx.db, goal.id, verdict, verdict === 'met' ? 'more windows met the count than fell short' : 'more windows fell short than met the count', now);
1422
+ out.push(verdict === 'met'
1423
+ ? {
1424
+ ...base,
1425
+ key: goalKey(goal.id, 'target-reached'),
1426
+ severity: 'info',
1427
+ wake: true,
1428
+ title: `Goal reached: ${goal.title}`,
1429
+ detail: detail(`${standing.met} ${per}s met the count and ${standing.short} fell short.`),
1430
+ }
1431
+ : {
1432
+ ...base,
1433
+ key: goalKey(goal.id, 'deadline-passed'),
1434
+ severity: 'urgent',
1435
+ title: `Goal deadline passed: ${goal.title}`,
1436
+ detail: detail(`The deadline was ${localDateString(goal.deadline, ctx.timezone)}: ${standing.met} ${per}s met ` +
1437
+ `the count and ${standing.short} fell short.`),
1438
+ });
1439
+ return out;
1440
+ }
1441
+ // Drift: the newest closed window fell short. Said once, for that window.
1442
+ if (lastClosed?.state === 'short') {
1443
+ out.push({
1444
+ ...base,
1445
+ key: goalKey(goal.id, `short.${lastClosed.start}`),
1446
+ severity: 'info',
1447
+ wake: true,
1448
+ title: `Short last ${per}: ${goal.title}`,
1449
+ detail: detail(`${lastClosed.count} of ${count} in the ${per} of ${lastClosed.start}.`),
1450
+ });
1451
+ }
1452
+ // Streaks: each milestone once, ever, for the window that reached it.
1453
+ if (lastClosed?.state === 'met' && goal.milestones.length > 0) {
1454
+ const { rows } = await ctx.db.query(`select key, data->>'window' as window from core.sentinel_findings
1455
+ where sentinel_id = $1 and key like $2`, [GOALS_SENTINEL_ID, `goal.${goal.id}.milestone.%`]);
1456
+ const raisedFor = new Map(rows.map((r) => [r.key, r.window]));
1457
+ for (const milestone of goal.milestones) {
1458
+ if (standing.streak < milestone)
1459
+ continue;
1460
+ const key = goalKey(goal.id, `milestone.${milestone}`);
1461
+ if (raisedFor.has(key) && raisedFor.get(key) !== lastClosed.start)
1462
+ continue;
1463
+ out.push({
1464
+ ...base,
1465
+ data: { ...data, window: lastClosed.start },
1466
+ key,
1467
+ severity: 'info',
1468
+ wake: true,
1469
+ title: `Milestone on ${goal.title}`,
1470
+ detail: detail(`${milestone} ${per}s in a row at ${frequencyWords(goal.target)}.`),
1471
+ });
1472
+ }
1473
+ }
1474
+ return out;
1475
+ }
817
1476
  return {
818
1477
  id: GOALS_SENTINEL_ID,
819
1478
  description: 'Measures every open goal on its cadence and says when one is off track, past a milestone, out of time or done.',
@@ -821,11 +1480,17 @@ export function createGoalsSentinel(source) {
821
1480
  async run(ctx) {
822
1481
  const now = ctx.now();
823
1482
  const findings = [];
1483
+ await source.refresh(ctx.db);
824
1484
  const goals = await listGoals(ctx.db, { openOnly: true, limit: MAX_OPEN_GOALS });
825
1485
  for (const goal of goals) {
826
1486
  const metric = source.metric(goal.metric);
827
- const unit = metric?.unit ?? 'number';
1487
+ const unit = valueUnitOf(source, goal.metric);
828
1488
  const direction = metric?.direction ?? 'down';
1489
+ const owned = isOwnerMetric(metric);
1490
+ if (goal.target.kind === 'frequency') {
1491
+ findings.push(...(await frequencyTick(goal, ctx, now)));
1492
+ continue;
1493
+ }
829
1494
  const currency = goal.currency;
830
1495
  /*
831
1496
  * What was true *before* this tick's check, which is what "newly
@@ -845,44 +1510,62 @@ export function createGoalsSentinel(source) {
845
1510
  const lastAt = checks[0]?.at ?? null;
846
1511
  const deadlineUnmeasured = pastDeadline && (lastAt === null || lastAt.getTime() < goal.deadline.getTime());
847
1512
  if (deadlineUnmeasured || cadenceDue(goal.cadence, lastAt, now)) {
848
- const reading = await measureMetricResult(source, goal.metric, goal.params, goalToolContext(goal, ctx));
1513
+ // An owner metric is read against *this goal's* cadence: "older than
1514
+ // two cadences" is two weeks for a weekly goal, two days for a daily one.
1515
+ const reading = await measureMetricResult(source, goal.metric, owned ? { ...goal.params, cadence: goal.cadence } : goal.params, goalToolContext(goal, ctx));
849
1516
  const value = reading.ok ? reading.reading.value : null;
850
1517
  /*
851
- * The arithmetic of the row about to be written, over the same
852
- * window every other surface reads — with this tick's number stood
853
- * in front of it, because it is not a row yet. `standingOf` is what
854
- * the page and `goal.status` call afterwards, so the number stored
855
- * here is the number they will read back.
1518
+ * An owner metric is only news when the owner said something new.
1519
+ * Re-recording last week's number under this week's date would draw
1520
+ * a flat line the owner never reported and bend the projection
1521
+ * toward it, so a cadence with no new value records nothing — the
1522
+ * staleness finding below is what says so. The deadline still gets
1523
+ * its row, so the verdict is about a number taken at or after it.
856
1524
  */
857
- const before = await measuredChecks(ctx.db, goal.id, STANDING_CHECKS);
858
- const pending = {
859
- id: '',
860
- goalId: goal.id,
861
- at: now,
862
- asOf: null,
863
- value,
864
- currency: null,
865
- note: null,
866
- onTrack: null,
867
- paceNeeded: null,
868
- projected: null,
869
- };
870
- const prospective = standingOf(goal, direction, [pending, ...before], now);
871
- await recordCheck(ctx.db, {
872
- goalId: goal.id,
873
- at: now,
874
- asOf: reading.ok ? (reading.reading.asOf ?? null) : null,
875
- value,
876
- currency: reading.ok ? (reading.reading.currency ?? null) : null,
877
- // The reason it could not be measured is the check's note, so
878
- // "the plugin is gone" and "the bank has not synced" stay
879
- // different facts a week later.
880
- note: reading.ok ? (reading.reading.note ?? null) : (reading.note ?? null),
881
- onTrack: prospective.onTrack,
882
- paceNeeded: value === null ? null : prospective.paceNeeded,
883
- projected: prospective.projected,
884
- });
885
- checks = await recentChecks(ctx.db, goal.id, 4);
1525
+ const lastSeen = checks[0] === undefined ? null : (checks[0].asOf ?? checks[0].at);
1526
+ const nothingNew = owned &&
1527
+ !deadlineUnmeasured &&
1528
+ reading.ok &&
1529
+ lastSeen !== null &&
1530
+ (reading.reading.asOf?.getTime() ?? 0) <= lastSeen.getTime();
1531
+ if (!nothingNew) {
1532
+ /*
1533
+ * The arithmetic of the row about to be written, over the same
1534
+ * window every other surface reads — with this tick's number stood
1535
+ * in front of it, because it is not a row yet. `standingOf` is what
1536
+ * the page and `goal.status` call afterwards, so the number stored
1537
+ * here is the number they will read back.
1538
+ */
1539
+ const before = await measuredChecks(ctx.db, goal.id, STANDING_CHECKS);
1540
+ const pending = {
1541
+ id: '',
1542
+ goalId: goal.id,
1543
+ at: now,
1544
+ asOf: null,
1545
+ value,
1546
+ currency: null,
1547
+ note: null,
1548
+ onTrack: null,
1549
+ paceNeeded: null,
1550
+ projected: null,
1551
+ };
1552
+ const prospective = standingOf(goal, direction, [pending, ...before], now);
1553
+ await recordCheck(ctx.db, {
1554
+ goalId: goal.id,
1555
+ at: now,
1556
+ asOf: reading.ok ? (reading.reading.asOf ?? null) : null,
1557
+ value,
1558
+ currency: reading.ok ? (reading.reading.currency ?? null) : null,
1559
+ // The reason it could not be measured is the check's note, so
1560
+ // "the plugin is gone" and "the bank has not synced" stay
1561
+ // different facts a week later.
1562
+ note: reading.ok ? (reading.reading.note ?? null) : (reading.note ?? null),
1563
+ onTrack: prospective.onTrack,
1564
+ paceNeeded: value === null ? null : prospective.paceNeeded,
1565
+ projected: prospective.projected,
1566
+ });
1567
+ checks = await recentChecks(ctx.db, goal.id, 4);
1568
+ }
886
1569
  }
887
1570
  /*
888
1571
  * Every verdict below is read off *measured* checks, over the same
@@ -993,6 +1676,10 @@ export function createGoalsSentinel(source) {
993
1676
  ...base,
994
1677
  key: goalKey(goal.id, 'off-track'),
995
1678
  severity: 'urgent',
1679
+ // Bound to the goal's cadence: a weekly goal off track wakes its
1680
+ // holder at most once a week for the same drift, a daily one once
1681
+ // a day. The first time still wakes at once.
1682
+ cooldownMs: offTrackCooldownMs(goal.cadence),
996
1683
  title: `Off track: ${goal.title}`,
997
1684
  detail: detail(`Two checks running, the pace of the last four lands at ` +
998
1685
  `${formatValue(newest.projected, unit, currency)} by ` +
@@ -1018,6 +1705,37 @@ export function createGoalsSentinel(source) {
1018
1705
  * in a row, and fire on day four of an outage saying a number that
1019
1706
  * existed yesterday has been missing for months.
1020
1707
  */
1708
+ if (owned) {
1709
+ /*
1710
+ * 6'. For a number the owner reports, silence is not an outage: it is
1711
+ * a week nobody said anything. Counted from the newest value (or
1712
+ * from when the goal was set, if that is later), once per cadence
1713
+ * that passes: the key and the notification's dedupe key carry
1714
+ * the window, so it is said at most once in it, and the line
1715
+ * waits for the end of the day — never `now`. "Not measurable"
1716
+ * is not raised as well; it would be the same silence twice.
1717
+ */
1718
+ const slug = ownerSlugOf(goal.metric);
1719
+ const newest = await latestOwnerValue(ctx.db, slug);
1720
+ const since = new Date(Math.max(newest?.asOf.getTime() ?? 0, goal.baseline.asOf.getTime()));
1721
+ const window = staleWindow(since, now, goal.cadence, ctx.timezone);
1722
+ if (window !== null) {
1723
+ const label = isOwnerMetric(metric) ? metric.label.toLowerCase() : goal.metric;
1724
+ const period = goal.cadence === 'daily' ? 'today' : 'this week';
1725
+ findings.push({
1726
+ ...base,
1727
+ key: goalKey(goal.id, `stale.${window}`),
1728
+ severity: 'info',
1729
+ wake: true,
1730
+ notify: { urgency: 'today', dedupeKey: `goal:${goal.id}:stale:${window}` },
1731
+ title: `Not told ${period}: ${goal.title}`,
1732
+ detail: detail(`The owner has not told buddi their ${label} since ${localDateString(since, ctx.timezone)}. ` +
1733
+ `Say one line and nothing more, like "You have not told me your ${label} ${period}." ` +
1734
+ 'When they answer, record it with goal.record.'),
1735
+ });
1736
+ }
1737
+ continue;
1738
+ }
1021
1739
  const lastMeasuredAt = latest?.at ?? goal.baseline.asOf;
1022
1740
  if (now.getTime() - lastMeasuredAt.getTime() >= NOT_MEASURABLE_MS) {
1023
1741
  findings.push({