hypervibes 0.0.0-stage → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (362) hide show
  1. package/.build/client/assets/_authenticated-C87LKvH0.js +1 -0
  2. package/.build/client/assets/_authenticated.account-D-nTh_fC.js +1 -0
  3. package/.build/client/assets/_authenticated.agents-HwcveLyB.js +1 -0
  4. package/.build/client/assets/_authenticated.agents._agentId-DuE8Lv62.js +1 -0
  5. package/.build/client/assets/_authenticated.agents._agentId.chart-Bb_XkvQx.js +4 -0
  6. package/.build/client/assets/_authenticated.agents._agentId.chat-ClMMKw_N.js +1 -0
  7. package/.build/client/assets/_authenticated.agents._agentId.index-DNJwBwn9.js +1 -0
  8. package/.build/client/assets/_authenticated.agents._agentId.indicators-BmVjLtRI.js +1 -0
  9. package/.build/client/assets/_authenticated.agents._agentId.indicators_._indicatorId-CMqaaFQf.js +2 -0
  10. package/.build/client/assets/_authenticated.agents._agentId.indicators_._indicatorId-DtgkoNV5.js +6 -0
  11. package/.build/client/assets/_authenticated.agents._agentId.memories-O-4f6a4G.js +1 -0
  12. package/.build/client/assets/_authenticated.agents._agentId.memories_._memoryRef-BUPtUcj3.js +1 -0
  13. package/.build/client/assets/_authenticated.agents._agentId.notifications-2lyhXCSk.js +1 -0
  14. package/.build/client/assets/_authenticated.agents._agentId.notifications_._notificationId-Da3ZVT4v.js +1 -0
  15. package/.build/client/assets/_authenticated.agents._agentId.settings-C5cWHoYw.js +1 -0
  16. package/.build/client/assets/_authenticated.agents._agentId.sub-agents-HwcveLyB.js +1 -0
  17. package/.build/client/assets/_authenticated.agents._agentId.sub-agents._subagentId-CZ4MeRy4.js +1 -0
  18. package/.build/client/assets/_authenticated.agents._agentId.sub-agents._subagentId_.runs._runId-dVbo5kMU.js +3 -0
  19. package/.build/client/assets/_authenticated.agents._agentId.sub-agents.index-DeWTOYJD.js +1 -0
  20. package/.build/client/assets/_authenticated.agents._agentId.sub-agents.new-D02TflCE.js +37 -0
  21. package/.build/client/assets/_authenticated.agents._agentId.transactions-D3a8Glgj.js +1 -0
  22. package/.build/client/assets/_authenticated.agents.index-CEqeDEgP.js +1 -0
  23. package/.build/client/assets/_authenticated.agents.new-DLRWK5Uk.js +1 -0
  24. package/.build/client/assets/_authenticated.providers-D0iPLXra.js +1 -0
  25. package/.build/client/assets/agent-chat-page-C5eYFgxw.js +1 -0
  26. package/.build/client/assets/agent-enablement-control-CUzOp9ce.js +1 -0
  27. package/.build/client/assets/agent-events-DC8eDZf3.js +1 -0
  28. package/.build/client/assets/agent-input-aolvZ47U.js +1 -0
  29. package/.build/client/assets/agent-notifications-page-B0mr-Zz8.js +1 -0
  30. package/.build/client/assets/agent-queries-CNMBImun.js +1 -0
  31. package/.build/client/assets/app-DjmsvbUC.css +2 -0
  32. package/.build/client/assets/artifact-content-COO4VTSZ.js +1 -0
  33. package/.build/client/assets/auth-client-K13li9H1.js +3 -0
  34. package/.build/client/assets/call-DxlGie4_.js +31 -0
  35. package/.build/client/assets/ccip-BgPlVPYP.js +1 -0
  36. package/.build/client/assets/chart-contracts-Cr5SFCbE.js +1 -0
  37. package/.build/client/assets/chart-queries-Bpvrgd0-.js +1 -0
  38. package/.build/client/assets/chat-bubble-CAJkT-vL.js +1 -0
  39. package/.build/client/assets/config-dialog-C9x9gKW9.js +1 -0
  40. package/.build/client/assets/contracts-HhAL7Tt7.js +1 -0
  41. package/.build/client/assets/createServerFn-DfZIp2Fu.js +3 -0
  42. package/.build/client/assets/custom-BeqXuIIS.js +5 -0
  43. package/.build/client/assets/decimal-B0DEG-1V.js +1 -0
  44. package/.build/client/assets/esm-BTb2nBdW.js +1 -0
  45. package/.build/client/assets/identicon-Lb9pS4O1.js +1 -0
  46. package/.build/client/assets/index-B4hvjMmI.js +10 -0
  47. package/.build/client/assets/indicator-input-fields-CtmrfVfS.js +1 -0
  48. package/.build/client/assets/indicator-queries-CzZ6u1Mh.js +1 -0
  49. package/.build/client/assets/indicator-renderer-CfccsIt2.js +1 -0
  50. package/.build/client/assets/instrument-label-BZYlWXR5.js +1 -0
  51. package/.build/client/assets/instrument-picker-DLPpryOm.js +1 -0
  52. package/.build/client/assets/journal-decimal-DBvd_Ibi.js +1 -0
  53. package/.build/client/assets/jsx-runtime-CIazjfqY.js +1 -0
  54. package/.build/client/assets/lazyRouteComponent-CJgUa7_r.js +1 -0
  55. package/.build/client/assets/lightweight-charts.production-BiBiFZSH.js +1 -0
  56. package/.build/client/assets/login-MNgYxCOY.js +3 -0
  57. package/.build/client/assets/memory-badge-BbN-DJQi.js +1 -0
  58. package/.build/client/assets/memory-contracts-Doi_Q1XJ.js +1 -0
  59. package/.build/client/assets/memory-queries-BttL580R.js +1 -0
  60. package/.build/client/assets/model-picker-YCQNNmyw.js +1 -0
  61. package/.build/client/assets/notification-queries-B12DG3aS.js +1 -0
  62. package/.build/client/assets/preload-helper-Czpn1I53.js +1 -0
  63. package/.build/client/assets/provider-logo-Ddb77aBD.js +1 -0
  64. package/.build/client/assets/provider-queries-DxPwhDOU.js +1 -0
  65. package/.build/client/assets/query-nvK_95Jo.js +1 -0
  66. package/.build/client/assets/random-uuid-DJR3ygJS.js +1 -0
  67. package/.build/client/assets/react-dom-C6bOPxes.js +1 -0
  68. package/.build/client/assets/risk-queries-D_6PHwus.js +1 -0
  69. package/.build/client/assets/run-subagent-dialog-B4yQe9T9.js +1 -0
  70. package/.build/client/assets/secp256k1-BbZnIgDP.js +1 -0
  71. package/.build/client/assets/setup-checklist-CUW-K484.js +1 -0
  72. package/.build/client/assets/sha2-hT-Q4pur.js +1 -0
  73. package/.build/client/assets/subagent-enabled-toggle-Bk5wfScg.js +1 -0
  74. package/.build/client/assets/subagent-input-TQke90I1.js +1 -0
  75. package/.build/client/assets/subagent-mutations-DfsDbmFu.js +1 -0
  76. package/.build/client/assets/subagent-queries-DVv5JCfs.js +1 -0
  77. package/.build/client/assets/subagent-run-history-D55Uh5VK.js +1 -0
  78. package/.build/client/assets/toggle-timestamp-B50FTPmA.js +1 -0
  79. package/.build/client/assets/useBaseQuery-Bxruo_mV.js +1 -0
  80. package/.build/client/assets/useMutation-DrF53EQG.js +1 -0
  81. package/.build/client/assets/useQuery-BMSU16jY.js +1 -0
  82. package/.build/client/assets/useRouter-c0I1aURm.js +1 -0
  83. package/.build/client/assets/useSelector-ERD9RroG.js +2 -0
  84. package/.build/client/assets/useSuspenseQuery-vODFwDb8.js +1 -0
  85. package/.build/client/assets/utils-Bpv4vmSW.js +1 -0
  86. package/.build/client/assets/worker-triggers-DtXyn40O.js +1 -0
  87. package/.build/server/assets/_authenticated-Bch0_2QH.js +264 -0
  88. package/.build/server/assets/_authenticated-DGV5cZYE.js +22 -0
  89. package/.build/server/assets/_authenticated.account-6qu6oQ1y.js +685 -0
  90. package/.build/server/assets/_authenticated.agents-DYDnQxli.js +5 -0
  91. package/.build/server/assets/_authenticated.agents._agentId-4HvB3mhd.js +17 -0
  92. package/.build/server/assets/_authenticated.agents._agentId-Cl608F1T.js +260 -0
  93. package/.build/server/assets/_authenticated.agents._agentId-G2jAHjma.js +15 -0
  94. package/.build/server/assets/_authenticated.agents._agentId.chart-BIA8wy73.js +2644 -0
  95. package/.build/server/assets/_authenticated.agents._agentId.chart-D4aj8Qy9.js +22 -0
  96. package/.build/server/assets/_authenticated.agents._agentId.chat-ClsVle4g.js +13 -0
  97. package/.build/server/assets/_authenticated.agents._agentId.chat-JV3FDsTp.js +18 -0
  98. package/.build/server/assets/_authenticated.agents._agentId.index-Ct4kbu2f.js +1044 -0
  99. package/.build/server/assets/_authenticated.agents._agentId.index-DJgrupvW.js +18 -0
  100. package/.build/server/assets/_authenticated.agents._agentId.indicators-DrKd0aP5.js +184 -0
  101. package/.build/server/assets/_authenticated.agents._agentId.indicators-wiVaY8fW.js +18 -0
  102. package/.build/server/assets/_authenticated.agents._agentId.indicators_._indicatorId-C4Ox2R-a.js +25 -0
  103. package/.build/server/assets/_authenticated.agents._agentId.indicators_._indicatorId-D8Q0hCW0.js +489 -0
  104. package/.build/server/assets/_authenticated.agents._agentId.memories-BUTyVLYy.js +42 -0
  105. package/.build/server/assets/_authenticated.agents._agentId.memories-E8-nSEMT.js +362 -0
  106. package/.build/server/assets/_authenticated.agents._agentId.memories_._memoryRef-C-SC6wzq.js +14 -0
  107. package/.build/server/assets/_authenticated.agents._agentId.memories_._memoryRef-ZCrdrc8C.js +490 -0
  108. package/.build/server/assets/_authenticated.agents._agentId.notifications-DQeQqTzh.js +14 -0
  109. package/.build/server/assets/_authenticated.agents._agentId.notifications-DlFyYyHh.js +16 -0
  110. package/.build/server/assets/_authenticated.agents._agentId.notifications_._notificationId-B0RosY_R.js +15 -0
  111. package/.build/server/assets/_authenticated.agents._agentId.notifications_._notificationId-C1NikQ9e.js +13 -0
  112. package/.build/server/assets/_authenticated.agents._agentId.settings-DDwjT3Ui.js +42 -0
  113. package/.build/server/assets/_authenticated.agents._agentId.settings-PEzEvOfo.js +751 -0
  114. package/.build/server/assets/_authenticated.agents._agentId.sub-agents-z34-J3Tu.js +5 -0
  115. package/.build/server/assets/_authenticated.agents._agentId.sub-agents._subagentId-D-6TOi1U.js +1371 -0
  116. package/.build/server/assets/_authenticated.agents._agentId.sub-agents._subagentId-DipzVdnr.js +13 -0
  117. package/.build/server/assets/_authenticated.agents._agentId.sub-agents._subagentId_.runs._runId-BZR3VLV7.js +610 -0
  118. package/.build/server/assets/_authenticated.agents._agentId.sub-agents._subagentId_.runs._runId-DsYJKfGm.js +12 -0
  119. package/.build/server/assets/_authenticated.agents._agentId.sub-agents.index-BF8Cuzcr.js +231 -0
  120. package/.build/server/assets/_authenticated.agents._agentId.sub-agents.index-D2x0qCnt.js +12 -0
  121. package/.build/server/assets/_authenticated.agents._agentId.sub-agents.new-CYm_vuDD.js +13 -0
  122. package/.build/server/assets/_authenticated.agents._agentId.sub-agents.new-CiVShIka.js +497 -0
  123. package/.build/server/assets/_authenticated.agents._agentId.transactions-BC_XfGqw.js +95 -0
  124. package/.build/server/assets/_authenticated.agents._agentId.transactions-WtBW3xmh.js +495 -0
  125. package/.build/server/assets/_authenticated.agents.index-C8zmu8T8.js +78 -0
  126. package/.build/server/assets/_authenticated.agents.new-Bt-tBR47.js +302 -0
  127. package/.build/server/assets/_authenticated.providers-C0pb-ujV.js +672 -0
  128. package/.build/server/assets/_tanstack-start-manifest_v-Baw5u9sk.js +354 -0
  129. package/.build/server/assets/account-XvMwzRXh.js +66 -0
  130. package/.build/server/assets/account-queries-AJJVkLAV.js +32 -0
  131. package/.build/server/assets/agent-chat-page-DChiz8KQ.js +686 -0
  132. package/.build/server/assets/agent-enablement-control-C90f0uIb.js +143 -0
  133. package/.build/server/assets/agent-events-DK9Yz5__.js +217 -0
  134. package/.build/server/assets/agent-notifications-page-BdyKAyds.js +276 -0
  135. package/.build/server/assets/agent-queries-Cmx8RiUx.js +51 -0
  136. package/.build/server/assets/agents-CZq-0mna.js +106 -0
  137. package/.build/server/assets/agents-CfjmtW7A.js +278 -0
  138. package/.build/server/assets/artifact-content-B6uHmPz7.js +35 -0
  139. package/.build/server/assets/artifacts-HtLwHs8q.js +42 -0
  140. package/.build/server/assets/auth-DTikZAVz.js +12 -0
  141. package/.build/server/assets/auth-client-Vb3O0v7P.js +1897 -0
  142. package/.build/server/assets/auth-user-DkLo8ojH.js +108 -0
  143. package/.build/server/assets/chart-queries-Bc3AfsEb.js +109 -0
  144. package/.build/server/assets/charts-C4dKuwTJ.js +130 -0
  145. package/.build/server/assets/chat-bubble-ik3itVbx.js +109 -0
  146. package/.build/server/assets/config-dialog-9gSAjwwT.js +55 -0
  147. package/.build/server/assets/contracts-CG-nZuYJ.js +155 -0
  148. package/.build/server/assets/createServerRpc-DNqhdU4z.js +12 -0
  149. package/.build/server/assets/decimal-BthtZ77w.js +97 -0
  150. package/.build/server/assets/empty-plugin-adapters-MOWBbCMw.js +5 -0
  151. package/.build/server/assets/errors-DaZqUftX.js +151 -0
  152. package/.build/server/assets/gateway-CHkruO_a.js +193 -0
  153. package/.build/server/assets/identicon-CyawcVnx.js +41 -0
  154. package/.build/server/assets/indicator-input-fields-bzuhIh6t.js +75 -0
  155. package/.build/server/assets/indicator-queries-4dY1-LXx.js +171 -0
  156. package/.build/server/assets/indicator-renderer-B2muPS-8.js +395 -0
  157. package/.build/server/assets/indicators-ENxAHN42.js +192 -0
  158. package/.build/server/assets/insight-contracts-B5rmUDfO.js +14 -0
  159. package/.build/server/assets/instrument-label-C-tTGGsD.js +40 -0
  160. package/.build/server/assets/instrument-picker-CRroLwVq.js +381 -0
  161. package/.build/server/assets/journal-9aE-um9f.js +107 -0
  162. package/.build/server/assets/journal-decimal-Bh9EL029.js +66 -0
  163. package/.build/server/assets/login-BsEFUvTX.js +125 -0
  164. package/.build/server/assets/memories-2WF2fW1G.js +100 -0
  165. package/.build/server/assets/memory-badge-DVYLTtRR.js +53 -0
  166. package/.build/server/assets/memory-contracts-DQGN2IKV.js +22 -0
  167. package/.build/server/assets/memory-queries--4RbPRrk.js +92 -0
  168. package/.build/server/assets/model-picker-CA1uYb--.js +225 -0
  169. package/.build/server/assets/notification-queries-0CJUkBt9.js +52 -0
  170. package/.build/server/assets/notifications-Cn7BiCyw.js +72 -0
  171. package/.build/server/assets/provider-logo-BRdbOr9I.js +32 -0
  172. package/.build/server/assets/provider-queries-BaZWLw8J.js +78 -0
  173. package/.build/server/assets/providers-CyoN_jgW.js +89 -0
  174. package/.build/server/assets/risk-Bqrb6hKQ.js +34 -0
  175. package/.build/server/assets/risk-queries-JQpByHT2.js +18 -0
  176. package/.build/server/assets/router-Hkr5TH_I.js +419 -0
  177. package/.build/server/assets/run-subagent-dialog-C7iUOZa-.js +59 -0
  178. package/.build/server/assets/setup-DqMlwgNr.js +30 -0
  179. package/.build/server/assets/setup-checklist-BICTmBo2.js +60 -0
  180. package/.build/server/assets/start-C-GPoWRt.js +4 -0
  181. package/.build/server/assets/subagent-enabled-toggle-Djte00oo.js +22 -0
  182. package/.build/server/assets/subagent-input-BHQKs2cW.js +57 -0
  183. package/.build/server/assets/subagent-mutations-dUo3B_SG.js +32 -0
  184. package/.build/server/assets/subagent-queries-sAwBjFxC.js +146 -0
  185. package/.build/server/assets/subagent-run-history-C_xmucO-.js +186 -0
  186. package/.build/server/assets/subagents-CIoilC66.js +180 -0
  187. package/.build/server/assets/toggle-timestamp-CToUVn67.js +28 -0
  188. package/.build/server/assets/trading-contracts-zWq1Hxnb.js +26 -0
  189. package/.build/server/assets/validation-BX-krL0i.js +33 -0
  190. package/.build/server/assets/worker-triggers-CJF5HWzR.js +51 -0
  191. package/.build/server/server.js +7501 -0
  192. package/LICENSE +661 -0
  193. package/README.md +145 -2
  194. package/bin/hypervibes.js +24 -0
  195. package/dist/agents/agent-enablement.js +10 -0
  196. package/dist/agents/agent-input.js +38 -0
  197. package/dist/agents/agent-service.js +1004 -0
  198. package/dist/agents/cancel-task-scope.js +52 -0
  199. package/dist/agents/candle-intervals.js +1 -0
  200. package/dist/agents/default-subagents.js +36 -0
  201. package/dist/agents/execution-context-contracts.js +1 -0
  202. package/dist/agents/execution-context.js +206 -0
  203. package/dist/agents/execution-warnings.js +33 -0
  204. package/dist/agents/orchestrator-insights.js +14 -0
  205. package/dist/agents/run-inspection.js +99 -0
  206. package/dist/agents/subagent-dashboard.js +1 -0
  207. package/dist/agents/subagent-extension.js +156 -0
  208. package/dist/agents/subagent-history.js +5 -0
  209. package/dist/agents/subagent-input.js +73 -0
  210. package/dist/agents/subagent-service.js +1177 -0
  211. package/dist/agents/subagent-templates.js +20 -0
  212. package/dist/agents/template-manifest.js +34 -0
  213. package/dist/agents/templates/basic-analysis.json +40 -0
  214. package/dist/agents/templates/basic-review.json +48 -0
  215. package/dist/agents/templates/basic-trading.json +45 -0
  216. package/dist/agents/templates/indicator-developer.json +16 -0
  217. package/dist/agents/time-guidance.js +29 -0
  218. package/dist/agents/worker-events.js +18 -0
  219. package/dist/agents/worker-inbox.js +28 -0
  220. package/dist/agents/worker-triggers.js +23 -0
  221. package/dist/auth-options.js +98 -0
  222. package/dist/auth-user.js +15 -0
  223. package/dist/auth.js +28 -0
  224. package/dist/cache/svg-logo-cache.js +155 -0
  225. package/dist/canonical.js +13 -0
  226. package/dist/charts/chart-service.js +236 -0
  227. package/dist/charts/contracts.js +118 -0
  228. package/dist/charts/durable-extension.js +27 -0
  229. package/dist/charts/trading-contracts.js +14 -0
  230. package/dist/config.js +85 -0
  231. package/dist/db/agent-schema.js +26 -0
  232. package/dist/db/auth-schema.js +122 -0
  233. package/dist/db/database.js +31 -0
  234. package/dist/db/exchange-schema.js +23 -0
  235. package/dist/db/gateway-schema.js +24 -0
  236. package/dist/db/indicator-schema.js +19 -0
  237. package/dist/db/migrations/20261015221717_auth/migration.sql +62 -0
  238. package/dist/db/migrations/20261015231224_agents/migration.sql +35 -0
  239. package/dist/db/migrations/20261016040641_provider_connections/migration.sql +19 -0
  240. package/dist/db/migrations/20261016061831_trade_journal/migration.sql +48 -0
  241. package/dist/db/migrations/20261016080320_order_execution/migration.sql +62 -0
  242. package/dist/db/migrations/20261016181110_subagents/migration.sql +51 -0
  243. package/dist/db/migrations/20261017222959_research_artifacts/migration.sql +37 -0
  244. package/dist/db/migrations/20261018061531_memories_and_insights/migration.sql +162 -0
  245. package/dist/db/migrations/20261020091156_worker_event_inbox/migration.sql +54 -0
  246. package/dist/db/migrations/20261021051229_chart_workspace/migration.sql +33 -0
  247. package/dist/db/migrations/20261021222116_gateways/migration.sql +15 -0
  248. package/dist/db/migrations/20261022015813_pine_indicators/migration.sql +43 -0
  249. package/dist/db/migrations/20261022101559_notifications/migration.sql +51 -0
  250. package/dist/db/migrations.js +12 -0
  251. package/dist/db/order-provenance-schema.js +17 -0
  252. package/dist/db/order-schema.js +32 -0
  253. package/dist/db/profile-lock.js +12 -0
  254. package/dist/db/provider-schema.js +16 -0
  255. package/dist/db/shared-database.js +128 -0
  256. package/dist/db/subagent-schema.js +79 -0
  257. package/dist/gateway/bridge-service.js +223 -0
  258. package/dist/gateway/gateway-store.js +77 -0
  259. package/dist/gateway/handlers.js +122 -0
  260. package/dist/gateway/telegram/connector.js +115 -0
  261. package/dist/gateway/telegram/transport.js +96 -0
  262. package/dist/hyperliquid/account-info.js +117 -0
  263. package/dist/hyperliquid/account-status.js +64 -0
  264. package/dist/hyperliquid/agent-dashboard.js +56 -0
  265. package/dist/hyperliquid/chart-contracts.js +9 -0
  266. package/dist/hyperliquid/constants.js +25 -0
  267. package/dist/hyperliquid/crypto.js +29 -0
  268. package/dist/hyperliquid/currency-logo.js +7 -0
  269. package/dist/hyperliquid/decimal.js +98 -0
  270. package/dist/hyperliquid/durable-extension.js +39 -0
  271. package/dist/hyperliquid/errors.js +8 -0
  272. package/dist/hyperliquid/exchange.js +118 -0
  273. package/dist/hyperliquid/journal-decimal.js +48 -0
  274. package/dist/hyperliquid/journal-model.js +167 -0
  275. package/dist/hyperliquid/journal-service.js +648 -0
  276. package/dist/hyperliquid/live-dashboard.js +245 -0
  277. package/dist/hyperliquid/logo-cache.js +27 -0
  278. package/dist/hyperliquid/market-charts.js +242 -0
  279. package/dist/hyperliquid/market-snapshot.js +45 -0
  280. package/dist/hyperliquid/orders/order-contracts.js +42 -0
  281. package/dist/hyperliquid/orders/order-math.js +89 -0
  282. package/dist/hyperliquid/orders/order-outcomes.js +74 -0
  283. package/dist/hyperliquid/orders/order-prepare.js +109 -0
  284. package/dist/hyperliquid/orders/order-service.js +432 -0
  285. package/dist/hyperliquid/orders/order-store.js +134 -0
  286. package/dist/hyperliquid/orders/order-tools.js +40 -0
  287. package/dist/hyperliquid/orders/protection.js +35 -0
  288. package/dist/hyperliquid/orders/risk-settings.js +44 -0
  289. package/dist/hyperliquid/orders/trade-admission.js +117 -0
  290. package/dist/hyperliquid/orders/trade-planning.js +60 -0
  291. package/dist/hyperliquid/service.js +529 -0
  292. package/dist/hyperliquid/shared-feeds.js +40 -0
  293. package/dist/hyperliquid/signing.js +89 -0
  294. package/dist/indicators/builtin-catalog.js +59 -0
  295. package/dist/indicators/builtins/adx.pine +22 -0
  296. package/dist/indicators/builtins/atr.pine +11 -0
  297. package/dist/indicators/builtins/bollinger.pine +10 -0
  298. package/dist/indicators/builtins/ema.pine +5 -0
  299. package/dist/indicators/builtins/macd.pine +18 -0
  300. package/dist/indicators/builtins/rsi.pine +18 -0
  301. package/dist/indicators/builtins/sma.pine +5 -0
  302. package/dist/indicators/candle-window.js +36 -0
  303. package/dist/indicators/chart-contracts.js +64 -0
  304. package/dist/indicators/contracts.js +14 -0
  305. package/dist/indicators/durable-extension.js +32 -0
  306. package/dist/indicators/indicator-service.js +300 -0
  307. package/dist/indicators/indicator-store.js +137 -0
  308. package/dist/indicators/normalize.js +149 -0
  309. package/dist/indicators/result-schema.js +79 -0
  310. package/dist/indicators/runtime-wrapper.js +52 -0
  311. package/dist/indicators/runtime.js +201 -0
  312. package/dist/indicators/validation.js +124 -0
  313. package/dist/indicators/worker-queue.js +48 -0
  314. package/dist/initialize-config.js +160 -0
  315. package/dist/main.js +59 -0
  316. package/dist/memory/decision-audit.js +47 -0
  317. package/dist/memory/durable-extension.js +40 -0
  318. package/dist/memory/execution-audit.js +83 -0
  319. package/dist/memory/insight-contracts.js +33 -0
  320. package/dist/memory/insight-service.js +117 -0
  321. package/dist/memory/memory-contracts.js +113 -0
  322. package/dist/memory/memory-service.js +179 -0
  323. package/dist/memory/operations.js +46 -0
  324. package/dist/memory/tool-schemas.js +44 -0
  325. package/dist/memory/validation.js +42 -0
  326. package/dist/notifications/contracts.js +1 -0
  327. package/dist/notifications/delivery.js +11 -0
  328. package/dist/notifications/durable-extension.js +20 -0
  329. package/dist/notifications/notification-service.js +324 -0
  330. package/dist/providers/credential-store.js +86 -0
  331. package/dist/providers/ollama-cloud.js +148 -0
  332. package/dist/providers/provider-logo-url.js +18 -0
  333. package/dist/providers/provider-logo.js +31 -0
  334. package/dist/providers/provider-service.js +295 -0
  335. package/dist/research/artifacts.js +151 -0
  336. package/dist/research/call-queue.js +43 -0
  337. package/dist/research/contracts.js +37 -0
  338. package/dist/research/evidence.js +43 -0
  339. package/dist/research/journal.js +30 -0
  340. package/dist/research/research-service.js +191 -0
  341. package/dist/research/tool-activity.js +49 -0
  342. package/dist/research/tool-response.js +25 -0
  343. package/dist/runtime.js +145 -0
  344. package/dist/web/agent-events.js +418 -0
  345. package/dist/web/app.js +206 -0
  346. package/dist/web/artifact-downloads.js +23 -0
  347. package/dist/web/middleware.js +25 -0
  348. package/dist/web/random-uuid.js +14 -0
  349. package/dist/web/start.js +56 -0
  350. package/package.json +93 -3
  351. package/scripts/check-node.mjs +24 -0
  352. package/static/android-chrome-192x192.png +0 -0
  353. package/static/android-chrome-512x512.png +0 -0
  354. package/static/apple-touch-icon.png +0 -0
  355. package/static/favicon-16x16.png +0 -0
  356. package/static/favicon-32x32.png +0 -0
  357. package/static/favicon.ico +0 -0
  358. package/static/github.svg +3 -0
  359. package/static/hypervibes.png +0 -0
  360. package/static/logo.png +0 -0
  361. package/static/site.webmanifest +1 -0
  362. package/static/telegram.svg +1 -0
