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.
- package/.build/client/assets/_authenticated-C87LKvH0.js +1 -0
- package/.build/client/assets/_authenticated.account-D-nTh_fC.js +1 -0
- package/.build/client/assets/_authenticated.agents-HwcveLyB.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId-DuE8Lv62.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.chart-Bb_XkvQx.js +4 -0
- package/.build/client/assets/_authenticated.agents._agentId.chat-ClMMKw_N.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.index-DNJwBwn9.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.indicators-BmVjLtRI.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.indicators_._indicatorId-CMqaaFQf.js +2 -0
- package/.build/client/assets/_authenticated.agents._agentId.indicators_._indicatorId-DtgkoNV5.js +6 -0
- package/.build/client/assets/_authenticated.agents._agentId.memories-O-4f6a4G.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.memories_._memoryRef-BUPtUcj3.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.notifications-2lyhXCSk.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.notifications_._notificationId-Da3ZVT4v.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.settings-C5cWHoYw.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.sub-agents-HwcveLyB.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.sub-agents._subagentId-CZ4MeRy4.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.sub-agents._subagentId_.runs._runId-dVbo5kMU.js +3 -0
- package/.build/client/assets/_authenticated.agents._agentId.sub-agents.index-DeWTOYJD.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.sub-agents.new-D02TflCE.js +37 -0
- package/.build/client/assets/_authenticated.agents._agentId.transactions-D3a8Glgj.js +1 -0
- package/.build/client/assets/_authenticated.agents.index-CEqeDEgP.js +1 -0
- package/.build/client/assets/_authenticated.agents.new-DLRWK5Uk.js +1 -0
- package/.build/client/assets/_authenticated.providers-D0iPLXra.js +1 -0
- package/.build/client/assets/agent-chat-page-C5eYFgxw.js +1 -0
- package/.build/client/assets/agent-enablement-control-CUzOp9ce.js +1 -0
- package/.build/client/assets/agent-events-DC8eDZf3.js +1 -0
- package/.build/client/assets/agent-input-aolvZ47U.js +1 -0
- package/.build/client/assets/agent-notifications-page-B0mr-Zz8.js +1 -0
- package/.build/client/assets/agent-queries-CNMBImun.js +1 -0
- package/.build/client/assets/app-DjmsvbUC.css +2 -0
- package/.build/client/assets/artifact-content-COO4VTSZ.js +1 -0
- package/.build/client/assets/auth-client-K13li9H1.js +3 -0
- package/.build/client/assets/call-DxlGie4_.js +31 -0
- package/.build/client/assets/ccip-BgPlVPYP.js +1 -0
- package/.build/client/assets/chart-contracts-Cr5SFCbE.js +1 -0
- package/.build/client/assets/chart-queries-Bpvrgd0-.js +1 -0
- package/.build/client/assets/chat-bubble-CAJkT-vL.js +1 -0
- package/.build/client/assets/config-dialog-C9x9gKW9.js +1 -0
- package/.build/client/assets/contracts-HhAL7Tt7.js +1 -0
- package/.build/client/assets/createServerFn-DfZIp2Fu.js +3 -0
- package/.build/client/assets/custom-BeqXuIIS.js +5 -0
- package/.build/client/assets/decimal-B0DEG-1V.js +1 -0
- package/.build/client/assets/esm-BTb2nBdW.js +1 -0
- package/.build/client/assets/identicon-Lb9pS4O1.js +1 -0
- package/.build/client/assets/index-B4hvjMmI.js +10 -0
- package/.build/client/assets/indicator-input-fields-CtmrfVfS.js +1 -0
- package/.build/client/assets/indicator-queries-CzZ6u1Mh.js +1 -0
- package/.build/client/assets/indicator-renderer-CfccsIt2.js +1 -0
- package/.build/client/assets/instrument-label-BZYlWXR5.js +1 -0
- package/.build/client/assets/instrument-picker-DLPpryOm.js +1 -0
- package/.build/client/assets/journal-decimal-DBvd_Ibi.js +1 -0
- package/.build/client/assets/jsx-runtime-CIazjfqY.js +1 -0
- package/.build/client/assets/lazyRouteComponent-CJgUa7_r.js +1 -0
- package/.build/client/assets/lightweight-charts.production-BiBiFZSH.js +1 -0
- package/.build/client/assets/login-MNgYxCOY.js +3 -0
- package/.build/client/assets/memory-badge-BbN-DJQi.js +1 -0
- package/.build/client/assets/memory-contracts-Doi_Q1XJ.js +1 -0
- package/.build/client/assets/memory-queries-BttL580R.js +1 -0
- package/.build/client/assets/model-picker-YCQNNmyw.js +1 -0
- package/.build/client/assets/notification-queries-B12DG3aS.js +1 -0
- package/.build/client/assets/preload-helper-Czpn1I53.js +1 -0
- package/.build/client/assets/provider-logo-Ddb77aBD.js +1 -0
- package/.build/client/assets/provider-queries-DxPwhDOU.js +1 -0
- package/.build/client/assets/query-nvK_95Jo.js +1 -0
- package/.build/client/assets/random-uuid-DJR3ygJS.js +1 -0
- package/.build/client/assets/react-dom-C6bOPxes.js +1 -0
- package/.build/client/assets/risk-queries-D_6PHwus.js +1 -0
- package/.build/client/assets/run-subagent-dialog-B4yQe9T9.js +1 -0
- package/.build/client/assets/secp256k1-BbZnIgDP.js +1 -0
- package/.build/client/assets/setup-checklist-CUW-K484.js +1 -0
- package/.build/client/assets/sha2-hT-Q4pur.js +1 -0
- package/.build/client/assets/subagent-enabled-toggle-Bk5wfScg.js +1 -0
- package/.build/client/assets/subagent-input-TQke90I1.js +1 -0
- package/.build/client/assets/subagent-mutations-DfsDbmFu.js +1 -0
- package/.build/client/assets/subagent-queries-DVv5JCfs.js +1 -0
- package/.build/client/assets/subagent-run-history-D55Uh5VK.js +1 -0
- package/.build/client/assets/toggle-timestamp-B50FTPmA.js +1 -0
- package/.build/client/assets/useBaseQuery-Bxruo_mV.js +1 -0
- package/.build/client/assets/useMutation-DrF53EQG.js +1 -0
- package/.build/client/assets/useQuery-BMSU16jY.js +1 -0
- package/.build/client/assets/useRouter-c0I1aURm.js +1 -0
- package/.build/client/assets/useSelector-ERD9RroG.js +2 -0
- package/.build/client/assets/useSuspenseQuery-vODFwDb8.js +1 -0
- package/.build/client/assets/utils-Bpv4vmSW.js +1 -0
- package/.build/client/assets/worker-triggers-DtXyn40O.js +1 -0
- package/.build/server/assets/_authenticated-Bch0_2QH.js +264 -0
- package/.build/server/assets/_authenticated-DGV5cZYE.js +22 -0
- package/.build/server/assets/_authenticated.account-6qu6oQ1y.js +685 -0
- package/.build/server/assets/_authenticated.agents-DYDnQxli.js +5 -0
- package/.build/server/assets/_authenticated.agents._agentId-4HvB3mhd.js +17 -0
- package/.build/server/assets/_authenticated.agents._agentId-Cl608F1T.js +260 -0
- package/.build/server/assets/_authenticated.agents._agentId-G2jAHjma.js +15 -0
- package/.build/server/assets/_authenticated.agents._agentId.chart-BIA8wy73.js +2644 -0
- package/.build/server/assets/_authenticated.agents._agentId.chart-D4aj8Qy9.js +22 -0
- package/.build/server/assets/_authenticated.agents._agentId.chat-ClsVle4g.js +13 -0
- package/.build/server/assets/_authenticated.agents._agentId.chat-JV3FDsTp.js +18 -0
- package/.build/server/assets/_authenticated.agents._agentId.index-Ct4kbu2f.js +1044 -0
- package/.build/server/assets/_authenticated.agents._agentId.index-DJgrupvW.js +18 -0
- package/.build/server/assets/_authenticated.agents._agentId.indicators-DrKd0aP5.js +184 -0
- package/.build/server/assets/_authenticated.agents._agentId.indicators-wiVaY8fW.js +18 -0
- package/.build/server/assets/_authenticated.agents._agentId.indicators_._indicatorId-C4Ox2R-a.js +25 -0
- package/.build/server/assets/_authenticated.agents._agentId.indicators_._indicatorId-D8Q0hCW0.js +489 -0
- package/.build/server/assets/_authenticated.agents._agentId.memories-BUTyVLYy.js +42 -0
- package/.build/server/assets/_authenticated.agents._agentId.memories-E8-nSEMT.js +362 -0
- package/.build/server/assets/_authenticated.agents._agentId.memories_._memoryRef-C-SC6wzq.js +14 -0
- package/.build/server/assets/_authenticated.agents._agentId.memories_._memoryRef-ZCrdrc8C.js +490 -0
- package/.build/server/assets/_authenticated.agents._agentId.notifications-DQeQqTzh.js +14 -0
- package/.build/server/assets/_authenticated.agents._agentId.notifications-DlFyYyHh.js +16 -0
- package/.build/server/assets/_authenticated.agents._agentId.notifications_._notificationId-B0RosY_R.js +15 -0
- package/.build/server/assets/_authenticated.agents._agentId.notifications_._notificationId-C1NikQ9e.js +13 -0
- package/.build/server/assets/_authenticated.agents._agentId.settings-DDwjT3Ui.js +42 -0
- package/.build/server/assets/_authenticated.agents._agentId.settings-PEzEvOfo.js +751 -0
- package/.build/server/assets/_authenticated.agents._agentId.sub-agents-z34-J3Tu.js +5 -0
- package/.build/server/assets/_authenticated.agents._agentId.sub-agents._subagentId-D-6TOi1U.js +1371 -0
- package/.build/server/assets/_authenticated.agents._agentId.sub-agents._subagentId-DipzVdnr.js +13 -0
- package/.build/server/assets/_authenticated.agents._agentId.sub-agents._subagentId_.runs._runId-BZR3VLV7.js +610 -0
- package/.build/server/assets/_authenticated.agents._agentId.sub-agents._subagentId_.runs._runId-DsYJKfGm.js +12 -0
- package/.build/server/assets/_authenticated.agents._agentId.sub-agents.index-BF8Cuzcr.js +231 -0
- package/.build/server/assets/_authenticated.agents._agentId.sub-agents.index-D2x0qCnt.js +12 -0
- package/.build/server/assets/_authenticated.agents._agentId.sub-agents.new-CYm_vuDD.js +13 -0
- package/.build/server/assets/_authenticated.agents._agentId.sub-agents.new-CiVShIka.js +497 -0
- package/.build/server/assets/_authenticated.agents._agentId.transactions-BC_XfGqw.js +95 -0
- package/.build/server/assets/_authenticated.agents._agentId.transactions-WtBW3xmh.js +495 -0
- package/.build/server/assets/_authenticated.agents.index-C8zmu8T8.js +78 -0
- package/.build/server/assets/_authenticated.agents.new-Bt-tBR47.js +302 -0
- package/.build/server/assets/_authenticated.providers-C0pb-ujV.js +672 -0
- package/.build/server/assets/_tanstack-start-manifest_v-Baw5u9sk.js +354 -0
- package/.build/server/assets/account-XvMwzRXh.js +66 -0
- package/.build/server/assets/account-queries-AJJVkLAV.js +32 -0
- package/.build/server/assets/agent-chat-page-DChiz8KQ.js +686 -0
- package/.build/server/assets/agent-enablement-control-C90f0uIb.js +143 -0
- package/.build/server/assets/agent-events-DK9Yz5__.js +217 -0
- package/.build/server/assets/agent-notifications-page-BdyKAyds.js +276 -0
- package/.build/server/assets/agent-queries-Cmx8RiUx.js +51 -0
- package/.build/server/assets/agents-CZq-0mna.js +106 -0
- package/.build/server/assets/agents-CfjmtW7A.js +278 -0
- package/.build/server/assets/artifact-content-B6uHmPz7.js +35 -0
- package/.build/server/assets/artifacts-HtLwHs8q.js +42 -0
- package/.build/server/assets/auth-DTikZAVz.js +12 -0
- package/.build/server/assets/auth-client-Vb3O0v7P.js +1897 -0
- package/.build/server/assets/auth-user-DkLo8ojH.js +108 -0
- package/.build/server/assets/chart-queries-Bc3AfsEb.js +109 -0
- package/.build/server/assets/charts-C4dKuwTJ.js +130 -0
- package/.build/server/assets/chat-bubble-ik3itVbx.js +109 -0
- package/.build/server/assets/config-dialog-9gSAjwwT.js +55 -0
- package/.build/server/assets/contracts-CG-nZuYJ.js +155 -0
- package/.build/server/assets/createServerRpc-DNqhdU4z.js +12 -0
- package/.build/server/assets/decimal-BthtZ77w.js +97 -0
- package/.build/server/assets/empty-plugin-adapters-MOWBbCMw.js +5 -0
- package/.build/server/assets/errors-DaZqUftX.js +151 -0
- package/.build/server/assets/gateway-CHkruO_a.js +193 -0
- package/.build/server/assets/identicon-CyawcVnx.js +41 -0
- package/.build/server/assets/indicator-input-fields-bzuhIh6t.js +75 -0
- package/.build/server/assets/indicator-queries-4dY1-LXx.js +171 -0
- package/.build/server/assets/indicator-renderer-B2muPS-8.js +395 -0
- package/.build/server/assets/indicators-ENxAHN42.js +192 -0
- package/.build/server/assets/insight-contracts-B5rmUDfO.js +14 -0
- package/.build/server/assets/instrument-label-C-tTGGsD.js +40 -0
- package/.build/server/assets/instrument-picker-CRroLwVq.js +381 -0
- package/.build/server/assets/journal-9aE-um9f.js +107 -0
- package/.build/server/assets/journal-decimal-Bh9EL029.js +66 -0
- package/.build/server/assets/login-BsEFUvTX.js +125 -0
- package/.build/server/assets/memories-2WF2fW1G.js +100 -0
- package/.build/server/assets/memory-badge-DVYLTtRR.js +53 -0
- package/.build/server/assets/memory-contracts-DQGN2IKV.js +22 -0
- package/.build/server/assets/memory-queries--4RbPRrk.js +92 -0
- package/.build/server/assets/model-picker-CA1uYb--.js +225 -0
- package/.build/server/assets/notification-queries-0CJUkBt9.js +52 -0
- package/.build/server/assets/notifications-Cn7BiCyw.js +72 -0
- package/.build/server/assets/provider-logo-BRdbOr9I.js +32 -0
- package/.build/server/assets/provider-queries-BaZWLw8J.js +78 -0
- package/.build/server/assets/providers-CyoN_jgW.js +89 -0
- package/.build/server/assets/risk-Bqrb6hKQ.js +34 -0
- package/.build/server/assets/risk-queries-JQpByHT2.js +18 -0
- package/.build/server/assets/router-Hkr5TH_I.js +419 -0
- package/.build/server/assets/run-subagent-dialog-C7iUOZa-.js +59 -0
- package/.build/server/assets/setup-DqMlwgNr.js +30 -0
- package/.build/server/assets/setup-checklist-BICTmBo2.js +60 -0
- package/.build/server/assets/start-C-GPoWRt.js +4 -0
- package/.build/server/assets/subagent-enabled-toggle-Djte00oo.js +22 -0
- package/.build/server/assets/subagent-input-BHQKs2cW.js +57 -0
- package/.build/server/assets/subagent-mutations-dUo3B_SG.js +32 -0
- package/.build/server/assets/subagent-queries-sAwBjFxC.js +146 -0
- package/.build/server/assets/subagent-run-history-C_xmucO-.js +186 -0
- package/.build/server/assets/subagents-CIoilC66.js +180 -0
- package/.build/server/assets/toggle-timestamp-CToUVn67.js +28 -0
- package/.build/server/assets/trading-contracts-zWq1Hxnb.js +26 -0
- package/.build/server/assets/validation-BX-krL0i.js +33 -0
- package/.build/server/assets/worker-triggers-CJF5HWzR.js +51 -0
- package/.build/server/server.js +7501 -0
- package/LICENSE +661 -0
- package/README.md +145 -2
- package/bin/hypervibes.js +24 -0
- package/dist/agents/agent-enablement.js +10 -0
- package/dist/agents/agent-input.js +38 -0
- package/dist/agents/agent-service.js +1004 -0
- package/dist/agents/cancel-task-scope.js +52 -0
- package/dist/agents/candle-intervals.js +1 -0
- package/dist/agents/default-subagents.js +36 -0
- package/dist/agents/execution-context-contracts.js +1 -0
- package/dist/agents/execution-context.js +206 -0
- package/dist/agents/execution-warnings.js +33 -0
- package/dist/agents/orchestrator-insights.js +14 -0
- package/dist/agents/run-inspection.js +99 -0
- package/dist/agents/subagent-dashboard.js +1 -0
- package/dist/agents/subagent-extension.js +156 -0
- package/dist/agents/subagent-history.js +5 -0
- package/dist/agents/subagent-input.js +73 -0
- package/dist/agents/subagent-service.js +1177 -0
- package/dist/agents/subagent-templates.js +20 -0
- package/dist/agents/template-manifest.js +34 -0
- package/dist/agents/templates/basic-analysis.json +40 -0
- package/dist/agents/templates/basic-review.json +48 -0
- package/dist/agents/templates/basic-trading.json +45 -0
- package/dist/agents/templates/indicator-developer.json +16 -0
- package/dist/agents/time-guidance.js +29 -0
- package/dist/agents/worker-events.js +18 -0
- package/dist/agents/worker-inbox.js +28 -0
- package/dist/agents/worker-triggers.js +23 -0
- package/dist/auth-options.js +98 -0
- package/dist/auth-user.js +15 -0
- package/dist/auth.js +28 -0
- package/dist/cache/svg-logo-cache.js +155 -0
- package/dist/canonical.js +13 -0
- package/dist/charts/chart-service.js +236 -0
- package/dist/charts/contracts.js +118 -0
- package/dist/charts/durable-extension.js +27 -0
- package/dist/charts/trading-contracts.js +14 -0
- package/dist/config.js +85 -0
- package/dist/db/agent-schema.js +26 -0
- package/dist/db/auth-schema.js +122 -0
- package/dist/db/database.js +31 -0
- package/dist/db/exchange-schema.js +23 -0
- package/dist/db/gateway-schema.js +24 -0
- package/dist/db/indicator-schema.js +19 -0
- package/dist/db/migrations/20261015221717_auth/migration.sql +62 -0
- package/dist/db/migrations/20261015231224_agents/migration.sql +35 -0
- package/dist/db/migrations/20261016040641_provider_connections/migration.sql +19 -0
- package/dist/db/migrations/20261016061831_trade_journal/migration.sql +48 -0
- package/dist/db/migrations/20261016080320_order_execution/migration.sql +62 -0
- package/dist/db/migrations/20261016181110_subagents/migration.sql +51 -0
- package/dist/db/migrations/20261017222959_research_artifacts/migration.sql +37 -0
- package/dist/db/migrations/20261018061531_memories_and_insights/migration.sql +162 -0
- package/dist/db/migrations/20261020091156_worker_event_inbox/migration.sql +54 -0
- package/dist/db/migrations/20261021051229_chart_workspace/migration.sql +33 -0
- package/dist/db/migrations/20261021222116_gateways/migration.sql +15 -0
- package/dist/db/migrations/20261022015813_pine_indicators/migration.sql +43 -0
- package/dist/db/migrations/20261022101559_notifications/migration.sql +51 -0
- package/dist/db/migrations.js +12 -0
- package/dist/db/order-provenance-schema.js +17 -0
- package/dist/db/order-schema.js +32 -0
- package/dist/db/profile-lock.js +12 -0
- package/dist/db/provider-schema.js +16 -0
- package/dist/db/shared-database.js +128 -0
- package/dist/db/subagent-schema.js +79 -0
- package/dist/gateway/bridge-service.js +223 -0
- package/dist/gateway/gateway-store.js +77 -0
- package/dist/gateway/handlers.js +122 -0
- package/dist/gateway/telegram/connector.js +115 -0
- package/dist/gateway/telegram/transport.js +96 -0
- package/dist/hyperliquid/account-info.js +117 -0
- package/dist/hyperliquid/account-status.js +64 -0
- package/dist/hyperliquid/agent-dashboard.js +56 -0
- package/dist/hyperliquid/chart-contracts.js +9 -0
- package/dist/hyperliquid/constants.js +25 -0
- package/dist/hyperliquid/crypto.js +29 -0
- package/dist/hyperliquid/currency-logo.js +7 -0
- package/dist/hyperliquid/decimal.js +98 -0
- package/dist/hyperliquid/durable-extension.js +39 -0
- package/dist/hyperliquid/errors.js +8 -0
- package/dist/hyperliquid/exchange.js +118 -0
- package/dist/hyperliquid/journal-decimal.js +48 -0
- package/dist/hyperliquid/journal-model.js +167 -0
- package/dist/hyperliquid/journal-service.js +648 -0
- package/dist/hyperliquid/live-dashboard.js +245 -0
- package/dist/hyperliquid/logo-cache.js +27 -0
- package/dist/hyperliquid/market-charts.js +242 -0
- package/dist/hyperliquid/market-snapshot.js +45 -0
- package/dist/hyperliquid/orders/order-contracts.js +42 -0
- package/dist/hyperliquid/orders/order-math.js +89 -0
- package/dist/hyperliquid/orders/order-outcomes.js +74 -0
- package/dist/hyperliquid/orders/order-prepare.js +109 -0
- package/dist/hyperliquid/orders/order-service.js +432 -0
- package/dist/hyperliquid/orders/order-store.js +134 -0
- package/dist/hyperliquid/orders/order-tools.js +40 -0
- package/dist/hyperliquid/orders/protection.js +35 -0
- package/dist/hyperliquid/orders/risk-settings.js +44 -0
- package/dist/hyperliquid/orders/trade-admission.js +117 -0
- package/dist/hyperliquid/orders/trade-planning.js +60 -0
- package/dist/hyperliquid/service.js +529 -0
- package/dist/hyperliquid/shared-feeds.js +40 -0
- package/dist/hyperliquid/signing.js +89 -0
- package/dist/indicators/builtin-catalog.js +59 -0
- package/dist/indicators/builtins/adx.pine +22 -0
- package/dist/indicators/builtins/atr.pine +11 -0
- package/dist/indicators/builtins/bollinger.pine +10 -0
- package/dist/indicators/builtins/ema.pine +5 -0
- package/dist/indicators/builtins/macd.pine +18 -0
- package/dist/indicators/builtins/rsi.pine +18 -0
- package/dist/indicators/builtins/sma.pine +5 -0
- package/dist/indicators/candle-window.js +36 -0
- package/dist/indicators/chart-contracts.js +64 -0
- package/dist/indicators/contracts.js +14 -0
- package/dist/indicators/durable-extension.js +32 -0
- package/dist/indicators/indicator-service.js +300 -0
- package/dist/indicators/indicator-store.js +137 -0
- package/dist/indicators/normalize.js +149 -0
- package/dist/indicators/result-schema.js +79 -0
- package/dist/indicators/runtime-wrapper.js +52 -0
- package/dist/indicators/runtime.js +201 -0
- package/dist/indicators/validation.js +124 -0
- package/dist/indicators/worker-queue.js +48 -0
- package/dist/initialize-config.js +160 -0
- package/dist/main.js +59 -0
- package/dist/memory/decision-audit.js +47 -0
- package/dist/memory/durable-extension.js +40 -0
- package/dist/memory/execution-audit.js +83 -0
- package/dist/memory/insight-contracts.js +33 -0
- package/dist/memory/insight-service.js +117 -0
- package/dist/memory/memory-contracts.js +113 -0
- package/dist/memory/memory-service.js +179 -0
- package/dist/memory/operations.js +46 -0
- package/dist/memory/tool-schemas.js +44 -0
- package/dist/memory/validation.js +42 -0
- package/dist/notifications/contracts.js +1 -0
- package/dist/notifications/delivery.js +11 -0
- package/dist/notifications/durable-extension.js +20 -0
- package/dist/notifications/notification-service.js +324 -0
- package/dist/providers/credential-store.js +86 -0
- package/dist/providers/ollama-cloud.js +148 -0
- package/dist/providers/provider-logo-url.js +18 -0
- package/dist/providers/provider-logo.js +31 -0
- package/dist/providers/provider-service.js +295 -0
- package/dist/research/artifacts.js +151 -0
- package/dist/research/call-queue.js +43 -0
- package/dist/research/contracts.js +37 -0
- package/dist/research/evidence.js +43 -0
- package/dist/research/journal.js +30 -0
- package/dist/research/research-service.js +191 -0
- package/dist/research/tool-activity.js +49 -0
- package/dist/research/tool-response.js +25 -0
- package/dist/runtime.js +145 -0
- package/dist/web/agent-events.js +418 -0
- package/dist/web/app.js +206 -0
- package/dist/web/artifact-downloads.js +23 -0
- package/dist/web/middleware.js +25 -0
- package/dist/web/random-uuid.js +14 -0
- package/dist/web/start.js +56 -0
- package/package.json +93 -3
- package/scripts/check-node.mjs +24 -0
- package/static/android-chrome-192x192.png +0 -0
- package/static/android-chrome-512x512.png +0 -0
- package/static/apple-touch-icon.png +0 -0
- package/static/favicon-16x16.png +0 -0
- package/static/favicon-32x32.png +0 -0
- package/static/favicon.ico +0 -0
- package/static/github.svg +3 -0
- package/static/hypervibes.png +0 -0
- package/static/logo.png +0 -0
- package/static/site.webmanifest +1 -0
- package/static/telegram.svg +1 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { envApiKeyAuth } from "@earendil-works/pi-ai";
|
|
2
|
+
import { createModels, getSupportedThinkingLevels } from "@earendil-works/pi-ai/models";
|
|
3
|
+
import { builtinProviders } from "@earendil-works/pi-ai/providers/all";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { AgentError } from "../agents/agent-input.js";
|
|
6
|
+
import { hvInstallation } from "../db/provider-schema.js";
|
|
7
|
+
import { SqliteCredentials } from "./credential-store.js";
|
|
8
|
+
import { ollamaCloudProvider } from "./ollama-cloud.js";
|
|
9
|
+
/** Every provider a user can connect, with their own live-refreshed catalog. */
|
|
10
|
+
const defaultProviders = () => [...builtinProviders(), ollamaCloudProvider()];
|
|
11
|
+
/** App-owned credentials/catalogs. No ambient server credentials or files are
|
|
12
|
+
* inherited by a user's collection. A provider connection belongs to that user. */
|
|
13
|
+
export class ProviderService {
|
|
14
|
+
db;
|
|
15
|
+
key;
|
|
16
|
+
options;
|
|
17
|
+
users = new Map();
|
|
18
|
+
attempts = new Map();
|
|
19
|
+
stopping = false;
|
|
20
|
+
shutdown = new AbortController();
|
|
21
|
+
deviceId = "";
|
|
22
|
+
onChanged = async () => { };
|
|
23
|
+
constructor(db, key, options = {}) {
|
|
24
|
+
this.db = db;
|
|
25
|
+
this.key = key;
|
|
26
|
+
this.options = options;
|
|
27
|
+
}
|
|
28
|
+
async initialize() {
|
|
29
|
+
await this.db.insert(hvInstallation).values({ id: 1, deviceId: randomUUID() }).onConflictDoNothing().run();
|
|
30
|
+
this.deviceId = (await this.db.select().from(hvInstallation).get()).deviceId;
|
|
31
|
+
}
|
|
32
|
+
admit() { if (this.stopping)
|
|
33
|
+
throw new AgentError("Provider service is shutting down", 503); }
|
|
34
|
+
user(userId) {
|
|
35
|
+
this.admit();
|
|
36
|
+
let pending = this.users.get(userId);
|
|
37
|
+
if (!pending) {
|
|
38
|
+
pending = (async () => {
|
|
39
|
+
const credentials = new SqliteCredentials(this.db, userId, this.key);
|
|
40
|
+
const models = createModels({ credentials, modelsStore: credentials.catalogStore(), authContext: {
|
|
41
|
+
env: async () => undefined, fileExists: async () => false,
|
|
42
|
+
} });
|
|
43
|
+
for (let provider of (this.options.providers ?? defaultProviders)()) {
|
|
44
|
+
// A web user must never select a server AWS profile/credential chain
|
|
45
|
+
// or a local Google service-account file. These built-ins still work
|
|
46
|
+
// with user-supplied bearer/API keys, using their native wire APIs.
|
|
47
|
+
if (provider.id === "amazon-bedrock" || provider.id === "google-vertex") {
|
|
48
|
+
provider = { ...provider, auth: { ...provider.auth, apiKey: envApiKeyAuth(provider.id === "amazon-bedrock" ? "Bedrock bearer token" : "Google Cloud API key", []) } };
|
|
49
|
+
}
|
|
50
|
+
models.setProvider(provider);
|
|
51
|
+
}
|
|
52
|
+
await models.refresh({ allowNetwork: false });
|
|
53
|
+
return { models, credentials };
|
|
54
|
+
})();
|
|
55
|
+
this.users.set(userId, pending);
|
|
56
|
+
void pending.catch(() => { if (this.users.get(userId) === pending)
|
|
57
|
+
this.users.delete(userId); });
|
|
58
|
+
}
|
|
59
|
+
return pending;
|
|
60
|
+
}
|
|
61
|
+
async models(userId) { return (await this.user(userId)).models; }
|
|
62
|
+
async available(models, credentials) {
|
|
63
|
+
const connected = await credentials.list();
|
|
64
|
+
const names = new Map(models.getProviders().map((provider) => [provider.id, provider.name]));
|
|
65
|
+
const result = [];
|
|
66
|
+
for (const { providerId } of connected) {
|
|
67
|
+
try {
|
|
68
|
+
for (const model of await models.getAvailable(providerId))
|
|
69
|
+
result.push({
|
|
70
|
+
provider: model.provider, modelId: model.id, name: model.name, providerName: names.get(model.provider) ?? model.provider,
|
|
71
|
+
thinkingLevels: getSupportedThinkingLevels(model), contextWindow: model.contextWindow, reasoning: model.reasoning,
|
|
72
|
+
inputCost: model.cost.input, outputCost: model.cost.output,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
catch { /* A broken connection remains visible, but cannot admit work. */ }
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
async choices(userId) {
|
|
80
|
+
const { models, credentials } = await this.user(userId);
|
|
81
|
+
return this.available(models, credentials);
|
|
82
|
+
}
|
|
83
|
+
async validate(userId, ref) {
|
|
84
|
+
const choices = await this.choices(userId);
|
|
85
|
+
if (!choices.length)
|
|
86
|
+
throw new AgentError("Connect a provider on the Providers page first.", 409);
|
|
87
|
+
if (!ref || !choices.some((model) => model.provider === ref.provider && model.modelId === ref.modelId)) {
|
|
88
|
+
throw new AgentError("Select an available provider and model.", 409);
|
|
89
|
+
}
|
|
90
|
+
return { provider: ref.provider, modelId: ref.modelId };
|
|
91
|
+
}
|
|
92
|
+
async configured(userId, ref) {
|
|
93
|
+
const { models, credentials } = await this.user(userId);
|
|
94
|
+
if (!(await credentials.list()).some((entry) => entry.providerId === ref.provider))
|
|
95
|
+
return false;
|
|
96
|
+
try {
|
|
97
|
+
return (await models.getAvailable(ref.provider)).some((model) => model.id === ref.modelId);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async settings(userId) {
|
|
104
|
+
const { models, credentials } = await this.user(userId);
|
|
105
|
+
const connected = await credentials.list();
|
|
106
|
+
const summaries = await Promise.all(models.getProviders().map(async (provider) => {
|
|
107
|
+
const credential = connected.find((entry) => entry.providerId === provider.id);
|
|
108
|
+
let configured = false;
|
|
109
|
+
if (credential) {
|
|
110
|
+
try {
|
|
111
|
+
configured = Boolean(await models.checkAuth(provider.id));
|
|
112
|
+
}
|
|
113
|
+
catch { /* sanitized status */ }
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
id: provider.id, name: provider.name, connected: Boolean(credential), configured, authType: credential?.type,
|
|
117
|
+
methods: [
|
|
118
|
+
...(provider.auth.apiKey?.login ? [{ type: "api_key", label: provider.auth.apiKey.name }] : []),
|
|
119
|
+
...(provider.auth.oauth ? [{ type: "oauth", label: provider.auth.oauth.loginLabel ?? provider.auth.oauth.name }] : []),
|
|
120
|
+
],
|
|
121
|
+
};
|
|
122
|
+
}));
|
|
123
|
+
const active = [...this.attempts.values()].find((attempt) => attempt.userId === userId && attempt.view.state === "pending");
|
|
124
|
+
return { storageReady: Boolean(this.key), providers: summaries, models: await this.available(models, credentials), activeLogin: active ? this.view(active) : undefined };
|
|
125
|
+
}
|
|
126
|
+
async refresh(userId) {
|
|
127
|
+
const { models, credentials } = await this.user(userId);
|
|
128
|
+
const result = await models.refresh({ providers: (await credentials.list()).map((entry) => entry.providerId), force: true, signal: AbortSignal.any([this.shutdown.signal, AbortSignal.timeout(20_000)]) });
|
|
129
|
+
await this.onChanged(userId);
|
|
130
|
+
if (result.aborted || result.errors.size)
|
|
131
|
+
throw new AgentError("Some model catalogs could not be refreshed. Previous models were kept.");
|
|
132
|
+
}
|
|
133
|
+
async disconnect(userId, providerId) {
|
|
134
|
+
const { models } = await this.user(userId);
|
|
135
|
+
for (const attempt of this.attempts.values()) {
|
|
136
|
+
if (attempt.userId === userId && attempt.view.providerId === providerId && attempt.view.state === "pending")
|
|
137
|
+
this.cancelAttempt(attempt);
|
|
138
|
+
}
|
|
139
|
+
await Promise.all([...this.attempts.values()].filter((attempt) => attempt.userId === userId && attempt.view.providerId === providerId).map((attempt) => attempt.done));
|
|
140
|
+
await models.logout(providerId);
|
|
141
|
+
await this.onChanged(userId);
|
|
142
|
+
}
|
|
143
|
+
async begin(userId, providerId, type) {
|
|
144
|
+
const { models, credentials } = await this.user(userId);
|
|
145
|
+
credentials.requireKey();
|
|
146
|
+
const provider = models.getProvider(providerId);
|
|
147
|
+
if (!provider || !(type === "oauth" ? provider.auth.oauth : provider.auth.apiKey?.login))
|
|
148
|
+
throw new AgentError("This provider does not support that login method.");
|
|
149
|
+
for (const [oldId, previous] of this.attempts) {
|
|
150
|
+
if (previous.userId === userId && previous.view.state !== "pending") {
|
|
151
|
+
await previous.done;
|
|
152
|
+
clearTimeout(previous.timer);
|
|
153
|
+
this.attempts.delete(oldId);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
this.admit();
|
|
157
|
+
// Check after cleanup awaits; reservation below has no intervening await.
|
|
158
|
+
// Built-in OAuth flows may own fixed loopback callback ports, so serialize
|
|
159
|
+
// OAuth sign-ins across users as well as all sign-ins within one user.
|
|
160
|
+
for (const attempt of this.attempts.values()) {
|
|
161
|
+
if (attempt.view.state === "pending" && (attempt.userId === userId || (type === "oauth" && attempt.type === "oauth"))) {
|
|
162
|
+
throw new AgentError("Another provider sign-in is in progress. Finish or cancel it first.", 409);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const id = randomUUID();
|
|
166
|
+
const controller = new AbortController();
|
|
167
|
+
const attempt = {
|
|
168
|
+
userId, type, controller, listeners: new Set(), view: { id, providerId, state: "pending", events: [] }, done: Promise.resolve(),
|
|
169
|
+
timer: setTimeout(() => { this.cancelAttempt(attempt); this.attempts.delete(id); }, 10 * 60_000),
|
|
170
|
+
};
|
|
171
|
+
attempt.timer.unref();
|
|
172
|
+
this.attempts.set(id, attempt);
|
|
173
|
+
attempt.done = models.login(providerId, type, {
|
|
174
|
+
signal: controller.signal,
|
|
175
|
+
notify: (event) => {
|
|
176
|
+
if (attempt.view.state !== "pending")
|
|
177
|
+
return;
|
|
178
|
+
attempt.view.events = [...attempt.view.events.slice(-19), event];
|
|
179
|
+
this.publish(attempt);
|
|
180
|
+
},
|
|
181
|
+
prompt: (input) => new Promise((resolve, reject) => {
|
|
182
|
+
const signal = AbortSignal.any([controller.signal, ...(input.signal ? [input.signal] : [])]);
|
|
183
|
+
const promptId = randomUUID();
|
|
184
|
+
const browserInput = { ...input };
|
|
185
|
+
delete browserInput.signal;
|
|
186
|
+
const cleanup = () => {
|
|
187
|
+
signal.removeEventListener("abort", abort);
|
|
188
|
+
if (attempt.view.prompt?.id === promptId) {
|
|
189
|
+
delete attempt.view.prompt;
|
|
190
|
+
delete attempt.answer;
|
|
191
|
+
this.publish(attempt);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
const abort = () => { cleanup(); reject(new Error("Login prompt cancelled")); };
|
|
195
|
+
attempt.view.prompt = { id: promptId, input: browserInput };
|
|
196
|
+
attempt.answer = (value) => { cleanup(); resolve(value); };
|
|
197
|
+
this.publish(attempt);
|
|
198
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
199
|
+
if (signal.aborted)
|
|
200
|
+
abort();
|
|
201
|
+
}),
|
|
202
|
+
}, { getDeviceId: () => this.deviceId, agentName: "HyperVibes" }).then(async () => {
|
|
203
|
+
if (controller.signal.aborted)
|
|
204
|
+
return;
|
|
205
|
+
await models.refresh({ providers: [providerId], signal: AbortSignal.any([controller.signal, AbortSignal.timeout(20_000)]) });
|
|
206
|
+
attempt.view.state = "complete";
|
|
207
|
+
delete attempt.view.prompt;
|
|
208
|
+
this.publish(attempt);
|
|
209
|
+
await this.onChanged(userId);
|
|
210
|
+
}).catch(() => {
|
|
211
|
+
if (attempt.view.state === "pending") {
|
|
212
|
+
attempt.view.state = "failed";
|
|
213
|
+
attempt.view.error = "Provider sign-in failed. Check your credentials and try again. For OAuth, check that its callback port is available.";
|
|
214
|
+
}
|
|
215
|
+
delete attempt.view.prompt;
|
|
216
|
+
delete attempt.answer;
|
|
217
|
+
this.publish(attempt);
|
|
218
|
+
});
|
|
219
|
+
return this.view(attempt);
|
|
220
|
+
}
|
|
221
|
+
owned(userId, id) {
|
|
222
|
+
this.admit();
|
|
223
|
+
const attempt = this.attempts.get(id);
|
|
224
|
+
if (!attempt || attempt.userId !== userId)
|
|
225
|
+
throw new AgentError("Provider sign-in not found or expired.", 404);
|
|
226
|
+
return attempt;
|
|
227
|
+
}
|
|
228
|
+
view(attempt) { return structuredClone(attempt.view); }
|
|
229
|
+
status(userId, id) { return this.view(this.owned(userId, id)); }
|
|
230
|
+
publish(attempt) {
|
|
231
|
+
for (const listener of attempt.listeners)
|
|
232
|
+
listener(this.view(attempt));
|
|
233
|
+
}
|
|
234
|
+
watchLogin(userId, id, signal, publish) {
|
|
235
|
+
const attempt = this.owned(userId, id);
|
|
236
|
+
if (signal.aborted)
|
|
237
|
+
return Promise.resolve();
|
|
238
|
+
return new Promise((resolve) => {
|
|
239
|
+
const stop = () => {
|
|
240
|
+
attempt.listeners.delete(publish);
|
|
241
|
+
signal.removeEventListener("abort", stop);
|
|
242
|
+
resolve();
|
|
243
|
+
};
|
|
244
|
+
attempt.listeners.add(publish);
|
|
245
|
+
signal.addEventListener("abort", stop, { once: true });
|
|
246
|
+
publish(this.view(attempt));
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
answer(userId, id, promptId, value) {
|
|
250
|
+
const attempt = this.owned(userId, id);
|
|
251
|
+
if (attempt.view.state !== "pending" || attempt.view.prompt?.id !== promptId || !attempt.answer)
|
|
252
|
+
throw new AgentError("This sign-in prompt has expired.", 409);
|
|
253
|
+
const input = attempt.view.prompt.input;
|
|
254
|
+
// Text prompts can intentionally accept blank (e.g. Copilot's default
|
|
255
|
+
// github.com domain). Secret/code/select prompts always require a value.
|
|
256
|
+
if (value.length > 16_000 || (input.type !== "text" && !value.trim()))
|
|
257
|
+
throw new AgentError("Enter a valid sign-in response (maximum 16000 characters).");
|
|
258
|
+
if (input.type === "select" && !input.options.some((option) => option.id === value.trim()))
|
|
259
|
+
throw new AgentError("Select one of the sign-in options.");
|
|
260
|
+
// Copilot's native enterprise prompt otherwise permits arbitrary backend
|
|
261
|
+
// network destinations. Custom endpoint configuration is not in this slice.
|
|
262
|
+
if (attempt.view.providerId === "github-copilot" && input.type === "text" && value.trim() && value.trim() !== "github.com") {
|
|
263
|
+
throw new AgentError("Leave the domain blank for github.com. Custom enterprise endpoints are not supported yet.");
|
|
264
|
+
}
|
|
265
|
+
attempt.answer(value.trim());
|
|
266
|
+
return this.view(attempt);
|
|
267
|
+
}
|
|
268
|
+
cancelAttempt(attempt) {
|
|
269
|
+
if (attempt.view.state === "pending") {
|
|
270
|
+
attempt.view.state = "cancelled";
|
|
271
|
+
attempt.controller.abort();
|
|
272
|
+
delete attempt.view.prompt;
|
|
273
|
+
delete attempt.answer;
|
|
274
|
+
this.publish(attempt);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
async cancel(userId, id) {
|
|
278
|
+
const attempt = this.owned(userId, id);
|
|
279
|
+
this.cancelAttempt(attempt);
|
|
280
|
+
await attempt.done;
|
|
281
|
+
}
|
|
282
|
+
async close() {
|
|
283
|
+
this.stopping = true;
|
|
284
|
+
this.shutdown.abort();
|
|
285
|
+
for (const attempt of this.attempts.values()) {
|
|
286
|
+
clearTimeout(attempt.timer);
|
|
287
|
+
this.cancelAttempt(attempt);
|
|
288
|
+
}
|
|
289
|
+
await Promise.all([...this.attempts.values()].map((attempt) => attempt.done));
|
|
290
|
+
for (const user of this.users.values())
|
|
291
|
+
await (await user).credentials.drain();
|
|
292
|
+
this.attempts.clear();
|
|
293
|
+
this.users.clear();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { AgentError } from "../agents/agent-input.js";
|
|
3
|
+
export const artifactTypes = ["text/markdown", "text/plain", "text/csv", "application/json", "image/png", "image/jpeg", "image/webp"];
|
|
4
|
+
const columns = `id,agent_id AS agentId,title,description,media_type AS mediaType,size,tags,instrument,interval,created_at AS createdAt,expires_at AS expiresAt,conversation_id AS conversationId,task_id AS taskId,EXISTS(SELECT 1 FROM hv_memory_artifacts m WHERE m.agent_id=a.agent_id AND (m.artifact_id=a.id OR m.artifact_id IN(SELECT parent FROM hv_artifact_links WHERE child=a.id))) AS protectedByMemory`;
|
|
5
|
+
// Only a one-level report -> attachment graph is allowed. An unexpired report
|
|
6
|
+
// protects its children even when their own temporary retention has elapsed.
|
|
7
|
+
export const artifactVisibleSql = `(a.expires_at IS NULL OR a.expires_at>? OR EXISTS(SELECT 1 FROM hv_memory_artifacts m WHERE m.agent_id=a.agent_id AND m.artifact_id=a.id) OR EXISTS(SELECT 1 FROM hv_artifact_links l JOIN hv_artifacts p ON p.id=l.parent WHERE l.child=a.id AND (p.expires_at IS NULL OR p.expires_at>? OR EXISTS(SELECT 1 FROM hv_memory_artifacts m WHERE m.agent_id=p.agent_id AND m.artifact_id=p.id))))`;
|
|
8
|
+
const visible = artifactVisibleSql;
|
|
9
|
+
const memoryProtected = `SELECT 1 FROM hv_memory_artifacts m WHERE m.agent_id=? AND (m.artifact_id=? OR m.artifact_id IN (SELECT parent FROM hv_artifact_links WHERE child=?)) LIMIT 1`;
|
|
10
|
+
const meta = (row) => ({ ...row, tags: JSON.parse(row.tags), protectedByMemory: Boolean(row.protectedByMemory) });
|
|
11
|
+
const maxBytes = 5 * 1024 * 1024;
|
|
12
|
+
function text(value, max, label) { if (typeof value !== "string" || value.length > max)
|
|
13
|
+
throw new AgentError(`Invalid ${label}`); return value.trim(); }
|
|
14
|
+
/** Payloads are immutable, root-Agent scoped and never stored in Pi documents. */
|
|
15
|
+
export class ArtifactService {
|
|
16
|
+
db;
|
|
17
|
+
constructor(db) {
|
|
18
|
+
this.db = db;
|
|
19
|
+
}
|
|
20
|
+
async cleanupTx(tx, now) {
|
|
21
|
+
// Delete expired parents first; child references no longer protect them.
|
|
22
|
+
await tx.run(`DELETE FROM hv_artifacts WHERE id IN(SELECT a.id FROM hv_artifacts a WHERE NOT ${visible} LIMIT 100)`, now, now);
|
|
23
|
+
}
|
|
24
|
+
async cleanup(now = Date.now()) {
|
|
25
|
+
await this.db.transaction((tx) => this.cleanupTx(tx, now));
|
|
26
|
+
// No implicit rebuild of an existing NONE database. Fresh profiles can
|
|
27
|
+
// reclaim space in small batches; WAL checkpointing remains separate.
|
|
28
|
+
await this.db.all("PRAGMA incremental_vacuum(256)");
|
|
29
|
+
}
|
|
30
|
+
async create(agentId, operationKey, input, producer = {}) {
|
|
31
|
+
const title = text(input.title, 160, "title");
|
|
32
|
+
if (!title || !artifactTypes.includes(input.mediaType))
|
|
33
|
+
throw new AgentError("Invalid artifact type/title");
|
|
34
|
+
const description = text(input.description ?? "", 1000, "description");
|
|
35
|
+
if (typeof input.content !== "string" || input.content.length > maxBytes * 1.4)
|
|
36
|
+
throw new AgentError("Artifact exceeds 5 MiB");
|
|
37
|
+
const image = input.mediaType.startsWith("image/");
|
|
38
|
+
if (image !== (input.encoding === "base64"))
|
|
39
|
+
throw new AgentError("Images require base64; text requires UTF-8");
|
|
40
|
+
const payload = Buffer.from(input.content, image ? "base64" : "utf8");
|
|
41
|
+
// Round-trip validation avoids permissive decoding and deep-regex stack
|
|
42
|
+
// exhaustion on multi-megabyte images.
|
|
43
|
+
if (image && payload.toString("base64") !== input.content)
|
|
44
|
+
throw new AgentError("Invalid base64");
|
|
45
|
+
if (!payload.length || payload.length > maxBytes)
|
|
46
|
+
throw new AgentError("Artifacts must contain 1 byte to 5 MiB");
|
|
47
|
+
if (image) {
|
|
48
|
+
const valid = input.mediaType === "image/png" ? payload.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])) : input.mediaType === "image/jpeg" ? payload[0] === 255 && payload[1] === 216 && payload[2] === 255 : payload.toString("ascii", 0, 4) === "RIFF" && payload.toString("ascii", 8, 12) === "WEBP";
|
|
49
|
+
if (!valid)
|
|
50
|
+
throw new AgentError("Image signature does not match its type");
|
|
51
|
+
}
|
|
52
|
+
if (input.mediaType === "application/json") {
|
|
53
|
+
try {
|
|
54
|
+
JSON.parse(input.content);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
throw new AgentError("Invalid JSON artifact");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (input.tags && (!Array.isArray(input.tags) || input.tags.length > 12))
|
|
61
|
+
throw new AgentError("At most 12 tags");
|
|
62
|
+
const tags = [...new Set((input.tags ?? []).map((tag) => text(tag, 40, "tag").toLowerCase()))].filter(Boolean).sort();
|
|
63
|
+
const instrument = text(input.instrument ?? "", 64, "instrument"), interval = text(input.interval ?? "", 8, "interval");
|
|
64
|
+
const embedded = input.mediaType === "text/markdown" ? [...input.content.matchAll(/artifact:([a-f0-9-]{36})/g)].map((match) => match[1]) : [];
|
|
65
|
+
const attachments = [...new Set([...(input.attachments ?? []), ...embedded])].sort();
|
|
66
|
+
if (attachments.length > 20 || attachments.some((id) => !/^[a-f0-9-]{36}$/.test(id)))
|
|
67
|
+
throw new AgentError("Invalid attachments (maximum 20)");
|
|
68
|
+
if (attachments.length && input.mediaType !== "text/markdown")
|
|
69
|
+
throw new AgentError("Only Markdown reports may contain attachments");
|
|
70
|
+
const hash = createHash("sha256").update(JSON.stringify({ title, description, mediaType: input.mediaType, tags, instrument, interval, attachments, retained: Boolean(input.retained) })).update(payload).digest("hex");
|
|
71
|
+
return this.db.transaction(async (tx) => {
|
|
72
|
+
const existing = await tx.get("SELECT artifact_id AS id,request_hash FROM hv_artifact_receipts WHERE agent_id=? AND operation_key=?", agentId, operationKey);
|
|
73
|
+
if (existing) {
|
|
74
|
+
if (existing.request_hash !== hash)
|
|
75
|
+
throw new AgentError("Artifact operation key reused with different content", 409);
|
|
76
|
+
return this.metadata(tx, agentId, existing.id);
|
|
77
|
+
}
|
|
78
|
+
await this.cleanupTx(tx, Date.now());
|
|
79
|
+
for (const id of attachments) {
|
|
80
|
+
const child = await this.metadata(tx, agentId, id);
|
|
81
|
+
if (child.mediaType === "text/markdown")
|
|
82
|
+
throw new AgentError("Nested reports are not supported");
|
|
83
|
+
}
|
|
84
|
+
const usage = await tx.get("SELECT coalesce(sum(size),0) AS bytes,count(*) AS total FROM hv_artifacts WHERE agent_id=?", agentId);
|
|
85
|
+
if ((usage?.bytes ?? 0) + payload.length > 100 * 1024 * 1024 || (usage?.total ?? 0) >= 2000)
|
|
86
|
+
throw new AgentError("Agent artifact quota reached. Delete old artifacts.", 409);
|
|
87
|
+
const id = randomUUID(), now = Date.now();
|
|
88
|
+
await tx.run(`INSERT INTO hv_artifacts(id,agent_id,title,description,media_type,payload,size,tags,instrument,interval,conversation_id,task_id,created_at,expires_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)`, id, agentId, title, description, input.mediaType, payload, payload.length, JSON.stringify(tags), instrument, interval, producer.conversationId ?? null, producer.taskId ?? null, now, input.retained ? null : now + 7 * 86400_000);
|
|
89
|
+
await tx.run("INSERT INTO hv_artifact_receipts(agent_id,operation_key,request_hash,artifact_id) VALUES (?,?,?,?)", agentId, operationKey, hash, id);
|
|
90
|
+
for (const child of attachments)
|
|
91
|
+
await tx.run("INSERT INTO hv_artifact_links(parent,child) VALUES (?,?)", id, child);
|
|
92
|
+
return this.metadata(tx, agentId, id);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/** Trusted tool task identity is immutable; reuse its original exported snapshot. */
|
|
96
|
+
async operation(agentId, operationKey) {
|
|
97
|
+
return this.db.transaction(async (tx) => {
|
|
98
|
+
const receipt = await tx.get("SELECT artifact_id AS id FROM hv_artifact_receipts WHERE agent_id=? AND operation_key=?", agentId, operationKey);
|
|
99
|
+
return receipt ? this.metadata(tx, agentId, receipt.id) : undefined;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
async metadata(tx, agentId, id) {
|
|
103
|
+
const now = Date.now();
|
|
104
|
+
const row = await tx.get(`SELECT ${columns} FROM hv_artifacts a WHERE a.agent_id=? AND a.id=? AND ${visible}`, agentId, id, now, now);
|
|
105
|
+
if (!row)
|
|
106
|
+
throw new AgentError("Artifact unavailable or expired", 404);
|
|
107
|
+
return meta(row);
|
|
108
|
+
}
|
|
109
|
+
async list(agentId, filters = {}) {
|
|
110
|
+
const offset = filters.offset ?? 0;
|
|
111
|
+
if (!Number.isSafeInteger(offset) || offset < 0 || offset > 10000)
|
|
112
|
+
throw new AgentError("Invalid artifact offset");
|
|
113
|
+
const now = Date.now();
|
|
114
|
+
const rows = await this.db.all(`SELECT ${columns} FROM hv_artifacts a WHERE agent_id=? AND ${visible} AND (?='' OR instrument=?) AND (?='' OR interval=?) AND (?='' OR EXISTS(SELECT 1 FROM json_each(a.tags) WHERE value=?)) AND (?='' OR instr(lower(title||' '||description),lower(?))>0) ORDER BY created_at DESC,id DESC LIMIT 51 OFFSET ?`, agentId, now, now, filters.instrument ?? "", filters.instrument ?? "", filters.interval ?? "", filters.interval ?? "", filters.tag ?? "", filters.tag ?? "", filters.query ?? "", filters.query ?? "", offset);
|
|
115
|
+
return { items: rows.slice(0, 50).map(meta), nextOffset: rows.length > 50 ? offset + 50 : null };
|
|
116
|
+
}
|
|
117
|
+
async get(agentId, id) {
|
|
118
|
+
return this.db.transaction(async (tx) => {
|
|
119
|
+
const item = await this.metadata(tx, agentId, id);
|
|
120
|
+
const row = (await tx.get("SELECT payload FROM hv_artifacts WHERE id=? AND agent_id=?", id, agentId));
|
|
121
|
+
const attachments = await tx.all("SELECT a.id,a.title,a.media_type AS mediaType FROM hv_artifact_links l JOIN hv_artifacts a ON a.id=l.child WHERE l.parent=? AND a.agent_id=?", id, agentId);
|
|
122
|
+
return { ...item, payload: Buffer.from(row.payload), attachments };
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
async read(agentId, id, offset = 0, limit = 16000) {
|
|
126
|
+
if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(limit) || limit < 1 || limit > 16000)
|
|
127
|
+
throw new AgentError("Invalid read bounds");
|
|
128
|
+
const { payload, attachments, ...item } = await this.get(agentId, id);
|
|
129
|
+
const content = item.mediaType.startsWith("image/") ? null : payload.toString("utf8");
|
|
130
|
+
return { ...item, attachments, content: content?.slice(offset, offset + limit) ?? null, nextOffset: content && offset + limit < content.length ? offset + limit : null };
|
|
131
|
+
}
|
|
132
|
+
async retention(agentId, id, retained) {
|
|
133
|
+
await this.db.transaction(async (tx) => {
|
|
134
|
+
await this.metadata(tx, agentId, id);
|
|
135
|
+
if (!retained && await tx.get(memoryProtected, agentId, id, id))
|
|
136
|
+
throw new AgentError("Artifact is evidence referenced by a memory; retention cannot be removed.", 409);
|
|
137
|
+
await tx.run("UPDATE hv_artifacts SET expires_at=? WHERE agent_id=? AND id=?", retained ? null : Date.now() + 7 * 86400_000, agentId, id);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
async remove(agentId, id) {
|
|
141
|
+
await this.db.transaction(async (tx) => {
|
|
142
|
+
await this.metadata(tx, agentId, id);
|
|
143
|
+
if (await tx.get(memoryProtected, agentId, id, id))
|
|
144
|
+
throw new AgentError("Artifact is evidence referenced by a memory and cannot be deleted.", 409);
|
|
145
|
+
const ref = await tx.get("SELECT l.parent FROM hv_artifact_links l JOIN hv_artifacts p ON p.id=l.parent WHERE l.child=? AND (p.expires_at IS NULL OR p.expires_at>? OR EXISTS(SELECT 1 FROM hv_memory_artifacts m WHERE m.agent_id=p.agent_id AND m.artifact_id=p.id)) LIMIT 1", id, Date.now());
|
|
146
|
+
if (ref)
|
|
147
|
+
throw new AgentError("Artifact is attached to a report. Delete the report first.", 409);
|
|
148
|
+
await tx.run("DELETE FROM hv_artifacts WHERE agent_id=? AND id=?", agentId, id);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Per-script FIFO admission. Closing rejects waiters; active operations own their cancellation. */
|
|
2
|
+
export class CallQueue {
|
|
3
|
+
concurrency;
|
|
4
|
+
active = 0;
|
|
5
|
+
waiting = [];
|
|
6
|
+
failure;
|
|
7
|
+
constructor(concurrency) {
|
|
8
|
+
this.concurrency = concurrency;
|
|
9
|
+
if (!Number.isSafeInteger(concurrency) || concurrency < 1)
|
|
10
|
+
throw new Error("Invalid call concurrency");
|
|
11
|
+
}
|
|
12
|
+
get closed() { return this.failure !== undefined; }
|
|
13
|
+
get saturated() { return this.active >= this.concurrency; }
|
|
14
|
+
run(operation) {
|
|
15
|
+
if (this.failure)
|
|
16
|
+
return Promise.reject(this.failure.reason);
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
const start = () => {
|
|
19
|
+
this.active++;
|
|
20
|
+
void Promise.resolve().then(() => {
|
|
21
|
+
if (this.failure)
|
|
22
|
+
throw this.failure.reason;
|
|
23
|
+
return operation();
|
|
24
|
+
}).then(resolve, reject).finally(() => {
|
|
25
|
+
this.active--;
|
|
26
|
+
if (!this.failure)
|
|
27
|
+
this.waiting.shift()?.start();
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
if (this.active < this.concurrency)
|
|
31
|
+
start();
|
|
32
|
+
else
|
|
33
|
+
this.waiting.push({ start, reject });
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
close(reason) {
|
|
37
|
+
if (this.failure)
|
|
38
|
+
return;
|
|
39
|
+
this.failure = { reason };
|
|
40
|
+
for (const waiter of this.waiting.splice(0))
|
|
41
|
+
waiter.reject(reason);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Type } from "@earendil-works/pi-ai";
|
|
2
|
+
const nonnegativeInteger = Type.Integer({ minimum: 0 });
|
|
3
|
+
const nullableInteger = Type.Union([nonnegativeInteger, Type.Null()]);
|
|
4
|
+
const epochSeconds = Type.Integer({ minimum: 0, description: "Unix epoch seconds (UTC)" });
|
|
5
|
+
const nullableEpochSeconds = Type.Union([epochSeconds, Type.Null()]);
|
|
6
|
+
const epochMilliseconds = Type.Integer({ minimum: 0, description: "Unix epoch milliseconds (UTC)" });
|
|
7
|
+
const nullableEpochMilliseconds = Type.Union([epochMilliseconds, Type.Null()]);
|
|
8
|
+
const nullableMoney = Type.Union([Type.String(), Type.Null()]);
|
|
9
|
+
const metadataFields = {
|
|
10
|
+
id: Type.String(), agentId: Type.String(), title: Type.String(), description: Type.String(), mediaType: Type.String(), size: Type.Integer({ minimum: 1 }),
|
|
11
|
+
tags: Type.Array(Type.String(), { maxItems: 12 }), instrument: Type.String(), interval: Type.String(), createdAt: epochMilliseconds, expiresAt: nullableEpochMilliseconds,
|
|
12
|
+
conversationId: nullableInteger, taskId: nullableInteger, protectedByMemory: Type.Optional(Type.Boolean()),
|
|
13
|
+
};
|
|
14
|
+
export const ArtifactMetaSchema = Type.Object(metadataFields);
|
|
15
|
+
export const ArtifactReadSchema = Type.Object({ ...metadataFields,
|
|
16
|
+
attachments: Type.Array(Type.Object({ id: Type.String(), title: Type.String(), mediaType: Type.String() })),
|
|
17
|
+
content: Type.Union([Type.String(), Type.Null()]), nextOffset: nullableInteger,
|
|
18
|
+
});
|
|
19
|
+
export const ArtifactListSchema = Type.Object({ items: Type.Array(ArtifactMetaSchema, { maxItems: 50 }), nextOffset: nullableInteger });
|
|
20
|
+
export const ExportSchema = Type.Object({ artifact: ArtifactMetaSchema, reference: Type.String(), summary: Type.Record(Type.String(), Type.Unknown()) });
|
|
21
|
+
export const CandleCoverageSchema = Type.Object({ complete: Type.Boolean(), returned: nonnegativeInteger, expected: Type.Number({ minimum: 0 }), sourceLimit: Type.String(), closedOnly: Type.Literal(true) });
|
|
22
|
+
export const DatasetSchema = Type.Object({ count: nonnegativeInteger, firstTime: nullableEpochSeconds, lastTime: nullableEpochSeconds });
|
|
23
|
+
const candle = Type.Object({ time: Type.Number({ description: "Candle open time in Unix epoch seconds (UTC)" }), open: Type.Number(), high: Type.Number(), low: Type.Number(), close: Type.Number(), volume: Type.Number() });
|
|
24
|
+
export const CandleResultSchema = Type.Union([Type.Object({ coin: Type.String(), interval: Type.String(), start: epochMilliseconds, end: epochMilliseconds, pageEnd: epochMilliseconds, dataset: DatasetSchema, candles: Type.Array(candle, { maxItems: 2000 }), nextStart: nullableEpochMilliseconds, coverage: CandleCoverageSchema }), ExportSchema]);
|
|
25
|
+
const trade = Type.Object({
|
|
26
|
+
id: Type.String(), symbol: Type.String(), direction: Type.String(), status: Type.String(), openedAt: epochMilliseconds, lastActivityAt: epochMilliseconds, closedAt: nullableEpochMilliseconds,
|
|
27
|
+
remainingSize: Type.String(), entrySize: Type.String(), exitSize: Type.String(), entryNotional: Type.String(), exitNotional: Type.String(),
|
|
28
|
+
grossPnl: Type.String(), fees: Type.String(), funding: nullableMoney, net: nullableMoney, feeCoverage: Type.Boolean(), coverageReason: Type.Union([Type.String(), Type.Null()]),
|
|
29
|
+
});
|
|
30
|
+
export const JournalResultSchema = Type.Union([Type.Object({
|
|
31
|
+
trades: Type.Optional(Type.Array(trade, { maxItems: 1000 })), next: Type.Union([Type.Object({ time: epochMilliseconds, id: Type.String() }), Type.Null()]),
|
|
32
|
+
performance: Type.Object({ scope: Type.String(), closed: nonnegativeInteger, knownNetCount: nonnegativeInteger, knownNetSum: Type.String(), knownNetWins: nonnegativeInteger, grossPnl: Type.String(), fees: Type.String() }),
|
|
33
|
+
coverage: Type.Object({ complete: Type.Boolean(), historyLimited: Type.Boolean(), filter: Type.String(), truncated: Type.Boolean(),
|
|
34
|
+
catchUp: Type.Optional(Type.Object({ attempted: Type.Boolean(), limitation: Type.Union([Type.String(), Type.Null()]) })),
|
|
35
|
+
sync: Type.Array(Type.Object({ stream: Type.String(), cursor: epochMilliseconds, updated_at: nullableEpochMilliseconds, error: Type.Union([Type.String(), Type.Null()]) })),
|
|
36
|
+
}),
|
|
37
|
+
}), ExportSchema]);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Actual candle-open bounds in UTC seconds, not requested millisecond boundaries. */
|
|
2
|
+
export function describeDataset(rows) {
|
|
3
|
+
return { count: rows.length, firstTime: rows[0]?.time ?? null, lastTime: rows.at(-1)?.time ?? null };
|
|
4
|
+
}
|
|
5
|
+
export const utcFormattingExample = `function utcMilliseconds(value) {
|
|
6
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) return null;
|
|
7
|
+
const date = new Date(value);
|
|
8
|
+
return Number.isFinite(date.getTime()) ? date.toISOString() : null;
|
|
9
|
+
}
|
|
10
|
+
function utcSeconds(value) { return typeof value === 'number' ? utcMilliseconds(value * 1000) : null; }
|
|
11
|
+
function datasetEvidence(dataset) {
|
|
12
|
+
return dataset ? {...dataset, firstTimeUtc:utcSeconds(dataset.firstTime ?? dataset.firstOpenTime), lastTimeUtc:utcSeconds(dataset.lastTime ?? dataset.lastOpenTime)} : null;
|
|
13
|
+
}`;
|
|
14
|
+
export const technicalEvidenceExample = `${utcFormattingExample}
|
|
15
|
+
const latest = candleResult.candles.at(-1);
|
|
16
|
+
const priceValid = candleResult.coverage.complete && candleResult.nextStart === null && latest && Number.isFinite(latest.close);
|
|
17
|
+
function scalar(result, indicator, period, output) {
|
|
18
|
+
const point = result.values?.at(-1), value = point?.value?.[output];
|
|
19
|
+
return priceValid && result.coin === candleResult.coin && result.interval === candleResult.interval &&
|
|
20
|
+
result.indicatorId === 'builtin:' + indicator && result.resolvedInputs.period === period &&
|
|
21
|
+
(!('source' in result.resolvedInputs) || result.resolvedInputs.source === 'close') &&
|
|
22
|
+
result.coverage.complete && point?.time === latest.time && typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
23
|
+
}
|
|
24
|
+
const ema20 = scalar(ema20Result, 'ema', 20, 'EMA'), ema50 = scalar(ema50Result, 'ema', 50, 'EMA');
|
|
25
|
+
const atr = scalar(atrResult, 'atr', atrResult.resolvedInputs?.period, 'ATR');
|
|
26
|
+
function provenance(result) {
|
|
27
|
+
return {coin:result.coin, interval:result.interval, requestedStartUtc:utcMilliseconds(result.start), effectiveEndExclusiveUtc:utcMilliseconds(result.end),
|
|
28
|
+
input:datasetEvidence(result.dataset), output:datasetEvidence(result.outputDataset), coverage:result.coverage,
|
|
29
|
+
inputs:result.resolvedInputs ?? null, version:result.version ?? null, sourceHash:result.sourceHash ?? null, resultHash:result.resultHash ?? null};
|
|
30
|
+
}
|
|
31
|
+
text({datasets:{candles:provenance(candleResult), ema20:provenance(ema20Result), ema50:provenance(ema50Result), atr:provenance(atrResult)},
|
|
32
|
+
latestClosed:latest ? {...latest, openTimeUtc:utcSeconds(latest.time)} : null,
|
|
33
|
+
facts:{priceBelowEma20:ema20 === null ? null : latest.close < ema20,
|
|
34
|
+
ema20AboveEma50:ema20 === null || ema50 === null ? null : ema20 > ema50,
|
|
35
|
+
atrPercent:atr === null || latest.close <= 0 ? null : atr / latest.close * 100}});`;
|
|
36
|
+
export const bollingerEvidenceExample = `const bandPoint = bollingerResult.values?.at(-1), bands = bandPoint?.value;
|
|
37
|
+
const bandsValid = priceValid && bollingerResult.coin === candleResult.coin && bollingerResult.interval === candleResult.interval &&
|
|
38
|
+
bollingerResult.indicatorId === 'builtin:bollinger' && bollingerResult.resolvedInputs.source === 'close' &&
|
|
39
|
+
bollingerResult.coverage.complete && bandPoint?.time === latest.time && bands &&
|
|
40
|
+
['Lower','Middle','Upper'].every(key => typeof bands[key] === 'number' && Number.isFinite(bands[key])) && bands.Lower <= bands.Middle && bands.Middle <= bands.Upper;
|
|
41
|
+
text({bollinger:{openTimeUtc:utcSeconds(bandPoint?.time), value:bandsValid ? bands : null, provenance:provenance(bollingerResult)},
|
|
42
|
+
facts:{priceAboveUpperBand:bandsValid ? latest.close > bands.Upper : null, priceBelowLowerBand:bandsValid ? latest.close < bands.Lower : null}});`;
|
|
43
|
+
export const technicalEvidenceInstructions = `Calculate comparison flags in Code Mode before writing prose; Check the final prose against the flags. Facts are not forecasts. Quote the emitted ISO UTC strings verbatim. Request start/end are milliseconds, candle-open times are seconds. All indicators use point.value.<outputId>: EMA, ATR, RSI, ADX, MACD/Signal/Histogram and Lower/Middle/Upper. Null/missing/nonfinite/misaligned/non-close-source evidence is unknown, never zero or fabricated false. Report actual seed/output bounds, coverage, resolvedInputs, concrete version/sourceHash/resultHash and per-output finite availability. A finite value does not prove initialization effects have disappeared. About 5× the longest period plus trailing history is a seed recommendation, not universal stability. A one-value request still computes the entire seed window. No universal stable/requiredInputs claim exists for arbitrary PineScript. Use indicator_get for exact keys. Example (bind these results using selected tools on a common closed window):\n\n${technicalEvidenceExample}\n\n${bollingerEvidenceExample}`;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { amount } from "../hyperliquid/journal-decimal.js";
|
|
2
|
+
import { AgentError } from "../agents/agent-input.js";
|
|
3
|
+
/** Reads only application journal tables; no private harness storage. */
|
|
4
|
+
export async function researchJournal(db, agentId, start, end, limit, before) {
|
|
5
|
+
if (![start, end, limit].every(Number.isSafeInteger) || start < 0 || end <= start || limit < 1 || limit > 1000 || (before && (!Number.isSafeInteger(before.time) || before.id.length > 128)))
|
|
6
|
+
throw new AgentError("Invalid journal bounds");
|
|
7
|
+
return db.transaction(async (tx) => {
|
|
8
|
+
const agent = await tx.get("SELECT trading_account_address AS account FROM hv_agents WHERE id=?", agentId);
|
|
9
|
+
if (!agent?.account)
|
|
10
|
+
throw new AgentError("Agent has no trading account");
|
|
11
|
+
const account = agent.account.toLowerCase();
|
|
12
|
+
const rows = await tx.all(`SELECT id,time,json_remove(data,'$.parts') AS data FROM hv_journal_entries WHERE account=? AND kind='trade' AND active=1 AND time>=? AND time<? AND (time<? OR (time=? AND id<?)) ORDER BY time DESC,id DESC LIMIT ?`, account, start, end, before?.time ?? end, before?.time ?? end, before?.id ?? "", limit + 1);
|
|
13
|
+
const sync = await tx.all("SELECT stream,cursor,updated_at,error FROM hv_journal_sync WHERE account=?", account);
|
|
14
|
+
const state = await tx.get("SELECT revision,projected_revision,history_limited FROM hv_journal_accounts WHERE account=?", account);
|
|
15
|
+
const complete = Boolean(state && !state.history_limited && state.revision === state.projected_revision && sync.length === 3 && sync.every((row) => row.updated_at && !row.error && row.cursor >= end));
|
|
16
|
+
const trades = rows.slice(0, limit).map((row) => {
|
|
17
|
+
const trade = JSON.parse(row.data);
|
|
18
|
+
return { ...trade, funding: complete ? trade.funding : null, net: complete ? trade.net : null };
|
|
19
|
+
});
|
|
20
|
+
const closed = trades.filter((trade) => trade.status === "closed");
|
|
21
|
+
const knownNet = closed.filter((trade) => trade.net !== null);
|
|
22
|
+
const sum = (values) => values.reduce((total, value) => total.add(amount(value)), amount("0")).toString();
|
|
23
|
+
const tail = rows[Math.min(limit, rows.length) - 1];
|
|
24
|
+
return { trades, next: rows.length > limit && tail ? { time: tail.time, id: tail.id } : null,
|
|
25
|
+
performance: { scope: "returned closed trades only; not account equity/returns", closed: closed.length, knownNetCount: knownNet.length,
|
|
26
|
+
knownNetSum: sum(knownNet.map((trade) => trade.net)), knownNetWins: knownNet.filter((trade) => amount(trade.net).sign > 0).length,
|
|
27
|
+
grossPnl: sum(closed.map((trade) => trade.grossPnl)), fees: sum(closed.map((trade) => trade.fees)) },
|
|
28
|
+
coverage: { complete, historyLimited: state?.history_limited === 1, sync, filter: "lastActivityAt in [start,end); persistent trade projections may change during sync", truncated: rows.length > limit } };
|
|
29
|
+
});
|
|
30
|
+
}
|