@askexenow/exe-os 0.9.299 → 0.9.301
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/deploy/compose/.env.customer.example +1 -1
- package/deploy/compose/.env.example +1 -1
- package/deploy/compose/docker-compose.yml +31 -5
- package/deploy/compose/erp-nginx/nginx.conf +6 -3
- package/deploy/compose/generate-env.ts +1 -1
- package/deploy/compose/observability/otel-collector-config.yaml +10 -1
- package/deploy/compose/setup.sh +1 -1
- package/dist/active-agent-56J56WW5.js +27 -0
- package/dist/active-agent-UNJO6AJ2.js +27 -0
- package/dist/active-agent-VDWK7TES.js +28 -0
- package/dist/active-agent-Y5LSIMVC.js +28 -0
- package/dist/agentic-ontology-MZ4WHFDE.js +25 -0
- package/dist/agentic-ontology-QEV7GI3T.js +25 -0
- package/dist/assets/com.askexe.exed.plist +6 -5
- package/dist/backfill-metadata-IHKI5GXV.js +600 -0
- package/dist/backfill-metadata-RQZ4BN7G.js +600 -0
- package/dist/backfill-metadata-ZU3SZNBD.js +600 -0
- package/dist/behaviors-A3L5CWMK.js +46 -0
- package/dist/behaviors-G4KWGS24.js +46 -0
- package/dist/behaviors-WO67R6C4.js +46 -0
- package/dist/bin/agentic-ontology-backfill.js +5 -5
- package/dist/bin/agentic-reflection-backfill.js +6 -6
- package/dist/bin/agentic-semantic-label.js +5 -5
- package/dist/bin/backfill-conversations.js +6 -6
- package/dist/bin/backfill-responses.js +6 -6
- package/dist/bin/backfill-vectors.js +8 -8
- package/dist/bin/bulk-sync-postgres.js +13 -7
- package/dist/bin/cc-doctor.js +5 -5
- package/dist/bin/cleanup-stale-review-tasks.js +10 -10
- package/dist/bin/cli.js +16 -16
- package/dist/bin/deferred-daemon-restart.js +1 -1
- package/dist/bin/exe-agent-config.js +2 -2
- package/dist/bin/exe-agent.js +10 -10
- package/dist/bin/exe-assign.js +8 -8
- package/dist/bin/exe-boot.js +36 -21
- package/dist/bin/exe-call.js +4 -4
- package/dist/bin/exe-cloud.js +13 -7
- package/dist/bin/exe-dispatch.js +10 -10
- package/dist/bin/exe-doctor.js +2 -2
- package/dist/bin/exe-export-behaviors.js +7 -7
- package/dist/bin/exe-forget.js +6 -6
- package/dist/bin/exe-gateway.js +7 -7
- package/dist/bin/exe-healthcheck.js +5 -5
- package/dist/bin/exe-heartbeat.js +10 -10
- package/dist/bin/exe-kill.js +13 -13
- package/dist/bin/exe-launch-agent.js +29 -19
- package/dist/bin/exe-new-employee.js +6 -6
- package/dist/bin/exe-pending-messages.js +11 -11
- package/dist/bin/exe-pending-notifications.js +10 -10
- package/dist/bin/exe-pending-reviews.js +10 -10
- package/dist/bin/exe-rename.js +4 -4
- package/dist/bin/exe-review.js +12 -12
- package/dist/bin/exe-search.js +5 -5
- package/dist/bin/exe-session-cleanup.js +15 -15
- package/dist/bin/exe-settings.js +13 -7
- package/dist/bin/exe-start-codex.js +11 -11
- package/dist/bin/exe-start-opencode.js +8 -8
- package/dist/bin/exe-status.js +11 -11
- package/dist/bin/exe-team.js +3 -3
- package/dist/bin/exe-watchdog.js +3 -3
- package/dist/bin/git-sweep.js +11 -11
- package/dist/bin/graph-backfill.js +6 -6
- package/dist/bin/graph-export.js +5 -5
- package/dist/bin/import-history.js +9 -9
- package/dist/bin/install-launchd.js +7 -3
- package/dist/bin/install.js +19 -15
- package/dist/bin/intercom-check.js +4 -4
- package/dist/bin/mcp-sessions.js +2 -2
- package/dist/bin/orchestration-metrics.js +4 -4
- package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
- package/dist/bin/postgres-agentic-semantic-backfill.js +1 -1
- package/dist/bin/pre-publish.js +24 -1
- package/dist/bin/scan-tasks.js +10 -10
- package/dist/bin/setup.js +1 -1
- package/dist/bin/shard-migrate.js +4 -4
- package/dist/bin/stack-update.js +123 -3
- package/dist/bin/vps-health-gate.js +1 -1
- package/dist/capability-cards-VTGDTOZ4.js +89 -0
- package/dist/capability-cards-XAQSL26B.js +89 -0
- package/dist/capacity-monitor-6MQLFFQT.js +51 -0
- package/dist/capacity-monitor-BB3LKJLE.js +51 -0
- package/dist/capacity-monitor-G2MAJPRP.js +51 -0
- package/dist/catchup-brief-BQSL5M5S.js +175 -0
- package/dist/catchup-brief-COALBX6L.js +175 -0
- package/dist/catchup-brief-VBGEJQRS.js +175 -0
- package/dist/cc-binary-detect-B5JDCJ5J.js +19 -0
- package/dist/chunk-22LDUTY7.js +14597 -0
- package/dist/chunk-24JVDLQJ.js +345 -0
- package/dist/chunk-24M4AJPG.js +128 -0
- package/dist/chunk-2B7RCTPT.js +85 -0
- package/dist/chunk-2QBA6YE6.js +127 -0
- package/dist/chunk-2RGDEBZC.js +731 -0
- package/dist/chunk-2T3OYZMR.js +1158 -0
- package/dist/chunk-32DBQWR5.js +333 -0
- package/dist/chunk-3ABY3QDX.js +199 -0
- package/dist/chunk-3EOPMTG2.js +128 -0
- package/dist/chunk-3G3ECFB5.js +668 -0
- package/dist/chunk-3LHOFGHT.js +280 -0
- package/dist/chunk-3NS4V4JW.js +382 -0
- package/dist/chunk-3NYY2NZ3.js +836 -0
- package/dist/chunk-3RA62PNQ.js +58 -0
- package/dist/chunk-3ROUD5WA.js +97 -0
- package/dist/chunk-3RY2ARDN.js +129 -0
- package/dist/chunk-3XRS5AVV.js +567 -0
- package/dist/chunk-3Y5IRIRU.js +290 -0
- package/dist/chunk-3YK7X5FD.js +1186 -0
- package/dist/chunk-3ZWWUKBI.js +210 -0
- package/dist/chunk-463G3VAH.js +122 -0
- package/dist/chunk-4L6PVYFE.js +54 -0
- package/dist/chunk-4LC7BFI2.js +76 -0
- package/dist/chunk-4O67LBMK.js +377 -0
- package/dist/chunk-4OIU3N6U.js +167 -0
- package/dist/chunk-4Z3FWLOE.js +836 -0
- package/dist/chunk-5BAU4T5G.js +208 -0
- package/dist/chunk-5FP7YHCG.js +1158 -0
- package/dist/chunk-5SYYMNPE.js +30 -0
- package/dist/chunk-5TO5PH7O.js +304 -0
- package/dist/chunk-5U3JZP62.js +1352 -0
- package/dist/chunk-5VNFXIGF.js +85 -0
- package/dist/chunk-5XD2Y463.js +402 -0
- package/dist/chunk-63AENHJC.js +123 -0
- package/dist/chunk-673IFJYB.js +731 -0
- package/dist/chunk-6UVUJNLY.js +1186 -0
- package/dist/chunk-7AWH47AR.js +448 -0
- package/dist/chunk-7KPWYWYL.js +290 -0
- package/dist/chunk-7P4B6AEP.js +227 -0
- package/dist/chunk-7URNGDEY.js +2145 -0
- package/dist/chunk-7VHOALNC.js +244 -0
- package/dist/chunk-ADZQBZOX.js +122 -0
- package/dist/chunk-APSZAEDO.js +1186 -0
- package/dist/chunk-ASHF6VO4.js +2265 -0
- package/dist/chunk-ASJHCAVL.js +38 -0
- package/dist/chunk-BT2LEHIW.js +448 -0
- package/dist/chunk-BTS5QUWB.js +50 -0
- package/dist/chunk-BWJDJ3BS.js +604 -0
- package/dist/chunk-CME46VWP.js +150 -0
- package/dist/chunk-D3ICCKXY.js +54 -0
- package/dist/chunk-D3IOU3NO.js +377 -0
- package/dist/chunk-DFGXRKI2.js +221 -0
- package/dist/chunk-DICIFTCS.js +150 -0
- package/dist/chunk-DIFI5JDC.js +76 -0
- package/dist/chunk-DO65VHQZ.js +128 -0
- package/dist/chunk-DPOIJ5SM.js +284 -0
- package/dist/chunk-E2OMUBXQ.js +567 -0
- package/dist/chunk-ECMXIV6N.js +97 -0
- package/dist/chunk-EDMVA3PT.js +727 -0
- package/dist/chunk-F5OSXH4A.js +4388 -0
- package/dist/chunk-F5OWHPRG.js +236 -0
- package/dist/chunk-F7MZA3QP.js +199 -0
- package/dist/chunk-FAZNXNA5.js +33 -0
- package/dist/chunk-FCHG5RC4.js +197 -0
- package/dist/chunk-FFKSPZO2.js +157 -0
- package/dist/chunk-FNHYH5U6.js +331 -0
- package/dist/chunk-FRNXQSB4.js +134 -0
- package/dist/chunk-FTAASABV.js +362 -0
- package/dist/chunk-FWPDAQ6Q.js +1350 -0
- package/dist/chunk-FZB73QOI.js +210 -0
- package/dist/chunk-GBL5QSTM.js +197 -0
- package/dist/chunk-GJBR6QLD.js +630 -0
- package/dist/chunk-GRSYAHKI.js +535 -0
- package/dist/chunk-GRXWINOW.js +244 -0
- package/dist/chunk-GUPNVUG5.js +348 -0
- package/dist/chunk-GY66UPMX.js +167 -0
- package/dist/chunk-HCCG67BY.js +43 -0
- package/dist/chunk-HCSZZXZZ.js +197 -0
- package/dist/chunk-HNGNZU62.js +240 -0
- package/dist/chunk-HP2D5LIE.js +214 -0
- package/dist/chunk-HUABQHDC.js +1352 -0
- package/dist/chunk-HYKO2LNW.js +157 -0
- package/dist/chunk-IFL6DG2K.js +181 -0
- package/dist/chunk-IKPQRHVQ.js +304 -0
- package/dist/chunk-J5HFRVNW.js +362 -0
- package/dist/chunk-J6SD7LT2.js +171 -0
- package/dist/chunk-JCWA3X6A.js +402 -0
- package/dist/chunk-JHPK33IP.js +2162 -0
- package/dist/chunk-JURL2S27.js +128 -0
- package/dist/chunk-JWGDH5I2.js +127 -0
- package/dist/chunk-KBXQFXYM.js +567 -0
- package/dist/chunk-KGY5QIOJ.js +1350 -0
- package/dist/chunk-KLES22FB.js +1094 -0
- package/dist/chunk-KPUYYOFS.js +122 -0
- package/dist/chunk-KY43UELJ.js +331 -0
- package/dist/chunk-L32V4O5Z.js +58 -0
- package/dist/chunk-LAFARYU5.js +456 -0
- package/dist/chunk-LC7ETNTJ.js +1350 -0
- package/dist/chunk-LEJ5FKIK.js +55 -0
- package/dist/chunk-LNLLCAI4.js +377 -0
- package/dist/chunk-LQWZYMNU.js +448 -0
- package/dist/chunk-LSDXEHKL.js +381 -0
- package/dist/chunk-LY3SOO73.js +76 -0
- package/dist/chunk-M6CIHXXB.js +159 -0
- package/dist/chunk-MJOQ35DX.js +427 -0
- package/dist/chunk-MO5HER5Y.js +345 -0
- package/dist/chunk-MS2EOZJQ.js +290 -0
- package/dist/chunk-MUIMJGSQ.js +128 -0
- package/dist/chunk-MY4TGLT6.js +284 -0
- package/dist/chunk-N3ARGCVG.js +345 -0
- package/dist/chunk-N4XG2M2U.js +735 -0
- package/dist/chunk-N72JNFJ4.js +535 -0
- package/dist/chunk-NJMPNYBS.js +427 -0
- package/dist/chunk-NM3AUMFE.js +2145 -0
- package/dist/chunk-NPPQ3TR4.js +735 -0
- package/dist/chunk-NTWF4DAF.js +581 -0
- package/dist/chunk-NXL3VKXM.js +331 -0
- package/dist/chunk-OJACH2JF.js +128 -0
- package/dist/chunk-OMSLHEEF.js +456 -0
- package/dist/chunk-OO4IFABD.js +382 -0
- package/dist/chunk-OYIP3QVN.js +167 -0
- package/dist/chunk-P2IOW54H.js +668 -0
- package/dist/chunk-P5KXQ3RN.js +731 -0
- package/dist/chunk-P5UXP53T.js +81 -0
- package/dist/chunk-PH6VRRFR.js +395 -0
- package/dist/chunk-Q3GKOF7Z.js +85 -0
- package/dist/chunk-Q65NCNL4.js +1352 -0
- package/dist/chunk-QIGS2LRT.js +735 -0
- package/dist/chunk-QKBN3CY2.js +381 -0
- package/dist/chunk-QNNAVMQH.js +1094 -0
- package/dist/chunk-QODDW4YI.js +171 -0
- package/dist/chunk-QPAYPTSH.js +2162 -0
- package/dist/chunk-QRWDJ5RI.js +381 -0
- package/dist/chunk-RBFZCHVB.js +105 -0
- package/dist/chunk-RCEULTPF.js +185 -0
- package/dist/chunk-RCGHXBCX.js +630 -0
- package/dist/chunk-ROSCLRTH.js +204 -0
- package/dist/chunk-RYAOSGUW.js +227 -0
- package/dist/chunk-S2SPGHPY.js +38 -0
- package/dist/chunk-S73ZAJ2S.js +262 -0
- package/dist/chunk-SBPEWD7Z.js +171 -0
- package/dist/chunk-SDPUWZP5.js +333 -0
- package/dist/chunk-SJ4UF7YK.js +1094 -0
- package/dist/chunk-SOZ7D77I.js +204 -0
- package/dist/chunk-SVLSHDNL.js +54 -0
- package/dist/chunk-SVUYBT5N.js +262 -0
- package/dist/chunk-T7PTLVJV.js +284 -0
- package/dist/chunk-TDX2LK2M.js +240 -0
- package/dist/chunk-TGUSLO4B.js +50 -0
- package/dist/chunk-TPJH6PE6.js +1158 -0
- package/dist/chunk-TVW7EDOJ.js +382 -0
- package/dist/chunk-TYRUIE6P.js +58 -0
- package/dist/chunk-U5RKGLV6.js +50 -0
- package/dist/chunk-UFGTHBHP.js +127 -0
- package/dist/chunk-ULCYWCPI.js +1079 -0
- package/dist/chunk-UN5EPVBN.js +14597 -0
- package/dist/chunk-URLH7ZVR.js +70 -0
- package/dist/chunk-USYRTGR7.js +402 -0
- package/dist/chunk-V4ABCEHM.js +30 -0
- package/dist/chunk-V6LOEOXG.js +3372 -0
- package/dist/chunk-VAZOVAW4.js +2162 -0
- package/dist/chunk-VEUQVKKT.js +185 -0
- package/dist/chunk-VIDDJ5RF.js +214 -0
- package/dist/chunk-VKCNXOQ6.js +214 -0
- package/dist/chunk-VNB4ROYG.js +348 -0
- package/dist/chunk-VWUQFZFB.js +395 -0
- package/dist/chunk-W77GRCNA.js +85 -0
- package/dist/chunk-WB2B25UM.js +230 -0
- package/dist/chunk-WCUZX7F7.js +204 -0
- package/dist/chunk-WL5RMOZQ.js +362 -0
- package/dist/chunk-WPAXAOHD.js +1079 -0
- package/dist/chunk-WVMG4ZRH.js +14597 -0
- package/dist/chunk-WYVOTRRZ.js +129 -0
- package/dist/chunk-XABJRAUW.js +3346 -0
- package/dist/chunk-XQQ7D4I4.js +85 -0
- package/dist/chunk-YDFY6YCH.js +280 -0
- package/dist/chunk-YGUMRYCN.js +33 -0
- package/dist/chunk-YHJPTIPR.js +836 -0
- package/dist/chunk-YJSP5PPG.js +128 -0
- package/dist/chunk-YLKS7KKC.js +2145 -0
- package/dist/chunk-YOMETWOJ.js +4388 -0
- package/dist/chunk-YU3KEVCO.js +333 -0
- package/dist/chunk-Z4FVFSE3.js +81 -0
- package/dist/chunk-Z4TLSNUW.js +244 -0
- package/dist/chunk-ZDPU3JTF.js +221 -0
- package/dist/chunk-ZDY4LYAJ.js +81 -0
- package/dist/chunk-ZG33AACD.js +70 -0
- package/dist/chunk-ZKHPZ6KN.js +181 -0
- package/dist/chunk-ZO2TM5N5.js +97 -0
- package/dist/chunk-ZP6T5K6I.js +535 -0
- package/dist/chunk-ZR6ZJT32.js +123 -0
- package/dist/chunk-ZSUACDQC.js +4388 -0
- package/dist/co-activation-JGF5YIDU.js +74 -0
- package/dist/co-activation-XM25BLZM.js +74 -0
- package/dist/co-occurrence-CKEMDPWO.js +95 -0
- package/dist/co-occurrence-HLLC6GT2.js +95 -0
- package/dist/co-occurrence-W2LIAPHI.js +95 -0
- package/dist/code-context-index-FCL47WKE.js +30 -0
- package/dist/conversation-entity-extractor-WC2RU6RS.js +114 -0
- package/dist/core-memory-CRSR2PSL.js +110 -0
- package/dist/core-memory-VSKFRMEV.js +110 -0
- package/dist/core-memory-ZDA76EU3.js +110 -0
- package/dist/crdt-sync-6VH2YDVY.js +33 -0
- package/dist/crdt-sync-BJKZB6T6.js +33 -0
- package/dist/crm-webhook-E5PAFAUN.js +10 -0
- package/dist/crm-webhook-RXFPZJXP.js +10 -0
- package/dist/crm-webhook-Z26LEFKG.js +10 -0
- package/dist/cto-delegation-gate-45IBLPTK.js +280 -0
- package/dist/cto-delegation-gate-EMY6ZZ2F.js +280 -0
- package/dist/cto-delegation-gate-SF4EUB2Q.js +280 -0
- package/dist/daemon-orchestration-VB3BLYIT.js +143 -0
- package/dist/daemon-orchestration-W66UYGUD.js +143 -0
- package/dist/daemon-orchestration-Y5Y6YNE3.js +143 -0
- package/dist/db-backup-HFJ53IBU.js +43 -0
- package/dist/db-backup-NVUTS7L5.js +43 -0
- package/dist/doc-graph-extractor-ID45AQ2P.js +133 -0
- package/dist/doc-graph-extractor-MLYQYT4B.js +133 -0
- package/dist/doc-graph-extractor-SVFSXKL6.js +133 -0
- package/dist/dreaming-AZYRAGKA.js +34 -0
- package/dist/dreaming-N6B7KBIE.js +34 -0
- package/dist/dreaming-WG5CDUHX.js +34 -0
- package/dist/entity-boost-XAFCDDB6.js +375 -0
- package/dist/exe-drift-3SGA53CL.js +70 -0
- package/dist/exe-drift-CPUEAPIU.js +70 -0
- package/dist/exe-export-4RTGDV53.js +77 -0
- package/dist/exe-export-APUNLKWF.js +77 -0
- package/dist/exe-export-NM4SXB3P.js +77 -0
- package/dist/exe-import-6GLNCP62.js +80 -0
- package/dist/exe-import-AZMIF34Z.js +80 -0
- package/dist/exe-import-U4H4ES3Z.js +80 -0
- package/dist/exe-key-FIPXUTMF.js +673 -0
- package/dist/exe-key-LJV23AJI.js +673 -0
- package/dist/exe-key-WTLCMOYJ.js +673 -0
- package/dist/exe-snapshot-ILO3WSEC.js +338 -0
- package/dist/exe-snapshot-IODRQLBX.js +338 -0
- package/dist/exe-snapshot-ZOZBW7V6.js +338 -0
- package/dist/fast-db-init-7LYYUCSJ.js +7 -0
- package/dist/fast-db-init-ATRZGHOL.js +7 -0
- package/dist/fast-db-init-IU7GYFWB.js +7 -0
- package/dist/gateway/index.js +11 -11
- package/dist/git-staleness-GGCFPHQ5.js +112 -0
- package/dist/git-staleness-XNOKI4D3.js +112 -0
- package/dist/git-task-sweep-3MO4OVND.js +42 -0
- package/dist/git-task-sweep-M3SWXFKJ.js +42 -0
- package/dist/git-task-sweep-Y6KNWB67.js +42 -0
- package/dist/global-procedures-626WAU3I.js +22 -0
- package/dist/global-procedures-EBAPPWGZ.js +22 -0
- package/dist/graph-auto-extract-TKHQ4OR3.js +183 -0
- package/dist/graph-auto-extract-VGFEWFZX.js +183 -0
- package/dist/graph-auto-extract-VJOUQBPK.js +183 -0
- package/dist/graph-rag-KECA5TE4.js +35 -0
- package/dist/hooks/bug-report-worker.js +12 -12
- package/dist/hooks/codex-stop-task-finalizer.js +12 -12
- package/dist/hooks/commit-complete.js +12 -12
- package/dist/hooks/error-recall.js +6 -6
- package/dist/hooks/exe-heartbeat-hook.js +3 -3
- package/dist/hooks/ingest-worker.js +3 -3
- package/dist/hooks/ingest.js +6 -6
- package/dist/hooks/instructions-loaded.js +4 -4
- package/dist/hooks/manifest.json +20 -20
- package/dist/hooks/notification.js +4 -4
- package/dist/hooks/post-compact.js +12 -12
- package/dist/hooks/post-tool-combined.js +6 -6
- package/dist/hooks/pre-compact.js +16 -16
- package/dist/hooks/pre-tool-use.js +15 -15
- package/dist/hooks/prompt-submit.js +28 -26
- package/dist/hooks/session-end.js +20 -20
- package/dist/hooks/session-start.js +12 -12
- package/dist/hooks/stop.js +18 -18
- package/dist/hooks/subagent-stop.js +11 -11
- package/dist/hooks/summary-worker.js +18 -18
- package/dist/index.js +20 -20
- package/dist/installer-37KFNAWE.js +344 -0
- package/dist/installer-3FB5EMPB.js +40 -0
- package/dist/installer-BRQ42CPB.js +344 -0
- package/dist/installer-F55NR4E2.js +298 -0
- package/dist/installer-KOYBUS4J.js +40 -0
- package/dist/installer-PXZJG256.js +298 -0
- package/dist/lib/cloud-sync.js +13 -7
- package/dist/lib/consolidation.js +7 -7
- package/dist/lib/database.js +6 -4
- package/dist/lib/db-daemon-client.js +11 -202
- package/dist/lib/db.js +6 -4
- package/dist/lib/embedder.js +3 -3
- package/dist/lib/employee-templates.js +4 -4
- package/dist/lib/employees.js +2 -2
- package/dist/lib/exe-daemon-client.js +2 -2
- package/dist/lib/exe-daemon.js +53 -51
- package/dist/lib/hybrid-search.js +5 -5
- package/dist/lib/identity.js +2 -2
- package/dist/lib/messaging.js +10 -10
- package/dist/lib/reminders.js +3 -3
- package/dist/lib/schedules.js +5 -5
- package/dist/lib/session-registry.js +4 -4
- package/dist/lib/skill-learning.js +6 -6
- package/dist/lib/store.js +4 -4
- package/dist/lib/task-router.js +3 -3
- package/dist/lib/tasks.js +11 -11
- package/dist/lib/tmux-routing.js +9 -9
- package/dist/lib/token-spend.js +3 -3
- package/dist/mcp/register-tools.js +65 -63
- package/dist/mcp/server.js +66 -64
- package/dist/mcp/tools/complete-reminder.js +4 -4
- package/dist/mcp/tools/create-reminder.js +4 -4
- package/dist/mcp/tools/create-task.js +13 -13
- package/dist/mcp/tools/deactivate-behavior.js +7 -7
- package/dist/mcp/tools/list-reminders.js +4 -4
- package/dist/mcp/tools/list-tasks.js +13 -13
- package/dist/mcp/tools/send-message.js +12 -12
- package/dist/mcp/tools/update-task.js +12 -12
- package/dist/mcp-http-config-7KJZI7UD.js +31 -0
- package/dist/mcp-http-config-OF3MB7M5.js +31 -0
- package/dist/memory-cards-CPIZVXWI.js +180 -0
- package/dist/memory-cards-ZIT7ZKL5.js +180 -0
- package/dist/memory-graph-extractor-JIYWLBFF.js +22 -0
- package/dist/memory-graph-extractor-LY2VORZT.js +22 -0
- package/dist/memory-graph-extractor-MDPSLZDM.js +22 -0
- package/dist/memory-poisoning-defense-5UZT3WWA.js +224 -0
- package/dist/memory-poisoning-defense-SEM25TY5.js +224 -0
- package/dist/memory-queue-client-WKWRFERJ.js +16 -0
- package/dist/memory-reflection-J2W7CJ7C.js +244 -0
- package/dist/memory-reflection-TA2VQYPH.js +244 -0
- package/dist/message-queue-client-IFQQ2HI7.js +92 -0
- package/dist/notifications-CZBQ3H5T.js +47 -0
- package/dist/notifications-EIIL2EQV.js +47 -0
- package/dist/notifications-RLMSI4YE.js +47 -0
- package/dist/orchestration-events-TGQYA72K.js +27 -0
- package/dist/orchestration-events-VUYR6MXE.js +27 -0
- package/dist/orchestrator-JQD5G3CW.js +35 -0
- package/dist/orchestrator-MAMR4C37.js +35 -0
- package/dist/orchestrator-VE5WHEJH.js +35 -0
- package/dist/pipeline-router-3Q3YBYSM.js +15 -0
- package/dist/pipeline-router-DKXD6DJO.js +15 -0
- package/dist/pipeline-router-YNW63BY5.js +15 -0
- package/dist/plan-limits-YGXTYCW4.js +28 -0
- package/dist/plan-limits-ZM4MNZKY.js +28 -0
- package/dist/project-boot-E2TWYTAC.js +299 -0
- package/dist/project-boot-TDOZKKDR.js +299 -0
- package/dist/projection-worker-TMKUSVGD.js +1084 -0
- package/dist/projection-worker-WFPRM4AI.js +1084 -0
- package/dist/projection-worker-YKV3PFCV.js +1084 -0
- package/dist/prospective-memory-CNJDBNWF.js +232 -0
- package/dist/prospective-memory-OAFZUODU.js +232 -0
- package/dist/reranker-AFU75HEX.js +19 -0
- package/dist/reranker-RYNSJNDF.js +19 -0
- package/dist/reranker-YQIRNGDM.js +19 -0
- package/dist/retrieval-health-M5BVB7EV.js +12 -0
- package/dist/retrieval-health-RSQEIYIB.js +12 -0
- package/dist/review-polling-LGX7DUNT.js +126 -0
- package/dist/review-polling-MJBCYV4I.js +126 -0
- package/dist/review-polling-ZMB3OBPC.js +126 -0
- package/dist/runtime/index.js +16 -16
- package/dist/services/codex-reviewd/index.js +855 -0
- package/dist/session-events-5N47BRFK.js +38 -0
- package/dist/session-events-PT6SVS2P.js +38 -0
- package/dist/session-events-UTMCKDIN.js +38 -0
- package/dist/session-kill-telemetry-FRQA5MVD.js +31 -0
- package/dist/session-kill-telemetry-HKL2NQMR.js +31 -0
- package/dist/session-scope-2BD6QLNI.js +88 -0
- package/dist/session-scope-GQNCM6UQ.js +88 -0
- package/dist/session-scope-MMGM232A.js +88 -0
- package/dist/setup-wizard-SELXXVLD.js +12 -0
- package/dist/setup-wizard-W64I6SHC.js +12 -0
- package/dist/setup-wizard-X7YSRDNQ.js +12 -0
- package/dist/skill-refinement-CCP4ULZ3.js +159 -0
- package/dist/skill-refinement-FXCXTUS2.js +159 -0
- package/dist/skill-refinement-WCPDNHZ5.js +159 -0
- package/dist/stack-update-5VSGG36W.js +84 -0
- package/dist/steward-gate-JDR3SLH3.js +15 -0
- package/dist/steward-gate-PIXNK4BK.js +15 -0
- package/dist/task-enforcement-LEBWCYZT.js +506 -0
- package/dist/task-enforcement-VOSQRAQB.js +506 -0
- package/dist/task-enforcement-WCEA4FZI.js +506 -0
- package/dist/task-scope-CQZ33PRU.js +37 -0
- package/dist/task-scope-JTTEZKDU.js +37 -0
- package/dist/task-scope-L5GDL2AV.js +37 -0
- package/dist/tasks-crud-DC4GCXQQ.js +79 -0
- package/dist/tasks-crud-S36AFYYM.js +79 -0
- package/dist/tasks-crud-T32IRPXF.js +79 -0
- package/dist/tasks-notify-OBFVHJDP.js +40 -0
- package/dist/tasks-notify-OUQWUM6W.js +40 -0
- package/dist/tasks-notify-W2W2BJRB.js +40 -0
- package/dist/tasks-review-34WV7BX2.js +49 -0
- package/dist/tasks-review-N33MTHWJ.js +49 -0
- package/dist/tasks-review-OSBG2YN2.js +49 -0
- package/dist/telemetry-upload-3CSVO3J7.js +741 -0
- package/dist/telemetry-upload-EYHEWTKG.js +741 -0
- package/dist/telemetry-upload-XLBW4DRP.js +741 -0
- package/dist/token-budget-I6FMMDFX.js +86 -0
- package/dist/token-budget-ZG2MQ5GD.js +86 -0
- package/dist/tool-capability-index-IWQBQKM7.js +10 -0
- package/dist/tool-telemetry-2E3Z7CRV.js +17 -0
- package/dist/tool-telemetry-ODL4F2CW.js +17 -0
- package/dist/tui/App.js +17 -17
- package/dist/tui-data-VWT4Q5UT.js +260 -0
- package/dist/tui-data-XFBFBSBE.js +260 -0
- package/dist/tui-data-Z5UF7KEI.js +260 -0
- package/dist/wiki-acl-EUOPNUIQ.js +111 -0
- package/dist/wiki-acl-SZFHCEC4.js +111 -0
- package/dist/worker-gate-OOO6BWZ6.js +21 -0
- package/dist/worker-gate-OQMKAMP7.js +21 -0
- package/dist/worker-gate-ZPP3SZK6.js +21 -0
- package/dist/workflow-engine-P7WYJP2B.js +28 -0
- package/dist/workflow-engine-QY3IFFR2.js +28 -0
- package/dist/workflow-engine-UYNB5RTB.js +28 -0
- package/dist/worktree-CNOQZBNT.js +28 -0
- package/dist/worktree-H5C4LMQR.js +28 -0
- package/dist/worktree-sweep-KFWF3XZD.js +21 -0
- package/dist/worktree-sweep-SE7ITXC4.js +21 -0
- package/package.json +1 -1
- package/release-notes.json +117 -191
- package/src/commands/exe.md +18 -1
|
@@ -0,0 +1,1084 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractGoalCandidates,
|
|
3
|
+
inferIntention,
|
|
4
|
+
inferOntologyEventType,
|
|
5
|
+
inferOutcome,
|
|
6
|
+
inferSemanticLabel,
|
|
7
|
+
ontologyPayload
|
|
8
|
+
} from "./chunk-S73ZAJ2S.js";
|
|
9
|
+
import {
|
|
10
|
+
processCRMEvent
|
|
11
|
+
} from "./chunk-OO4IFABD.js";
|
|
12
|
+
import {
|
|
13
|
+
loadConfig
|
|
14
|
+
} from "./chunk-R36FAN53.js";
|
|
15
|
+
import "./chunk-LYH5HE24.js";
|
|
16
|
+
import "./chunk-MLKGABMK.js";
|
|
17
|
+
|
|
18
|
+
// src/lib/projection-worker.ts
|
|
19
|
+
import os from "os";
|
|
20
|
+
import path from "path";
|
|
21
|
+
import { existsSync } from "fs";
|
|
22
|
+
import { createRequire } from "module";
|
|
23
|
+
import { pathToFileURL } from "url";
|
|
24
|
+
var GRAPH_SCHEMA = "graph";
|
|
25
|
+
var prismaPromise = null;
|
|
26
|
+
var ontologySchemaReady = false;
|
|
27
|
+
var filteredSchemaReady = false;
|
|
28
|
+
function loadPrisma() {
|
|
29
|
+
if (!prismaPromise) {
|
|
30
|
+
prismaPromise = (async () => {
|
|
31
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
32
|
+
if (explicitPath) {
|
|
33
|
+
const mod = await import(pathToFileURL(explicitPath).href);
|
|
34
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
35
|
+
if (!Ctor) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
36
|
+
return new Ctor();
|
|
37
|
+
}
|
|
38
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path.join(os.homedir(), "exe-db");
|
|
39
|
+
const packagePath = path.join(exeDbRoot, "package.json");
|
|
40
|
+
if (existsSync(packagePath)) {
|
|
41
|
+
const req = createRequire(packagePath);
|
|
42
|
+
const entry = req.resolve("@prisma/client");
|
|
43
|
+
const mod = await import(pathToFileURL(entry).href);
|
|
44
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
45
|
+
if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
|
|
46
|
+
return new Ctor();
|
|
47
|
+
}
|
|
48
|
+
const { Pool } = await import("pg");
|
|
49
|
+
const { pgSslConfig } = await import("./pg-ssl-7JXQFL4I.js");
|
|
50
|
+
const pool = new Pool({ connectionString: process.env.DATABASE_URL, ...pgSslConfig() });
|
|
51
|
+
return {
|
|
52
|
+
async $queryRawUnsafe(query, ...values) {
|
|
53
|
+
const result = await pool.query(query, values);
|
|
54
|
+
return result.rows;
|
|
55
|
+
},
|
|
56
|
+
async $executeRawUnsafe(query, ...values) {
|
|
57
|
+
const result = await pool.query(query, values);
|
|
58
|
+
return result.rowCount ?? 0;
|
|
59
|
+
},
|
|
60
|
+
async $disconnect() {
|
|
61
|
+
await pool.end();
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
})();
|
|
65
|
+
}
|
|
66
|
+
return prismaPromise;
|
|
67
|
+
}
|
|
68
|
+
function setProjectionWorkerPrismaClientForTests(client) {
|
|
69
|
+
prismaPromise = client ? Promise.resolve(client) : null;
|
|
70
|
+
}
|
|
71
|
+
function resetProjectionWorkerForTests() {
|
|
72
|
+
running = false;
|
|
73
|
+
if (pollTimer) {
|
|
74
|
+
clearTimeout(pollTimer);
|
|
75
|
+
pollTimer = null;
|
|
76
|
+
}
|
|
77
|
+
consecutivePollErrors = 0;
|
|
78
|
+
prismaPromise = null;
|
|
79
|
+
ontologySchemaReady = false;
|
|
80
|
+
filteredSchemaReady = false;
|
|
81
|
+
lastWikiApiPushAt = 0;
|
|
82
|
+
}
|
|
83
|
+
function stableId(input) {
|
|
84
|
+
let h1 = 3735928559;
|
|
85
|
+
let h2 = 1103547991;
|
|
86
|
+
for (let i = 0; i < input.length; i++) {
|
|
87
|
+
const ch = input.charCodeAt(i);
|
|
88
|
+
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
89
|
+
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
90
|
+
}
|
|
91
|
+
h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507) ^ Math.imul(h2 ^ h2 >>> 13, 3266489909);
|
|
92
|
+
h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507) ^ Math.imul(h1 ^ h1 >>> 13, 3266489909);
|
|
93
|
+
let h3 = 305419896;
|
|
94
|
+
let h4 = 2596069104;
|
|
95
|
+
for (let i = 0; i < input.length; i++) {
|
|
96
|
+
const ch = input.charCodeAt(i);
|
|
97
|
+
h3 = Math.imul(h3 ^ ch, 2246822507);
|
|
98
|
+
h4 = Math.imul(h4 ^ ch, 3266489909);
|
|
99
|
+
}
|
|
100
|
+
h3 = Math.imul(h3 ^ h3 >>> 16, 2654435761) ^ Math.imul(h4 ^ h4 >>> 13, 1597334677);
|
|
101
|
+
h4 = Math.imul(h4 ^ h4 >>> 16, 2654435761) ^ Math.imul(h3 ^ h3 >>> 13, 1597334677);
|
|
102
|
+
const a = (h1 >>> 0).toString(16).padStart(8, "0");
|
|
103
|
+
const b = (h2 >>> 0).toString(16).padStart(8, "0");
|
|
104
|
+
const c = (h3 >>> 0).toString(16).padStart(8, "0");
|
|
105
|
+
const d = (h4 >>> 0).toString(16).padStart(8, "0");
|
|
106
|
+
const hex = a + b + c + d;
|
|
107
|
+
return hex.slice(0, 8) + "-" + hex.slice(8, 12) + "-4" + hex.slice(13, 16) + "-" + (parseInt(hex[16], 16) & 3 | 8).toString(16) + hex.slice(17, 20) + "-" + hex.slice(20, 32);
|
|
108
|
+
}
|
|
109
|
+
function graphEntityId(name, type) {
|
|
110
|
+
return stableId(`${name.toLowerCase()}::${type.toLowerCase()}`);
|
|
111
|
+
}
|
|
112
|
+
function normalizeEntityName(name) {
|
|
113
|
+
return name.trim().replace(/\s+/g, " ").slice(0, 160);
|
|
114
|
+
}
|
|
115
|
+
function addGraphEntity(entities, name, type, properties = {}) {
|
|
116
|
+
if (typeof name !== "string") return null;
|
|
117
|
+
const normalized = normalizeEntityName(name);
|
|
118
|
+
if (!normalized || normalized.length < 2) return null;
|
|
119
|
+
const id = graphEntityId(normalized, type);
|
|
120
|
+
if (!entities.has(id)) entities.set(id, { id, name: normalized, type, properties });
|
|
121
|
+
return id;
|
|
122
|
+
}
|
|
123
|
+
function addGraphRelationship(relationships, sourceId, targetId, type, confidence = 0.7, properties = {}) {
|
|
124
|
+
if (!sourceId || !targetId || sourceId === targetId) return;
|
|
125
|
+
const id = graphEntityId(`${sourceId}->${type}->${targetId}`, "relationship");
|
|
126
|
+
if (!relationships.has(id)) relationships.set(id, { id, sourceId, targetId, type, confidence, properties });
|
|
127
|
+
}
|
|
128
|
+
function deriveLightweightMemoryGraph(payload) {
|
|
129
|
+
const entities = /* @__PURE__ */ new Map();
|
|
130
|
+
const relationships = /* @__PURE__ */ new Map();
|
|
131
|
+
const memoryId = typeof payload.id === "string" ? payload.id : "";
|
|
132
|
+
const rawText = typeof payload.raw_text === "string" ? payload.raw_text : "";
|
|
133
|
+
const memoryEntity = addGraphEntity(entities, memoryId || stableId(rawText.slice(0, 256)), "memory", {
|
|
134
|
+
source: "cloud_sync",
|
|
135
|
+
version: payload.version
|
|
136
|
+
});
|
|
137
|
+
const agentEntity = addGraphEntity(entities, payload.agent_id, "person", { role: payload.agent_role });
|
|
138
|
+
const projectEntity = addGraphEntity(entities, payload.project_name, "project");
|
|
139
|
+
const toolEntity = addGraphEntity(entities, payload.tool_name, "tool");
|
|
140
|
+
const taskEntity = addGraphEntity(entities, payload.task_id, "task");
|
|
141
|
+
const domainEntity = addGraphEntity(entities, payload.domain, "concept");
|
|
142
|
+
const memoryTypeEntity = addGraphEntity(entities, payload.memory_type, "concept", { kind: "memory_type" });
|
|
143
|
+
addGraphRelationship(relationships, agentEntity, projectEntity, "worked_on", 0.8);
|
|
144
|
+
addGraphRelationship(relationships, agentEntity, toolEntity, "uses", 0.9);
|
|
145
|
+
addGraphRelationship(relationships, memoryEntity, agentEntity, "created_by", 0.9);
|
|
146
|
+
addGraphRelationship(relationships, memoryEntity, projectEntity, "part_of", 0.9);
|
|
147
|
+
addGraphRelationship(relationships, memoryEntity, toolEntity, "uses", 0.8);
|
|
148
|
+
addGraphRelationship(relationships, memoryEntity, taskEntity, "part_of", 0.8);
|
|
149
|
+
addGraphRelationship(relationships, memoryEntity, domainEntity, "about", 0.7);
|
|
150
|
+
addGraphRelationship(relationships, memoryEntity, memoryTypeEntity, "classified_as", 0.7);
|
|
151
|
+
const pathRegex = /(?:\/[\w .@-]+)+(?:\.[A-Za-z0-9]{1,10})|(?:[\w@.-]+\/)+[\w@.-]+\.(?:ts|tsx|js|jsx|json|md|yml|yaml|sql|py|go|rs|sh|css|html)/g;
|
|
152
|
+
const fileMatches = new Set((rawText.match(pathRegex) ?? []).slice(0, 8));
|
|
153
|
+
for (const filePath of fileMatches) {
|
|
154
|
+
const fileEntity = addGraphEntity(entities, filePath, "file", { source: "raw_text" });
|
|
155
|
+
addGraphRelationship(relationships, memoryEntity, fileEntity, "references", 0.75);
|
|
156
|
+
addGraphRelationship(relationships, projectEntity, fileEntity, "uses", 0.55);
|
|
157
|
+
}
|
|
158
|
+
const backtickMatches = [...rawText.matchAll(/`([^`\n]{2,80})`/g)].map((m) => m[1]).slice(0, 8);
|
|
159
|
+
for (const term of backtickMatches) {
|
|
160
|
+
if (!term || /[\s]{30,}/.test(term)) continue;
|
|
161
|
+
const conceptEntity = addGraphEntity(entities, term, term.includes("/") || term.includes(".") ? "file" : "concept", { source: "backtick" });
|
|
162
|
+
addGraphRelationship(relationships, memoryEntity, conceptEntity, "references", 0.65);
|
|
163
|
+
}
|
|
164
|
+
return { entities: [...entities.values()], relationships: [...relationships.values()] };
|
|
165
|
+
}
|
|
166
|
+
async function projectMemoryGraph(prisma, payload, memoryId, timestamp) {
|
|
167
|
+
const graph = deriveLightweightMemoryGraph(payload);
|
|
168
|
+
if (graph.entities.length === 0) return { entities: 0, relationships: 0 };
|
|
169
|
+
let entities = 0;
|
|
170
|
+
let relationships = 0;
|
|
171
|
+
const actualEntityIds = /* @__PURE__ */ new Map();
|
|
172
|
+
for (const entity of graph.entities) {
|
|
173
|
+
const inserted = await prisma.$queryRawUnsafe(
|
|
174
|
+
`INSERT INTO "${GRAPH_SCHEMA}"."entities" ("id", "name", "type", "first_seen", "last_seen", "properties")
|
|
175
|
+
VALUES ($1, $2, $3, $4, $4, $5::jsonb)
|
|
176
|
+
ON CONFLICT ("name", "type") DO UPDATE SET
|
|
177
|
+
"last_seen" = GREATEST("${GRAPH_SCHEMA}"."entities"."last_seen", EXCLUDED."last_seen"),
|
|
178
|
+
"properties" = "${GRAPH_SCHEMA}"."entities"."properties" || EXCLUDED."properties"
|
|
179
|
+
RETURNING "id"`,
|
|
180
|
+
entity.id,
|
|
181
|
+
entity.name,
|
|
182
|
+
entity.type,
|
|
183
|
+
timestamp,
|
|
184
|
+
JSON.stringify(entity.properties ?? {})
|
|
185
|
+
);
|
|
186
|
+
const actualId = inserted[0]?.id ?? entity.id;
|
|
187
|
+
actualEntityIds.set(entity.id, actualId);
|
|
188
|
+
await prisma.$executeRawUnsafe(
|
|
189
|
+
`INSERT INTO "${GRAPH_SCHEMA}"."entity_memories" ("entity_id", "memory_id")
|
|
190
|
+
VALUES ($1, $2)
|
|
191
|
+
ON CONFLICT DO NOTHING`,
|
|
192
|
+
actualId,
|
|
193
|
+
memoryId
|
|
194
|
+
);
|
|
195
|
+
entities++;
|
|
196
|
+
}
|
|
197
|
+
for (const relationship of graph.relationships) {
|
|
198
|
+
const sourceId = actualEntityIds.get(relationship.sourceId) ?? relationship.sourceId;
|
|
199
|
+
const targetId = actualEntityIds.get(relationship.targetId) ?? relationship.targetId;
|
|
200
|
+
const relId = graphEntityId(`${sourceId}->${relationship.type}->${targetId}`, "relationship");
|
|
201
|
+
await prisma.$executeRawUnsafe(
|
|
202
|
+
`INSERT INTO "${GRAPH_SCHEMA}"."relationships" ("id", "source_entity_id", "target_entity_id", "type", "weight", "confidence", "timestamp", "properties")
|
|
203
|
+
VALUES ($1, $2, $3, $4, 1.0, $5, $6, $7::jsonb)
|
|
204
|
+
ON CONFLICT ("source_entity_id", "target_entity_id", "type")
|
|
205
|
+
DO UPDATE SET "weight" = LEAST("${GRAPH_SCHEMA}"."relationships"."weight" + 0.1, 2.0),
|
|
206
|
+
"confidence" = GREATEST("${GRAPH_SCHEMA}"."relationships"."confidence", EXCLUDED."confidence"),
|
|
207
|
+
"timestamp" = GREATEST("${GRAPH_SCHEMA}"."relationships"."timestamp", EXCLUDED."timestamp"),
|
|
208
|
+
"properties" = "${GRAPH_SCHEMA}"."relationships"."properties" || EXCLUDED."properties"`,
|
|
209
|
+
relId,
|
|
210
|
+
sourceId,
|
|
211
|
+
targetId,
|
|
212
|
+
relationship.type,
|
|
213
|
+
relationship.confidence,
|
|
214
|
+
timestamp,
|
|
215
|
+
JSON.stringify(relationship.properties ?? {})
|
|
216
|
+
);
|
|
217
|
+
relationships++;
|
|
218
|
+
}
|
|
219
|
+
return { entities, relationships };
|
|
220
|
+
}
|
|
221
|
+
async function ensurePostgresOntologySchema(prisma) {
|
|
222
|
+
if (ontologySchemaReady) return;
|
|
223
|
+
await prisma.$executeRawUnsafe(`
|
|
224
|
+
CREATE TABLE IF NOT EXISTS "${GRAPH_SCHEMA}"."agent_sessions" (
|
|
225
|
+
"id" text PRIMARY KEY,
|
|
226
|
+
"agent_id" text NOT NULL,
|
|
227
|
+
"project_name" text,
|
|
228
|
+
"started_at" timestamp(3) NOT NULL,
|
|
229
|
+
"last_event_at" timestamp(3) NOT NULL,
|
|
230
|
+
"event_count" integer NOT NULL DEFAULT 0,
|
|
231
|
+
"properties" jsonb NOT NULL DEFAULT '{}'::jsonb
|
|
232
|
+
)`);
|
|
233
|
+
await prisma.$executeRawUnsafe(`CREATE INDEX IF NOT EXISTS "idx_agent_sessions_agent_time" ON "${GRAPH_SCHEMA}"."agent_sessions" ("agent_id", "started_at")`);
|
|
234
|
+
await prisma.$executeRawUnsafe(`
|
|
235
|
+
CREATE TABLE IF NOT EXISTS "${GRAPH_SCHEMA}"."agent_goals" (
|
|
236
|
+
"id" text PRIMARY KEY,
|
|
237
|
+
"statement" text NOT NULL,
|
|
238
|
+
"owner_agent_id" text,
|
|
239
|
+
"project_name" text,
|
|
240
|
+
"status" text NOT NULL DEFAULT 'open',
|
|
241
|
+
"priority" integer NOT NULL DEFAULT 5,
|
|
242
|
+
"success_criteria" text,
|
|
243
|
+
"parent_goal_id" text,
|
|
244
|
+
"due_at" timestamp(3),
|
|
245
|
+
"achieved_at" timestamp(3),
|
|
246
|
+
"supersedes_id" text,
|
|
247
|
+
"created_at" timestamp(3) NOT NULL,
|
|
248
|
+
"updated_at" timestamp(3) NOT NULL,
|
|
249
|
+
"source_memory_id" text
|
|
250
|
+
)`);
|
|
251
|
+
await prisma.$executeRawUnsafe(`CREATE INDEX IF NOT EXISTS "idx_agent_goals_project_status" ON "${GRAPH_SCHEMA}"."agent_goals" ("project_name", "status", "priority")`);
|
|
252
|
+
await prisma.$executeRawUnsafe(`
|
|
253
|
+
CREATE TABLE IF NOT EXISTS "${GRAPH_SCHEMA}"."agent_events" (
|
|
254
|
+
"id" text PRIMARY KEY,
|
|
255
|
+
"event_type" text NOT NULL,
|
|
256
|
+
"occurred_at" timestamp(3) NOT NULL,
|
|
257
|
+
"sequence_index" integer NOT NULL,
|
|
258
|
+
"actor_agent_id" text,
|
|
259
|
+
"agent_role" text,
|
|
260
|
+
"project_name" text,
|
|
261
|
+
"session_id" text,
|
|
262
|
+
"task_id" text,
|
|
263
|
+
"goal_id" text,
|
|
264
|
+
"parent_event_id" text,
|
|
265
|
+
"intention" text,
|
|
266
|
+
"outcome" text,
|
|
267
|
+
"evidence_memory_id" text,
|
|
268
|
+
"impact" text,
|
|
269
|
+
"payload" jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
270
|
+
"created_at" timestamp(3) NOT NULL
|
|
271
|
+
)`);
|
|
272
|
+
await prisma.$executeRawUnsafe(`CREATE INDEX IF NOT EXISTS "idx_agent_events_time" ON "${GRAPH_SCHEMA}"."agent_events" ("occurred_at", "sequence_index")`);
|
|
273
|
+
await prisma.$executeRawUnsafe(`CREATE INDEX IF NOT EXISTS "idx_agent_events_session_seq" ON "${GRAPH_SCHEMA}"."agent_events" ("session_id", "sequence_index")`);
|
|
274
|
+
await prisma.$executeRawUnsafe(`CREATE INDEX IF NOT EXISTS "idx_agent_events_goal_time" ON "${GRAPH_SCHEMA}"."agent_events" ("goal_id", "occurred_at")`);
|
|
275
|
+
await prisma.$executeRawUnsafe(`CREATE INDEX IF NOT EXISTS "idx_agent_events_memory" ON "${GRAPH_SCHEMA}"."agent_events" ("evidence_memory_id")`);
|
|
276
|
+
await prisma.$executeRawUnsafe(`
|
|
277
|
+
CREATE TABLE IF NOT EXISTS "${GRAPH_SCHEMA}"."agent_goal_links" (
|
|
278
|
+
"id" text PRIMARY KEY,
|
|
279
|
+
"goal_id" text NOT NULL,
|
|
280
|
+
"link_type" text NOT NULL,
|
|
281
|
+
"target_id" text NOT NULL,
|
|
282
|
+
"target_type" text NOT NULL,
|
|
283
|
+
"created_at" timestamp(3) NOT NULL
|
|
284
|
+
)`);
|
|
285
|
+
await prisma.$executeRawUnsafe(`CREATE INDEX IF NOT EXISTS "idx_agent_goal_links_goal" ON "${GRAPH_SCHEMA}"."agent_goal_links" ("goal_id", "target_type")`);
|
|
286
|
+
await prisma.$executeRawUnsafe(`
|
|
287
|
+
CREATE TABLE IF NOT EXISTS "${GRAPH_SCHEMA}"."agent_semantic_labels" (
|
|
288
|
+
"id" text PRIMARY KEY,
|
|
289
|
+
"source_memory_id" text NOT NULL,
|
|
290
|
+
"event_id" text,
|
|
291
|
+
"labeler" text NOT NULL,
|
|
292
|
+
"schema_version" integer NOT NULL DEFAULT 1,
|
|
293
|
+
"confidence" double precision NOT NULL DEFAULT 0,
|
|
294
|
+
"labels" jsonb NOT NULL,
|
|
295
|
+
"created_at" timestamp(3) NOT NULL,
|
|
296
|
+
"updated_at" timestamp(3) NOT NULL
|
|
297
|
+
)`);
|
|
298
|
+
await prisma.$executeRawUnsafe(`CREATE INDEX IF NOT EXISTS "idx_agent_semantic_labels_memory" ON "${GRAPH_SCHEMA}"."agent_semantic_labels" ("source_memory_id", "labeler")`);
|
|
299
|
+
await prisma.$executeRawUnsafe(`CREATE INDEX IF NOT EXISTS "idx_agent_semantic_labels_event" ON "${GRAPH_SCHEMA}"."agent_semantic_labels" ("event_id")`);
|
|
300
|
+
await prisma.$executeRawUnsafe(`
|
|
301
|
+
CREATE TABLE IF NOT EXISTS "${GRAPH_SCHEMA}"."agent_reflection_checkpoints" (
|
|
302
|
+
"id" text PRIMARY KEY,
|
|
303
|
+
"project_name" text,
|
|
304
|
+
"session_id" text,
|
|
305
|
+
"window_start_at" timestamp(3) NOT NULL,
|
|
306
|
+
"window_end_at" timestamp(3) NOT NULL,
|
|
307
|
+
"event_count" integer NOT NULL DEFAULT 0,
|
|
308
|
+
"goal_count" integer NOT NULL DEFAULT 0,
|
|
309
|
+
"success_count" integer NOT NULL DEFAULT 0,
|
|
310
|
+
"failure_count" integer NOT NULL DEFAULT 0,
|
|
311
|
+
"risk_count" integer NOT NULL DEFAULT 0,
|
|
312
|
+
"summary" text NOT NULL,
|
|
313
|
+
"learnings" jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
314
|
+
"next_actions" jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
315
|
+
"evidence_event_ids" jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
316
|
+
"confidence" double precision NOT NULL DEFAULT 0,
|
|
317
|
+
"created_at" timestamp(3) NOT NULL
|
|
318
|
+
)`);
|
|
319
|
+
await prisma.$executeRawUnsafe(`CREATE INDEX IF NOT EXISTS "idx_agent_reflection_project_time" ON "${GRAPH_SCHEMA}"."agent_reflection_checkpoints" ("project_name", "window_end_at")`);
|
|
320
|
+
await prisma.$executeRawUnsafe(`CREATE INDEX IF NOT EXISTS "idx_agent_reflection_session_time" ON "${GRAPH_SCHEMA}"."agent_reflection_checkpoints" ("session_id", "window_end_at")`);
|
|
321
|
+
ontologySchemaReady = true;
|
|
322
|
+
}
|
|
323
|
+
async function ensureFilteredSchema(prisma) {
|
|
324
|
+
if (filteredSchemaReady) return;
|
|
325
|
+
await prisma.$executeRawUnsafe(`CREATE SCHEMA IF NOT EXISTS filtered`);
|
|
326
|
+
await prisma.$executeRawUnsafe(`
|
|
327
|
+
CREATE TABLE IF NOT EXISTS "filtered"."documents" (
|
|
328
|
+
"id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
329
|
+
"title" TEXT NOT NULL,
|
|
330
|
+
"content" TEXT NOT NULL,
|
|
331
|
+
"doc_type" TEXT NOT NULL DEFAULT 'memory',
|
|
332
|
+
"source" TEXT NOT NULL DEFAULT 'exe-os',
|
|
333
|
+
"source_id" TEXT,
|
|
334
|
+
"metadata" JSONB DEFAULT '{}',
|
|
335
|
+
"created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
336
|
+
UNIQUE("source", "source_id")
|
|
337
|
+
)`);
|
|
338
|
+
await prisma.$executeRawUnsafe(`
|
|
339
|
+
CREATE TABLE IF NOT EXISTS "filtered"."contacts" (
|
|
340
|
+
"id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
341
|
+
"name" TEXT,
|
|
342
|
+
"phone" TEXT,
|
|
343
|
+
"email" TEXT,
|
|
344
|
+
"platform" TEXT NOT NULL DEFAULT 'whatsapp',
|
|
345
|
+
"platform_id" TEXT,
|
|
346
|
+
"metadata" JSONB DEFAULT '{}',
|
|
347
|
+
"first_seen" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
348
|
+
"last_seen" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
349
|
+
UNIQUE("platform", "platform_id")
|
|
350
|
+
)`);
|
|
351
|
+
await prisma.$executeRawUnsafe(`
|
|
352
|
+
CREATE TABLE IF NOT EXISTS "filtered"."conversations" (
|
|
353
|
+
"id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
354
|
+
"contact_id" UUID REFERENCES "filtered"."contacts"("id"),
|
|
355
|
+
"platform" TEXT NOT NULL DEFAULT 'whatsapp',
|
|
356
|
+
"thread_id" TEXT,
|
|
357
|
+
"direction" TEXT NOT NULL DEFAULT 'inbound',
|
|
358
|
+
"content" TEXT NOT NULL,
|
|
359
|
+
"message_type" TEXT NOT NULL DEFAULT 'text',
|
|
360
|
+
"metadata" JSONB DEFAULT '{}',
|
|
361
|
+
"sent_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
362
|
+
"source_ref" TEXT
|
|
363
|
+
)`);
|
|
364
|
+
await selfHealFilteredSchema(prisma);
|
|
365
|
+
filteredSchemaReady = true;
|
|
366
|
+
}
|
|
367
|
+
async function selfHealFilteredSchema(prisma) {
|
|
368
|
+
const stmts = [
|
|
369
|
+
// --- filtered.contacts: ensure new-generation columns ---
|
|
370
|
+
`ALTER TABLE "filtered"."contacts" ADD COLUMN IF NOT EXISTS "name" TEXT`,
|
|
371
|
+
`ALTER TABLE "filtered"."contacts" ADD COLUMN IF NOT EXISTS "platform" TEXT DEFAULT 'whatsapp'`,
|
|
372
|
+
`ALTER TABLE "filtered"."contacts" ADD COLUMN IF NOT EXISTS "platform_id" TEXT`,
|
|
373
|
+
`ALTER TABLE "filtered"."contacts" ADD COLUMN IF NOT EXISTS "first_seen" TIMESTAMPTZ DEFAULT NOW()`,
|
|
374
|
+
`ALTER TABLE "filtered"."contacts" ADD COLUMN IF NOT EXISTS "last_seen" TIMESTAMPTZ DEFAULT NOW()`,
|
|
375
|
+
// Legacy NOT NULL columns the new worker never writes — relax if present.
|
|
376
|
+
`DO $$ BEGIN
|
|
377
|
+
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='filtered' AND table_name='contacts' AND column_name='display_name' AND is_nullable='NO') THEN
|
|
378
|
+
ALTER TABLE "filtered"."contacts" ALTER COLUMN "display_name" DROP NOT NULL;
|
|
379
|
+
END IF;
|
|
380
|
+
END $$`,
|
|
381
|
+
// Backfill new columns from legacy ones where possible, then enforce upsert key.
|
|
382
|
+
`DO $$ BEGIN
|
|
383
|
+
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='filtered' AND table_name='contacts' AND column_name='display_name') THEN
|
|
384
|
+
UPDATE "filtered"."contacts" SET
|
|
385
|
+
"name" = COALESCE("name", "display_name"),
|
|
386
|
+
"platform" = COALESCE("platform", "source", 'whatsapp'),
|
|
387
|
+
"platform_id" = COALESCE("platform_id", "source_id", "phone"),
|
|
388
|
+
"first_seen" = COALESCE("first_seen", "first_seen_at", NOW()),
|
|
389
|
+
"last_seen" = COALESCE("last_seen", "last_seen_at", NOW())
|
|
390
|
+
WHERE "platform" IS NULL OR "platform_id" IS NULL;
|
|
391
|
+
END IF;
|
|
392
|
+
END $$`,
|
|
393
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS "contacts_platform_platform_id_uq" ON "filtered"."contacts"("platform", "platform_id")`,
|
|
394
|
+
// --- filtered.conversations: ensure new-generation columns ---
|
|
395
|
+
`ALTER TABLE "filtered"."conversations" ADD COLUMN IF NOT EXISTS "thread_id" TEXT`,
|
|
396
|
+
`ALTER TABLE "filtered"."conversations" ADD COLUMN IF NOT EXISTS "message_type" TEXT DEFAULT 'text'`,
|
|
397
|
+
`ALTER TABLE "filtered"."conversations" ADD COLUMN IF NOT EXISTS "sent_at" TIMESTAMPTZ DEFAULT NOW()`,
|
|
398
|
+
`ALTER TABLE "filtered"."conversations" ADD COLUMN IF NOT EXISTS "source_ref" TEXT`,
|
|
399
|
+
`DO $$ BEGIN
|
|
400
|
+
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='filtered' AND table_name='conversations' AND column_name='timestamp' AND is_nullable='NO') THEN
|
|
401
|
+
ALTER TABLE "filtered"."conversations" ALTER COLUMN "timestamp" DROP NOT NULL;
|
|
402
|
+
END IF;
|
|
403
|
+
END $$`,
|
|
404
|
+
`DO $$ BEGIN
|
|
405
|
+
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='filtered' AND table_name='conversations' AND column_name='remote_jid') THEN
|
|
406
|
+
UPDATE "filtered"."conversations" SET
|
|
407
|
+
"thread_id" = COALESCE("thread_id", "remote_jid"),
|
|
408
|
+
"sent_at" = COALESCE("sent_at", "timestamp", NOW())
|
|
409
|
+
WHERE "thread_id" IS NULL OR "sent_at" IS NULL;
|
|
410
|
+
END IF;
|
|
411
|
+
END $$`,
|
|
412
|
+
// Retire the weak same-second dedup key; keep a plain index for queries.
|
|
413
|
+
`ALTER TABLE "filtered"."conversations" DROP CONSTRAINT IF EXISTS "conversations_platform_thread_id_sent_at_key"`,
|
|
414
|
+
`DROP INDEX IF EXISTS "filtered"."conversations_platform_thread_sent_uq"`,
|
|
415
|
+
`CREATE INDEX IF NOT EXISTS "conversations_platform_thread_sent_idx" ON "filtered"."conversations"("platform", "thread_id", "sent_at")`,
|
|
416
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS "conversations_platform_source_ref_uq" ON "filtered"."conversations"("platform", "source_ref")`,
|
|
417
|
+
// --- raw.raw_events: retry-model columns required by the poll loop ---
|
|
418
|
+
`ALTER TABLE "raw"."raw_events" ADD COLUMN IF NOT EXISTS "retry_count" INTEGER NOT NULL DEFAULT 0`,
|
|
419
|
+
`ALTER TABLE "raw"."raw_events" ADD COLUMN IF NOT EXISTS "failed_at" TIMESTAMPTZ`,
|
|
420
|
+
`ALTER TABLE "raw"."raw_events" ADD COLUMN IF NOT EXISTS "error" TEXT`,
|
|
421
|
+
`ALTER TABLE "raw"."raw_events" ADD COLUMN IF NOT EXISTS "projections" JSONB`
|
|
422
|
+
];
|
|
423
|
+
for (const stmt of stmts) {
|
|
424
|
+
try {
|
|
425
|
+
await prisma.$executeRawUnsafe(stmt);
|
|
426
|
+
} catch (err) {
|
|
427
|
+
console.error(
|
|
428
|
+
`[projection-worker] schema self-heal statement failed (continuing): ${err instanceof Error ? err.message.split("\n")[0] : String(err)}`
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
async function projectAgenticOntology(prisma, payload, memoryId, timestamp) {
|
|
434
|
+
await ensurePostgresOntologySchema(prisma);
|
|
435
|
+
const row = {
|
|
436
|
+
id: memoryId,
|
|
437
|
+
agent_id: String(payload.agent_id ?? "system"),
|
|
438
|
+
agent_role: String(payload.agent_role ?? "ingest"),
|
|
439
|
+
session_id: String(payload.session_id ?? "projection-worker"),
|
|
440
|
+
timestamp: timestamp.toISOString(),
|
|
441
|
+
tool_name: String(payload.tool_name ?? "cloud_sync"),
|
|
442
|
+
project_name: String(payload.project_name ?? "exe-os"),
|
|
443
|
+
has_error: payload.has_error ? 1 : 0,
|
|
444
|
+
raw_text: String(payload.raw_text ?? JSON.stringify(payload)),
|
|
445
|
+
version: typeof payload.version === "number" ? payload.version : Number(payload.version ?? 0),
|
|
446
|
+
task_id: typeof payload.task_id === "string" ? payload.task_id : null,
|
|
447
|
+
intent: typeof payload.intent === "string" ? payload.intent : null,
|
|
448
|
+
outcome: typeof payload.outcome === "string" ? payload.outcome : null,
|
|
449
|
+
domain: typeof payload.domain === "string" ? payload.domain : null,
|
|
450
|
+
trajectory: typeof payload.trajectory === "string" ? payload.trajectory : null
|
|
451
|
+
};
|
|
452
|
+
const eventId = stableId(`event:${memoryId}`);
|
|
453
|
+
const sequence = Number(row.version ?? 0) || Math.floor(timestamp.getTime() / 1e3);
|
|
454
|
+
const intention = inferIntention(row);
|
|
455
|
+
const outcome = inferOutcome(row);
|
|
456
|
+
const now = /* @__PURE__ */ new Date();
|
|
457
|
+
await prisma.$executeRawUnsafe(
|
|
458
|
+
`INSERT INTO "${GRAPH_SCHEMA}"."agent_sessions" ("id", "agent_id", "project_name", "started_at", "last_event_at", "event_count", "properties")
|
|
459
|
+
VALUES ($1, $2, $3, $4, $4, 1, $5::jsonb)
|
|
460
|
+
ON CONFLICT ("id") DO UPDATE SET "last_event_at" = GREATEST("${GRAPH_SCHEMA}"."agent_sessions"."last_event_at", EXCLUDED."last_event_at"),
|
|
461
|
+
"event_count" = "${GRAPH_SCHEMA}"."agent_sessions"."event_count" + 1`,
|
|
462
|
+
row.session_id,
|
|
463
|
+
row.agent_id,
|
|
464
|
+
row.project_name,
|
|
465
|
+
timestamp,
|
|
466
|
+
JSON.stringify({ agent_role: row.agent_role })
|
|
467
|
+
);
|
|
468
|
+
await prisma.$executeRawUnsafe(
|
|
469
|
+
`INSERT INTO "${GRAPH_SCHEMA}"."agent_events"
|
|
470
|
+
("id", "event_type", "occurred_at", "sequence_index", "actor_agent_id", "agent_role", "project_name",
|
|
471
|
+
"session_id", "task_id", "goal_id", "parent_event_id", "intention", "outcome", "evidence_memory_id",
|
|
472
|
+
"impact", "payload", "created_at")
|
|
473
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, NULL, NULL, $10, $11, $12, $13, $14::jsonb, $15)
|
|
474
|
+
ON CONFLICT ("id") DO NOTHING`,
|
|
475
|
+
eventId,
|
|
476
|
+
inferOntologyEventType(row),
|
|
477
|
+
timestamp,
|
|
478
|
+
sequence,
|
|
479
|
+
row.agent_id,
|
|
480
|
+
row.agent_role,
|
|
481
|
+
row.project_name,
|
|
482
|
+
row.session_id,
|
|
483
|
+
row.task_id ?? null,
|
|
484
|
+
intention,
|
|
485
|
+
outcome,
|
|
486
|
+
row.id,
|
|
487
|
+
row.has_error ? "negative" : outcome === "success_signal" ? "positive" : "neutral",
|
|
488
|
+
JSON.stringify(ontologyPayload(row)),
|
|
489
|
+
now
|
|
490
|
+
);
|
|
491
|
+
const semantic = inferSemanticLabel(row);
|
|
492
|
+
await prisma.$executeRawUnsafe(
|
|
493
|
+
`INSERT INTO "${GRAPH_SCHEMA}"."agent_semantic_labels"
|
|
494
|
+
("id", "source_memory_id", "event_id", "labeler", "schema_version", "confidence", "labels", "created_at", "updated_at")
|
|
495
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8, $8)
|
|
496
|
+
ON CONFLICT ("id") DO UPDATE SET "confidence" = EXCLUDED."confidence",
|
|
497
|
+
"labels" = EXCLUDED."labels", "updated_at" = EXCLUDED."updated_at"`,
|
|
498
|
+
stableId(`semantic:${row.id}:${semantic.labeler}:${semantic.schemaVersion}`),
|
|
499
|
+
row.id,
|
|
500
|
+
eventId,
|
|
501
|
+
semantic.labeler,
|
|
502
|
+
semantic.schemaVersion,
|
|
503
|
+
semantic.confidence,
|
|
504
|
+
JSON.stringify(semantic),
|
|
505
|
+
now
|
|
506
|
+
);
|
|
507
|
+
let goals = 0;
|
|
508
|
+
for (const statement of extractGoalCandidates(row)) {
|
|
509
|
+
const goalId = stableId(`goal:${row.project_name}:${statement.toLowerCase()}`);
|
|
510
|
+
await prisma.$executeRawUnsafe(
|
|
511
|
+
`INSERT INTO "${GRAPH_SCHEMA}"."agent_goals"
|
|
512
|
+
("id", "statement", "owner_agent_id", "project_name", "status", "priority", "created_at", "updated_at", "source_memory_id")
|
|
513
|
+
VALUES ($1, $2, $3, $4, 'open', 5, $5, $5, $6)
|
|
514
|
+
ON CONFLICT ("id") DO UPDATE SET "updated_at" = EXCLUDED."updated_at"`,
|
|
515
|
+
goalId,
|
|
516
|
+
statement,
|
|
517
|
+
row.agent_id,
|
|
518
|
+
row.project_name,
|
|
519
|
+
now,
|
|
520
|
+
row.id
|
|
521
|
+
);
|
|
522
|
+
await prisma.$executeRawUnsafe(
|
|
523
|
+
`INSERT INTO "${GRAPH_SCHEMA}"."agent_goal_links" ("id", "goal_id", "link_type", "target_id", "target_type", "created_at")
|
|
524
|
+
VALUES ($1, $2, 'evidence', $3, 'memory', $4)
|
|
525
|
+
ON CONFLICT ("id") DO NOTHING`,
|
|
526
|
+
stableId(`goal_link:${goalId}:${row.id}:memory`),
|
|
527
|
+
goalId,
|
|
528
|
+
row.id,
|
|
529
|
+
now
|
|
530
|
+
);
|
|
531
|
+
await prisma.$executeRawUnsafe(
|
|
532
|
+
`INSERT INTO "${GRAPH_SCHEMA}"."agent_goal_links" ("id", "goal_id", "link_type", "target_id", "target_type", "created_at")
|
|
533
|
+
VALUES ($1, $2, 'event', $3, 'event', $4)
|
|
534
|
+
ON CONFLICT ("id") DO NOTHING`,
|
|
535
|
+
stableId(`goal_link:${goalId}:${eventId}:event`),
|
|
536
|
+
goalId,
|
|
537
|
+
eventId,
|
|
538
|
+
now
|
|
539
|
+
);
|
|
540
|
+
goals++;
|
|
541
|
+
}
|
|
542
|
+
return { events: 1, goals };
|
|
543
|
+
}
|
|
544
|
+
var WIKI_PROJECTABLE_TOOLS = /* @__PURE__ */ new Set(["manual", "auto-decision", "SessionEnd", "AssistantResponse"]);
|
|
545
|
+
var WIKI_MIN_TEXT_LENGTH = 100;
|
|
546
|
+
async function ensureWikiWorkspace(prisma, projectName) {
|
|
547
|
+
const slug = projectName.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-");
|
|
548
|
+
const existing = await prisma.$queryRawUnsafe(
|
|
549
|
+
`SELECT "id" FROM "wiki"."workspaces" WHERE "slug" = $1`,
|
|
550
|
+
slug
|
|
551
|
+
);
|
|
552
|
+
if (existing.length > 0) return existing[0].id;
|
|
553
|
+
const inserted = await prisma.$queryRawUnsafe(
|
|
554
|
+
`INSERT INTO "wiki"."workspaces" ("name", "slug", "chat_mode", "updated_at")
|
|
555
|
+
VALUES ($1, $2, 'chat', NOW())
|
|
556
|
+
ON CONFLICT ("slug") DO UPDATE SET "name" = EXCLUDED."name"
|
|
557
|
+
RETURNING "id"`,
|
|
558
|
+
projectName,
|
|
559
|
+
slug
|
|
560
|
+
);
|
|
561
|
+
return inserted[0].id;
|
|
562
|
+
}
|
|
563
|
+
function isWikiWorthy(payload) {
|
|
564
|
+
const toolName = String(payload.tool_name ?? "");
|
|
565
|
+
const rawText = String(payload.raw_text ?? "");
|
|
566
|
+
const importance = typeof payload.importance === "number" ? payload.importance : 5;
|
|
567
|
+
const memoryType = String(payload.memory_type ?? "raw");
|
|
568
|
+
if (memoryType === "decision" || memoryType === "procedure") return true;
|
|
569
|
+
if (WIKI_PROJECTABLE_TOOLS.has(toolName)) return true;
|
|
570
|
+
if (importance >= 7 && rawText.length >= WIKI_MIN_TEXT_LENGTH) return true;
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
573
|
+
async function projectToWiki(prisma, payload, timestamp) {
|
|
574
|
+
if (!isWikiWorthy(payload)) return { documents: 0 };
|
|
575
|
+
const rawText = String(payload.raw_text ?? "");
|
|
576
|
+
if (rawText.length < WIKI_MIN_TEXT_LENGTH) return { documents: 0 };
|
|
577
|
+
const projectName = String(payload.project_name ?? "exe-os");
|
|
578
|
+
const agentId = String(payload.agent_id ?? "system");
|
|
579
|
+
const toolName = String(payload.tool_name ?? "unknown");
|
|
580
|
+
const memoryId = String(payload.id ?? "");
|
|
581
|
+
const docId = memoryId || stableId(rawText.slice(0, 512));
|
|
582
|
+
const firstLine = rawText.split("\n")[0]?.slice(0, 120)?.replace(/[^a-zA-Z0-9 _-]/g, "") || "untitled";
|
|
583
|
+
const filename = `${agentId}-${firstLine.slice(0, 80).trim().replace(/\s+/g, "-").toLowerCase()}.md`;
|
|
584
|
+
const docpath = `${projectName}/${filename}`;
|
|
585
|
+
const workspaceId = await ensureWikiWorkspace(prisma, projectName);
|
|
586
|
+
await prisma.$executeRawUnsafe(
|
|
587
|
+
`INSERT INTO "wiki"."workspace_documents" ("doc_id", "filename", "docpath", "workspace_id", "metadata", "pinned")
|
|
588
|
+
VALUES ($1, $2, $3, $4, $5, false)
|
|
589
|
+
ON CONFLICT ("doc_id") DO UPDATE SET
|
|
590
|
+
"filename" = EXCLUDED."filename",
|
|
591
|
+
"metadata" = EXCLUDED."metadata"`,
|
|
592
|
+
docId,
|
|
593
|
+
filename,
|
|
594
|
+
docpath,
|
|
595
|
+
workspaceId,
|
|
596
|
+
JSON.stringify({
|
|
597
|
+
agent_id: agentId,
|
|
598
|
+
tool_name: toolName,
|
|
599
|
+
memory_type: payload.memory_type ?? "raw",
|
|
600
|
+
importance: payload.importance ?? 5,
|
|
601
|
+
timestamp: timestamp.toISOString(),
|
|
602
|
+
content_preview: rawText.slice(0, 500)
|
|
603
|
+
})
|
|
604
|
+
);
|
|
605
|
+
return { documents: 1 };
|
|
606
|
+
}
|
|
607
|
+
var lastWikiApiPushAt = 0;
|
|
608
|
+
var WIKI_API_MIN_INTERVAL_MS = 500;
|
|
609
|
+
async function pushToWikiApi(payload, _timestamp) {
|
|
610
|
+
const now = Date.now();
|
|
611
|
+
if (now - lastWikiApiPushAt < WIKI_API_MIN_INTERVAL_MS) {
|
|
612
|
+
return false;
|
|
613
|
+
}
|
|
614
|
+
try {
|
|
615
|
+
const { createWikiClient, uploadDocumentToWiki } = await import("./wiki-client-Q4O6EMTP.js");
|
|
616
|
+
const client = await createWikiClient();
|
|
617
|
+
if (!client) return false;
|
|
618
|
+
const rawText = String(payload.raw_text ?? "");
|
|
619
|
+
const projectName = String(payload.project_name ?? "exe-os");
|
|
620
|
+
const agentId = String(payload.agent_id ?? "system");
|
|
621
|
+
const firstLine = rawText.split("\n")[0]?.slice(0, 120) ?? "Untitled";
|
|
622
|
+
const title = `[${agentId}] ${firstLine}`;
|
|
623
|
+
const slug = projectName.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-");
|
|
624
|
+
const result = await uploadDocumentToWiki(client, slug, title, rawText);
|
|
625
|
+
lastWikiApiPushAt = Date.now();
|
|
626
|
+
if (!result.success) {
|
|
627
|
+
process.stderr.write(`[projection-worker] Wiki API push failed: ${result.error}
|
|
628
|
+
`);
|
|
629
|
+
}
|
|
630
|
+
return result.success;
|
|
631
|
+
} catch (err) {
|
|
632
|
+
process.stderr.write(`[projection-worker] Wiki API push error: ${err instanceof Error ? err.message : String(err)}
|
|
633
|
+
`);
|
|
634
|
+
return false;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
function resetLastWikiApiPushAtForTests() {
|
|
638
|
+
lastWikiApiPushAt = 0;
|
|
639
|
+
}
|
|
640
|
+
var projectionHandlers = {
|
|
641
|
+
async whatsapp(event, prisma) {
|
|
642
|
+
const targets = [];
|
|
643
|
+
const payload = event.payload;
|
|
644
|
+
await prisma.$executeRawUnsafe(
|
|
645
|
+
`INSERT INTO "${GRAPH_SCHEMA}"."memory_records" ("id", "agent_id", "agent_role", "session_id", "tool_name", "project_name", "raw_text", "memory_type", "source_type", "intent")
|
|
646
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
647
|
+
ON CONFLICT ("id") DO NOTHING`,
|
|
648
|
+
crypto.randomUUID(),
|
|
649
|
+
"system",
|
|
650
|
+
"ingest",
|
|
651
|
+
"projection-worker",
|
|
652
|
+
"projection_worker",
|
|
653
|
+
"exe-os",
|
|
654
|
+
JSON.stringify(payload),
|
|
655
|
+
"raw",
|
|
656
|
+
"whatsapp",
|
|
657
|
+
event.event_type
|
|
658
|
+
);
|
|
659
|
+
targets.push("graph");
|
|
660
|
+
await ensureFilteredSchema(prisma);
|
|
661
|
+
const senderName = payload.sender_name ?? payload.contact_name ?? null;
|
|
662
|
+
const rawSender = payload.from ?? payload.phone ?? null;
|
|
663
|
+
const threadId = payload.conversation_id ?? payload.chat_id ?? event.source_id;
|
|
664
|
+
if (rawSender) {
|
|
665
|
+
const bareSender = rawSender.replace(/@.*$/, "");
|
|
666
|
+
const phone = !rawSender.endsWith("@lid") && /^\+?\d{6,20}$/.test(bareSender) ? bareSender : null;
|
|
667
|
+
const name = senderName ?? phone ?? "Unknown (WhatsApp)";
|
|
668
|
+
const contactResult = await prisma.$queryRawUnsafe(
|
|
669
|
+
`INSERT INTO "filtered"."contacts" ("name", "phone", "platform", "platform_id", "last_seen")
|
|
670
|
+
VALUES ($1, $2, 'whatsapp', $3, $4)
|
|
671
|
+
ON CONFLICT ("platform", "platform_id") DO UPDATE SET
|
|
672
|
+
"name" = CASE
|
|
673
|
+
WHEN EXCLUDED."name" IS NOT NULL AND EXCLUDED."name" <> 'Unknown (WhatsApp)' THEN EXCLUDED."name"
|
|
674
|
+
WHEN "filtered"."contacts"."name" ~ '@(lid|s\\.whatsapp\\.net)$' THEN EXCLUDED."name"
|
|
675
|
+
ELSE "filtered"."contacts"."name"
|
|
676
|
+
END,
|
|
677
|
+
"phone" = COALESCE(EXCLUDED."phone", "filtered"."contacts"."phone"),
|
|
678
|
+
"last_seen" = GREATEST("filtered"."contacts"."last_seen", EXCLUDED."last_seen")
|
|
679
|
+
RETURNING "id"`,
|
|
680
|
+
name,
|
|
681
|
+
phone,
|
|
682
|
+
rawSender,
|
|
683
|
+
event.timestamp
|
|
684
|
+
);
|
|
685
|
+
const contactId = contactResult[0]?.id;
|
|
686
|
+
targets.push("filtered.contacts");
|
|
687
|
+
const messageContent = payload.message ?? payload.text ?? payload.body ?? JSON.stringify(payload);
|
|
688
|
+
const direction = payload.direction ?? "inbound";
|
|
689
|
+
if (contactId && messageContent) {
|
|
690
|
+
const sourceRef = event.source_id ?? event.id;
|
|
691
|
+
await prisma.$executeRawUnsafe(
|
|
692
|
+
`INSERT INTO "filtered"."conversations" ("contact_id", "platform", "thread_id", "direction", "content", "message_type", "sent_at", "source_ref")
|
|
693
|
+
VALUES ($1::uuid, 'whatsapp', $2, $3, $4, $5, $6, $7)
|
|
694
|
+
ON CONFLICT ("platform", "source_ref") DO NOTHING`,
|
|
695
|
+
contactId,
|
|
696
|
+
threadId,
|
|
697
|
+
direction,
|
|
698
|
+
typeof messageContent === "string" ? messageContent : JSON.stringify(messageContent),
|
|
699
|
+
payload.type ?? "text",
|
|
700
|
+
event.timestamp,
|
|
701
|
+
String(sourceRef)
|
|
702
|
+
);
|
|
703
|
+
targets.push("filtered.conversations");
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
return { targets };
|
|
707
|
+
},
|
|
708
|
+
async shopify(event, prisma) {
|
|
709
|
+
const targets = [];
|
|
710
|
+
const payload = event.payload;
|
|
711
|
+
await prisma.$executeRawUnsafe(
|
|
712
|
+
`INSERT INTO "${GRAPH_SCHEMA}"."memory_records" ("id", "agent_id", "agent_role", "session_id", "tool_name", "project_name", "raw_text", "memory_type", "source_type", "intent")
|
|
713
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
714
|
+
ON CONFLICT ("id") DO NOTHING`,
|
|
715
|
+
crypto.randomUUID(),
|
|
716
|
+
"system",
|
|
717
|
+
"ingest",
|
|
718
|
+
"projection-worker",
|
|
719
|
+
"projection_worker",
|
|
720
|
+
"exe-os",
|
|
721
|
+
JSON.stringify(payload),
|
|
722
|
+
"raw",
|
|
723
|
+
"shopify",
|
|
724
|
+
event.event_type
|
|
725
|
+
);
|
|
726
|
+
targets.push("graph");
|
|
727
|
+
return { targets };
|
|
728
|
+
},
|
|
729
|
+
async cloud_sync(event, prisma) {
|
|
730
|
+
const targets = [];
|
|
731
|
+
const payload = event.payload;
|
|
732
|
+
const memoryId = payload.id ?? crypto.randomUUID();
|
|
733
|
+
await prisma.$executeRawUnsafe(
|
|
734
|
+
`INSERT INTO "${GRAPH_SCHEMA}"."memory_records" ("id", "agent_id", "agent_role", "session_id", "tool_name", "project_name", "raw_text", "memory_type", "source_type", "intent", "domain")
|
|
735
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
|
736
|
+
ON CONFLICT ("id") DO NOTHING`,
|
|
737
|
+
memoryId,
|
|
738
|
+
payload.agent_id ?? "system",
|
|
739
|
+
payload.agent_role ?? "ingest",
|
|
740
|
+
payload.session_id ?? "projection-worker",
|
|
741
|
+
payload.tool_name ?? "cloud_sync",
|
|
742
|
+
payload.project_name ?? "exe-os",
|
|
743
|
+
payload.raw_text ?? JSON.stringify(payload),
|
|
744
|
+
payload.memory_type ?? "raw",
|
|
745
|
+
"cloud_sync",
|
|
746
|
+
event.event_type,
|
|
747
|
+
payload.domain ?? null
|
|
748
|
+
);
|
|
749
|
+
targets.push("memory");
|
|
750
|
+
const graph = await projectMemoryGraph(prisma, payload, memoryId, event.timestamp);
|
|
751
|
+
if (graph.entities > 0 || graph.relationships > 0) targets.push("graph");
|
|
752
|
+
const ontology = await projectAgenticOntology(prisma, payload, memoryId, event.timestamp);
|
|
753
|
+
if (ontology.events > 0) targets.push("ontology");
|
|
754
|
+
const wiki = await projectToWiki(prisma, payload, event.timestamp);
|
|
755
|
+
if (wiki.documents > 0) {
|
|
756
|
+
targets.push("wiki");
|
|
757
|
+
await pushToWikiApi(payload, event.timestamp);
|
|
758
|
+
}
|
|
759
|
+
await ensureFilteredSchema(prisma);
|
|
760
|
+
const rawText = payload.raw_text ?? "";
|
|
761
|
+
if (rawText.length > 50) {
|
|
762
|
+
await prisma.$executeRawUnsafe(
|
|
763
|
+
`INSERT INTO "filtered"."documents" ("title", "content", "doc_type", "source", "source_id", "metadata", "created_at")
|
|
764
|
+
VALUES ($1, $2, $3, $4, $5, $6::jsonb, $7)
|
|
765
|
+
ON CONFLICT ("source", "source_id") DO NOTHING`,
|
|
766
|
+
rawText.slice(0, 80),
|
|
767
|
+
rawText,
|
|
768
|
+
payload.tool_name ?? "memory",
|
|
769
|
+
"exe-os",
|
|
770
|
+
memoryId,
|
|
771
|
+
JSON.stringify({
|
|
772
|
+
agent_id: payload.agent_id,
|
|
773
|
+
project_name: payload.project_name
|
|
774
|
+
}),
|
|
775
|
+
event.timestamp
|
|
776
|
+
);
|
|
777
|
+
targets.push("filtered.documents");
|
|
778
|
+
}
|
|
779
|
+
return { targets };
|
|
780
|
+
},
|
|
781
|
+
async external_agent(event, prisma) {
|
|
782
|
+
const targets = [];
|
|
783
|
+
const payload = event.payload;
|
|
784
|
+
await prisma.$executeRawUnsafe(
|
|
785
|
+
`INSERT INTO "${GRAPH_SCHEMA}"."memory_records" ("id", "agent_id", "agent_role", "session_id", "tool_name", "project_name", "raw_text", "memory_type", "source_type", "intent", "domain")
|
|
786
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
|
787
|
+
ON CONFLICT ("id") DO NOTHING`,
|
|
788
|
+
crypto.randomUUID(),
|
|
789
|
+
"system",
|
|
790
|
+
"ingest",
|
|
791
|
+
"projection-worker",
|
|
792
|
+
"projection_worker",
|
|
793
|
+
"exe-os",
|
|
794
|
+
JSON.stringify(payload),
|
|
795
|
+
"raw",
|
|
796
|
+
"external_agent",
|
|
797
|
+
event.event_type,
|
|
798
|
+
payload.domain ?? null
|
|
799
|
+
);
|
|
800
|
+
targets.push("graph");
|
|
801
|
+
return { targets };
|
|
802
|
+
},
|
|
803
|
+
async erp(event, prisma) {
|
|
804
|
+
const targets = [];
|
|
805
|
+
const payload = event.payload;
|
|
806
|
+
await prisma.$executeRawUnsafe(
|
|
807
|
+
`INSERT INTO "${GRAPH_SCHEMA}"."memory_records" ("id", "agent_id", "agent_role", "session_id", "tool_name", "project_name", "raw_text", "memory_type", "source_type", "intent", "domain")
|
|
808
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
|
809
|
+
ON CONFLICT ("id") DO NOTHING`,
|
|
810
|
+
crypto.randomUUID(),
|
|
811
|
+
"system",
|
|
812
|
+
"ingest",
|
|
813
|
+
"projection-worker",
|
|
814
|
+
"projection_worker",
|
|
815
|
+
payload.project_name ?? "exe-erp",
|
|
816
|
+
JSON.stringify(payload),
|
|
817
|
+
"raw",
|
|
818
|
+
"erp",
|
|
819
|
+
event.event_type,
|
|
820
|
+
payload.domain ?? "business"
|
|
821
|
+
);
|
|
822
|
+
targets.push("graph");
|
|
823
|
+
await ensureFilteredSchema(prisma);
|
|
824
|
+
const rawText = typeof payload.description === "string" ? payload.description : JSON.stringify(payload);
|
|
825
|
+
if (rawText.length > 50) {
|
|
826
|
+
await prisma.$executeRawUnsafe(
|
|
827
|
+
`INSERT INTO "filtered"."documents" ("title", "content", "doc_type", "source", "source_id", "metadata", "created_at")
|
|
828
|
+
VALUES ($1, $2, $3, $4, $5, $6::jsonb, $7)
|
|
829
|
+
ON CONFLICT ("source", "source_id") DO NOTHING`,
|
|
830
|
+
rawText.length > 80 ? rawText.slice(0, 77) + "..." : rawText,
|
|
831
|
+
rawText,
|
|
832
|
+
event.event_type,
|
|
833
|
+
"erp",
|
|
834
|
+
event.source_id ?? event.id,
|
|
835
|
+
JSON.stringify({
|
|
836
|
+
erp_doctype: payload.doctype,
|
|
837
|
+
erp_name: payload.name,
|
|
838
|
+
event_type: event.event_type
|
|
839
|
+
}),
|
|
840
|
+
event.timestamp
|
|
841
|
+
);
|
|
842
|
+
targets.push("filtered.documents");
|
|
843
|
+
}
|
|
844
|
+
const erpPayload = payload;
|
|
845
|
+
const doctype = String(erpPayload.doctype ?? erpPayload.docType ?? erpPayload.object_type ?? "Unknown");
|
|
846
|
+
const normalizedObjectType = doctype.charAt(0).toUpperCase() + doctype.slice(1);
|
|
847
|
+
const triggerEvent = {
|
|
848
|
+
eventType: event.event_type.replace(/^.*\./, ""),
|
|
849
|
+
// "sales_order.created" -> "created"
|
|
850
|
+
objectType: normalizedObjectType,
|
|
851
|
+
record: erpPayload
|
|
852
|
+
};
|
|
853
|
+
try {
|
|
854
|
+
const logs = await processCRMEvent(triggerEvent);
|
|
855
|
+
if (logs.length > 0) {
|
|
856
|
+
targets.push("triggers");
|
|
857
|
+
process.stderr.write(
|
|
858
|
+
`[projection-worker] ERP event ${normalizedObjectType}.${triggerEvent.eventType} fired ${logs.length} trigger(s)
|
|
859
|
+
`
|
|
860
|
+
);
|
|
861
|
+
}
|
|
862
|
+
} catch (err) {
|
|
863
|
+
process.stderr.write(
|
|
864
|
+
`[projection-worker] ERP trigger evaluation failed: ${err instanceof Error ? err.message : String(err)}
|
|
865
|
+
`
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
return { targets };
|
|
869
|
+
}
|
|
870
|
+
};
|
|
871
|
+
var projectionHandlersForTests = projectionHandlers;
|
|
872
|
+
var defaultHandler = async (event, prisma) => {
|
|
873
|
+
await prisma.$executeRawUnsafe(
|
|
874
|
+
`INSERT INTO "${GRAPH_SCHEMA}"."memory_records" ("id", "agent_id", "agent_role", "session_id", "tool_name", "project_name", "raw_text", "memory_type", "source_type", "intent")
|
|
875
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
876
|
+
ON CONFLICT ("id") DO NOTHING`,
|
|
877
|
+
crypto.randomUUID(),
|
|
878
|
+
"system",
|
|
879
|
+
"ingest",
|
|
880
|
+
"projection-worker",
|
|
881
|
+
"projection_worker",
|
|
882
|
+
"exe-os",
|
|
883
|
+
JSON.stringify(event.payload),
|
|
884
|
+
"raw",
|
|
885
|
+
event.source,
|
|
886
|
+
event.event_type
|
|
887
|
+
);
|
|
888
|
+
return { targets: ["memory"] };
|
|
889
|
+
};
|
|
890
|
+
var BATCH_SIZE = 50;
|
|
891
|
+
var POLL_INTERVAL_MS = 1e4;
|
|
892
|
+
var MAX_POLL_BACKOFF_MS = 5 * 6e4;
|
|
893
|
+
var MAX_PROJECTION_RETRIES = 5;
|
|
894
|
+
function isTruthyEnv(value) {
|
|
895
|
+
return /^(1|true|yes|on)$/i.test(value ?? "");
|
|
896
|
+
}
|
|
897
|
+
var running = false;
|
|
898
|
+
var pollTimer = null;
|
|
899
|
+
var consecutivePollErrors = 0;
|
|
900
|
+
async function processBatch() {
|
|
901
|
+
const prisma = await loadPrisma();
|
|
902
|
+
await ensureFilteredSchema(prisma);
|
|
903
|
+
const events = await prisma.$queryRawUnsafe(
|
|
904
|
+
`SELECT "id", "source", "source_id", "event_type", "payload", "metadata", "timestamp"
|
|
905
|
+
FROM "raw"."raw_events"
|
|
906
|
+
WHERE "processed_at" IS NULL
|
|
907
|
+
AND ("failed_at" IS NULL OR (
|
|
908
|
+
"retry_count" < $2
|
|
909
|
+
AND "failed_at" < NOW() - make_interval(secs => POWER(2, LEAST("retry_count", 8))::int)
|
|
910
|
+
))
|
|
911
|
+
ORDER BY "timestamp" ASC
|
|
912
|
+
LIMIT $1`,
|
|
913
|
+
BATCH_SIZE,
|
|
914
|
+
MAX_PROJECTION_RETRIES
|
|
915
|
+
);
|
|
916
|
+
if (events.length === 0) return 0;
|
|
917
|
+
let processed = 0;
|
|
918
|
+
for (const event of events) {
|
|
919
|
+
try {
|
|
920
|
+
const handler = projectionHandlers[event.source] ?? defaultHandler;
|
|
921
|
+
const result = await handler(event, prisma);
|
|
922
|
+
await prisma.$executeRawUnsafe(
|
|
923
|
+
`UPDATE "raw"."raw_events"
|
|
924
|
+
SET "processed_at" = NOW(), "projections" = $1::jsonb
|
|
925
|
+
WHERE "id" = $2`,
|
|
926
|
+
JSON.stringify({ targets: [...new Set(result.targets)], skipped: result.skipped }),
|
|
927
|
+
event.id
|
|
928
|
+
);
|
|
929
|
+
processed++;
|
|
930
|
+
} catch (err) {
|
|
931
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
932
|
+
process.stderr.write(
|
|
933
|
+
`[projection-worker] Error processing event ${event.id}: ${message}
|
|
934
|
+
`
|
|
935
|
+
);
|
|
936
|
+
await prisma.$executeRawUnsafe(
|
|
937
|
+
`UPDATE "raw"."raw_events"
|
|
938
|
+
SET "failed_at" = NOW(),
|
|
939
|
+
"retry_count" = COALESCE("retry_count", 0) + 1,
|
|
940
|
+
"error" = $1,
|
|
941
|
+
"projections" = $2::jsonb
|
|
942
|
+
WHERE "id" = $3`,
|
|
943
|
+
message,
|
|
944
|
+
JSON.stringify({ error: message }),
|
|
945
|
+
event.id
|
|
946
|
+
);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
return processed;
|
|
950
|
+
}
|
|
951
|
+
async function shouldStartProjectionWorker() {
|
|
952
|
+
try {
|
|
953
|
+
const config = await loadConfig();
|
|
954
|
+
const envEnabled = isTruthyEnv(process.env.EXE_CLOUD_SYNC_TO_POSTGRES);
|
|
955
|
+
if (!envEnabled && config.cloud?.syncToPostgres !== true) {
|
|
956
|
+
return { start: false, reason: "cloud.syncToPostgres is not enabled" };
|
|
957
|
+
}
|
|
958
|
+
} catch (err) {
|
|
959
|
+
return { start: false, reason: `config unavailable: ${err instanceof Error ? err.message : String(err)}` };
|
|
960
|
+
}
|
|
961
|
+
if (!process.env.DATABASE_URL) {
|
|
962
|
+
return { start: false, reason: "DATABASE_URL is not set" };
|
|
963
|
+
}
|
|
964
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path.join(os.homedir(), "exe-db");
|
|
965
|
+
if (!existsSync(path.join(exeDbRoot, "package.json")) && !process.env.EXE_OS_PRISMA_CLIENT_PATH) {
|
|
966
|
+
try {
|
|
967
|
+
await import("pg");
|
|
968
|
+
} catch {
|
|
969
|
+
return { start: false, reason: "no exe-db Prisma client or pg adapter available" };
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
return { start: true };
|
|
973
|
+
}
|
|
974
|
+
function computeProjectionBackoffMs(errorCount) {
|
|
975
|
+
if (errorCount <= 0) return POLL_INTERVAL_MS;
|
|
976
|
+
return Math.min(POLL_INTERVAL_MS * 2 ** Math.min(errorCount, 5), MAX_POLL_BACKOFF_MS);
|
|
977
|
+
}
|
|
978
|
+
function startProjectionWorker() {
|
|
979
|
+
if (running) return;
|
|
980
|
+
void (async () => {
|
|
981
|
+
const decision = await shouldStartProjectionWorker();
|
|
982
|
+
if (!decision.start) {
|
|
983
|
+
process.stderr.write(`[projection-worker] Skipped \u2014 ${decision.reason}.
|
|
984
|
+
`);
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
987
|
+
running = true;
|
|
988
|
+
consecutivePollErrors = 0;
|
|
989
|
+
process.stderr.write("[projection-worker] Starting...\n");
|
|
990
|
+
if (isTruthyEnv(process.env.EXE_PROJECTION_REQUEUE_ON_START)) {
|
|
991
|
+
try {
|
|
992
|
+
await requeueRetryExhausted({
|
|
993
|
+
source: process.env.EXE_PROJECTION_REQUEUE_SOURCE || void 0,
|
|
994
|
+
eventType: process.env.EXE_PROJECTION_REQUEUE_EVENT_TYPE || void 0
|
|
995
|
+
});
|
|
996
|
+
} catch (err) {
|
|
997
|
+
process.stderr.write(
|
|
998
|
+
`[projection-worker] Requeue-on-start failed (continuing): ${err instanceof Error ? err.message : String(err)}
|
|
999
|
+
`
|
|
1000
|
+
);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
const tick = async () => {
|
|
1004
|
+
if (!running) return;
|
|
1005
|
+
let nextDelay = POLL_INTERVAL_MS;
|
|
1006
|
+
try {
|
|
1007
|
+
const count = await processBatch();
|
|
1008
|
+
consecutivePollErrors = 0;
|
|
1009
|
+
if (count > 0) {
|
|
1010
|
+
process.stderr.write(`[projection-worker] Processed ${count} events.
|
|
1011
|
+
`);
|
|
1012
|
+
}
|
|
1013
|
+
} catch (err) {
|
|
1014
|
+
consecutivePollErrors++;
|
|
1015
|
+
nextDelay = computeProjectionBackoffMs(consecutivePollErrors);
|
|
1016
|
+
process.stderr.write(
|
|
1017
|
+
`[projection-worker] Poll error (${consecutivePollErrors}; next retry ${Math.round(nextDelay / 1e3)}s): ${err instanceof Error ? err.message : String(err)}
|
|
1018
|
+
`
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
1021
|
+
if (running) {
|
|
1022
|
+
pollTimer = setTimeout(tick, nextDelay);
|
|
1023
|
+
}
|
|
1024
|
+
};
|
|
1025
|
+
void tick();
|
|
1026
|
+
})();
|
|
1027
|
+
}
|
|
1028
|
+
function stopProjectionWorker() {
|
|
1029
|
+
running = false;
|
|
1030
|
+
if (pollTimer) {
|
|
1031
|
+
clearTimeout(pollTimer);
|
|
1032
|
+
pollTimer = null;
|
|
1033
|
+
}
|
|
1034
|
+
process.stderr.write("[projection-worker] Stopped.\n");
|
|
1035
|
+
}
|
|
1036
|
+
async function processProjectionBatch() {
|
|
1037
|
+
return processBatch();
|
|
1038
|
+
}
|
|
1039
|
+
async function requeueRetryExhausted(opts = {}) {
|
|
1040
|
+
const prisma = await loadPrisma();
|
|
1041
|
+
await ensureFilteredSchema(prisma);
|
|
1042
|
+
const conditions = [
|
|
1043
|
+
`"processed_at" IS NULL`,
|
|
1044
|
+
`"retry_count" >= $1`
|
|
1045
|
+
];
|
|
1046
|
+
const params = [MAX_PROJECTION_RETRIES];
|
|
1047
|
+
if (opts.source) {
|
|
1048
|
+
params.push(opts.source);
|
|
1049
|
+
conditions.push(`"source" = $${params.length}`);
|
|
1050
|
+
}
|
|
1051
|
+
if (opts.eventType) {
|
|
1052
|
+
params.push(opts.eventType);
|
|
1053
|
+
conditions.push(`"event_type" = $${params.length}`);
|
|
1054
|
+
}
|
|
1055
|
+
const result = await prisma.$executeRawUnsafe(
|
|
1056
|
+
`UPDATE "raw"."raw_events"
|
|
1057
|
+
SET "failed_at" = NULL,
|
|
1058
|
+
"retry_count" = 0,
|
|
1059
|
+
"error" = NULL
|
|
1060
|
+
WHERE ${conditions.join(" AND ")}`,
|
|
1061
|
+
...params
|
|
1062
|
+
);
|
|
1063
|
+
const count = typeof result === "number" ? result : 0;
|
|
1064
|
+
if (count > 0) {
|
|
1065
|
+
const scope = [opts.source && `source=${opts.source}`, opts.eventType && `event_type=${opts.eventType}`].filter(Boolean).join(" ") || "all sources";
|
|
1066
|
+
process.stderr.write(
|
|
1067
|
+
`[projection-worker] Requeued ${count} retry-exhausted event(s) (${scope}).
|
|
1068
|
+
`
|
|
1069
|
+
);
|
|
1070
|
+
}
|
|
1071
|
+
return count;
|
|
1072
|
+
}
|
|
1073
|
+
export {
|
|
1074
|
+
computeProjectionBackoffMs,
|
|
1075
|
+
processProjectionBatch,
|
|
1076
|
+
projectionHandlersForTests,
|
|
1077
|
+
requeueRetryExhausted,
|
|
1078
|
+
resetLastWikiApiPushAtForTests,
|
|
1079
|
+
resetProjectionWorkerForTests,
|
|
1080
|
+
setProjectionWorkerPrismaClientForTests,
|
|
1081
|
+
shouldStartProjectionWorker,
|
|
1082
|
+
startProjectionWorker,
|
|
1083
|
+
stopProjectionWorker
|
|
1084
|
+
};
|