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,117 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { AgentError } from "../agents/agent-input.js";
|
|
3
|
+
import { insightMutation, insightScope } from "./insight-contracts.js";
|
|
4
|
+
import { memoryRef, parseMemoryRef, timestamp } from "./memory-contracts.js";
|
|
5
|
+
import { assertActiveActor, operationReplay, requestHash, saveOperation } from "./operations.js";
|
|
6
|
+
import { fields, integer } from "./validation.js";
|
|
7
|
+
export class InsightConflict extends AgentError {
|
|
8
|
+
currentVersion;
|
|
9
|
+
constructor(currentVersion) {
|
|
10
|
+
super(`Insights changed to v${currentVersion}; reread and reconcile your draft`, 409);
|
|
11
|
+
this.currentVersion = currentVersion;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class InsightService {
|
|
15
|
+
db;
|
|
16
|
+
authorize;
|
|
17
|
+
clock;
|
|
18
|
+
constructor(db, authorize, clock = Date.now) {
|
|
19
|
+
this.db = db;
|
|
20
|
+
this.authorize = authorize;
|
|
21
|
+
this.clock = clock;
|
|
22
|
+
}
|
|
23
|
+
async checkScope(tx, agentId, scope) {
|
|
24
|
+
const owner = await tx.get("SELECT id FROM hv_agents WHERE id=?", agentId);
|
|
25
|
+
if (!owner)
|
|
26
|
+
throw new AgentError("Agent not found", 404);
|
|
27
|
+
if (scope.kind === "worker" && !await tx.get("SELECT id FROM hv_subagents WHERE agent_id=? AND id=? AND kind='worker'", agentId, scope.subagentId))
|
|
28
|
+
throw new AgentError("Worker not found in this Agent", 404);
|
|
29
|
+
}
|
|
30
|
+
set(tx, agentId, scope) {
|
|
31
|
+
return tx.get("SELECT id,current_version FROM hv_insight_sets WHERE agent_id=? AND scope_kind=? AND subagent_id IS ?", agentId, scope.kind, scope.kind === "worker" ? scope.subagentId : null);
|
|
32
|
+
}
|
|
33
|
+
/** Can be used inside the owning admission transaction, without re-entering the queue. */
|
|
34
|
+
async snapshotTx(tx, agentId, scope) {
|
|
35
|
+
await this.checkScope(tx, agentId, scope);
|
|
36
|
+
const set = await this.set(tx, agentId, scope);
|
|
37
|
+
if (!set || set.current_version === 0)
|
|
38
|
+
return { scope, version: 0, content: "" };
|
|
39
|
+
const row = await tx.get("SELECT content FROM hv_insight_revisions WHERE agent_id=? AND set_id=? AND version=?", agentId, set.id, set.current_version);
|
|
40
|
+
if (!row)
|
|
41
|
+
throw new Error("Insight head has no revision");
|
|
42
|
+
return { scope, version: set.current_version, content: row.content };
|
|
43
|
+
}
|
|
44
|
+
current(agentId, value) {
|
|
45
|
+
const scope = insightScope(value);
|
|
46
|
+
return this.db.transaction((tx) => this.snapshotTx(tx, agentId, scope));
|
|
47
|
+
}
|
|
48
|
+
append(actor, key, input) { return this.mutate(actor, key, "append", input); }
|
|
49
|
+
replace(actor, key, input) { return this.mutate(actor, key, "replace", input); }
|
|
50
|
+
async mutate(actor, key, operation, value) {
|
|
51
|
+
const input = insightMutation(value, operation), kind = operation === "append" ? "append_insight" : "replace_insights";
|
|
52
|
+
const hash = requestHash(actor, kind, input);
|
|
53
|
+
return this.db.transaction(async (tx) => {
|
|
54
|
+
const replay = await operationReplay(tx, actor, key, kind, hash);
|
|
55
|
+
if (replay)
|
|
56
|
+
return replay;
|
|
57
|
+
const now = timestamp(this.clock());
|
|
58
|
+
await assertActiveActor(tx, actor);
|
|
59
|
+
await this.authorize(tx, actor, kind, input.scope.kind === "worker" ? input.scope.subagentId : null);
|
|
60
|
+
await this.checkScope(tx, actor.agentId, input.scope);
|
|
61
|
+
let set = await this.set(tx, actor.agentId, input.scope);
|
|
62
|
+
if (!set) {
|
|
63
|
+
set = { id: randomUUID(), current_version: 0 };
|
|
64
|
+
await tx.run("INSERT INTO hv_insight_sets(id,agent_id,scope_kind,subagent_id,current_version,updated_at) VALUES (?,?,?,?,0,?)", set.id, actor.agentId, input.scope.kind, input.scope.kind === "worker" ? input.scope.subagentId : null, now);
|
|
65
|
+
}
|
|
66
|
+
if (operation === "replace" && set.current_version !== input.expectedVersion)
|
|
67
|
+
throw new InsightConflict(set.current_version);
|
|
68
|
+
let content = input.content;
|
|
69
|
+
const refs = new Set(input.evidenceRefs.map(parseMemoryRef));
|
|
70
|
+
if (operation === "append" && set.current_version > 0) {
|
|
71
|
+
const previous = await tx.get("SELECT content FROM hv_insight_revisions WHERE agent_id=? AND set_id=? AND version=?", actor.agentId, set.id, set.current_version);
|
|
72
|
+
if (!previous)
|
|
73
|
+
throw new Error("Insight head has no revision");
|
|
74
|
+
content = previous.content ? `${previous.content}\n\n---\n\n${content}` : content;
|
|
75
|
+
const inherited = await tx.all("SELECT memory_number FROM hv_insight_revision_memories WHERE agent_id=? AND set_id=? AND version=?", actor.agentId, set.id, set.current_version);
|
|
76
|
+
for (const ref of inherited)
|
|
77
|
+
refs.add(ref.memory_number);
|
|
78
|
+
}
|
|
79
|
+
if (content.length > 8000)
|
|
80
|
+
throw new AgentError("Insight document exceeds 8,000 characters; reread and consolidate using replace", 409);
|
|
81
|
+
for (const ref of refs)
|
|
82
|
+
if (!await tx.get("SELECT number FROM hv_memories WHERE agent_id=? AND number=?", actor.agentId, ref))
|
|
83
|
+
throw new AgentError("Evidence memory not found in this Agent", 404);
|
|
84
|
+
const version = integer(set.current_version + 1, 1, Number.MAX_SAFE_INTEGER - 1, "insight version");
|
|
85
|
+
await tx.run("INSERT INTO hv_insight_revisions(agent_id,set_id,version,content,operation,reason,created_at,actor_user_id,producer_subagent_id,source_run_id,conversation_id,tool_task_id) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", actor.agentId, set.id, version, content, operation, input.reason, now, actor.userId, actor.subagentId, actor.runId, actor.conversationId, actor.toolTaskId);
|
|
86
|
+
for (const ref of refs)
|
|
87
|
+
await tx.run("INSERT INTO hv_insight_revision_memories(agent_id,set_id,version,memory_number) VALUES (?,?,?,?)", actor.agentId, set.id, version, ref);
|
|
88
|
+
await tx.run("UPDATE hv_insight_sets SET current_version=?,updated_at=? WHERE agent_id=? AND id=?", version, now, actor.agentId, set.id);
|
|
89
|
+
return saveOperation(tx, actor, key, kind, hash, { scope: input.scope, version }, now);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
async revision(agentId, value, version) {
|
|
93
|
+
const scope = insightScope(value);
|
|
94
|
+
integer(version, 1, Number.MAX_SAFE_INTEGER - 1, "revision version");
|
|
95
|
+
return this.db.transaction(async (tx) => {
|
|
96
|
+
await this.checkScope(tx, agentId, scope);
|
|
97
|
+
const set = await this.set(tx, agentId, scope);
|
|
98
|
+
const row = set && await tx.get("SELECT * FROM hv_insight_revisions WHERE agent_id=? AND set_id=? AND version=?", agentId, set.id, version);
|
|
99
|
+
if (!set || !row)
|
|
100
|
+
throw new AgentError("Insight revision not found", 404);
|
|
101
|
+
const refs = await tx.all("SELECT memory_number FROM hv_insight_revision_memories WHERE agent_id=? AND set_id=? AND version=? ORDER BY memory_number", agentId, set.id, version);
|
|
102
|
+
return { scope, version, content: row.content, operation: row.operation, reason: row.reason, createdAt: row.created_at, userId: row.actor_user_id, producerSubagentId: row.producer_subagent_id, sourceRunId: row.source_run_id, evidenceRefs: refs.map((ref) => memoryRef(ref.memory_number)) };
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
async history(agentId, value, options = {}) {
|
|
106
|
+
const scope = insightScope(value), input = fields(options, ["beforeVersion", "limit"]);
|
|
107
|
+
const before = input.beforeVersion === undefined ? Number.MAX_SAFE_INTEGER : integer(input.beforeVersion, 1, Number.MAX_SAFE_INTEGER, "history cursor");
|
|
108
|
+
const limit = input.limit === undefined ? 25 : integer(input.limit, 1, 25, "history page size");
|
|
109
|
+
return this.db.transaction(async (tx) => {
|
|
110
|
+
await this.checkScope(tx, agentId, scope);
|
|
111
|
+
const set = await this.set(tx, agentId, scope);
|
|
112
|
+
const rows = set ? await tx.all("SELECT version,operation,reason,created_at,actor_user_id,producer_subagent_id,source_run_id FROM hv_insight_revisions WHERE agent_id=? AND set_id=? AND version<? ORDER BY version DESC LIMIT ?", agentId, set.id, before, limit + 1) : [];
|
|
113
|
+
const items = rows.slice(0, limit).map((row) => ({ scope, version: row.version, operation: row.operation, reason: row.reason, createdAt: row.created_at, userId: row.actor_user_id, producerSubagentId: row.producer_subagent_id, sourceRunId: row.source_run_id }));
|
|
114
|
+
return { items, nextBeforeVersion: rows.length > limit ? items.at(-1).version : null };
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { AgentError } from "../agents/agent-input.js";
|
|
2
|
+
import { candleIntervals } from "../agents/candle-intervals.js";
|
|
3
|
+
import { array, choice, fields, integer, object, text, unique } from "./validation.js";
|
|
4
|
+
export const memoryToolNames = ["publish_research", "record_trade_decision", "publish_review", "list_memories", "get_memory", "get_trading_context", "get_insights", "append_insight", "replace_insights", "research_decisions", "research_execution_audit"];
|
|
5
|
+
export const memoryKinds = ["research", "decision", "review"];
|
|
6
|
+
export const relations = ["based_on", "corrects", "supersedes", "reviews"];
|
|
7
|
+
export function memoryRef(number) {
|
|
8
|
+
integer(number, 1, Number.MAX_SAFE_INTEGER, "memory number");
|
|
9
|
+
return `m${number}`;
|
|
10
|
+
}
|
|
11
|
+
export function parseMemoryRef(value) {
|
|
12
|
+
if (typeof value !== "string" || !/^m[1-9][0-9]*$/.test(value) || value.trim() !== value)
|
|
13
|
+
throw new AgentError("Use an exact memory reference such as m42");
|
|
14
|
+
return integer(Number(value.slice(1)), 1, Number.MAX_SAFE_INTEGER, "memory reference");
|
|
15
|
+
}
|
|
16
|
+
export function reference(value) {
|
|
17
|
+
if (typeof value !== "string")
|
|
18
|
+
throw new AgentError("Use an exact memory reference such as m42");
|
|
19
|
+
parseMemoryRef(value);
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
// Unlike $, the final lookahead cannot match before a trailing newline.
|
|
23
|
+
export const topicPattern = /^[a-z][a-z0-9_]{0,63}(?![\s\S])/;
|
|
24
|
+
export function topic(value) {
|
|
25
|
+
if (typeof value !== "string" || !topicPattern.test(value) || value.trim() !== value)
|
|
26
|
+
throw new AgentError("Use a lowercase topic such as trend_setup");
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
/** Syntax only; the host must also check the current main-DEX instrument catalog. */
|
|
30
|
+
export function instrument(value) {
|
|
31
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9_-]{0,31}$/.test(value) || value.trim() !== value)
|
|
32
|
+
throw new AgentError("Use an exact main-DEX perpetual symbol");
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
export function interval(value) {
|
|
36
|
+
if (typeof value !== "string" || !Object.hasOwn(candleIntervals, value))
|
|
37
|
+
throw new AgentError("Unsupported analytical interval");
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
export const timestamp = (value) => integer(value, 0, Number.MAX_SAFE_INTEGER, "UTC epoch milliseconds");
|
|
41
|
+
const strings = (value) => array(value, 20, (item) => text(item, 1000, "payload text"));
|
|
42
|
+
const refs = (value) => unique(array(value, 20, reference)).sort();
|
|
43
|
+
function researchData(value, depth = 0) {
|
|
44
|
+
if (depth > 32)
|
|
45
|
+
throw new AgentError("Research data exceeds 32 nesting levels");
|
|
46
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
47
|
+
return value;
|
|
48
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
49
|
+
return value;
|
|
50
|
+
if (Array.isArray(value))
|
|
51
|
+
return Array.from(value, (item) => researchData(item, depth + 1));
|
|
52
|
+
if (typeof value === "object" && value !== null)
|
|
53
|
+
return researchObject(value, depth);
|
|
54
|
+
throw new AgentError("Research data must contain only JSON values");
|
|
55
|
+
}
|
|
56
|
+
function researchObject(value, depth = 0) {
|
|
57
|
+
const row = object(value);
|
|
58
|
+
if ((Object.getPrototypeOf(row) !== Object.prototype && Object.getPrototypeOf(row) !== null) || Object.getOwnPropertySymbols(row).length !== 0)
|
|
59
|
+
throw new AgentError("Research data must be a plain JSON object");
|
|
60
|
+
return Object.fromEntries(Object.entries(row).map(([key, item]) => [key, researchData(item, depth + 1)]));
|
|
61
|
+
}
|
|
62
|
+
export function publicationInput(kind, value) {
|
|
63
|
+
const row = fields(value, ["topic", "instrument", "interval", "summary", "content", "artifactIds", "links", "payload", ...(kind === "research" ? ["evidenceAsOf", "requestedValidUntil"] : [])]);
|
|
64
|
+
const base = {
|
|
65
|
+
topic: topic(row.topic), instrument: row.instrument == null ? null : instrument(row.instrument), interval: row.interval == null ? null : interval(row.interval),
|
|
66
|
+
summary: text(row.summary, 500, "summary"), content: text(row.content, 16000, "content"),
|
|
67
|
+
artifactIds: unique(array(row.artifactIds ?? [], 20, (id) => {
|
|
68
|
+
if (typeof id !== "string" || !/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(id) || id.trim() !== id)
|
|
69
|
+
throw new AgentError("Invalid artifact ID");
|
|
70
|
+
return id;
|
|
71
|
+
})).sort(),
|
|
72
|
+
links: array(row.links ?? [], 20, (item) => { const link = fields(item, ["ref", "relation"]); return { ref: reference(link.ref), relation: choice(link.relation, relations) }; }),
|
|
73
|
+
};
|
|
74
|
+
unique(base.links.map((link) => `${link.relation}:${link.ref}`));
|
|
75
|
+
base.links.sort((a, b) => parseMemoryRef(a.ref) - parseMemoryRef(b.ref) || a.relation.localeCompare(b.relation));
|
|
76
|
+
const p = row.payload;
|
|
77
|
+
let result;
|
|
78
|
+
if (kind === "research") {
|
|
79
|
+
const payload = researchObject(p === undefined ? {} : p);
|
|
80
|
+
if (!base.instrument || !base.interval)
|
|
81
|
+
throw new AgentError("Research requires an instrument and interval");
|
|
82
|
+
result = { ...base, kind, evidenceAsOf: timestamp(row.evidenceAsOf), requestedValidUntil: row.requestedValidUntil == null ? null : timestamp(row.requestedValidUntil), payload };
|
|
83
|
+
}
|
|
84
|
+
else if (kind === "decision") {
|
|
85
|
+
const payload = fields(p, ["version", "action", "direction", "reason", "researchRefs", "plan"]);
|
|
86
|
+
integer(payload.version, 1, 1, "payload version");
|
|
87
|
+
const action = choice(payload.action, ["enter", "hold", "skip", "reduce", "close", "protect"]);
|
|
88
|
+
const direction = payload.direction == null ? null : choice(payload.direction, ["long", "short"]);
|
|
89
|
+
if (action === "enter" && (!base.instrument || !direction || !payload.plan))
|
|
90
|
+
throw new AgentError("Entry decisions require instrument, direction, and protection plan");
|
|
91
|
+
result = { ...base, kind, payload: { version: 1, action, direction, reason: text(payload.reason, 4000, "reason"), researchRefs: refs(payload.researchRefs), plan: text(payload.plan, 4000, "plan", action !== "enter") } };
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
const payload = fields(p, ["version", "start", "end", "coverage", "limitations", "findings", "recommendations", "memoryRefs"]);
|
|
95
|
+
integer(payload.version, 1, 1, "payload version");
|
|
96
|
+
const start = timestamp(payload.start), end = timestamp(payload.end);
|
|
97
|
+
if (end <= start || end - start > 90 * 86400_000)
|
|
98
|
+
throw new AgentError("Review window must be positive and at most 90 days");
|
|
99
|
+
result = { ...base, kind, payload: { version: 1, start, end, coverage: choice(payload.coverage, ["complete", "incomplete", "unknown"]), limitations: strings(payload.limitations), findings: strings(payload.findings), recommendations: strings(payload.recommendations), memoryRefs: refs(payload.memoryRefs) } };
|
|
100
|
+
}
|
|
101
|
+
if (new TextEncoder().encode(JSON.stringify(result.payload)).length > 16 * 1024)
|
|
102
|
+
throw new AgentError("Structured payload exceeds 16 KiB");
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
/** Model-supplied advisory requirements; callers never gain trading authority from them. */
|
|
106
|
+
export function researchRequirements(value) {
|
|
107
|
+
const result = array(value, 20, (item) => {
|
|
108
|
+
const row = fields(item, ["subagentId", "topic", "interval", "maxAgeSeconds"]);
|
|
109
|
+
return { subagentId: text(row.subagentId, 100, "worker ID"), topic: topic(row.topic), interval: interval(row.interval), maxAgeSeconds: integer(row.maxAgeSeconds, 1, 7 * 86400, "research maximum age") };
|
|
110
|
+
});
|
|
111
|
+
unique(result.map((requirement) => `${requirement.subagentId}:${requirement.topic}:${requirement.interval}`));
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { enqueueWorkerEvent } from "../agents/worker-events.js";
|
|
2
|
+
import { AgentError } from "../agents/agent-input.js";
|
|
3
|
+
import { candleIntervals } from "../agents/candle-intervals.js";
|
|
4
|
+
import { artifactVisibleSql } from "../research/artifacts.js";
|
|
5
|
+
import { assertActiveActor, operationReplay, requestHash, saveOperation } from "./operations.js";
|
|
6
|
+
import { instrument, interval, memoryKinds, memoryRef, parseMemoryRef, publicationInput, researchRequirements, timestamp, topic } from "./memory-contracts.js";
|
|
7
|
+
import { choice, fields, integer, text } from "./validation.js";
|
|
8
|
+
/** Host validity cap: research cannot claim validity longer than this past its evidence. */
|
|
9
|
+
export const maxResearchAgeSeconds = 7 * 86400;
|
|
10
|
+
const columns = "number,kind,topic,instrument,interval,summary,created_at,evidence_as_of,valid_until,producer_subagent_id,source_run_id,retracted_at,retraction_reason";
|
|
11
|
+
const intervalWidthSql = `CASE interval ${Object.entries(candleIntervals).map(([name, seconds]) => `WHEN '${name}' THEN ${seconds * 1000}`).join(" ")} END`;
|
|
12
|
+
function statusSql(now) {
|
|
13
|
+
timestamp(now);
|
|
14
|
+
// Worker lifecycle controls future execution, not published evidence validity.
|
|
15
|
+
return `CASE WHEN kind!='research' THEN 'durable' WHEN retracted_at IS NOT NULL THEN 'retracted' WHEN evidence_as_of>${now} OR (${intervalWidthSql}) IS NULL OR evidence_as_of % (${intervalWidthSql}) != 0 THEN 'invalid_evidence' WHEN valid_until<=${now} THEN 'stale' ELSE 'fresh' END`;
|
|
16
|
+
}
|
|
17
|
+
function metadata(row, replacements) {
|
|
18
|
+
return { correctedBy: replacements?.correctedBy ?? null, supersededBy: replacements?.supersededBy ?? null, status: row.status, ref: memoryRef(row.number), kind: row.kind, topic: row.topic, instrument: row.instrument, interval: row.interval, summary: row.summary, createdAt: row.created_at, evidenceAsOf: row.evidence_as_of, validUntil: row.valid_until, producerSubagentId: row.producer_subagent_id, sourceRunId: row.source_run_id, retractedAt: row.retracted_at, retractionReason: row.retraction_reason };
|
|
19
|
+
}
|
|
20
|
+
export class MemoryService {
|
|
21
|
+
db;
|
|
22
|
+
authorize;
|
|
23
|
+
clock;
|
|
24
|
+
constructor(db, authorize, clock = Date.now) {
|
|
25
|
+
this.db = db;
|
|
26
|
+
this.authorize = authorize;
|
|
27
|
+
this.clock = clock;
|
|
28
|
+
}
|
|
29
|
+
publishResearch(actor, key, input) { return this.publish(actor, key, "research", input); }
|
|
30
|
+
recordDecision(actor, key, input) { return this.publish(actor, key, "decision", input); }
|
|
31
|
+
publishReview(actor, key, input) { return this.publish(actor, key, "review", input); }
|
|
32
|
+
async publish(actor, key, kind, value) {
|
|
33
|
+
const input = publicationInput(kind, value);
|
|
34
|
+
const operation = kind === "research" ? "publish_research" : kind === "decision" ? "record_trade_decision" : "publish_review";
|
|
35
|
+
const hash = requestHash(actor, operation, input);
|
|
36
|
+
return this.db.transaction(async (tx) => {
|
|
37
|
+
const replay = await operationReplay(tx, actor, key, operation, hash);
|
|
38
|
+
if (replay)
|
|
39
|
+
return replay;
|
|
40
|
+
const now = timestamp(this.clock());
|
|
41
|
+
await assertActiveActor(tx, actor);
|
|
42
|
+
await this.authorize(tx, actor, operation);
|
|
43
|
+
let deadline = null;
|
|
44
|
+
if (input.kind === "research") {
|
|
45
|
+
const width = candleIntervals[interval(input.interval)] * 1000;
|
|
46
|
+
if (input.evidenceAsOf > now || input.evidenceAsOf % width !== 0)
|
|
47
|
+
throw new AgentError("Evidence must be an aligned closed-candle end boundary in UTC milliseconds, not in the future");
|
|
48
|
+
// Two bars is a fallback, not a strategy limit. Explicit analyst deadlines
|
|
49
|
+
// may be shorter or longer, but never exceed the evidence-based ceiling.
|
|
50
|
+
deadline = timestamp(Math.min(input.requestedValidUntil ?? input.evidenceAsOf + 2 * width, input.evidenceAsOf + maxResearchAgeSeconds * 1000));
|
|
51
|
+
if (deadline <= input.evidenceAsOf)
|
|
52
|
+
throw new AgentError("Research validity must follow its evidence boundary");
|
|
53
|
+
}
|
|
54
|
+
const cited = input.kind === "decision" ? input.payload.researchRefs : input.kind === "review" ? input.payload.memoryRefs : [];
|
|
55
|
+
const links = [...input.links];
|
|
56
|
+
for (const ref of cited)
|
|
57
|
+
if (!links.some((link) => link.ref === ref && link.relation === (kind === "review" ? "reviews" : "based_on")))
|
|
58
|
+
links.push({ ref, relation: kind === "review" ? "reviews" : "based_on" });
|
|
59
|
+
if (links.length > 20)
|
|
60
|
+
throw new AgentError("At most 20 combined memory relationships");
|
|
61
|
+
for (const link of links) {
|
|
62
|
+
const target = await this.row(tx, actor.agentId, parseMemoryRef(link.ref));
|
|
63
|
+
if (input.kind === "decision" && input.payload.researchRefs.includes(link.ref) && target.kind !== "research")
|
|
64
|
+
throw new AgentError("Decision research refs must identify research publications");
|
|
65
|
+
if (link.relation === "corrects" || link.relation === "supersedes") {
|
|
66
|
+
if (input.kind !== "research" || target.kind !== "research" || target.producer_subagent_id !== actor.subagentId || target.instrument !== input.instrument || target.topic !== input.topic || target.interval !== input.interval)
|
|
67
|
+
throw new AgentError("Corrections and supersessions require the same research producer and stream");
|
|
68
|
+
if (link.relation === "corrects") {
|
|
69
|
+
if (input.evidenceAsOf !== target.evidence_as_of)
|
|
70
|
+
throw new AgentError("A correction requires the same evidence boundary; use supersedes for newer evidence");
|
|
71
|
+
deadline = Math.min(deadline, target.valid_until);
|
|
72
|
+
}
|
|
73
|
+
else if (input.evidenceAsOf <= target.evidence_as_of)
|
|
74
|
+
throw new AgentError("Supersession requires strictly newer evidence; use corrects for the same evidence boundary");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
for (const id of input.artifactIds) {
|
|
78
|
+
const evidence = await tx.get(`SELECT a.id FROM hv_artifacts a WHERE a.agent_id=? AND a.id=? AND ${artifactVisibleSql}`, actor.agentId, id, now, now);
|
|
79
|
+
if (!evidence)
|
|
80
|
+
throw new AgentError("Artifact unavailable, foreign, or expired", 404);
|
|
81
|
+
}
|
|
82
|
+
// Linking retains existing bytes; never creates an alternative quota pool.
|
|
83
|
+
if (input.artifactIds.length) {
|
|
84
|
+
const usage = await tx.get("SELECT coalesce(sum(size),0) AS bytes,count(*) AS total FROM hv_artifacts WHERE agent_id=?", actor.agentId);
|
|
85
|
+
if ((usage?.bytes ?? 0) > 100 * 1024 * 1024 || (usage?.total ?? 0) > 2000)
|
|
86
|
+
throw new AgentError("Artifact retention quota exceeded", 409);
|
|
87
|
+
}
|
|
88
|
+
await tx.run("INSERT INTO hv_memory_counters(agent_id,next_number) VALUES (?,1) ON CONFLICT DO NOTHING", actor.agentId);
|
|
89
|
+
const counter = (await tx.get("SELECT next_number FROM hv_memory_counters WHERE agent_id=?", actor.agentId));
|
|
90
|
+
const number = integer(counter.next_number, 1, Number.MAX_SAFE_INTEGER - 1, "next memory number");
|
|
91
|
+
await tx.run("UPDATE hv_memory_counters SET next_number=? WHERE agent_id=?", number + 1, actor.agentId);
|
|
92
|
+
await tx.run("INSERT INTO hv_memories(agent_id,number,kind,topic,instrument,interval,summary,content,payload,created_at,evidence_as_of,valid_until,producer_subagent_id,source_run_id,conversation_id,tool_task_id) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", actor.agentId, number, kind, input.topic, input.instrument, input.interval, input.summary, input.content, JSON.stringify(input.payload), now, input.kind === "research" ? input.evidenceAsOf : null, deadline, actor.subagentId, actor.runId, actor.conversationId, actor.toolTaskId);
|
|
93
|
+
for (const link of links)
|
|
94
|
+
await tx.run("INSERT INTO hv_memory_links(agent_id,source_number,target_number,relation) VALUES (?,?,?,?)", actor.agentId, number, parseMemoryRef(link.ref), link.relation);
|
|
95
|
+
for (const id of input.artifactIds)
|
|
96
|
+
await tx.run("INSERT INTO hv_memory_artifacts(agent_id,memory_number,artifact_id) VALUES (?,?,?)", actor.agentId, number, id);
|
|
97
|
+
if (kind === "research")
|
|
98
|
+
await enqueueWorkerEvent(tx, actor.agentId, `research:${number}`, {
|
|
99
|
+
intent: "evaluation", reason: "research", task: `Evaluate newly published research ${memoryRef(number)}. Read the full report and current account state; decide independently whether any action is warranted.`,
|
|
100
|
+
callerSubagentId: actor.subagentId, callerRunId: actor.runId, researchRefs: [memoryRef(number)],
|
|
101
|
+
}, now);
|
|
102
|
+
return saveOperation(tx, actor, key, operation, hash, { ref: memoryRef(number), kind, createdAt: now, validUntil: deadline }, now);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
async replacements(tx, agentId, numbers) {
|
|
106
|
+
if (!numbers.length)
|
|
107
|
+
return new Map();
|
|
108
|
+
// One bounded lookup per page, not a query per publication. Relationship
|
|
109
|
+
// history remains immutable; expose the latest direct ref for each kind.
|
|
110
|
+
const rows = await tx.all(`SELECT target_number,
|
|
111
|
+
max(CASE WHEN relation='corrects' THEN source_number END) AS corrected_by,
|
|
112
|
+
max(CASE WHEN relation='supersedes' THEN source_number END) AS superseded_by
|
|
113
|
+
FROM hv_memory_links WHERE agent_id=? AND target_number IN (${numbers.map(() => "?").join(",")})
|
|
114
|
+
AND relation IN ('corrects','supersedes') GROUP BY target_number`, agentId, ...numbers);
|
|
115
|
+
return new Map(rows.map((row) => [row.target_number, { correctedBy: row.corrected_by === null ? null : memoryRef(row.corrected_by), supersededBy: row.superseded_by === null ? null : memoryRef(row.superseded_by) }]));
|
|
116
|
+
}
|
|
117
|
+
async row(tx, agentId, number) {
|
|
118
|
+
const row = await tx.get(`SELECT *,${statusSql(this.clock())} AS status FROM hv_memories WHERE agent_id=? AND number=?`, agentId, number);
|
|
119
|
+
if (!row)
|
|
120
|
+
throw new AgentError("Memory not found in this Agent", 404);
|
|
121
|
+
return row;
|
|
122
|
+
}
|
|
123
|
+
async get(agentId, ref) {
|
|
124
|
+
const number = parseMemoryRef(ref);
|
|
125
|
+
return this.db.transaction(async (tx) => {
|
|
126
|
+
const row = await this.row(tx, agentId, number);
|
|
127
|
+
const links = await tx.all("SELECT target_number,relation FROM hv_memory_links WHERE agent_id=? AND source_number=? ORDER BY target_number,relation", agentId, number);
|
|
128
|
+
const artifacts = await tx.all("SELECT artifact_id FROM hv_memory_artifacts WHERE agent_id=? AND memory_number=? ORDER BY artifact_id", agentId, number);
|
|
129
|
+
const replacements = await this.replacements(tx, agentId, [number]);
|
|
130
|
+
return { ...metadata(row, replacements.get(number)), content: row.content, payload: JSON.parse(row.payload), links: links.map((link) => ({ ref: memoryRef(link.target_number), relation: link.relation })), artifactIds: artifacts.map((item) => item.artifact_id), conversationId: row.conversation_id, toolTaskId: row.tool_task_id };
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
async list(agentId, value = {}) {
|
|
134
|
+
const filter = fields(value, ["kind", "instrument", "topic", "interval", "producerSubagentId", "sourceRunId", "query", "status", "start", "end", "limit", "cursor"]);
|
|
135
|
+
const kind = filter.kind === undefined ? "" : choice(filter.kind, memoryKinds);
|
|
136
|
+
const coin = filter.instrument === undefined ? "" : instrument(filter.instrument);
|
|
137
|
+
const stream = filter.topic === undefined ? "" : topic(filter.topic);
|
|
138
|
+
const width = filter.interval === undefined ? "" : interval(filter.interval);
|
|
139
|
+
const producer = filter.producerSubagentId === undefined ? "" : text(filter.producerSubagentId, 100, "producer ID");
|
|
140
|
+
const run = filter.sourceRunId === undefined ? "" : text(filter.sourceRunId, 100, "run ID");
|
|
141
|
+
const query = filter.query === undefined ? "" : text(filter.query, 200, "search", true);
|
|
142
|
+
const status = filter.status === undefined ? "" : choice(filter.status, ["durable", "missing", "fresh", "stale", "retracted", "invalid_evidence"]);
|
|
143
|
+
const start = filter.start === undefined ? 0 : timestamp(filter.start), end = filter.end === undefined ? Number.MAX_SAFE_INTEGER : timestamp(filter.end);
|
|
144
|
+
if (end <= start)
|
|
145
|
+
throw new AgentError("Invalid memory time window");
|
|
146
|
+
const limit = filter.limit === undefined ? 25 : integer(filter.limit, 1, 50, "page size");
|
|
147
|
+
const cursor = filter.cursor == null ? null : fields(filter.cursor, ["createdAt", "ref"]);
|
|
148
|
+
const time = cursor ? timestamp(cursor.createdAt) : Number.MAX_SAFE_INTEGER;
|
|
149
|
+
const number = cursor ? parseMemoryRef(text(cursor.ref, 32, "cursor reference")) : Number.MAX_SAFE_INTEGER;
|
|
150
|
+
return this.db.transaction(async (tx) => {
|
|
151
|
+
const rows = await tx.all(`SELECT ${columns},${statusSql(this.clock())} AS status FROM hv_memories WHERE agent_id=? AND created_at>=? AND created_at<? AND (?='' OR status=?) AND (?='' OR kind=?) AND (?='' OR instrument=?) AND (?='' OR topic=?) AND (?='' OR interval=?) AND (?='' OR producer_subagent_id=?) AND (?='' OR source_run_id=?) AND (?='' OR instr(lower(summary),lower(?))>0) AND (created_at<? OR (created_at=? AND number<?)) ORDER BY created_at DESC,number DESC LIMIT ?`, agentId, start, end, status, status, kind, kind, coin, coin, stream, stream, width, width, producer, producer, run, run, query, query, time, time, number, limit + 1);
|
|
152
|
+
const page = rows.slice(0, limit), replacements = await this.replacements(tx, agentId, page.map((row) => row.number));
|
|
153
|
+
const items = page.map((row) => metadata(row, replacements.get(row.number))), last = items.at(-1);
|
|
154
|
+
return { items, nextCursor: rows.length > limit && last ? { createdAt: last.createdAt, ref: last.ref } : null };
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/** Advisory current-research retrieval for caller-supplied stream requirements. */
|
|
158
|
+
tradingContext(agentId, coin, value) {
|
|
159
|
+
return this.db.transaction((tx) => this.tradingContextTx(tx, agentId, coin, value));
|
|
160
|
+
}
|
|
161
|
+
async tradingContextTx(tx, agentId, coin, value) {
|
|
162
|
+
instrument(coin);
|
|
163
|
+
const requirements = researchRequirements(value);
|
|
164
|
+
const now = timestamp(this.clock());
|
|
165
|
+
const items = [];
|
|
166
|
+
for (const requirement of requirements) {
|
|
167
|
+
// Select the latest BEFORE evaluating freshness: never resurrect older reports.
|
|
168
|
+
const row = await tx.get(`SELECT ${columns},${statusSql(now)} AS status FROM hv_memories WHERE agent_id=? AND kind='research' AND instrument=? AND topic=? AND interval=? AND producer_subagent_id=? ORDER BY number DESC LIMIT 1`, agentId, coin, requirement.topic, requirement.interval, requirement.subagentId);
|
|
169
|
+
const status = (row?.status ?? "missing");
|
|
170
|
+
const consumerValidUntil = row ? Math.min(row.valid_until, row.evidence_as_of + requirement.maxAgeSeconds * 1000) : null;
|
|
171
|
+
const consumerStatus = status === "fresh" && now >= consumerValidUntil ? "stale" : status;
|
|
172
|
+
items.push({ producerSubagentId: requirement.subagentId, topic: requirement.topic, interval: requirement.interval, ref: row ? memoryRef(row.number) : null, status, evidenceAsOf: row?.evidence_as_of ?? null, validUntil: row?.valid_until ?? null, maxAgeSeconds: requirement.maxAgeSeconds, consumerStatus, consumerValidUntil, summary: consumerStatus === "fresh" ? row.summary : null });
|
|
173
|
+
}
|
|
174
|
+
const replacements = await this.replacements(tx, agentId, items.flatMap((item) => item.ref ? [parseMemoryRef(item.ref)] : []));
|
|
175
|
+
// Publication age and caller-specific age requirements are separate. Neither
|
|
176
|
+
// freshness nor absence of a replacement implies suitability for a trade.
|
|
177
|
+
return { observedAt: now, instrument: coin, requirements: items.map((item) => ({ ...item, correctedBy: item.ref ? replacements.get(parseMemoryRef(item.ref))?.correctedBy ?? null : null, supersededBy: item.ref ? replacements.get(parseMemoryRef(item.ref))?.supersededBy ?? null : null })) };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { AgentError } from "../agents/agent-input.js";
|
|
3
|
+
import { canonical } from "../canonical.js";
|
|
4
|
+
import { text } from "./validation.js";
|
|
5
|
+
export function requestHash(actor, operation, input) {
|
|
6
|
+
return createHash("sha256").update(canonical({ actor, operation, input })).digest("hex");
|
|
7
|
+
}
|
|
8
|
+
export async function operationReplay(tx, actor, key, kind, hash) {
|
|
9
|
+
text(key, 200, "operation key");
|
|
10
|
+
const row = await tx.get("SELECT request_hash,operation_kind,result FROM hv_memory_operations WHERE agent_id=? AND operation_key=?", actor.agentId, key);
|
|
11
|
+
if (!row)
|
|
12
|
+
return undefined;
|
|
13
|
+
if (row.request_hash !== hash || row.operation_kind !== kind)
|
|
14
|
+
throw new AgentError("Operation key reused with different arguments or actor", 409);
|
|
15
|
+
return JSON.parse(row.result);
|
|
16
|
+
}
|
|
17
|
+
export async function saveOperation(tx, actor, key, kind, hash, result, now) {
|
|
18
|
+
await tx.run("INSERT INTO hv_memory_operations(agent_id,operation_key,operation_kind,request_hash,result,created_at) VALUES (?,?,?,?,?,?)", actor.agentId, key, kind, hash, JSON.stringify(result), now);
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
/** Call after receipt lookup: successful effects remain replayable after their run finished. */
|
|
22
|
+
export async function assertActiveActor(tx, actor) {
|
|
23
|
+
const owner = await tx.get("SELECT user_id,state,enabled FROM hv_agents WHERE id=?", actor.agentId);
|
|
24
|
+
if (!owner)
|
|
25
|
+
throw new AgentError("Agent not found", 404);
|
|
26
|
+
if (owner.state === "stopping" || owner.state === "stopped")
|
|
27
|
+
throw new AgentError("Agent is being deleted", 409);
|
|
28
|
+
if (actor.userId && actor.userId !== owner.user_id)
|
|
29
|
+
throw new AgentError("Agent not found", 404);
|
|
30
|
+
if (actor.subagentId && owner.enabled !== 1)
|
|
31
|
+
throw new AgentError("Agent is disabled", 409);
|
|
32
|
+
if (actor.runId) {
|
|
33
|
+
if (!actor.subagentId)
|
|
34
|
+
throw new AgentError("Missing run producer", 403);
|
|
35
|
+
const run = await tx.get("SELECT status,conversation_id FROM hv_subagent_runs WHERE agent_id=? AND id=? AND subagent_id=?", actor.agentId, actor.runId, actor.subagentId);
|
|
36
|
+
if (!run || !["queued", "running"].includes(run.status) || (run.conversation_id !== null && run.conversation_id !== actor.conversationId))
|
|
37
|
+
throw new AgentError("Run is no longer active or does not match execution", 409);
|
|
38
|
+
}
|
|
39
|
+
else if (actor.subagentId) {
|
|
40
|
+
const root = await tx.get("SELECT id FROM hv_subagents WHERE agent_id=? AND id=? AND kind='orchestrator' AND archived=0", actor.agentId, actor.subagentId);
|
|
41
|
+
if (!root)
|
|
42
|
+
throw new AgentError("Invalid root producer", 403);
|
|
43
|
+
}
|
|
44
|
+
else if (!actor.userId)
|
|
45
|
+
throw new AgentError("Unverified actor", 403);
|
|
46
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Type } from "@earendil-works/pi-ai";
|
|
2
|
+
import { topicPattern } from "./memory-contracts.js";
|
|
3
|
+
const options = { additionalProperties: false };
|
|
4
|
+
const str = (maxLength, minLength = 0) => Type.String({ minLength, maxLength });
|
|
5
|
+
const time = Type.Integer({ minimum: 0, maximum: Number.MAX_SAFE_INTEGER });
|
|
6
|
+
const nullable = (schema) => Type.Union([schema, Type.Null()]);
|
|
7
|
+
const enumeration = (values) => Type.Union(values.map((value) => Type.Literal(value)), { description: `Exactly one of: ${values.map((value) => JSON.stringify(value)).join(", ")}.` });
|
|
8
|
+
export const Ref = Type.String({ pattern: "^m[1-9][0-9]*$", maxLength: 17 });
|
|
9
|
+
const Topic = Type.String({ pattern: topicPattern.source, minLength: 1, maxLength: 64, description: "Lowercase topic identifier, e.g. trend_setup or trade_decision. Start with a letter; use only lowercase letters, numbers and underscores (1–64 characters). No spaces or hyphens." });
|
|
10
|
+
const refs = Type.Array(Ref, { maxItems: 20 });
|
|
11
|
+
const texts = Type.Array(str(1000, 1), { maxItems: 20 });
|
|
12
|
+
export const Scope = Type.Union([Type.Object({ kind: Type.Literal("agent") }, options), Type.Object({ kind: Type.Literal("worker"), subagentId: str(100, 1) }, options)]);
|
|
13
|
+
const relation = enumeration(["based_on", "corrects", "supersedes", "reviews"]);
|
|
14
|
+
const coverage = enumeration(["complete", "incomplete", "unknown"]);
|
|
15
|
+
export const ResearchPayloadSchema = Type.Record(Type.String(), Type.Unknown(), { description: "Optional free-form JSON data (maximum 16 KiB). No required keys, labels or analytical enums. The full report belongs in content." });
|
|
16
|
+
const payloadVersion = Type.Literal(1, { description: "Always the number 1: payload schema version, NOT a decision count or revision number. Keep 1 for every new publication and follow-up." });
|
|
17
|
+
export const DecisionPayloadSchema = Type.Object({ version: payloadVersion, action: enumeration(["enter", "hold", "skip", "reduce", "close", "protect"]), direction: Type.Union([enumeration(["long", "short"]), Type.Null()]), reason: str(4000, 1), researchRefs: { ...refs, description: "Exact research refs actually used for this decision. Use [] when none were used, including a direct operator/chart-based request; never invent refs." }, plan: str(4000) }, options);
|
|
18
|
+
export const ReviewPayloadSchema = Type.Object({ version: payloadVersion, start: time, end: time, coverage, limitations: texts, findings: texts, recommendations: texts, memoryRefs: refs }, options);
|
|
19
|
+
const link = Type.Object({ ref: Ref, relation }, options);
|
|
20
|
+
const base = { topic: Topic, instrument: Type.Optional(nullable(str(32, 1))), interval: Type.Optional(nullable(str(8, 1))), summary: Type.String({ minLength: 1, maxLength: 500, description: "Short, descriptive title for memory listings, ideally 5–10 words. Capture the main finding in plain language, not a miniature report. Do not include prices, statistics, indicator values or timestamps; put those details in content. Avoid repeating instrument and interval metadata. Example: Bounce fails as bearish momentum returns." }), content: Type.String({ minLength: 1, maxLength: 16000, description: "Standalone report for another agent to interpret. Plain text or Markdown; no required headings, analytical fields or classifications. Include relevant evidence, uncertainty and limitations in your own words." }), artifactIds: Type.Optional(Type.Array(str(36, 36), { maxItems: 20 })), links: Type.Optional(Type.Array(link, { maxItems: 20 })) };
|
|
21
|
+
export const ResearchInputSchema = Type.Object({ ...base, instrument: str(32, 1), interval: Type.String({ minLength: 1, maxLength: 8, description: "Primary decision timeframe (e.g. 15m or 1d), not the worker schedule or the longest contextual timeframe mentioned in the report." }), evidenceAsOf: Type.Integer({ minimum: 0, maximum: Number.MAX_SAFE_INTEGER, description: "Exclusive closed-candle end for the primary decision interval, as an absolute UTC Unix timestamp in MILLISECONDS, not seconds or publication time. In Code Mode, reuse the verified request end variable after checking returned candles reach that boundary; when deriving from a candle open time in seconds, use last.time * 1000 + intervalMs. Respect any assignment endMs ceiling. Must be interval-aligned and not in the future. Never hand-copy a computed timestamp into another call: await tools.publish_research({...report, evidenceAsOf: endMs}) in the same script, or store/load endMs between scripts. Do not advance old evidence to the current boundary." }), requestedValidUntil: Type.Optional(Type.Integer({ minimum: 0, maximum: Number.MAX_SAFE_INTEGER, description: "Optional absolute Unix timestamp in UTC milliseconds, NOT a duration; must be greater than evidenceAsOf. Calculate in code: requestedValidUntil = evidenceAsOf + 6 * 60 * 60 * 1000 for six hours, never pass 21600000 alone. Pass the computed variable directly to await tools.publish_research; never manually retype the numeric result. Omit for a two-candle-interval lifetime; explicitly request shorter or longer when justified by the analysis, explaining unusual lifetimes in content. The host caps all lifetimes at seven days from evidence. Corrections cannot extend the original deadline." })), payload: Type.Optional(ResearchPayloadSchema) }, options);
|
|
22
|
+
export const DecisionInputSchema = Type.Object({ ...base, payload: DecisionPayloadSchema }, options);
|
|
23
|
+
export const ReviewInputSchema = Type.Object({ ...base, payload: ReviewPayloadSchema }, options);
|
|
24
|
+
const kind = enumeration(["research", "decision", "review"]);
|
|
25
|
+
const replacements = { correctedBy: Type.Union([Type.String({ pattern: "^m[1-9][0-9]*$", description: "Latest direct correction ref, if any. Read it for corrected evidence and any subsequent replacement; the original remains historical even if recent." }), Type.Null()]), supersededBy: Type.Union([Type.String({ pattern: "^m[1-9][0-9]*$", description: "Latest direct superseding ref, if any. Read it for the replacement assessment and any subsequent replacement; freshness alone does not mean this report is current." }), Type.Null()]) };
|
|
26
|
+
const metadata = { ...replacements, ref: Ref, status: Type.String({ description: "Publication evidence-age status, independent of whether correctedBy or supersededBy replaces this assessment." }), kind, topic: Topic, instrument: nullable(str(32)), interval: nullable(str(8)), summary: str(500), createdAt: time, evidenceAsOf: nullable(time), validUntil: nullable(time), producerSubagentId: nullable(str(100)), sourceRunId: nullable(str(100)), retractedAt: nullable(time), retractionReason: nullable(str(1000)) };
|
|
27
|
+
export const MetadataSchema = Type.Object(metadata, options);
|
|
28
|
+
export const PublicationResultSchema = Type.Object({ ref: Ref, kind, createdAt: time, validUntil: nullable(time) }, options);
|
|
29
|
+
export const MemoryDetailSchema = Type.Object({ ...metadata, content: str(16000), payload: Type.Union([ResearchPayloadSchema, DecisionPayloadSchema, ReviewPayloadSchema]), links: Type.Array(link, { maxItems: 20 }), artifactIds: Type.Array(str(36)) }, options);
|
|
30
|
+
const cursor = Type.Object({ createdAt: time, ref: Ref }, options);
|
|
31
|
+
export const MemoryListInputSchema = Type.Object({ kind: Type.Optional(kind), instrument: Type.Optional(str(32)), topic: Type.Optional(Topic), interval: Type.Optional(str(8)), producerSubagentId: Type.Optional(str(100)), sourceRunId: Type.Optional(str(100)), query: Type.Optional(str(200)), status: Type.Optional(Type.String()), start: Type.Optional(time), end: Type.Optional(time), limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 50 })), cursor: Type.Optional(cursor) }, options);
|
|
32
|
+
export const MemoryListSchema = Type.Object({ items: Type.Array(MetadataSchema, { maxItems: 50 }), nextCursor: Type.Union([cursor, Type.Null()]) }, options);
|
|
33
|
+
const researchStatus = enumeration(["missing", "fresh", "stale", "retracted", "invalid_evidence"]);
|
|
34
|
+
export const TradingContextSchema = Type.Object({ observedAt: time, instrument: str(32), requirements: Type.Array(Type.Object({ ...replacements, producerSubagentId: str(100), topic: Topic, interval: str(8), ref: nullable(Ref), status: { ...researchStatus, description: "Publication status, consistent with get_memory/list_memories; replacement standing is separate." }, evidenceAsOf: nullable(time), validUntil: { ...nullable(time), description: "Accepted publication deadline, consistent with get_memory/list_memories." }, maxAgeSeconds: time, consumerStatus: { ...researchStatus, description: "Freshness under this caller's maxAgeSeconds as well as the publication deadline. May be stale while publication status is fresh." }, consumerValidUntil: { ...nullable(time), description: "Earlier of the publication deadline and evidenceAsOf plus caller maxAgeSeconds." }, summary: nullable(str(500)) }, options), { maxItems: 20 }) }, options);
|
|
35
|
+
export const TradingContextInputSchema = Type.Object({ instrument: str(32), streams: Type.Optional(Type.Array(Type.Object({ subagentId: str(100), topic: Topic, interval: str(8), maxAgeSeconds: time }, options), { maxItems: 20 })) }, options);
|
|
36
|
+
export const InsightMutationResultSchema = Type.Object({ scope: Scope, version: time }, options);
|
|
37
|
+
const insightBase = { scope: Scope, reason: str(1000, 1), evidenceRefs: Type.Optional(refs) };
|
|
38
|
+
export const AppendInsightSchema = Type.Object({ ...insightBase, content: str(2000, 1) }, options);
|
|
39
|
+
export const ReplaceInsightSchema = Type.Object({ ...insightBase, content: str(8000), expectedVersion: time }, options);
|
|
40
|
+
const snapshot = { scope: Scope, version: time, content: str(8000) };
|
|
41
|
+
const revisionMeta = { scope: Scope, version: time, operation: enumeration(["append", "replace"]), reason: str(1000), createdAt: time, userId: nullable(str(100)), producerSubagentId: nullable(str(100)), sourceRunId: nullable(str(100)) };
|
|
42
|
+
export const InsightReadInputSchema = Type.Object({ scope: Scope, version: Type.Optional(time), history: Type.Optional(Type.Boolean()), beforeVersion: Type.Optional(time) }, options);
|
|
43
|
+
export const FillAuditSchema = Type.Object({ items: Type.Array(Type.Object({ fillId: Type.String(), time, exchangeOid: nullable(Type.String()), requestId: nullable(Type.String()), decisionRef: nullable(Ref) }), { maxItems: 50 }), next: Type.Union([Type.Object({ time, id: Type.String() }), Type.Null()]), coverage: Type.String() });
|
|
44
|
+
export const InsightReadSchema = Type.Union([Type.Object(snapshot, options), Type.Object({ ...snapshot, ...revisionMeta, evidenceRefs: Type.Array(Ref) }, options), Type.Object({ items: Type.Array(Type.Object(revisionMeta, options), { maxItems: 25 }), nextBeforeVersion: nullable(time) }, options)]);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { AgentError } from "../agents/agent-input.js";
|
|
2
|
+
export function object(value) {
|
|
3
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
4
|
+
throw new AgentError("Expected an object");
|
|
5
|
+
return value;
|
|
6
|
+
}
|
|
7
|
+
export function fields(value, keys) {
|
|
8
|
+
const row = object(value);
|
|
9
|
+
if (Object.keys(row).some((key) => !keys.includes(key)))
|
|
10
|
+
throw new AgentError("Unknown field; provenance and authority are host-owned");
|
|
11
|
+
return row;
|
|
12
|
+
}
|
|
13
|
+
export function text(value, max, label, empty = false) {
|
|
14
|
+
if (typeof value !== "string" || value.length > max || (!empty && !value.trim()))
|
|
15
|
+
throw new AgentError(`Invalid ${label} (maximum ${max} characters)`);
|
|
16
|
+
return value.trim();
|
|
17
|
+
}
|
|
18
|
+
export function integer(value, min, max, label) {
|
|
19
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < min || value > max)
|
|
20
|
+
throw new AgentError(`Invalid ${label}`);
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
export function choice(value, options) {
|
|
24
|
+
if (typeof value !== "string" || !options.includes(value))
|
|
25
|
+
throw new AgentError(`Expected ${options.join(" / ")}`);
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
export function array(value, max, parse) {
|
|
29
|
+
if (!Array.isArray(value) || value.length > max)
|
|
30
|
+
throw new AgentError(`Expected at most ${max} items`);
|
|
31
|
+
return value.map(parse);
|
|
32
|
+
}
|
|
33
|
+
export function unique(values) {
|
|
34
|
+
if (new Set(values).size !== values.length)
|
|
35
|
+
throw new AgentError("Duplicate values are not allowed");
|
|
36
|
+
return values;
|
|
37
|
+
}
|
|
38
|
+
export function positiveDecimal(value) {
|
|
39
|
+
if (typeof value !== "string" || value.length > 80 || !/^(0|[1-9][0-9]*)(\.[0-9]+)?$/.test(value) || value.trim() !== value || !/[1-9]/.test(value))
|
|
40
|
+
throw new AgentError("Expected an exact positive decimal string");
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export class NotificationDeliveryError extends Error {
|
|
2
|
+
permanent;
|
|
3
|
+
retryAfterMs;
|
|
4
|
+
suppressed;
|
|
5
|
+
constructor(message, permanent = false, retryAfterMs, suppressed = false) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.permanent = permanent;
|
|
8
|
+
this.retryAfterMs = retryAfterMs;
|
|
9
|
+
this.suppressed = suppressed;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Type } from "@earendil-works/pi-ai";
|
|
2
|
+
import { defineExtension, defineTool, section } from "@earendil-works/pi-durable";
|
|
3
|
+
import { jsonResult } from "../research/tool-response.js";
|
|
4
|
+
export function createNotificationExtension(agentId, notifications, execution) {
|
|
5
|
+
return defineExtension({
|
|
6
|
+
name: "notifications",
|
|
7
|
+
tools: [defineTool({
|
|
8
|
+
name: "send_notification",
|
|
9
|
+
description: "Send a concise user-facing notification to this Agent's web history and linked Telegram chat. Returns queued (not proof of Telegram delivery) or disabled. Backend order submission/fill notifications are automatic when enabled; do not duplicate them. Do not include credentials or secrets. Cite full review/report references in the body where useful. This is one-way, not a question/approval tool.",
|
|
10
|
+
parameters: Type.Object({
|
|
11
|
+
title: Type.String({ minLength: 1, maxLength: 200 }), body: Type.String({ minLength: 1, maxLength: 12000 }),
|
|
12
|
+
severity: Type.Optional(Type.Union([Type.Literal("info"), Type.Literal("warning"), Type.Literal("error")])),
|
|
13
|
+
}, { additionalProperties: false }),
|
|
14
|
+
structuredOutputSchema: Type.Union([Type.Object({ status: Type.Literal("queued"), id: Type.String() }), Type.Object({ status: Type.Literal("disabled") })]),
|
|
15
|
+
replay: "safe", executionMode: "sequential",
|
|
16
|
+
execute: async (args, api, context) => jsonResult(await notifications.sendFromTool((await execution.resolve(agentId, api, context)).actor, args, execution)),
|
|
17
|
+
})],
|
|
18
|
+
sections: [section("notifications", () => "send_notification is a one-way message to the user, not permission to trade or an interactive question. Queued means stored, not delivered or read. If disabled, continue the assignment without retrying or working around the setting. Do not duplicate automatic order submission/fill notifications. After a review is published and intended insight edits finish, send one crafted summary with the review reference, exact window/coverage, findings, actual changes versus recommendations, and unresolved limitations. Notification failures must not cause an exchange action to be repeated.")],
|
|
19
|
+
});
|
|
20
|
+
}
|