@@ -0,0 +1,324 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { AgentError } from "../agents/agent-input.js";
3
+ import { assertActiveActor, requestHash } from "../memory/operations.js";
4
+ import { chunkText } from "../gateway/handlers.js";
5
+ import { NotificationDeliveryError } from "./delivery.js";
6
+ const columns = `id,agent_id AS agentId,category,title,body,severity,source,subagent_id AS subagentId,run_id AS runId,
7
+ conversation_id AS conversationId,order_request_id AS orderRequestId,created_at AS createdAt,delivery,attempts,sent_at AS sentAt,error`;
8
+ function input(value) {
9
+ if (typeof value.title !== "string" || !value.title.trim() || value.title.length > 200)
10
+ throw new AgentError("Notification title must contain 1–200 characters.");
11
+ if (typeof value.body !== "string" || !value.body.trim() || value.body.length > 12000)
12
+ throw new AgentError("Notification body must contain 1–12000 characters.");
13
+ const severity = value.severity ?? "info";
14
+ if (!["info", "warning", "error"].includes(severity))
15
+ throw new AgentError("Invalid notification severity.");
16
+ return { title: value.title.trim(), body: value.body.trim(), severity };
17
+ }
18
+ /** Application-owned inbox and durable Telegram outbox. Never performs network I/O in a SQL transaction. */
19
+ export class NotificationService {
20
+ db;
21
+ origin;
22
+ logger;
23
+ clock;
24
+ sender;
25
+ stopping = false;
26
+ started = false;
27
+ timer;
28
+ running;
29
+ closing;
30
+ shutdown = new AbortController();
31
+ listeners = new Map();
32
+ chatNext = new Map();
33
+ constructor(db, origin, logger, clock = Date.now) {
34
+ this.db = db;
35
+ this.origin = origin;
36
+ this.logger = logger;
37
+ this.clock = clock;
38
+ }
39
+ installSender(sender) { this.sender = sender; }
40
+ async settingsTx(tx, agentId) {
41
+ const agent = await tx.get("SELECT created_at,state FROM hv_agents WHERE id=?", agentId);
42
+ if (!agent)
43
+ throw new AgentError("Agent not found", 404);
44
+ await tx.run("INSERT INTO hv_notification_settings(agent_id,enabled_since,fills_since) VALUES (?,?,?) ON CONFLICT DO NOTHING", agentId, agent.created_at, agent.created_at);
45
+ return (await tx.get("SELECT * FROM hv_notification_settings WHERE agent_id=?", agentId));
46
+ }
47
+ async getSettings(agentId) {
48
+ const row = await this.db.transaction((tx) => this.settingsTx(tx, agentId));
49
+ return { enabled: Boolean(row.enabled), submissions: Boolean(row.submissions), fills: Boolean(row.fills) };
50
+ }
51
+ async saveSettings(agentId, value) {
52
+ if (!value || typeof value !== "object")
53
+ throw new AgentError("Invalid notification settings.");
54
+ const settings = value;
55
+ if ([settings.enabled, settings.submissions, settings.fills].some((v) => typeof v !== "boolean"))
56
+ throw new AgentError("Invalid notification settings.");
57
+ await this.db.transaction(async (tx) => {
58
+ const previous = await this.settingsTx(tx, agentId), now = this.clock();
59
+ await tx.run(`UPDATE hv_notification_settings SET enabled=?,submissions=?,fills=?,enabled_since=?,fills_since=? WHERE agent_id=?`, Number(settings.enabled), Number(settings.submissions), Number(settings.fills), settings.enabled && !previous.enabled ? now : previous.enabled_since, settings.fills && !previous.fills ? now : previous.fills_since, agentId);
60
+ await tx.run(`UPDATE hv_notifications SET delivery='suppressed',error='Notifications disabled before delivery' WHERE agent_id=? AND delivery='pending'
61
+ AND (?=0 OR (category='submission' AND ?=0) OR (category='fill' AND ?=0))`, agentId, Number(settings.enabled), Number(settings.submissions), Number(settings.fills));
62
+ });
63
+ this.changed(agentId);
64
+ return { enabled: settings.enabled, submissions: settings.submissions, fills: settings.fills };
65
+ }
66
+ async queueTx(tx, agentId, eventKey, category, value, provenance) {
67
+ const existing = await tx.get("SELECT id FROM hv_notifications WHERE agent_id=? AND event_key=?", agentId, eventKey);
68
+ if (existing)
69
+ return { status: "queued", id: existing.id };
70
+ const skipped = await tx.get("SELECT 1 FROM hv_notification_operations WHERE agent_id=? AND operation_key=?", agentId, `skipped:${eventKey}`);
71
+ if (skipped)
72
+ return { status: "disabled" };
73
+ const settings = await this.settingsTx(tx, agentId);
74
+ if (!settings.enabled || (category === "submission" && !settings.submissions) || (category === "fill" && !settings.fills)) {
75
+ await tx.run("INSERT INTO hv_notification_operations(agent_id,operation_key,request_hash,result) VALUES (?,?,'',?) ON CONFLICT DO NOTHING", agentId, `skipped:${eventKey}`, JSON.stringify({ status: "disabled" }));
76
+ return { status: "disabled" };
77
+ }
78
+ const agent = await tx.get("SELECT state FROM hv_agents WHERE id=?", agentId);
79
+ if (!agent || ["stopping", "stopped"].includes(agent.state))
80
+ return { status: "disabled" };
81
+ const id = await this.persistTx(tx, agentId, eventKey, category, value, provenance, true);
82
+ return { status: "queued", id };
83
+ }
84
+ async persistTx(tx, agentId, eventKey, category, value, provenance, deliver) {
85
+ const message = input(value), now = this.clock(), id = randomUUID();
86
+ const gateway = deliver ? await tx.get("SELECT chat_id,bot_token_hash FROM hv_gateways WHERE agent_id=? AND type='telegram'", agentId) : undefined;
87
+ const connected = gateway?.chat_id !== null && gateway?.chat_id !== undefined;
88
+ await tx.run(`INSERT INTO hv_notifications(id,agent_id,event_key,category,title,body,severity,source,subagent_id,run_id,conversation_id,order_request_id,created_at,delivery,chat_id,bot_token_hash,next_attempt_at)
89
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`, id, agentId, eventKey, category, message.title, message.body, message.severity, provenance.source, provenance.subagentId ?? null, provenance.runId ?? null, provenance.conversationId ?? null, provenance.orderRequestId ?? null, now, connected ? "pending" : "web_only", gateway?.chat_id ?? null, connected ? gateway.bot_token_hash : null, now);
90
+ return id;
91
+ }
92
+ /** Always records a system event. The caller must call changed(agentId) after the transaction commits. */
93
+ async systemWarningTx(tx, agentId, eventKey, value, provenance) {
94
+ input(value);
95
+ if (!eventKey.trim() || eventKey.length > 200)
96
+ throw new AgentError("Invalid notification event key.");
97
+ const key = `system:${eventKey}`;
98
+ const existing = await tx.get("SELECT id FROM hv_notifications WHERE agent_id=? AND event_key=?", agentId, key);
99
+ if (existing)
100
+ return { id: existing.id, created: false };
101
+ const settings = await this.settingsTx(tx, agentId);
102
+ const agent = await tx.get("SELECT state FROM hv_agents WHERE id=?", agentId);
103
+ const deliver = Boolean(settings.enabled) && !!agent && !["stopping", "stopped"].includes(agent.state);
104
+ const id = await this.persistTx(tx, agentId, key, "message", value, provenance, deliver);
105
+ return { id, created: true };
106
+ }
107
+ /** Trusted backend entry point. The key identifies one logical application event. */
108
+ async send(agentId, eventKey, value) {
109
+ input(value);
110
+ if (!eventKey.trim() || eventKey.length > 200)
111
+ throw new AgentError("Invalid notification event key.");
112
+ const result = await this.db.transaction((tx) => this.queueTx(tx, agentId, `backend:${eventKey}`, "message", value, { source: "Backend" }));
113
+ this.changed(agentId);
114
+ return result;
115
+ }
116
+ async sendFromTool(actor, value, execution) {
117
+ const message = input(value), key = `tool:${actor.toolTaskId}`, hash = requestHash(actor, "send_notification", message);
118
+ const result = await this.db.transaction(async (tx) => {
119
+ const receipt = await tx.get("SELECT request_hash,result FROM hv_notification_operations WHERE agent_id=? AND operation_key=?", actor.agentId, key);
120
+ if (receipt) {
121
+ if (receipt.request_hash !== hash)
122
+ throw new AgentError("Notification operation reused with different arguments or actor", 409);
123
+ return JSON.parse(receipt.result);
124
+ }
125
+ if (actor.toolTaskId === null || actor.conversationId === null || !actor.subagentId)
126
+ throw new AgentError("Unverified notification producer", 403);
127
+ await assertActiveActor(tx, actor);
128
+ await execution.authorize(tx, actor, "send_notification");
129
+ const worker = await tx.get("SELECT name,kind FROM hv_subagents WHERE agent_id=? AND id=?", actor.agentId, actor.subagentId);
130
+ const result = await this.queueTx(tx, actor.agentId, key, "message", message, { source: worker?.name ?? "Agent", subagentId: actor.subagentId, runId: actor.runId, conversationId: actor.conversationId });
131
+ await tx.run("INSERT INTO hv_notification_operations(agent_id,operation_key,request_hash,result) VALUES (?,?,?,?)", actor.agentId, key, hash, JSON.stringify(result));
132
+ return result;
133
+ });
134
+ this.changed(actor.agentId);
135
+ return result;
136
+ }
137
+ /** Runs inside the order outcome transaction; one aggregate receipt per request, plus uncertainty resolution. */
138
+ async orderOutcomeTx(tx, actionId) {
139
+ const request = await tx.get(`SELECT r.* FROM hv_order_requests r JOIN hv_order_actions a ON a.request_id=r.id WHERE a.id=? AND r.kind IN ('submit','close_position')`, actionId);
140
+ if (!request)
141
+ return;
142
+ const actions = await tx.all("SELECT state FROM hv_order_actions WHERE request_id=?", request.id);
143
+ const existing = await tx.get("SELECT title FROM hv_notifications WHERE agent_id=? AND event_key=?", request.agent_id, `order:${request.id}:submission`);
144
+ if (actions.some((a) => a.state === "submitting"))
145
+ return;
146
+ // A crash can leave later legs prepared forever. Still warn immediately about
147
+ // an uncertain attempted leg, without waiting for a model to replay the request.
148
+ if (actions.some((a) => a.state === "prepared") && !actions.some((a) => a.state === "unknown") && !existing)
149
+ return;
150
+ const settings = await this.settingsTx(tx, request.agent_id);
151
+ if (request.created_at < settings.enabled_since)
152
+ return;
153
+ const items = await tx.all(`SELECT i.view FROM hv_order_items i JOIN hv_order_actions a ON a.id=i.action_id WHERE a.request_id=? ORDER BY a.ordinal,i.ordinal`, request.id);
154
+ const views = items.map((row) => JSON.parse(row.view));
155
+ const unknown = views.some((v) => v.commandStatus === "unknown");
156
+ if (existing && (existing.title !== "Order submission outcome unknown" || unknown))
157
+ return;
158
+ if (existing && await tx.get("SELECT 1 FROM hv_notifications WHERE agent_id=? AND event_key=?", request.agent_id, `order:${request.id}:resolved`))
159
+ return;
160
+ const rejected = views.some((v) => v.commandStatus === "rejected" || v.commandStatus === "not_sent");
161
+ const accepted = views.some((v) => ["accepted", "reconciled"].includes(v.commandStatus));
162
+ const title = existing ? "Order submission outcome resolved" : unknown ? "Order submission outcome unknown" : rejected ? accepted ? "Order submission partially accepted" : "Order submission rejected / not sent" : "Orders submitted";
163
+ const body = [`Request: ${request.request_key}`, ...views.map((v) => `${v.symbol} ${v.side} ${v.kind}: ${v.size ?? "unknown"} @ ${v.triggerPrice ?? v.price ?? "market"} — ${v.commandStatus}${v.exchangeOid ? ` (order ${v.exchangeOid})` : ""}${v.error ? `; ${v.error}` : ""}`), unknown ? "Unknown is not failure. Reconcile this request; do not resubmit with a new ID." : "Submission acceptance is not proof of a fill. Fill notifications report actual executions."].join("\n");
164
+ const provenance = await tx.get("SELECT producer_subagent_id AS subagent_id,source_run_id,conversation_id FROM hv_order_request_provenance WHERE request_id=?", request.id);
165
+ const result = await this.queueTx(tx, request.agent_id, `order:${request.id}:${existing ? "resolved" : "submission"}`, "submission", { title, body: body.slice(0, 12000), severity: unknown || (rejected && accepted) ? "warning" : rejected ? "error" : "info" }, { source: "Order execution", subagentId: provenance?.subagent_id, runId: provenance?.source_run_id, conversationId: provenance?.conversation_id, orderRequestId: request.id });
166
+ return result.status === "queued" ? request.agent_id : undefined;
167
+ }
168
+ /** Called only for newly inserted exchange fills, atomically with journal evidence. */
169
+ async fillsTx(tx, account, events) {
170
+ const changed = new Set();
171
+ if (!events.length)
172
+ return [];
173
+ const agents = await tx.all("SELECT id FROM hv_agents WHERE trading_account_address=? AND state NOT IN ('stopping','stopped')", account);
174
+ for (const agent of agents) {
175
+ const settings = await this.settingsTx(tx, agent.id);
176
+ if (!settings.enabled || !settings.fills)
177
+ continue;
178
+ // Never announce old history imports. The persisted floor survives restarts and re-enabling.
179
+ const fresh = events.filter((event) => event.time >= Math.max(settings.enabled_since, settings.fills_since));
180
+ const groups = new Map();
181
+ for (const event of fresh) {
182
+ const raw = event.raw;
183
+ const key = typeof raw?.oid === "number" && Number.isSafeInteger(raw.oid) ? `${event.symbol}:${raw.oid}` : event.id;
184
+ const group = groups.get(key) ?? [];
185
+ group.push(event);
186
+ groups.set(key, group);
187
+ }
188
+ for (const group of groups.values()) {
189
+ group.sort((a, b) => a.time - b.time || a.id.localeCompare(b.id));
190
+ const event = group[0];
191
+ const raw = event.raw;
192
+ const oid = typeof raw?.oid === "number" && Number.isSafeInteger(raw.oid) ? String(raw.oid) : null;
193
+ const cloid = typeof raw?.cloid === "string" ? raw.cloid : null;
194
+ const local = oid || cloid ? await tx.get(`SELECT r.id AS request_id,p.producer_subagent_id,p.source_run_id FROM hv_order_items i JOIN hv_order_actions a ON a.id=i.action_id JOIN hv_order_requests r ON r.id=a.request_id LEFT JOIN hv_order_request_provenance p ON p.request_id=r.id WHERE r.agent_id=? AND r.account=? AND (i.oid=? OR i.cloid=?) LIMIT 1`, agent.id, account, oid, cloid) : undefined;
195
+ const title = `${event.symbol ?? "Account"} order fill${raw?.liquidation ? " (liquidation)" : ""}`;
196
+ const executions = group.slice(0, 30).map((entry) => {
197
+ const data = entry.raw;
198
+ const side = entry.fill?.side ?? data?.side, size = entry.fill?.size ?? data?.sz, price = entry.fill?.price ?? data?.px;
199
+ return `${side === "B" || side === "buy" ? "Buy" : side === "A" || side === "sell" ? "Sell" : "Unknown side"} ${typeof size === "string" ? size : "unknown quantity"} ${entry.symbol ?? ""} @ ${typeof price === "string" ? price : "unknown price"}`;
200
+ });
201
+ const body = `${executions.join("\n")}${group.length > 30 ? `\n${group.length - 30} more executions; see account transaction history.` : ""}\n${group.length > 1 ? `${group.length} executions observed together\n` : ""}${oid ? `Order: ${oid}\n` : ""}Observed: ${new Date(event.time).toISOString()}\nOrigin: ${local ? "HyperVibes order" : "External or unknown (not linked to a HyperVibes order)"}\nThese are executed quantities, not proof that the entire order filled.`;
202
+ const result = await this.queueTx(tx, agent.id, `fill:${event.id}`, "fill", { title: title.slice(0, 200), body: body.slice(0, 12000), severity: raw?.liquidation ? "warning" : "info" }, { source: local ? "Account fill · HyperVibes" : "Account fill · external/unknown", subagentId: local?.producer_subagent_id, runId: local?.source_run_id, orderRequestId: local?.request_id });
203
+ if (result.status === "queued")
204
+ changed.add(agent.id);
205
+ }
206
+ }
207
+ return [...changed];
208
+ }
209
+ async list(agentId, page = 1) {
210
+ if (!Number.isSafeInteger(page) || page < 1 || page > 100000)
211
+ throw new AgentError("Invalid notification page.");
212
+ return this.db.transaction(async (tx) => {
213
+ const total = (await tx.get("SELECT count(*) AS total FROM hv_notifications WHERE agent_id=?", agentId)).total;
214
+ const pages = Math.max(1, Math.ceil(total / 50));
215
+ page = Math.min(page, pages);
216
+ const items = await tx.all(`SELECT ${columns} FROM hv_notifications WHERE agent_id=? ORDER BY created_at DESC,id DESC LIMIT 50 OFFSET ?`, agentId, (page - 1) * 50);
217
+ return { items, page, pages, total };
218
+ });
219
+ }
220
+ async get(agentId, id) {
221
+ const row = await this.db.get(`SELECT ${columns} FROM hv_notifications WHERE agent_id=? AND id=?`, agentId, id);
222
+ if (!row)
223
+ throw new AgentError("Notification not found", 404);
224
+ return row;
225
+ }
226
+ /** Called after the owning transaction commits; delivery never waits on a model. */
227
+ changed(agentId) {
228
+ for (const listener of this.listeners.get(agentId) ?? [])
229
+ listener();
230
+ if (this.started && !this.stopping)
231
+ this.schedule(0);
232
+ }
233
+ async watch(agentId, signal, publish) {
234
+ let revision = 0, dirty = true, wake;
235
+ const changed = () => { dirty = true; wake?.(); };
236
+ const listeners = this.listeners.get(agentId) ?? new Set();
237
+ listeners.add(changed);
238
+ this.listeners.set(agentId, listeners);
239
+ signal.addEventListener("abort", changed);
240
+ try {
241
+ while (!signal.aborted) {
242
+ if (dirty) {
243
+ dirty = false;
244
+ publish({ revision: revision++ });
245
+ }
246
+ await new Promise((resolve) => { wake = resolve; if (dirty || signal.aborted)
247
+ resolve(); });
248
+ wake = undefined;
249
+ }
250
+ }
251
+ finally {
252
+ listeners.delete(changed);
253
+ if (!listeners.size)
254
+ this.listeners.delete(agentId);
255
+ signal.removeEventListener("abort", changed);
256
+ }
257
+ }
258
+ start() { if (this.started || this.stopping)
259
+ return; this.started = true; this.schedule(0); }
260
+ schedule(ms) {
261
+ clearTimeout(this.timer);
262
+ this.timer = setTimeout(() => { void this.flush().catch((error) => this.logger?.error(error, "Notification delivery pass failed")).finally(() => { if (!this.stopping)
263
+ this.schedule(1000); }); }, ms);
264
+ this.timer.unref();
265
+ }
266
+ /** Single-flight delivery pass, also used by isolated tests. */
267
+ flush() {
268
+ if (this.stopping)
269
+ return Promise.resolve();
270
+ return this.running ??= this.deliver().finally(() => { this.running = undefined; });
271
+ }
272
+ async deliver() {
273
+ if (!this.sender)
274
+ return;
275
+ const rows = await this.db.all(`SELECT ${columns},chat_id AS chatId,bot_token_hash AS botTokenHash,message_ids AS messageIds FROM hv_notifications WHERE id IN (
276
+ SELECT id FROM (SELECT id,row_number() OVER (PARTITION BY bot_token_hash,chat_id ORDER BY created_at,id) AS position
277
+ FROM hv_notifications WHERE delivery='pending' AND next_attempt_at<=?) WHERE position=1
278
+ ) ORDER BY created_at,id LIMIT 50`, this.clock());
279
+ for (const row of rows) {
280
+ if (this.stopping)
281
+ return;
282
+ const chatKey = `${row.botTokenHash}:${row.chatId}`;
283
+ if ((this.chatNext.get(chatKey) ?? 0) > this.clock())
284
+ continue;
285
+ const allowed = await this.db.transaction(async (tx) => {
286
+ const settings = await this.settingsTx(tx, row.agentId);
287
+ const current = await tx.get("SELECT delivery FROM hv_notifications WHERE id=?", row.id);
288
+ if (!current || current.delivery !== "pending")
289
+ return false;
290
+ const gateway = await tx.get("SELECT 1 FROM hv_gateways WHERE agent_id=? AND type='telegram' AND chat_id=? AND bot_token_hash=?", row.agentId, row.chatId, row.botTokenHash);
291
+ const agent = await tx.get("SELECT state FROM hv_agents WHERE id=?", row.agentId);
292
+ if (!settings.enabled || (row.category === "submission" && !settings.submissions) || (row.category === "fill" && !settings.fills) || !gateway || !agent || ["stopping", "stopped"].includes(agent.state)) {
293
+ await tx.run("UPDATE hv_notifications SET delivery='suppressed',error='Notifications disabled or Telegram destination changed' WHERE id=?", row.id);
294
+ return false;
295
+ }
296
+ return true;
297
+ });
298
+ if (!allowed) {
299
+ this.changed(row.agentId);
300
+ continue;
301
+ }
302
+ const emoji = row.severity === "error" ? "❌" : row.severity === "warning" ? "⚠️" : "ℹ️";
303
+ const chunks = chunkText(`${emoji} ${row.title}\n\n${row.body}\n\n${new URL(`/agents/${encodeURIComponent(row.agentId)}/notifications/${row.id}`, this.origin).href}`, 4000);
304
+ const ids = JSON.parse(row.messageIds);
305
+ try {
306
+ const messageId = await this.sender({ agentId: row.agentId, chatId: row.chatId, botTokenHash: row.botTokenHash }, chunks[ids.length], this.shutdown.signal);
307
+ ids.push(messageId);
308
+ await this.db.run(`UPDATE hv_notifications SET message_ids=?,delivery=CASE WHEN ?=1 THEN 'sent' ELSE delivery END,sent_at=?,error=NULL,next_attempt_at=? WHERE id=?`, JSON.stringify(ids), ids.length === chunks.length ? 1 : 0, ids.length === chunks.length ? this.clock() : null, this.clock() + 1100, row.id);
309
+ this.chatNext.set(chatKey, this.clock() + 1100);
310
+ }
311
+ catch (error) {
312
+ if (this.stopping)
313
+ return; // Interrupted sends remain recoverable; Telegram has no idempotency key.
314
+ const detail = error instanceof NotificationDeliveryError ? error : new NotificationDeliveryError("Telegram delivery failed; retrying");
315
+ const attempts = row.attempts + 1;
316
+ const delay = Math.max(detail.retryAfterMs ?? 0, Math.min(900000, 1000 * 2 ** attempts));
317
+ await this.db.run("UPDATE hv_notifications SET attempts=?,delivery=?,error=?,next_attempt_at=? WHERE id=? AND delivery='pending'", attempts, detail.suppressed ? "suppressed" : detail.permanent || attempts >= 8 ? "failed" : "pending", detail.message, this.clock() + delay, row.id);
318
+ this.chatNext.set(chatKey, this.clock() + delay);
319
+ }
320
+ this.changed(row.agentId);
321
+ }
322
+ }
323
+ close() { return this.closing ??= (async () => { this.stopping = true; clearTimeout(this.timer); this.shutdown.abort(); await this.running; this.chatNext.clear(); })(); }
324
+ }
@@ -0,0 +1,86 @@
1
+ import { and, eq } from "drizzle-orm";
2
+ import { hvProviderConnections } from "../db/provider-schema.js";
3
+ import { decryptSecret, encryptSecret } from "../hyperliquid/crypto.js";
4
+ import { AgentError } from "../agents/agent-input.js";
5
+ /** One instance per user, shared by all of that user's harnesses. */
6
+ export class SqliteCredentials {
7
+ db;
8
+ userId;
9
+ key;
10
+ pending = new Map();
11
+ constructor(db, userId, key) {
12
+ this.db = db;
13
+ this.userId = userId;
14
+ this.key = key;
15
+ }
16
+ where(providerId) {
17
+ return and(eq(hvProviderConnections.userId, this.userId), eq(hvProviderConnections.providerId, providerId));
18
+ }
19
+ requireKey() {
20
+ if (!this.key)
21
+ throw new AgentError("Provider connections require AGENTS_ENCRYPTION_KEY on the server.", 503);
22
+ return this.key;
23
+ }
24
+ serialize(id, operation) {
25
+ const result = (this.pending.get(id) ?? Promise.resolve()).then(operation);
26
+ const settled = result.then(() => { }, () => { });
27
+ this.pending.set(id, settled);
28
+ void settled.then(() => { if (this.pending.get(id) === settled)
29
+ this.pending.delete(id); });
30
+ return result;
31
+ }
32
+ async read(providerId, options) {
33
+ options?.signal?.throwIfAborted();
34
+ const row = await this.db.select().from(hvProviderConnections).where(this.where(providerId)).get();
35
+ if (!row)
36
+ return undefined;
37
+ const key = this.requireKey();
38
+ if (row.keyId !== key.id)
39
+ throw new Error("Provider encryption key is unavailable");
40
+ const payload = JSON.parse(decryptSecret(key, row.ciphertext));
41
+ if (payload.userId !== this.userId || payload.providerId !== providerId || payload.credential?.type !== row.authType) {
42
+ throw new Error("Provider credential identity mismatch");
43
+ }
44
+ return payload.credential;
45
+ }
46
+ async list(options) {
47
+ options?.signal?.throwIfAborted();
48
+ return this.db.select({ providerId: hvProviderConnections.providerId, type: hvProviderConnections.authType })
49
+ .from(hvProviderConnections).where(eq(hvProviderConnections.userId, this.userId)).all();
50
+ }
51
+ modify(providerId, fn, options) {
52
+ return this.serialize(providerId, async () => {
53
+ options?.signal?.throwIfAborted();
54
+ const current = await this.read(providerId, options);
55
+ // OAuth refresh may use the network here. Hold only the per-provider
56
+ // mutex, NEVER the shared SQLite queue/transaction across this callback.
57
+ const next = await fn(current);
58
+ if (next === undefined)
59
+ return current;
60
+ const key = this.requireKey();
61
+ const values = { authType: next.type, ciphertext: Buffer.from(encryptSecret(key, JSON.stringify({ userId: this.userId, providerId, credential: next }))), keyId: key.id, updatedAt: Date.now() };
62
+ await this.db.insert(hvProviderConnections).values({ userId: this.userId, providerId, ...values })
63
+ .onConflictDoUpdate({ target: [hvProviderConnections.userId, hvProviderConnections.providerId], set: values }).run();
64
+ return next;
65
+ });
66
+ }
67
+ delete(providerId, options) {
68
+ return this.serialize(providerId, async () => {
69
+ options?.signal?.throwIfAborted();
70
+ await this.db.delete(hvProviderConnections).where(this.where(providerId)).run();
71
+ });
72
+ }
73
+ catalogStore() {
74
+ return {
75
+ read: async (id) => {
76
+ const row = await this.db.select({ catalog: hvProviderConnections.catalog }).from(hvProviderConnections).where(this.where(id)).get();
77
+ return row?.catalog ? JSON.parse(row.catalog) : undefined;
78
+ },
79
+ write: async (id, entry) => {
80
+ await this.db.update(hvProviderConnections).set({ catalog: JSON.stringify(entry) }).where(this.where(id)).run();
81
+ },
82
+ delete: async (id) => { await this.db.update(hvProviderConnections).set({ catalog: null }).where(this.where(id)).run(); },
83
+ };
84
+ }
85
+ async drain() { await Promise.all(this.pending.values()); }
86
+ }
@@ -0,0 +1,148 @@
1
+ import { envApiKeyAuth } from "@earendil-works/pi-ai";
2
+ import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
3
+ import { createProvider } from "@earendil-works/pi-ai/models";
4
+ /** Ollama Cloud (https://ollama.com) is an OpenAI-compatible API with a purely
5
+ * dynamic catalog: no model list ships with the app. The catalog is fetched
6
+ * live per user, once that user has connected an API key. */
7
+ const PROVIDER_ID = "ollama-cloud";
8
+ const CLOUD_BASE = "https://ollama.com";
9
+ const API_BASE = `${CLOUD_BASE}/v1`;
10
+ const FETCH_TIMEOUT_MS = 10_000;
11
+ const SHOW_CONCURRENCY = 8;
12
+ const DEFAULT_CONTEXT_WINDOW = 128_000;
13
+ // /api/show does not expose an output limit; this matches Ollama Cloud's
14
+ // observed cap for the largest responses.
15
+ const DEFAULT_MAX_TOKENS = 32_768;
16
+ /** Compat overrides for ollama.com. pi-ai detects generic-OpenAI defaults from
17
+ * the base URL, which are wrong for Ollama (developer role, store field,
18
+ * max_completion_tokens, prompt cache retention). */
19
+ const compat = {
20
+ // Ollama serves "system" messages, not the "developer" role.
21
+ supportsDeveloperRole: false,
22
+ supportsStore: false,
23
+ maxTokensField: "max_tokens",
24
+ supportsReasoningEffort: true,
25
+ supportsUsageInStreaming: true,
26
+ thinkingFormat: "openai",
27
+ sendSessionAffinityHeaders: false,
28
+ supportsLongCacheRetention: false,
29
+ };
30
+ function isRecord(value) {
31
+ return typeof value === "object" && value !== null && !Array.isArray(value);
32
+ }
33
+ function authHeaders(credential) {
34
+ return credential?.type === "api_key" && credential.key ? { Authorization: `Bearer ${credential.key}` } : {};
35
+ }
36
+ async function fetchJson(url, init, signal) {
37
+ const response = await fetch(url, { ...init, signal: AbortSignal.any([signal, AbortSignal.timeout(FETCH_TIMEOUT_MS)]) });
38
+ if (!response.ok) {
39
+ if (response.status === 429)
40
+ throw new Error("Ollama Cloud rate limited the request. Try again shortly.");
41
+ throw new Error(`Ollama Cloud request to ${new URL(url).pathname} failed: ${response.status}`);
42
+ }
43
+ try {
44
+ return await response.json();
45
+ }
46
+ catch {
47
+ throw new Error(`Ollama Cloud returned invalid JSON from ${new URL(url).pathname}`);
48
+ }
49
+ }
50
+ async function fetchModelIds(credential, signal) {
51
+ const body = await fetchJson(`${API_BASE}/models`, { headers: authHeaders(credential) }, signal);
52
+ if (!isRecord(body) || !Array.isArray(body.data))
53
+ throw new Error("Ollama Cloud model list has an unexpected shape.");
54
+ const ids = [];
55
+ for (const entry of body.data) {
56
+ if (!isRecord(entry) || typeof entry.id !== "string" || !entry.id.trim()) {
57
+ throw new Error("Ollama Cloud model list has an unexpected shape.");
58
+ }
59
+ ids.push(entry.id);
60
+ }
61
+ return ids;
62
+ }
63
+ async function fetchModelDetails(id, credential, signal) {
64
+ const body = await fetchJson(`${CLOUD_BASE}/api/show`, {
65
+ method: "POST",
66
+ headers: { ...authHeaders(credential), "Content-Type": "application/json" },
67
+ body: JSON.stringify({ model: id }),
68
+ }, signal);
69
+ if (!isRecord(body) || !Array.isArray(body.capabilities) || !body.capabilities.every((capability) => typeof capability === "string")) {
70
+ throw new Error(`Ollama Cloud returned invalid details for ${id}.`);
71
+ }
72
+ return { capabilities: body.capabilities, modelInfo: isRecord(body.model_info) ? body.model_info : {} };
73
+ }
74
+ /** /api/show reports context lengths as `<family>.context_length` keys. */
75
+ function contextLength(modelInfo) {
76
+ for (const [key, value] of Object.entries(modelInfo)) {
77
+ if (key.endsWith(".context_length") && typeof value === "number" && Number.isInteger(value) && value > 0)
78
+ return value;
79
+ }
80
+ return DEFAULT_CONTEXT_WINDOW;
81
+ }
82
+ function assembleModel(id, details) {
83
+ return {
84
+ id,
85
+ name: id,
86
+ provider: PROVIDER_ID,
87
+ api: "openai-completions",
88
+ baseUrl: API_BASE,
89
+ input: details.capabilities.includes("vision") ? ["text", "image"] : ["text"],
90
+ reasoning: details.capabilities.includes("thinking"),
91
+ // Ollama Cloud is subscription-billed; zero rates keep usage accounting
92
+ // from inventing pay-as-you-go charges that do not apply.
93
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
94
+ contextWindow: contextLength(details.modelInfo),
95
+ maxTokens: DEFAULT_MAX_TOKENS,
96
+ compat,
97
+ };
98
+ }
99
+ /** Fetch the live catalog with the user's credential. Throws on total or
100
+ * partial failure so the caller keeps the previous catalog instead of
101
+ * persisting an incomplete one. Agents need tool support, so models without
102
+ * the "tools" capability are dropped; an empty remainder is an error. */
103
+ export async function fetchModels(context) {
104
+ const credential = context.credential;
105
+ const ids = await fetchModelIds(credential, context.signal);
106
+ if (context.signal.aborted)
107
+ return [];
108
+ const details = new Map();
109
+ const failures = [];
110
+ let next = 0;
111
+ await Promise.all(Array.from({ length: Math.min(SHOW_CONCURRENCY, ids.length) }, async () => {
112
+ while (next < ids.length) {
113
+ const id = ids[next++];
114
+ try {
115
+ details.set(id, await fetchModelDetails(id, credential, context.signal));
116
+ }
117
+ catch (error) {
118
+ if (context.signal.aborted)
119
+ return;
120
+ failures.push(error instanceof Error ? error.message : String(error));
121
+ }
122
+ }
123
+ }));
124
+ if (context.signal.aborted)
125
+ return [];
126
+ if (failures.length) {
127
+ throw new Error(`Ollama Cloud catalog refresh incomplete: ${failures.length} of ${ids.length} model(s) failed (${failures[0]})`);
128
+ }
129
+ const models = [...details].filter(([, shown]) => shown.capabilities.includes("tools"))
130
+ .map(([id, shown]) => assembleModel(id, shown));
131
+ if (!models.length)
132
+ throw new Error("Ollama Cloud catalog contains no models with tool support");
133
+ return models;
134
+ }
135
+ /** The Ollama Cloud provider. Its catalog starts empty and is populated from
136
+ * ollama.com on refresh with the user's stored API key. */
137
+ export function ollamaCloudProvider() {
138
+ return createProvider({
139
+ id: PROVIDER_ID,
140
+ name: "Ollama Cloud",
141
+ baseUrl: API_BASE,
142
+ // No ambient server credential: only a per-user stored key resolves.
143
+ auth: { apiKey: envApiKeyAuth("Ollama Cloud API key", []) },
144
+ models: [],
145
+ api: openAICompletionsApi(),
146
+ fetchModels,
147
+ });
148
+ }
@@ -0,0 +1,18 @@
1
+ /** Provider ids are URL path segments into /model-catalog/logos and upstream models.dev. */
2
+ export function isProviderSlug(provider) {
3
+ return provider.length <= 64 && /^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(provider);
4
+ }
5
+ /** Provider ids that don't match their models.dev logo slug get remapped here. */
6
+ const logoSlugAliases = {
7
+ // models.dev has no Codex logo; the OpenAI knot is the closest match.
8
+ "openai-codex": "openai",
9
+ // models.dev catalogs Ollama under its own slug.
10
+ "ollama-cloud": "ollama",
11
+ };
12
+ export function providerLogoSlug(provider) {
13
+ return isProviderSlug(provider) ? logoSlugAliases[provider] ?? provider : null;
14
+ }
15
+ export function providerLogoUrl(provider) {
16
+ const slug = providerLogoSlug(provider);
17
+ return slug ? `/model-catalog/logos/${encodeURIComponent(slug)}.svg` : null;
18
+ }
@@ -0,0 +1,31 @@
1
+ import { SvgLogoCache } from "../cache/svg-logo-cache.js";
2
+ import { isProviderSlug, providerLogoSlug } from "./provider-logo-url.js";
3
+ export { isProviderSlug, providerLogoUrl } from "./provider-logo-url.js";
4
+ export function fallbackProviderLogo(provider) {
5
+ // Only validated provider ids may reach the SVG template.
6
+ if (!isProviderSlug(provider))
7
+ throw new Error("Invalid provider id");
8
+ const initial = provider[0].toUpperCase();
9
+ return Buffer.from(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" role="img" aria-label="${provider}"><rect width="32" height="32" rx="8" fill="#18181b"/><text x="16" y="21" text-anchor="middle" font-family="ui-sans-serif,system-ui" font-size="14" fill="#fafafa">${initial}</text></svg>`);
10
+ }
11
+ const source = {
12
+ directory: "model-catalog/logos",
13
+ validate: isProviderSlug,
14
+ url: (slug) => `https://models.dev/logos/${slug}.svg`,
15
+ fallback: fallbackProviderLogo,
16
+ };
17
+ /** Caches models.dev provider logos; see {@link SvgLogoCache} for mechanics. */
18
+ export class ProviderLogoCache {
19
+ cache;
20
+ constructor(root, fetcher = fetch) {
21
+ this.cache = new SvgLogoCache(root, source, fetcher);
22
+ }
23
+ get(provider) {
24
+ // Aliases resolve before caching so each models.dev slug owns one disk entry.
25
+ const slug = providerLogoSlug(provider);
26
+ return slug ? this.cache.get(slug) : Promise.reject(new Error("Invalid logo key"));
27
+ }
28
+ close() {
29
+ return this.cache.close();
30
+ }
31
+ }