@askexenow/exe-os 0.9.127 → 0.9.129
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/dist/active-agent-3FDJYREJ.js +22 -0
- package/dist/active-agent-WC7YK3L5.js +23 -0
- package/dist/agent-context-CDWUZEXY.js +11 -0
- package/dist/agent-loop-G4VUR5ME.js +10 -0
- package/dist/agentic-ontology-2VYNTMNO.js +25 -0
- package/dist/backfill-metadata-OJWOCLSZ.js +601 -0
- package/dist/background-jobs-LGRDXGXC.js +25 -0
- package/dist/bash-Y5PEW5UQ.js +8 -0
- package/dist/behaviors-CSB2YLDT.js +22 -0
- package/dist/bin/age-ontology-load.js +10 -258
- package/dist/bin/agentic-ontology-backfill.js +23 -5296
- package/dist/bin/agentic-reflection-backfill.js +19 -4739
- package/dist/bin/agentic-semantic-label.js +28 -4723
- package/dist/bin/backfill-conversations.js +42 -5543
- package/dist/bin/backfill-responses.js +41 -5542
- package/dist/bin/backfill-vectors.js +43 -4795
- package/dist/bin/bulk-sync-postgres.js +22 -5313
- package/dist/bin/cc-doctor.js +10 -602
- package/dist/bin/cleanup-stale-review-tasks.js +32 -6803
- package/dist/bin/cli.js +169 -38993
- package/dist/bin/exe-agent-config.js +21 -253
- package/dist/bin/exe-agent.js +63 -2429
- package/dist/bin/exe-assign.js +34 -5810
- package/dist/bin/exe-boot.js +180 -12726
- package/dist/bin/exe-call.js +48 -1409
- package/dist/bin/exe-cloud.js +64 -8516
- package/dist/bin/exe-dispatch.js +35 -10818
- package/dist/bin/exe-doctor.js +24 -7537
- package/dist/bin/exe-export-behaviors.js +18 -6122
- package/dist/bin/exe-forget.js +22 -6274
- package/dist/bin/exe-gateway.js +62 -15286
- package/dist/bin/exe-healthcheck.js +9 -602
- package/dist/bin/exe-heartbeat.js +51 -6898
- package/dist/bin/exe-kill.js +30 -5961
- package/dist/bin/exe-launch-agent.js +101 -6788
- package/dist/bin/exe-new-employee.js +42 -3774
- package/dist/bin/exe-pending-messages.js +28 -6833
- package/dist/bin/exe-pending-notifications.js +31 -6869
- package/dist/bin/exe-pending-reviews.js +38 -6910
- package/dist/bin/exe-rename.js +57 -5766
- package/dist/bin/exe-review.js +29 -5948
- package/dist/bin/exe-search.js +22 -7692
- package/dist/bin/exe-session-cleanup.js +52 -11147
- package/dist/bin/exe-settings.js +35 -524
- package/dist/bin/exe-start-codex.js +70 -6641
- package/dist/bin/exe-start-opencode.js +54 -6584
- package/dist/bin/exe-status.js +26 -7081
- package/dist/bin/exe-support.js +10 -546
- package/dist/bin/exe-team.js +18 -5942
- package/dist/bin/git-sweep.js +26 -10909
- package/dist/bin/graph-backfill.js +24 -5979
- package/dist/bin/graph-export.js +31 -6128
- package/dist/bin/graph-layer-benchmark.js +4 -8
- package/dist/bin/install.js +47 -2322
- package/dist/bin/intercom-check.js +25 -11501
- package/dist/bin/list-providers.js +4 -18
- package/dist/bin/postgres-agentic-reflection-backfill.js +14 -357
- package/dist/bin/postgres-agentic-semantic-backfill.js +14 -404
- package/dist/bin/pre-publish.js +4 -298
- package/dist/bin/registry-proxy.js +8 -172
- package/dist/bin/scan-tasks.js +47 -10881
- package/dist/bin/setup.js +6 -9492
- package/dist/bin/shard-migrate.js +30 -5149
- package/dist/bin/stack-update.js +75 -203
- package/dist/bin/update.js +20 -654
- package/dist/capacity-monitor-3GZIHLB4.js +46 -0
- package/dist/catchup-brief-67QDCINS.js +148 -0
- package/dist/chunk-2QD7KBJN.js +664 -0
- package/dist/chunk-2WGSIVI4.js +149 -0
- package/dist/chunk-35TVNLJI.js +57 -0
- package/dist/chunk-3SYV36U5.js +47 -0
- package/dist/chunk-46SQTBQW.js +207 -0
- package/dist/chunk-4CMVLKEI.js +171 -0
- package/dist/chunk-4FLUOZTR.js +116 -0
- package/dist/chunk-4NYQAS33.js +17 -0
- package/dist/chunk-4QNPADWL.js +902 -0
- package/dist/chunk-4Y6QZBEO.js +58 -0
- package/dist/chunk-557C2IGL.js +0 -0
- package/dist/chunk-55S56YDO.js +305 -0
- package/dist/chunk-5BS5ELMC.js +85 -0
- package/dist/chunk-5DHM77JE.js +256 -0
- package/dist/chunk-5QTWQKNF.js +137 -0
- package/dist/chunk-6A7P74QV.js +53 -0
- package/dist/chunk-6BFIFI5F.js +45 -0
- package/dist/chunk-6KFICEAT.js +20 -0
- package/dist/chunk-6LKDJ5WX.js +317 -0
- package/dist/chunk-6M5II4EV.js +148 -0
- package/dist/chunk-6QEFN3DV.js +302 -0
- package/dist/chunk-6WTMJAG3.js +171 -0
- package/dist/chunk-6Y4B3QF6.js +19 -0
- package/dist/chunk-7RK3AYJE.js +163 -0
- package/dist/chunk-7TCP2XTP.js +390 -0
- package/dist/chunk-A2NZP64U.js +2307 -0
- package/dist/chunk-AFYIYM2K.js +322 -0
- package/dist/chunk-AJWFPKQS.js +573 -0
- package/dist/chunk-AVE2B4DQ.js +140 -0
- package/dist/chunk-B4TT2QQA.js +87 -0
- package/dist/chunk-BCMOO4OQ.js +1066 -0
- package/dist/chunk-BU3ID2WT.js +432 -0
- package/dist/chunk-CFIM43JH.js +862 -0
- package/dist/chunk-CURJUF3W.js +66 -0
- package/dist/chunk-DC4ZD76Y.js +288 -0
- package/dist/chunk-DL52JPGF.js +706 -0
- package/dist/chunk-DRSC6ZRA.js +36 -0
- package/dist/chunk-E2Q4Y22A.js +86 -0
- package/dist/chunk-ECGTESAP.js +236 -0
- package/dist/chunk-EHOJUXYG.js +365 -0
- package/dist/chunk-EIT6J37V.js +119 -0
- package/dist/chunk-ELE2C7L4.js +31 -0
- package/dist/chunk-EQSQ4HBW.js +9 -0
- package/dist/chunk-FWFFZGSC.js +94 -0
- package/dist/chunk-GCNWCYJI.js +249 -0
- package/dist/chunk-GGTUUI7J.js +6 -0
- package/dist/chunk-GU56L7OY.js +223 -0
- package/dist/chunk-HF25XPZE.js +83 -0
- package/dist/chunk-HOKOOBDL.js +52 -0
- package/dist/chunk-HP43GLZ6.js +89 -0
- package/dist/chunk-HSRM6PNM.js +682 -0
- package/dist/chunk-HVRTW3OP.js +164 -0
- package/dist/chunk-IHDHL5ML.js +125 -0
- package/dist/chunk-IHF5RXJM.js +168 -0
- package/dist/chunk-IJ73YE73.js +409 -0
- package/dist/chunk-INO4ZD5Y.js +162 -0
- package/dist/chunk-IRQTM7DY.js +475 -0
- package/dist/chunk-JD4FAPYG.js +1208 -0
- package/dist/chunk-JN44LQ53.js +316 -0
- package/dist/chunk-JQ6TLNIV.js +133 -0
- package/dist/chunk-JT76KRZQ.js +35 -0
- package/dist/chunk-K6OEWZTB.js +262 -0
- package/dist/chunk-KCUYRZSX.js +72 -0
- package/dist/chunk-KFQGP6VL.js +33 -0
- package/dist/chunk-KRLOFIPI.js +83 -0
- package/dist/chunk-KVPG5UT6.js +305 -0
- package/dist/chunk-L3TB7CC3.js +170 -0
- package/dist/chunk-L4X5EUHR.js +283 -0
- package/dist/chunk-LAN26C3W.js +655 -0
- package/dist/chunk-LPGBKR4H.js +54 -0
- package/dist/chunk-LVNMOVGD.js +132 -0
- package/dist/chunk-LX5WEEZ7.js +157 -0
- package/dist/chunk-MBH42QNH.js +295 -0
- package/dist/chunk-MFO7MNSD.js +102 -0
- package/dist/chunk-MILTNDZH.js +736 -0
- package/dist/chunk-MJAE4NB7.js +1090 -0
- package/dist/chunk-MLCUPTP7.js +132 -0
- package/dist/chunk-MPURH6JI.js +445 -0
- package/dist/chunk-MVBWW2EO.js +89 -0
- package/dist/chunk-MZIGUUDQ.js +42 -0
- package/dist/chunk-N3DTSVKE.js +157 -0
- package/dist/chunk-N5RRQOAC.js +134 -0
- package/dist/chunk-N7L33TED.js +1664 -0
- package/dist/chunk-NDYSNVI5.js +122 -0
- package/dist/chunk-NEAHEC5G.js +155 -0
- package/dist/chunk-NGVOA6ZQ.js +84 -0
- package/dist/chunk-NHK5KW6Y.js +193 -0
- package/dist/chunk-NMQKGFEP.js +458 -0
- package/dist/chunk-NPT4EHZK.js +352 -0
- package/dist/chunk-NXFJHTED.js +55 -0
- package/dist/chunk-NZLCDI6Y.js +216 -0
- package/dist/chunk-O377P7GM.js +100 -0
- package/dist/chunk-O4VFZRA5.js +33 -0
- package/dist/chunk-O7YD7JYE.js +237 -0
- package/dist/chunk-OFK72LTZ.js +81 -0
- package/dist/chunk-ORCCI2VV.js +140 -0
- package/dist/chunk-OVQR4KMC.js +579 -0
- package/dist/chunk-P3CVUMEH.js +54 -0
- package/dist/chunk-P4HIF72O.js +30 -0
- package/dist/chunk-PEDLSJBL.js +1039 -0
- package/dist/chunk-PFICKZZ5.js +219 -0
- package/dist/chunk-PGW7TIV6.js +142 -0
- package/dist/chunk-PJHHFJAN.js +63 -0
- package/dist/chunk-PP4SNOJ5.js +471 -0
- package/dist/chunk-PRKVT4KN.js +90 -0
- package/dist/chunk-Q2O4VJHH.js +305 -0
- package/dist/chunk-Q36CMZIX.js +38 -0
- package/dist/chunk-Q3V7K4ME.js +149 -0
- package/dist/chunk-QGYPJGHB.js +81 -0
- package/dist/chunk-R23EMFJV.js +198 -0
- package/dist/chunk-R4M6XCMU.js +39 -0
- package/dist/chunk-R4WMF32C.js +81 -0
- package/dist/chunk-RJT7H2KR.js +349 -0
- package/dist/chunk-RJZMRZNV.js +280 -0
- package/dist/chunk-RMFDPCA2.js +72 -0
- package/dist/chunk-RUG3N6P4.js +292 -0
- package/dist/chunk-RWO327FJ.js +84 -0
- package/dist/chunk-SG7EFHQ7.js +108 -0
- package/dist/chunk-SH45SJQW.js +0 -0
- package/dist/chunk-SPKE5ANC.js +42 -0
- package/dist/chunk-SVMFVPBF.js +104 -0
- package/dist/chunk-SVXDCELZ.js +163 -0
- package/dist/chunk-TMEE7AT7.js +72 -0
- package/dist/chunk-TWYEE4FB.js +125 -0
- package/dist/chunk-TYLF4LSW.js +110 -0
- package/dist/chunk-V4TZI6EO.js +60 -0
- package/dist/chunk-VFZTXKQI.js +372 -0
- package/dist/chunk-VQ4K2MLM.js +199 -0
- package/dist/chunk-VU447UAO.js +173 -0
- package/dist/chunk-WMC3TXJF.js +50 -0
- package/dist/chunk-WPOA7XAE.js +84 -0
- package/dist/chunk-WZQ4CPRG.js +64 -0
- package/dist/chunk-X2IMCCM5.js +49 -0
- package/dist/chunk-X2Z5GT3V.js +118 -0
- package/dist/chunk-XEKO37NM.js +43 -0
- package/dist/chunk-XUHFQHGZ.js +505 -0
- package/dist/chunk-YGAAZN3E.js +47 -0
- package/dist/chunk-YKOW7KMK.js +558 -0
- package/dist/chunk-YOQKMB6N.js +145 -0
- package/dist/chunk-YZFZDJWZ.js +107 -0
- package/dist/chunk-ZBY4AUD4.js +10342 -0
- package/dist/chunk-ZOJYPNCM.js +127 -0
- package/dist/chunk-ZS7MGQSD.js +1494 -0
- package/dist/chunk-ZTZQ5A6B.js +191 -0
- package/dist/code-context-index-HXGC4IJL.js +28 -0
- package/dist/content-extractor-UVZXRIPC.js +8 -0
- package/dist/conversation-entity-extractor-HKSNDF4L.js +113 -0
- package/dist/conversation-wiki-populator-NKDAQEZ5.js +105 -0
- package/dist/crdt-sync-T7MHVQCS.js +33 -0
- package/dist/crm-bridge-Q5JBVJ6P.js +19 -0
- package/dist/crm-webhook-OAFTLJYA.js +10 -0
- package/dist/cto-delegation-gate-XWHZCUJH.js +208 -0
- package/dist/daemon-auth-GWUXTUYO.js +15 -0
- package/dist/daemon-orchestration-2C3TVOHO.js +91 -0
- package/dist/daemon-protocol-RLH2XM2K.js +19 -0
- package/dist/db-backup-CH2KG3JY.js +23 -0
- package/dist/devtools-PLJCAU53.js +8 -0
- package/dist/discord-G4BQJ2VX.js +7 -0
- package/dist/email-XMRNMFXR.js +137 -0
- package/dist/entity-boost-IOER4VRR.js +249 -0
- package/dist/exe-drift-2IJFQV43.js +68 -0
- package/dist/exe-export-Y2RFXGNB.js +71 -0
- package/dist/exe-import-LN6PE2YT.js +74 -0
- package/dist/exe-key-DHKBWLIT.js +571 -0
- package/dist/exe-org-F7LZRHFB.js +73 -0
- package/dist/factory-YTD23AW7.js +67 -0
- package/dist/fast-db-init-IIP6EGWT.js +7 -0
- package/dist/file-edit-FJVEO7XZ.js +8 -0
- package/dist/file-read-Q2NH3J35.js +8 -0
- package/dist/file-write-UNSFXNH4.js +8 -0
- package/dist/gateway/index.js +87 -16563
- package/dist/gateway-client-APTYJNCL.js +11 -0
- package/dist/git-staleness-I2BUEDE2.js +111 -0
- package/dist/git-task-sweep-UEOTXRW4.js +35 -0
- package/dist/glob-GRMXLDMB.js +7 -0
- package/dist/global-procedures-MFE5LGRF.js +19 -0
- package/dist/graph-query-657O5JYJ.js +25 -0
- package/dist/graph-rag-ZE5N7C65.js +28 -0
- package/dist/grep-NBV6SKYZ.js +7 -0
- package/dist/hooks/bug-report-worker.js +41 -10823
- package/dist/hooks/codex-stop-task-finalizer.js +38 -9055
- package/dist/hooks/commit-complete.js +32 -10914
- package/dist/hooks/error-recall.js +35 -7955
- package/dist/hooks/exe-heartbeat-hook.js +17 -327
- package/dist/hooks/ingest-worker.js +16 -565
- package/dist/hooks/ingest.js +98 -11436
- package/dist/hooks/instructions-loaded.js +25 -6209
- package/dist/hooks/notification.js +16 -6098
- package/dist/hooks/post-compact.js +34 -7041
- package/dist/hooks/post-tool-combined.js +27 -9751
- package/dist/hooks/pre-compact.js +38 -11130
- package/dist/hooks/pre-tool-use.js +61 -7233
- package/dist/hooks/prompt-submit.js +113 -13358
- package/dist/hooks/session-end.js +57 -11503
- package/dist/hooks/session-start.js +46 -9423
- package/dist/hooks/stop.js +70 -7210
- package/dist/hooks/subagent-stop.js +33 -6897
- package/dist/hooks/summary-worker.js +86 -8629
- package/dist/hooks-KD3N2D7P.js +9 -0
- package/dist/imessage-L7WKA2WD.js +7 -0
- package/dist/index.js +49 -19391
- package/dist/installer-5N24GXFB.js +32 -0
- package/dist/installer-QSD76YRY.js +291 -0
- package/dist/installer-ZPQUKHU5.js +331 -0
- package/dist/intercom-queue-PEEGAJ33.js +19 -0
- package/dist/key-backup-status-57UB74DU.js +41 -0
- package/dist/lib/agent-config.js +19 -224
- package/dist/lib/cloud-sync.js +44 -5398
- package/dist/lib/cloudflare-dns.js +7 -111
- package/dist/lib/config.js +18 -278
- package/dist/lib/consolidation.js +21 -1047
- package/dist/lib/crypto.js +7 -45
- package/dist/lib/database.js +20 -2932
- package/dist/lib/db-daemon-client.js +24 -460
- package/dist/lib/db.js +22 -2932
- package/dist/lib/device-registry.js +20 -2992
- package/dist/lib/embedder.js +14 -822
- package/dist/lib/employee-templates.js +21 -1102
- package/dist/lib/employees.js +30 -497
- package/dist/lib/error-detector.js +9 -149
- package/dist/lib/exe-daemon-client.js +16 -570
- package/dist/lib/exe-daemon.js +509 -18398
- package/dist/lib/file-grep.js +2 -0
- package/dist/lib/hybrid-search.js +18 -7674
- package/dist/lib/identity-templates.js +2 -0
- package/dist/lib/identity.js +13 -309
- package/dist/lib/keychain.js +9 -499
- package/dist/lib/license.js +21 -549
- package/dist/lib/messaging.js +35 -1631
- package/dist/lib/post-tool-memory.js +8 -370
- package/dist/lib/registry-proxy.js +8 -155
- package/dist/lib/reminders.js +11 -231
- package/dist/lib/runtime-table.js +7 -20
- package/dist/lib/schedules.js +16 -4614
- package/dist/lib/session-registry.js +9 -57
- package/dist/lib/session-wrappers.js +3 -127
- package/dist/lib/skill-learning.js +20 -2101
- package/dist/lib/status-brief.js +4 -368
- package/dist/lib/store.js +25 -5593
- package/dist/lib/task-router.js +12 -254
- package/dist/lib/tasks.js +44 -5801
- package/dist/lib/tmux-routing.js +45 -5804
- package/dist/lib/tmux-status.js +16 -248
- package/dist/lib/tmux-transport.js +6 -90
- package/dist/lib/token-spend.js +9 -337
- package/dist/lib/transport.js +8 -134
- package/dist/lib/ws-auth.js +6 -14
- package/dist/lib/ws-client.js +4 -22
- package/dist/license-gate-NXXDOOAQ.js +14 -0
- package/dist/mcp/register-tools.js +90 -32454
- package/dist/mcp/server.js +139 -32625
- package/dist/mcp/tools/complete-reminder.js +10 -237
- package/dist/mcp/tools/create-reminder.js +10 -222
- package/dist/mcp/tools/create-task.js +28 -6730
- package/dist/mcp/tools/deactivate-behavior.js +13 -476
- package/dist/mcp/tools/list-reminders.js +10 -232
- package/dist/mcp/tools/list-tasks.js +28 -1571
- package/dist/mcp/tools/send-message.js +25 -1672
- package/dist/mcp/tools/update-task.js +27 -6123
- package/dist/mcp-diagnostics-OOZ3TDPY.js +200 -0
- package/dist/memory-cards-RIL5BZHK.js +175 -0
- package/dist/memory-queue-JYIJ7TMQ.js +13 -0
- package/dist/memory-queue-client-PE7757HU.js +14 -0
- package/dist/notifications-CJFULBMN.js +42 -0
- package/dist/orchestration-phase-Y2TYNW5H.js +23 -0
- package/dist/orchestrator-RXIPT6PZ.js +29 -0
- package/dist/permission-presets-WI524OSE.js +25 -0
- package/dist/permissions-TYT6YMNZ.js +14 -0
- package/dist/pg-ssl-KFHNJPGO.js +7 -0
- package/dist/plan-limits-5WY5YK6X.js +27 -0
- package/dist/preferences-MW3BYTQ5.js +10 -0
- package/dist/project-name-PL2ZDWNU.js +11 -0
- package/dist/projection-worker-OY4WYZW6.js +661 -0
- package/dist/reranker-LFJ73RRS.js +19 -0
- package/dist/review-gate-E6K7QQE5.js +120 -0
- package/dist/runtime/index.js +82 -13408
- package/dist/self-query-router-IZANO632.js +83 -0
- package/dist/session-events-TQ6S5KQN.js +32 -0
- package/dist/session-kill-telemetry-46YIJWE2.js +30 -0
- package/dist/session-scope-3MYK3YRV.js +34 -0
- package/dist/setup-wizard-ZJ7UCRBP.js +12 -0
- package/dist/shard-manager-RCFZ6RXY.js +29 -0
- package/dist/signal-XN2C7PIG.js +7 -0
- package/dist/slack-Z77TFJCK.js +7 -0
- package/dist/task-enforcement-337DZI3I.js +252 -0
- package/dist/task-scanner-IMIR3T7T.js +11 -0
- package/dist/task-scope-MUU4KXE6.js +32 -0
- package/dist/tasks-crud-PMLK3MM4.js +53 -0
- package/dist/tasks-review-56LVKKNL.js +44 -0
- package/dist/telegram-CRPZO7JT.js +7 -0
- package/dist/telemetry-SPVGPKU7.js +9 -0
- package/dist/tool-capability-index-XNBSDCH3.js +10 -0
- package/dist/tool-gates-LGH6CSY4.js +14 -0
- package/dist/tool-gates-W7SZCYTX.js +13 -0
- package/dist/tool-registry-436LVVLY.js +11 -0
- package/dist/tool-telemetry-L77WNAYT.js +15 -0
- package/dist/tui/App.js +253 -14668
- package/dist/tui-data-SB45REYU.js +161 -0
- package/dist/update-check-LZGZISRJ.js +11 -0
- package/dist/webhook-VVP7KQVC.js +96 -0
- package/dist/whatsapp-J6QNDBGY.js +9 -0
- package/dist/whatsapp-accounts-RTJF3LM6.js +15 -0
- package/dist/wiki-client-EKCIKCVC.js +136 -0
- package/dist/worker-gate-UYNN62GJ.js +19 -0
- package/dist/worktree-4T4BPWPN.js +205 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1208 @@
|
|
|
1
|
+
import {
|
|
2
|
+
initCRMBridge
|
|
3
|
+
} from "./chunk-GCNWCYJI.js";
|
|
4
|
+
import {
|
|
5
|
+
init_state_bus,
|
|
6
|
+
orgBus
|
|
7
|
+
} from "./chunk-WZQ4CPRG.js";
|
|
8
|
+
import {
|
|
9
|
+
getClient,
|
|
10
|
+
init_database
|
|
11
|
+
} from "./chunk-A2NZP64U.js";
|
|
12
|
+
|
|
13
|
+
// src/gateway/router.ts
|
|
14
|
+
function matchesPlatform(msgPlatform, matchPlatform) {
|
|
15
|
+
if (!matchPlatform) return true;
|
|
16
|
+
const platforms = Array.isArray(matchPlatform) ? matchPlatform : [matchPlatform];
|
|
17
|
+
return platforms.includes(msgPlatform);
|
|
18
|
+
}
|
|
19
|
+
function matchesChannel(msgChannel, matchChannel) {
|
|
20
|
+
if (!matchChannel) return true;
|
|
21
|
+
const channels = Array.isArray(matchChannel) ? matchChannel : [matchChannel];
|
|
22
|
+
return channels.includes(msgChannel);
|
|
23
|
+
}
|
|
24
|
+
var MAX_REGEX_LENGTH = 200;
|
|
25
|
+
function safeRegExp(pattern, flags) {
|
|
26
|
+
if (pattern.length > MAX_REGEX_LENGTH) return null;
|
|
27
|
+
try {
|
|
28
|
+
return new RegExp(pattern, flags);
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function matchesSender(msgSender, matchSender) {
|
|
34
|
+
if (!matchSender) return true;
|
|
35
|
+
const re = safeRegExp(matchSender);
|
|
36
|
+
return re ? re.test(msgSender) : false;
|
|
37
|
+
}
|
|
38
|
+
function matchesTextPattern(msgText, matchPattern) {
|
|
39
|
+
if (!matchPattern) return true;
|
|
40
|
+
const re = safeRegExp(matchPattern, "i");
|
|
41
|
+
return re ? re.test(msgText) : false;
|
|
42
|
+
}
|
|
43
|
+
function matchesRoute(msg, match) {
|
|
44
|
+
return matchesPlatform(msg.platform, match.platform) && matchesChannel(msg.channelId, match.channelId) && matchesSender(msg.senderId, match.senderId) && matchesTextPattern(msg.text, match.textPattern);
|
|
45
|
+
}
|
|
46
|
+
function routeMessage(msg, config) {
|
|
47
|
+
for (const route of config.routes) {
|
|
48
|
+
if (matchesRoute(msg, route.match)) {
|
|
49
|
+
return {
|
|
50
|
+
employee: route.target,
|
|
51
|
+
modelTier: route.modelTier,
|
|
52
|
+
permissions: route.permissions,
|
|
53
|
+
routeName: route.name
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
employee: config.defaultRoute,
|
|
59
|
+
modelTier: config.defaultModelTier,
|
|
60
|
+
permissions: config.defaultPermissions,
|
|
61
|
+
routeName: "default"
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function validateGatewayConfig(config) {
|
|
65
|
+
const warnings = [];
|
|
66
|
+
if (!config.routes.length) {
|
|
67
|
+
warnings.push("No routes configured \u2014 all messages will use default route");
|
|
68
|
+
}
|
|
69
|
+
if (!config.defaultRoute) {
|
|
70
|
+
warnings.push("No default route specified");
|
|
71
|
+
}
|
|
72
|
+
const names = /* @__PURE__ */ new Set();
|
|
73
|
+
for (const route of config.routes) {
|
|
74
|
+
if (names.has(route.name)) {
|
|
75
|
+
warnings.push(`Duplicate route name: "${route.name}"`);
|
|
76
|
+
}
|
|
77
|
+
names.add(route.name);
|
|
78
|
+
if (!route.target) {
|
|
79
|
+
warnings.push(`Route "${route.name}" has no target employee`);
|
|
80
|
+
}
|
|
81
|
+
if (route.match.senderId && !safeRegExp(route.match.senderId)) {
|
|
82
|
+
warnings.push(`Route "${route.name}" has invalid senderId regex: ${route.match.senderId}`);
|
|
83
|
+
}
|
|
84
|
+
if (route.match.textPattern && !safeRegExp(route.match.textPattern, "i")) {
|
|
85
|
+
warnings.push(`Route "${route.name}" has invalid textPattern regex: ${route.match.textPattern}`);
|
|
86
|
+
}
|
|
87
|
+
const isEmptyMatch = !route.match.platform && !route.match.channelId && !route.match.senderId && !route.match.textPattern;
|
|
88
|
+
if (isEmptyMatch && config.routes.indexOf(route) !== config.routes.length - 1) {
|
|
89
|
+
warnings.push(
|
|
90
|
+
`Route "${route.name}" matches everything but is not the last route \u2014 routes after it will never match`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return warnings;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/gateway/reliability.ts
|
|
98
|
+
var DEFAULT_RETRY = {
|
|
99
|
+
maxRetries: 3,
|
|
100
|
+
baseDelayMs: 1e3,
|
|
101
|
+
maxDelayMs: 4e3
|
|
102
|
+
};
|
|
103
|
+
async function retryWithBackoff(fn, config = {}) {
|
|
104
|
+
const { maxRetries, baseDelayMs, maxDelayMs } = {
|
|
105
|
+
...DEFAULT_RETRY,
|
|
106
|
+
...config
|
|
107
|
+
};
|
|
108
|
+
let lastError;
|
|
109
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
110
|
+
try {
|
|
111
|
+
return await fn();
|
|
112
|
+
} catch (err) {
|
|
113
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
114
|
+
if (attempt < maxRetries) {
|
|
115
|
+
const delay = Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);
|
|
116
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
throw lastError;
|
|
121
|
+
}
|
|
122
|
+
var DEFAULT_CIRCUIT = {
|
|
123
|
+
windowMs: 6e4,
|
|
124
|
+
failureThreshold: 0.5,
|
|
125
|
+
minimumRequests: 5,
|
|
126
|
+
halfOpenAfterMs: 3e4
|
|
127
|
+
};
|
|
128
|
+
var CircuitBreaker = class {
|
|
129
|
+
state = "closed";
|
|
130
|
+
records = [];
|
|
131
|
+
lastStateChange = Date.now();
|
|
132
|
+
config;
|
|
133
|
+
name;
|
|
134
|
+
constructor(name, config = {}) {
|
|
135
|
+
this.name = name;
|
|
136
|
+
this.config = { ...DEFAULT_CIRCUIT, ...config };
|
|
137
|
+
}
|
|
138
|
+
/** Check if a request should be allowed through */
|
|
139
|
+
canRequest() {
|
|
140
|
+
this.pruneRecords();
|
|
141
|
+
if (this.state === "closed") return true;
|
|
142
|
+
if (this.state === "open") {
|
|
143
|
+
const elapsed = Date.now() - this.lastStateChange;
|
|
144
|
+
if (elapsed >= this.config.halfOpenAfterMs) {
|
|
145
|
+
this.transition("half-open");
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
/** Record a successful request */
|
|
153
|
+
recordSuccess() {
|
|
154
|
+
this.records.push({ timestamp: Date.now(), success: true });
|
|
155
|
+
if (this.state === "half-open") {
|
|
156
|
+
this.transition("closed");
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/** Record a failed request */
|
|
160
|
+
recordFailure() {
|
|
161
|
+
this.records.push({ timestamp: Date.now(), success: false });
|
|
162
|
+
if (this.state === "half-open") {
|
|
163
|
+
this.transition("open");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (this.state === "closed") {
|
|
167
|
+
this.pruneRecords();
|
|
168
|
+
if (this.records.length >= this.config.minimumRequests) {
|
|
169
|
+
const failures = this.records.filter((r) => !r.success).length;
|
|
170
|
+
const rate = failures / this.records.length;
|
|
171
|
+
if (rate >= this.config.failureThreshold) {
|
|
172
|
+
this.transition("open");
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/** Get current circuit state */
|
|
178
|
+
getState() {
|
|
179
|
+
if (this.state === "open") {
|
|
180
|
+
const elapsed = Date.now() - this.lastStateChange;
|
|
181
|
+
if (elapsed >= this.config.halfOpenAfterMs) {
|
|
182
|
+
this.transition("half-open");
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return this.state;
|
|
186
|
+
}
|
|
187
|
+
/** Get failure rate in current window */
|
|
188
|
+
getFailureRate() {
|
|
189
|
+
this.pruneRecords();
|
|
190
|
+
if (this.records.length === 0) return 0;
|
|
191
|
+
const failures = this.records.filter((r) => !r.success).length;
|
|
192
|
+
return failures / this.records.length;
|
|
193
|
+
}
|
|
194
|
+
/** Reset the circuit breaker */
|
|
195
|
+
reset() {
|
|
196
|
+
this.records = [];
|
|
197
|
+
this.transition("closed");
|
|
198
|
+
}
|
|
199
|
+
transition(newState) {
|
|
200
|
+
this.state = newState;
|
|
201
|
+
this.lastStateChange = Date.now();
|
|
202
|
+
}
|
|
203
|
+
pruneRecords() {
|
|
204
|
+
const cutoff = Date.now() - this.config.windowMs;
|
|
205
|
+
while (this.records.length > 0 && this.records[0].timestamp < cutoff) {
|
|
206
|
+
this.records.shift();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
function buildDegradationMessage() {
|
|
211
|
+
return "I'm having trouble right now. Let me get a human to help you.";
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// src/gateway/gateway.ts
|
|
215
|
+
init_state_bus();
|
|
216
|
+
|
|
217
|
+
// src/lib/pipeline-router.ts
|
|
218
|
+
init_database();
|
|
219
|
+
import crypto from "crypto";
|
|
220
|
+
async function sinkConversationStore(msg, agentResponse, agentName) {
|
|
221
|
+
try {
|
|
222
|
+
const client = getClient();
|
|
223
|
+
const id = crypto.randomUUID();
|
|
224
|
+
const mediaJson = msg.media ? JSON.stringify(msg.media) : null;
|
|
225
|
+
await client.execute({
|
|
226
|
+
sql: `INSERT INTO conversations
|
|
227
|
+
(id, platform, external_id, sender_id, sender_name, sender_phone, sender_email,
|
|
228
|
+
recipient_id, channel_id, thread_id, reply_to_id,
|
|
229
|
+
content_text, content_media, agent_response, agent_name,
|
|
230
|
+
timestamp, ingested_at)
|
|
231
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
232
|
+
args: [
|
|
233
|
+
id,
|
|
234
|
+
msg.platform,
|
|
235
|
+
msg.messageId,
|
|
236
|
+
msg.senderId,
|
|
237
|
+
msg.senderName ?? null,
|
|
238
|
+
msg.senderPhone ?? null,
|
|
239
|
+
msg.senderEmail ?? null,
|
|
240
|
+
msg.accountId ?? null,
|
|
241
|
+
msg.channelId,
|
|
242
|
+
msg.threadId ?? `${msg.platform}:${msg.channelId}`,
|
|
243
|
+
msg.replyTo?.messageId ?? null,
|
|
244
|
+
msg.text ?? null,
|
|
245
|
+
mediaJson,
|
|
246
|
+
agentResponse ?? null,
|
|
247
|
+
agentName ?? null,
|
|
248
|
+
msg.timestamp,
|
|
249
|
+
(/* @__PURE__ */ new Date()).toISOString()
|
|
250
|
+
]
|
|
251
|
+
});
|
|
252
|
+
return true;
|
|
253
|
+
} catch (err) {
|
|
254
|
+
process.stderr.write(
|
|
255
|
+
`[pipeline] conversation-store error: ${err instanceof Error ? err.message : String(err)}
|
|
256
|
+
`
|
|
257
|
+
);
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
async function sinkMemory(msg, agentResponse, agentName) {
|
|
262
|
+
try {
|
|
263
|
+
const { embed } = await import("./lib/embedder.js");
|
|
264
|
+
const { writeMemory, flushBatch } = await import("./lib/store.js");
|
|
265
|
+
const direction = agentResponse ? "conversation" : "inbound";
|
|
266
|
+
const rawText = [
|
|
267
|
+
`[${msg.platform}] ${direction} from ${msg.senderName ?? msg.senderId}`,
|
|
268
|
+
msg.text ? `Message: ${msg.text}` : null,
|
|
269
|
+
agentResponse ? `Agent (${agentName ?? "unknown"}): ${agentResponse}` : null
|
|
270
|
+
].filter(Boolean).join("\n");
|
|
271
|
+
const vector = await embed(rawText);
|
|
272
|
+
await writeMemory({
|
|
273
|
+
id: crypto.randomUUID(),
|
|
274
|
+
agent_id: agentName ?? "gateway",
|
|
275
|
+
agent_role: "gateway",
|
|
276
|
+
session_id: `gateway-${msg.platform}`,
|
|
277
|
+
timestamp: msg.timestamp,
|
|
278
|
+
tool_name: `gateway-${msg.platform}`,
|
|
279
|
+
project_name: "exe-os",
|
|
280
|
+
has_error: false,
|
|
281
|
+
raw_text: rawText,
|
|
282
|
+
vector,
|
|
283
|
+
importance: 4,
|
|
284
|
+
confidence: 0.8
|
|
285
|
+
});
|
|
286
|
+
await flushBatch();
|
|
287
|
+
return true;
|
|
288
|
+
} catch (err) {
|
|
289
|
+
process.stderr.write(
|
|
290
|
+
`[pipeline] memory-sink error: ${err instanceof Error ? err.message : String(err)}
|
|
291
|
+
`
|
|
292
|
+
);
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
async function sinkCRM(msg, agentResponse, agentName) {
|
|
297
|
+
try {
|
|
298
|
+
const { isCRMBridgeEnabled, pushConversationToCRM, pushInboundMessageToCRM } = await import("./crm-bridge-Q5JBVJ6P.js");
|
|
299
|
+
if (!isCRMBridgeEnabled()) return false;
|
|
300
|
+
if (agentResponse) {
|
|
301
|
+
await pushConversationToCRM({
|
|
302
|
+
platform: msg.platform,
|
|
303
|
+
senderId: msg.senderId,
|
|
304
|
+
senderName: msg.senderName,
|
|
305
|
+
messageText: msg.text,
|
|
306
|
+
agentResponse,
|
|
307
|
+
agentName: agentName ?? "unknown",
|
|
308
|
+
timestamp: msg.timestamp,
|
|
309
|
+
accountId: msg.accountId
|
|
310
|
+
});
|
|
311
|
+
} else {
|
|
312
|
+
await pushInboundMessageToCRM({
|
|
313
|
+
platform: msg.platform,
|
|
314
|
+
senderId: msg.senderId,
|
|
315
|
+
senderName: msg.senderName,
|
|
316
|
+
messageText: msg.text,
|
|
317
|
+
timestamp: msg.timestamp,
|
|
318
|
+
accountId: msg.accountId
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
return true;
|
|
322
|
+
} catch (err) {
|
|
323
|
+
process.stderr.write(
|
|
324
|
+
`[pipeline] crm-sink error: ${err instanceof Error ? err.message : String(err)}
|
|
325
|
+
`
|
|
326
|
+
);
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
async function sinkWiki(msg, agentResponse) {
|
|
331
|
+
try {
|
|
332
|
+
const { createWikiClient } = await import("./wiki-client-EKCIKCVC.js");
|
|
333
|
+
const client = await createWikiClient();
|
|
334
|
+
if (!client) return false;
|
|
335
|
+
const content = msg.text ?? "";
|
|
336
|
+
if (content.length < 50) return false;
|
|
337
|
+
const { chatInWorkspace } = await import("./wiki-client-EKCIKCVC.js");
|
|
338
|
+
const ingestText = [
|
|
339
|
+
`[${msg.platform}] Message from ${msg.senderName ?? msg.senderId} (${msg.timestamp}):`,
|
|
340
|
+
content,
|
|
341
|
+
agentResponse ? `
|
|
342
|
+
Agent response: ${agentResponse}` : ""
|
|
343
|
+
].join("\n");
|
|
344
|
+
await chatInWorkspace(client, "conversations", ingestText, "chat");
|
|
345
|
+
return true;
|
|
346
|
+
} catch (err) {
|
|
347
|
+
process.stderr.write(
|
|
348
|
+
`[pipeline] wiki-sink error: ${err instanceof Error ? err.message : String(err)}
|
|
349
|
+
`
|
|
350
|
+
);
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
async function sinkEntityExtraction(msg, agentResponse, agentName) {
|
|
355
|
+
try {
|
|
356
|
+
const { extractFromConversation } = await import("./conversation-entity-extractor-HKSNDF4L.js");
|
|
357
|
+
const { storeExtraction } = await import("./graph-rag-ZE5N7C65.js");
|
|
358
|
+
if ((msg.text?.length ?? 0) < 20) return false;
|
|
359
|
+
if (msg.isHistorical) return false;
|
|
360
|
+
const extraction = await extractFromConversation(msg, agentResponse, agentName);
|
|
361
|
+
if (extraction.entities.length === 0) return false;
|
|
362
|
+
const client = getClient();
|
|
363
|
+
const memoryId = `conv:${msg.messageId}`;
|
|
364
|
+
await storeExtraction(client, extraction, memoryId, msg.timestamp);
|
|
365
|
+
import("./conversation-wiki-populator-NKDAQEZ5.js").then(
|
|
366
|
+
({ populateWikiFromExtraction }) => populateWikiFromExtraction(extraction, msg, agentResponse, agentName)
|
|
367
|
+
).catch((err) => {
|
|
368
|
+
process.stderr.write(
|
|
369
|
+
`[pipeline] wiki-population error: ${err instanceof Error ? err.message : String(err)}
|
|
370
|
+
`
|
|
371
|
+
);
|
|
372
|
+
});
|
|
373
|
+
return true;
|
|
374
|
+
} catch (err) {
|
|
375
|
+
process.stderr.write(
|
|
376
|
+
`[pipeline] entity-extraction-sink error: ${err instanceof Error ? err.message : String(err)}
|
|
377
|
+
`
|
|
378
|
+
);
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
async function ingest(msg, agentResponse, agentName) {
|
|
383
|
+
const errors = [];
|
|
384
|
+
const [conversationStored, memorySunk, crmSunk, wikiSunk, entityExtracted] = await Promise.all([
|
|
385
|
+
sinkConversationStore(msg, agentResponse, agentName).catch((e) => {
|
|
386
|
+
errors.push(`conversation: ${e}`);
|
|
387
|
+
return false;
|
|
388
|
+
}),
|
|
389
|
+
sinkMemory(msg, agentResponse, agentName).catch((e) => {
|
|
390
|
+
errors.push(`memory: ${e}`);
|
|
391
|
+
return false;
|
|
392
|
+
}),
|
|
393
|
+
sinkCRM(msg, agentResponse, agentName).catch((e) => {
|
|
394
|
+
errors.push(`crm: ${e}`);
|
|
395
|
+
return false;
|
|
396
|
+
}),
|
|
397
|
+
sinkWiki(msg, agentResponse).catch((e) => {
|
|
398
|
+
errors.push(`wiki: ${e}`);
|
|
399
|
+
return false;
|
|
400
|
+
}),
|
|
401
|
+
sinkEntityExtraction(msg, agentResponse, agentName).catch((e) => {
|
|
402
|
+
errors.push(`entity-extraction: ${e}`);
|
|
403
|
+
return false;
|
|
404
|
+
})
|
|
405
|
+
]);
|
|
406
|
+
if (errors.length > 0) {
|
|
407
|
+
process.stderr.write(
|
|
408
|
+
`[pipeline] ${errors.length} sink error(s): ${errors.join("; ")}
|
|
409
|
+
`
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
return { conversationStored, memorySunk, crmSunk, wikiSunk, entityExtracted, errors };
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// src/gateway/gateway.ts
|
|
416
|
+
var Gateway = class {
|
|
417
|
+
config;
|
|
418
|
+
adapters = /* @__PURE__ */ new Map();
|
|
419
|
+
platformConfigs;
|
|
420
|
+
botRegistry;
|
|
421
|
+
rateLimiter;
|
|
422
|
+
sessionStore;
|
|
423
|
+
analytics;
|
|
424
|
+
alertMonitor;
|
|
425
|
+
failover;
|
|
426
|
+
customerStore;
|
|
427
|
+
startedAt = 0;
|
|
428
|
+
constructor(options) {
|
|
429
|
+
this.config = options.config;
|
|
430
|
+
this.platformConfigs = options.platformConfigs;
|
|
431
|
+
this.botRegistry = options.botRegistry;
|
|
432
|
+
this.rateLimiter = options.rateLimiter;
|
|
433
|
+
this.sessionStore = options.sessionStore;
|
|
434
|
+
this.analytics = options.analytics;
|
|
435
|
+
this.alertMonitor = options.alertMonitor;
|
|
436
|
+
this.failover = options.failover;
|
|
437
|
+
this.customerStore = options.customerStore;
|
|
438
|
+
}
|
|
439
|
+
registerAdapter(adapter) {
|
|
440
|
+
this.adapters.set(adapter.platform, adapter);
|
|
441
|
+
adapter.onMessage((msg) => this.handleMessage(msg));
|
|
442
|
+
}
|
|
443
|
+
async start() {
|
|
444
|
+
this.startedAt = Date.now();
|
|
445
|
+
initCRMBridge();
|
|
446
|
+
const startPromises = [];
|
|
447
|
+
for (const [platform, adapter] of this.adapters) {
|
|
448
|
+
const config = this.platformConfigs.get(platform);
|
|
449
|
+
if (!config) {
|
|
450
|
+
console.error(`[gateway] No config for platform: ${platform}`);
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
console.log(`[gateway] Connecting ${platform}...`);
|
|
454
|
+
startPromises.push(
|
|
455
|
+
adapter.connect(config).then(() => {
|
|
456
|
+
console.log(`[gateway] ${platform} connected`);
|
|
457
|
+
}).catch((err) => {
|
|
458
|
+
console.error(`[gateway] ${platform} connection failed:`, err);
|
|
459
|
+
this.alertMonitor?.alertAdapterDisconnected(
|
|
460
|
+
platform,
|
|
461
|
+
err instanceof Error ? err.message : String(err)
|
|
462
|
+
);
|
|
463
|
+
})
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
await Promise.allSettled(startPromises);
|
|
467
|
+
console.log(
|
|
468
|
+
`[gateway] Started with ${this.botRegistry.list().length} bots: ${this.botRegistry.list().join(", ")}`
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
async stop() {
|
|
472
|
+
const stopPromises = [...this.adapters.values()].map(
|
|
473
|
+
(a) => a.disconnect()
|
|
474
|
+
);
|
|
475
|
+
await Promise.allSettled(stopPromises);
|
|
476
|
+
console.log("[gateway] Stopped");
|
|
477
|
+
}
|
|
478
|
+
async handleMessage(msg) {
|
|
479
|
+
const start = Date.now();
|
|
480
|
+
if (this.rateLimiter) {
|
|
481
|
+
const limitResult = this.rateLimiter.check(msg.senderId);
|
|
482
|
+
if (!limitResult.allowed) {
|
|
483
|
+
const adapter2 = this.adapters.get(msg.platform);
|
|
484
|
+
if (adapter2) {
|
|
485
|
+
await adapter2.sendText(msg.channelId, limitResult.reason ?? "Please slow down.");
|
|
486
|
+
}
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
this.customerStore?.resolve(msg.platform, msg.senderId);
|
|
491
|
+
ingest(msg).catch((err) => {
|
|
492
|
+
console.error("[gateway] Pipeline inbound ingest error:", err);
|
|
493
|
+
});
|
|
494
|
+
const route = routeMessage(msg, this.config);
|
|
495
|
+
console.log(
|
|
496
|
+
`[gateway] ${msg.platform}/${msg.senderId} \u2192 ${route.employee} (${route.routeName})`
|
|
497
|
+
);
|
|
498
|
+
orgBus.emit({
|
|
499
|
+
type: "gateway_message",
|
|
500
|
+
platform: msg.platform,
|
|
501
|
+
senderId: msg.senderId,
|
|
502
|
+
botId: route.employee,
|
|
503
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
504
|
+
});
|
|
505
|
+
const bot = this.botRegistry.get(route.employee);
|
|
506
|
+
if (!bot) {
|
|
507
|
+
console.error(`[gateway] No bot registered for target: ${route.employee}`);
|
|
508
|
+
const adapter2 = this.adapters.get(msg.platform);
|
|
509
|
+
if (adapter2) {
|
|
510
|
+
await adapter2.sendText(msg.channelId, "Sorry, I'm not available right now.");
|
|
511
|
+
}
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
const adapter = this.adapters.get(msg.platform);
|
|
515
|
+
if (!adapter) return;
|
|
516
|
+
await adapter.sendTyping(msg.channelId);
|
|
517
|
+
const session = this.sessionStore?.getOrCreate(
|
|
518
|
+
msg.senderId,
|
|
519
|
+
route.employee,
|
|
520
|
+
msg.platform
|
|
521
|
+
);
|
|
522
|
+
if (session) {
|
|
523
|
+
this.sessionStore.addMessage(session.sessionId, {
|
|
524
|
+
role: "user",
|
|
525
|
+
content: msg.text
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
if (session?.messageCount === 1) {
|
|
529
|
+
this.analytics?.conversationStarted(msg.platform, route.employee);
|
|
530
|
+
}
|
|
531
|
+
try {
|
|
532
|
+
const response = await bot.processMessage(msg, route.permissions);
|
|
533
|
+
const latencyMs = Date.now() - start;
|
|
534
|
+
if (session) {
|
|
535
|
+
this.sessionStore.addMessage(session.sessionId, {
|
|
536
|
+
role: "assistant",
|
|
537
|
+
content: response
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
this.analytics?.responseRecorded(
|
|
541
|
+
msg.platform,
|
|
542
|
+
route.employee,
|
|
543
|
+
latencyMs,
|
|
544
|
+
0,
|
|
545
|
+
// Token counts not available from BotRuntime yet
|
|
546
|
+
0,
|
|
547
|
+
"anthropic",
|
|
548
|
+
true
|
|
549
|
+
);
|
|
550
|
+
this.alertMonitor?.recordEvent(latencyMs, true);
|
|
551
|
+
await adapter.sendText(msg.channelId, response, {
|
|
552
|
+
replyToMessageId: msg.messageId
|
|
553
|
+
});
|
|
554
|
+
ingest(msg, response, route.employee).catch((err) => {
|
|
555
|
+
console.error("[gateway] Pipeline conversation ingest error:", err);
|
|
556
|
+
});
|
|
557
|
+
} catch (err) {
|
|
558
|
+
const latencyMs = Date.now() - start;
|
|
559
|
+
this.alertMonitor?.recordEvent(latencyMs, false);
|
|
560
|
+
this.analytics?.responseRecorded(
|
|
561
|
+
msg.platform,
|
|
562
|
+
route.employee,
|
|
563
|
+
latencyMs,
|
|
564
|
+
0,
|
|
565
|
+
0,
|
|
566
|
+
"anthropic",
|
|
567
|
+
false
|
|
568
|
+
);
|
|
569
|
+
console.error(`[gateway] Error processing message:`, err);
|
|
570
|
+
await adapter.sendText(
|
|
571
|
+
msg.channelId,
|
|
572
|
+
buildDegradationMessage()
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
/** Health check all adapters + provider health */
|
|
577
|
+
async healthCheck() {
|
|
578
|
+
const adapterHealth = /* @__PURE__ */ new Map();
|
|
579
|
+
for (const [platform, adapter] of this.adapters) {
|
|
580
|
+
adapterHealth.set(platform, await adapter.healthCheck());
|
|
581
|
+
}
|
|
582
|
+
return {
|
|
583
|
+
adapters: adapterHealth,
|
|
584
|
+
providers: this.failover?.getProviderHealth(),
|
|
585
|
+
bots: this.botRegistry.list(),
|
|
586
|
+
uptime: this.startedAt > 0 ? Date.now() - this.startedAt : 0,
|
|
587
|
+
sessions: this.sessionStore?.stats(),
|
|
588
|
+
alerts: this.alertMonitor?.getActiveAlerts().length
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
// src/gateway/types.ts
|
|
594
|
+
var FULL_ACCESS = {
|
|
595
|
+
canRead: true,
|
|
596
|
+
canWrite: true,
|
|
597
|
+
canExecute: true
|
|
598
|
+
};
|
|
599
|
+
var READ_ONLY = {
|
|
600
|
+
canRead: true,
|
|
601
|
+
canWrite: false,
|
|
602
|
+
canExecute: false
|
|
603
|
+
};
|
|
604
|
+
var READ_TOOLS = [
|
|
605
|
+
"ask_team_memory",
|
|
606
|
+
"recall_my_memory",
|
|
607
|
+
"list_tasks",
|
|
608
|
+
"get_session_context",
|
|
609
|
+
"get_memory_by_id",
|
|
610
|
+
"list_reminders",
|
|
611
|
+
"query_conversations"
|
|
612
|
+
];
|
|
613
|
+
var WRITE_TOOLS = [
|
|
614
|
+
"create_task",
|
|
615
|
+
"update_task",
|
|
616
|
+
"send_message",
|
|
617
|
+
"store_memory",
|
|
618
|
+
"store_behavior",
|
|
619
|
+
"create_reminder",
|
|
620
|
+
"complete_reminder"
|
|
621
|
+
];
|
|
622
|
+
var EXECUTE_TOOLS = ["close_task"];
|
|
623
|
+
|
|
624
|
+
// src/gateway/permission-guard.ts
|
|
625
|
+
var TOOL_CLASSIFICATION = /* @__PURE__ */ new Map();
|
|
626
|
+
for (const tool of READ_TOOLS) TOOL_CLASSIFICATION.set(tool, "canRead");
|
|
627
|
+
for (const tool of WRITE_TOOLS) TOOL_CLASSIFICATION.set(tool, "canWrite");
|
|
628
|
+
for (const tool of EXECUTE_TOOLS) TOOL_CLASSIFICATION.set(tool, "canExecute");
|
|
629
|
+
function classifyTool(toolName) {
|
|
630
|
+
return TOOL_CLASSIFICATION.get(toolName) ?? null;
|
|
631
|
+
}
|
|
632
|
+
function checkToolPermission(toolName, permissions) {
|
|
633
|
+
const classification = classifyTool(toolName);
|
|
634
|
+
if (!classification) {
|
|
635
|
+
return {
|
|
636
|
+
allowed: false,
|
|
637
|
+
tool: toolName,
|
|
638
|
+
requiredPermission: "canExecute",
|
|
639
|
+
reason: `Unknown tool "${toolName}" is not in the allowed tool list`
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
const allowed = permissions[classification];
|
|
643
|
+
return {
|
|
644
|
+
allowed,
|
|
645
|
+
tool: toolName,
|
|
646
|
+
requiredPermission: classification,
|
|
647
|
+
reason: allowed ? void 0 : `Tool "${toolName}" requires ${classification} permission, which is not granted on this channel`
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
function guardToolUseBlocks(blocks, permissions) {
|
|
651
|
+
const allowed = [];
|
|
652
|
+
const blocked = [];
|
|
653
|
+
for (const block of blocks) {
|
|
654
|
+
const result = checkToolPermission(block.name, permissions);
|
|
655
|
+
if (result.allowed) {
|
|
656
|
+
allowed.push(block);
|
|
657
|
+
} else {
|
|
658
|
+
blocked.push({ block, check: result });
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return { allowed, blocked };
|
|
662
|
+
}
|
|
663
|
+
function buildPermissionContext(platform, permissions) {
|
|
664
|
+
if (permissions.canRead && permissions.canWrite && permissions.canExecute) {
|
|
665
|
+
return `[FULL ACCESS \u2014 you can read, write, and execute via ${platform}]`;
|
|
666
|
+
}
|
|
667
|
+
if (permissions.canRead && !permissions.canWrite && !permissions.canExecute) {
|
|
668
|
+
return `[READ-ONLY \u2014 this ${platform} channel cannot create tasks, send messages, or execute commands. Tell the founder to use Signal for commands.]`;
|
|
669
|
+
}
|
|
670
|
+
const parts = [];
|
|
671
|
+
if (permissions.canRead) parts.push("read");
|
|
672
|
+
if (permissions.canWrite) parts.push("write");
|
|
673
|
+
if (permissions.canExecute) parts.push("execute");
|
|
674
|
+
return `[${platform.toUpperCase()} \u2014 allowed: ${parts.join(", ")}]`;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// src/gateway/bot-errors.ts
|
|
678
|
+
var FatalBotError = class extends Error {
|
|
679
|
+
constructor(message, cause) {
|
|
680
|
+
super(message);
|
|
681
|
+
this.cause = cause;
|
|
682
|
+
this.name = "FatalBotError";
|
|
683
|
+
}
|
|
684
|
+
fatal = true;
|
|
685
|
+
};
|
|
686
|
+
var RecoverableBotError = class extends Error {
|
|
687
|
+
constructor(message, toolName, cause) {
|
|
688
|
+
super(message);
|
|
689
|
+
this.toolName = toolName;
|
|
690
|
+
this.cause = cause;
|
|
691
|
+
this.name = "RecoverableBotError";
|
|
692
|
+
}
|
|
693
|
+
recoverable = true;
|
|
694
|
+
};
|
|
695
|
+
var MaxStepsError = class extends Error {
|
|
696
|
+
constructor(stepsTaken, maxSteps) {
|
|
697
|
+
super(
|
|
698
|
+
`Reached maximum steps (${stepsTaken}/${maxSteps}). Returning partial result.`
|
|
699
|
+
);
|
|
700
|
+
this.stepsTaken = stepsTaken;
|
|
701
|
+
this.maxSteps = maxSteps;
|
|
702
|
+
this.name = "MaxStepsError";
|
|
703
|
+
}
|
|
704
|
+
};
|
|
705
|
+
function classifyError(err, toolName) {
|
|
706
|
+
if (err instanceof FatalBotError) return err;
|
|
707
|
+
if (err instanceof RecoverableBotError) return err;
|
|
708
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
709
|
+
if (message.includes("401") || message.includes("403") || message.includes("authentication") || message.includes("rate_limit")) {
|
|
710
|
+
return new FatalBotError(message, err);
|
|
711
|
+
}
|
|
712
|
+
return new RecoverableBotError(message, toolName, err);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// src/gateway/bot-runtime.ts
|
|
716
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
717
|
+
var DEFAULT_MODEL = "claude-sonnet-4-20250514";
|
|
718
|
+
var MAX_TURNS = 10;
|
|
719
|
+
var MAX_HISTORY = 50;
|
|
720
|
+
var DEFAULT_PLANNING_INTERVAL = 3;
|
|
721
|
+
function buildExecAssistantSystemPrompt(platform, permissions) {
|
|
722
|
+
const permContext = buildPermissionContext(platform, permissions);
|
|
723
|
+
return `You are the founder's executive assistant (agent_id: "ea").
|
|
724
|
+
|
|
725
|
+
You are a relay between the founder and their AI team. You have access to team memories, task lists, and session context. Your job is to answer the founder's questions by querying the team and presenting clear, synthesized answers.
|
|
726
|
+
|
|
727
|
+
${permContext}
|
|
728
|
+
|
|
729
|
+
Your personality:
|
|
730
|
+
- Direct and concise \u2014 the founder is busy
|
|
731
|
+
- Lead with the answer, not the process
|
|
732
|
+
- When summarizing team work, be specific: names, counts, what shipped
|
|
733
|
+
- If you don't know something, say so and suggest who might
|
|
734
|
+
|
|
735
|
+
Your tools:
|
|
736
|
+
- ask_team_memory: Query any employee's memories by their configured name
|
|
737
|
+
- recall_my_memory: Search your own past conversations with the founder
|
|
738
|
+
- list_tasks: See what's in progress across the org
|
|
739
|
+
- get_session_context: Get context from a specific session
|
|
740
|
+
- list_reminders: Check active reminders
|
|
741
|
+
|
|
742
|
+
${permissions.canWrite ? "You also have write access: create_task, send_message, store_memory, etc." : "You are READ-ONLY on this channel. If the founder wants to create tasks or send commands, they should use Signal."}`;
|
|
743
|
+
}
|
|
744
|
+
function filterToolsForPermissions(tools, permissions) {
|
|
745
|
+
const allAllowed = /* @__PURE__ */ new Set();
|
|
746
|
+
if (permissions.canRead) {
|
|
747
|
+
for (const t of READ_TOOLS) allAllowed.add(t);
|
|
748
|
+
}
|
|
749
|
+
if (permissions.canWrite) {
|
|
750
|
+
for (const t of WRITE_TOOLS) allAllowed.add(t);
|
|
751
|
+
}
|
|
752
|
+
if (permissions.canExecute) {
|
|
753
|
+
for (const t of EXECUTE_TOOLS) allAllowed.add(t);
|
|
754
|
+
}
|
|
755
|
+
return tools.filter((t) => allAllowed.has(t.name));
|
|
756
|
+
}
|
|
757
|
+
var BotRuntime = class {
|
|
758
|
+
client;
|
|
759
|
+
config;
|
|
760
|
+
conversations = /* @__PURE__ */ new Map();
|
|
761
|
+
constructor(config) {
|
|
762
|
+
this.config = config;
|
|
763
|
+
this.client = new Anthropic({ apiKey: config.apiKey });
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Process an incoming message and return the text response.
|
|
767
|
+
* Handles multi-turn tool use internally.
|
|
768
|
+
*/
|
|
769
|
+
async processMessage(msg, permissions) {
|
|
770
|
+
const sessionKey = msg.chatType === "group" ? msg.channelId : msg.senderId;
|
|
771
|
+
const history = this.getHistory(sessionKey);
|
|
772
|
+
history.push({ role: "user", content: msg.text, frameType: "task" });
|
|
773
|
+
const systemPrompt = this.config.systemPrompt + "\n\n" + buildPermissionContext(msg.platform, permissions);
|
|
774
|
+
const allowedTools = filterToolsForPermissions(
|
|
775
|
+
this.config.tools,
|
|
776
|
+
permissions
|
|
777
|
+
);
|
|
778
|
+
const model = this.config.model ?? DEFAULT_MODEL;
|
|
779
|
+
const maxTurns = this.config.maxTurns ?? MAX_TURNS;
|
|
780
|
+
const planningInterval = this.config.planningInterval ?? DEFAULT_PLANNING_INTERVAL;
|
|
781
|
+
let turns = 0;
|
|
782
|
+
while (turns < maxTurns) {
|
|
783
|
+
turns++;
|
|
784
|
+
if (planningInterval > 0 && turns > 1 && turns % planningInterval === 1 && maxTurns - turns >= 2) {
|
|
785
|
+
history.push({
|
|
786
|
+
role: "user",
|
|
787
|
+
content: "[Planning checkpoint] Review what you know so far. What facts have you gathered? What is your plan for the remaining steps? Be concise.",
|
|
788
|
+
frameType: "planning"
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
let response;
|
|
792
|
+
try {
|
|
793
|
+
response = await this.client.messages.create({
|
|
794
|
+
model,
|
|
795
|
+
max_tokens: 4096,
|
|
796
|
+
system: systemPrompt,
|
|
797
|
+
messages: history.map((m) => ({
|
|
798
|
+
role: m.role,
|
|
799
|
+
content: m.content
|
|
800
|
+
})),
|
|
801
|
+
tools: allowedTools.map((t) => ({
|
|
802
|
+
name: t.name,
|
|
803
|
+
description: t.description,
|
|
804
|
+
input_schema: t.input_schema
|
|
805
|
+
}))
|
|
806
|
+
});
|
|
807
|
+
} catch (err) {
|
|
808
|
+
const classified = classifyError(err);
|
|
809
|
+
if (classified instanceof FatalBotError) {
|
|
810
|
+
const errorMsg = `Bot error: ${classified.message}`;
|
|
811
|
+
history.push({ role: "assistant", content: errorMsg, frameType: "error" });
|
|
812
|
+
this.trimHistory(sessionKey);
|
|
813
|
+
return errorMsg;
|
|
814
|
+
}
|
|
815
|
+
history.push({
|
|
816
|
+
role: "assistant",
|
|
817
|
+
content: `[API error \u2014 retrying] ${classified.message}`,
|
|
818
|
+
frameType: "error"
|
|
819
|
+
});
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
const toolUseBlocks = response.content.filter(
|
|
823
|
+
(b) => b.type === "tool_use"
|
|
824
|
+
);
|
|
825
|
+
if (toolUseBlocks.length === 0) {
|
|
826
|
+
const textContent = response.content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
|
|
827
|
+
history.push({ role: "assistant", content: textContent, frameType: "assistant" });
|
|
828
|
+
this.trimHistory(sessionKey);
|
|
829
|
+
return textContent;
|
|
830
|
+
}
|
|
831
|
+
const { allowed, blocked } = guardToolUseBlocks(
|
|
832
|
+
toolUseBlocks,
|
|
833
|
+
permissions
|
|
834
|
+
);
|
|
835
|
+
history.push({
|
|
836
|
+
role: "assistant",
|
|
837
|
+
content: response.content,
|
|
838
|
+
frameType: "assistant"
|
|
839
|
+
});
|
|
840
|
+
const toolResults = [];
|
|
841
|
+
let hadFatalError = false;
|
|
842
|
+
for (const block of allowed) {
|
|
843
|
+
try {
|
|
844
|
+
const result = await this.config.toolExecutor(
|
|
845
|
+
block.name,
|
|
846
|
+
block.input
|
|
847
|
+
);
|
|
848
|
+
toolResults.push({
|
|
849
|
+
type: "tool_result",
|
|
850
|
+
tool_use_id: block.id,
|
|
851
|
+
content: result
|
|
852
|
+
});
|
|
853
|
+
} catch (err) {
|
|
854
|
+
const classified = classifyError(err, block.name);
|
|
855
|
+
if (classified instanceof FatalBotError) {
|
|
856
|
+
toolResults.push({
|
|
857
|
+
type: "tool_result",
|
|
858
|
+
tool_use_id: block.id,
|
|
859
|
+
content: `Fatal error: ${classified.message}`,
|
|
860
|
+
is_error: true
|
|
861
|
+
});
|
|
862
|
+
hadFatalError = true;
|
|
863
|
+
} else {
|
|
864
|
+
toolResults.push({
|
|
865
|
+
type: "tool_result",
|
|
866
|
+
tool_use_id: block.id,
|
|
867
|
+
content: `Error (recoverable): ${classified.message}`,
|
|
868
|
+
is_error: true
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
for (const { block, check } of blocked) {
|
|
874
|
+
toolResults.push({
|
|
875
|
+
type: "tool_result",
|
|
876
|
+
tool_use_id: block.id,
|
|
877
|
+
content: `Permission denied: ${check.reason}`,
|
|
878
|
+
is_error: true
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
history.push({
|
|
882
|
+
role: "user",
|
|
883
|
+
content: toolResults,
|
|
884
|
+
frameType: hadFatalError ? "error" : "tool_result"
|
|
885
|
+
});
|
|
886
|
+
if (hadFatalError) {
|
|
887
|
+
this.trimHistory(sessionKey);
|
|
888
|
+
return `A fatal error occurred during tool execution. The bot loop has been stopped.`;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
const maxErr = new MaxStepsError(turns, maxTurns);
|
|
892
|
+
history.push({
|
|
893
|
+
role: "assistant",
|
|
894
|
+
content: maxErr.message,
|
|
895
|
+
frameType: "error"
|
|
896
|
+
});
|
|
897
|
+
this.trimHistory(sessionKey);
|
|
898
|
+
return maxErr.message;
|
|
899
|
+
}
|
|
900
|
+
getHistory(sessionKey) {
|
|
901
|
+
if (!this.conversations.has(sessionKey)) {
|
|
902
|
+
this.conversations.set(sessionKey, []);
|
|
903
|
+
}
|
|
904
|
+
return this.conversations.get(sessionKey);
|
|
905
|
+
}
|
|
906
|
+
trimHistory(sessionKey) {
|
|
907
|
+
const history = this.conversations.get(sessionKey);
|
|
908
|
+
if (!history || history.length <= MAX_HISTORY) return;
|
|
909
|
+
const firstTaskIdx = history.findIndex((m) => m.frameType === "task");
|
|
910
|
+
let trimmed = history.filter(
|
|
911
|
+
(m, i) => m.frameType !== "planning" || i >= history.length - MAX_HISTORY
|
|
912
|
+
);
|
|
913
|
+
if (trimmed.length > MAX_HISTORY) {
|
|
914
|
+
const tail = trimmed.slice(-MAX_HISTORY);
|
|
915
|
+
if (firstTaskIdx >= 0 && !tail.includes(history[firstTaskIdx])) {
|
|
916
|
+
tail[0] = history[firstTaskIdx];
|
|
917
|
+
}
|
|
918
|
+
trimmed = tail;
|
|
919
|
+
}
|
|
920
|
+
this.conversations.set(sessionKey, trimmed);
|
|
921
|
+
}
|
|
922
|
+
/** Clear conversation history for a session */
|
|
923
|
+
clearHistory(sessionKey) {
|
|
924
|
+
this.conversations.delete(sessionKey);
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
function buildExecAssistantTools() {
|
|
928
|
+
return [
|
|
929
|
+
{
|
|
930
|
+
name: "ask_team_memory",
|
|
931
|
+
description: "Search another employee's memories. Use this to find what a team member worked on, learned, or solved.",
|
|
932
|
+
input_schema: {
|
|
933
|
+
type: "object",
|
|
934
|
+
properties: {
|
|
935
|
+
team_member: {
|
|
936
|
+
type: "string",
|
|
937
|
+
description: "Configured name of the team member"
|
|
938
|
+
},
|
|
939
|
+
query: { type: "string", description: "What to search for" },
|
|
940
|
+
project_name: {
|
|
941
|
+
type: "string",
|
|
942
|
+
description: "Filter by project name"
|
|
943
|
+
},
|
|
944
|
+
limit: { type: "number", description: "Max results (default 10)" }
|
|
945
|
+
},
|
|
946
|
+
required: ["team_member", "query"]
|
|
947
|
+
}
|
|
948
|
+
},
|
|
949
|
+
{
|
|
950
|
+
name: "recall_my_memory",
|
|
951
|
+
description: "Search your own past memories using semantic search. Returns memory IDs; use get_memory_by_id for full long records.",
|
|
952
|
+
input_schema: {
|
|
953
|
+
type: "object",
|
|
954
|
+
properties: {
|
|
955
|
+
query: { type: "string", description: "What to search for" },
|
|
956
|
+
project_name: {
|
|
957
|
+
type: "string",
|
|
958
|
+
description: "Filter by project name"
|
|
959
|
+
},
|
|
960
|
+
limit: { type: "number", description: "Max results (default 10)" },
|
|
961
|
+
body_chars: { type: "number", description: "Max characters per memory body, up to 50000" }
|
|
962
|
+
},
|
|
963
|
+
required: ["query"]
|
|
964
|
+
}
|
|
965
|
+
},
|
|
966
|
+
{
|
|
967
|
+
name: "get_memory_by_id",
|
|
968
|
+
description: "Fetch one memory by ID with the full untruncated raw_text body.",
|
|
969
|
+
input_schema: {
|
|
970
|
+
type: "object",
|
|
971
|
+
properties: {
|
|
972
|
+
id: { type: "string", description: "Memory UUID from recall_my_memory" }
|
|
973
|
+
},
|
|
974
|
+
required: ["id"]
|
|
975
|
+
}
|
|
976
|
+
},
|
|
977
|
+
{
|
|
978
|
+
name: "list_tasks",
|
|
979
|
+
description: "List tasks across the organization.",
|
|
980
|
+
input_schema: {
|
|
981
|
+
type: "object",
|
|
982
|
+
properties: {
|
|
983
|
+
assigned_to: {
|
|
984
|
+
type: "string",
|
|
985
|
+
description: "Filter by assignee"
|
|
986
|
+
},
|
|
987
|
+
status: {
|
|
988
|
+
type: "string",
|
|
989
|
+
description: "Filter by status (open, in_progress, done)"
|
|
990
|
+
},
|
|
991
|
+
project_name: {
|
|
992
|
+
type: "string",
|
|
993
|
+
description: "Filter by project"
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
},
|
|
998
|
+
{
|
|
999
|
+
name: "get_session_context",
|
|
1000
|
+
description: "Get context from a specific session.",
|
|
1001
|
+
input_schema: {
|
|
1002
|
+
type: "object",
|
|
1003
|
+
properties: {
|
|
1004
|
+
session_key: {
|
|
1005
|
+
type: "string",
|
|
1006
|
+
description: "Session key to look up"
|
|
1007
|
+
}
|
|
1008
|
+
},
|
|
1009
|
+
required: ["session_key"]
|
|
1010
|
+
}
|
|
1011
|
+
},
|
|
1012
|
+
{
|
|
1013
|
+
name: "list_reminders",
|
|
1014
|
+
description: "List active reminders.",
|
|
1015
|
+
input_schema: {
|
|
1016
|
+
type: "object",
|
|
1017
|
+
properties: {
|
|
1018
|
+
status: {
|
|
1019
|
+
type: "string",
|
|
1020
|
+
description: "Filter by status"
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
},
|
|
1025
|
+
// Write tools (only available on Signal with full access)
|
|
1026
|
+
{
|
|
1027
|
+
name: "create_task",
|
|
1028
|
+
description: "Create a new task and assign it to an employee.",
|
|
1029
|
+
input_schema: {
|
|
1030
|
+
type: "object",
|
|
1031
|
+
properties: {
|
|
1032
|
+
title: { type: "string", description: "Task title" },
|
|
1033
|
+
description: { type: "string", description: "Task description" },
|
|
1034
|
+
assigned_to: {
|
|
1035
|
+
type: "string",
|
|
1036
|
+
description: "Employee to assign to"
|
|
1037
|
+
},
|
|
1038
|
+
priority: {
|
|
1039
|
+
type: "string",
|
|
1040
|
+
enum: ["p0", "p1", "p2"],
|
|
1041
|
+
description: "Task priority"
|
|
1042
|
+
},
|
|
1043
|
+
project_name: { type: "string", description: "Project name" }
|
|
1044
|
+
},
|
|
1045
|
+
required: ["title", "assigned_to"]
|
|
1046
|
+
}
|
|
1047
|
+
},
|
|
1048
|
+
{
|
|
1049
|
+
name: "send_message",
|
|
1050
|
+
description: "Send a message to an employee.",
|
|
1051
|
+
input_schema: {
|
|
1052
|
+
type: "object",
|
|
1053
|
+
properties: {
|
|
1054
|
+
to: { type: "string", description: "Employee name" },
|
|
1055
|
+
message: { type: "string", description: "Message content" }
|
|
1056
|
+
},
|
|
1057
|
+
required: ["to", "message"]
|
|
1058
|
+
}
|
|
1059
|
+
},
|
|
1060
|
+
{
|
|
1061
|
+
name: "store_memory",
|
|
1062
|
+
description: "Store a memory for future reference.",
|
|
1063
|
+
input_schema: {
|
|
1064
|
+
type: "object",
|
|
1065
|
+
properties: {
|
|
1066
|
+
content: { type: "string", description: "Memory content" },
|
|
1067
|
+
project_name: { type: "string", description: "Project name" }
|
|
1068
|
+
},
|
|
1069
|
+
required: ["content"]
|
|
1070
|
+
}
|
|
1071
|
+
},
|
|
1072
|
+
{
|
|
1073
|
+
name: "store_behavior",
|
|
1074
|
+
description: "Store a behavioral directive.",
|
|
1075
|
+
input_schema: {
|
|
1076
|
+
type: "object",
|
|
1077
|
+
properties: {
|
|
1078
|
+
content: {
|
|
1079
|
+
type: "string",
|
|
1080
|
+
description: "Behavioral directive content"
|
|
1081
|
+
}
|
|
1082
|
+
},
|
|
1083
|
+
required: ["content"]
|
|
1084
|
+
}
|
|
1085
|
+
},
|
|
1086
|
+
{
|
|
1087
|
+
name: "update_task",
|
|
1088
|
+
description: "Update a task's status.",
|
|
1089
|
+
input_schema: {
|
|
1090
|
+
type: "object",
|
|
1091
|
+
properties: {
|
|
1092
|
+
task_id: { type: "string", description: "Task ID" },
|
|
1093
|
+
status: {
|
|
1094
|
+
type: "string",
|
|
1095
|
+
enum: ["open", "in_progress", "done", "blocked", "cancelled"],
|
|
1096
|
+
description: "New status"
|
|
1097
|
+
},
|
|
1098
|
+
result: { type: "string", description: "Result summary" }
|
|
1099
|
+
},
|
|
1100
|
+
required: ["task_id", "status"]
|
|
1101
|
+
}
|
|
1102
|
+
},
|
|
1103
|
+
{
|
|
1104
|
+
name: "create_reminder",
|
|
1105
|
+
description: "Create a reminder.",
|
|
1106
|
+
input_schema: {
|
|
1107
|
+
type: "object",
|
|
1108
|
+
properties: {
|
|
1109
|
+
content: { type: "string", description: "Reminder content" },
|
|
1110
|
+
due_at: { type: "string", description: "ISO 8601 due date" }
|
|
1111
|
+
},
|
|
1112
|
+
required: ["content"]
|
|
1113
|
+
}
|
|
1114
|
+
},
|
|
1115
|
+
{
|
|
1116
|
+
name: "complete_reminder",
|
|
1117
|
+
description: "Mark a reminder as complete.",
|
|
1118
|
+
input_schema: {
|
|
1119
|
+
type: "object",
|
|
1120
|
+
properties: {
|
|
1121
|
+
reminder_id: { type: "string", description: "Reminder ID" }
|
|
1122
|
+
},
|
|
1123
|
+
required: ["reminder_id"]
|
|
1124
|
+
}
|
|
1125
|
+
},
|
|
1126
|
+
{
|
|
1127
|
+
name: "close_task",
|
|
1128
|
+
description: "Reviewer-only: finalize a task after review. Only coordinators/reviewers can use this.",
|
|
1129
|
+
input_schema: {
|
|
1130
|
+
type: "object",
|
|
1131
|
+
properties: {
|
|
1132
|
+
task_id: { type: "string", description: "Task ID" },
|
|
1133
|
+
result: { type: "string", description: "Completion summary" }
|
|
1134
|
+
},
|
|
1135
|
+
required: ["task_id"]
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
];
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// src/gateway/bot-registry.ts
|
|
1142
|
+
var BotRegistry = class {
|
|
1143
|
+
bots = /* @__PURE__ */ new Map();
|
|
1144
|
+
/** Register a bot from a template */
|
|
1145
|
+
register(template, apiKey) {
|
|
1146
|
+
const config = {
|
|
1147
|
+
apiKey,
|
|
1148
|
+
model: template.model,
|
|
1149
|
+
agentId: template.agentId,
|
|
1150
|
+
systemPrompt: template.systemPrompt,
|
|
1151
|
+
tools: template.tools.map((t) => ({
|
|
1152
|
+
name: t.name,
|
|
1153
|
+
description: t.description ?? "",
|
|
1154
|
+
input_schema: t.input_schema
|
|
1155
|
+
})),
|
|
1156
|
+
toolExecutor: async (name, input) => {
|
|
1157
|
+
const handler = template.toolHandlers[name];
|
|
1158
|
+
if (!handler) {
|
|
1159
|
+
throw new Error(`No handler for tool "${name}"`);
|
|
1160
|
+
}
|
|
1161
|
+
const result = await handler(input);
|
|
1162
|
+
return typeof result === "string" ? result : JSON.stringify(result);
|
|
1163
|
+
},
|
|
1164
|
+
maxTurns: template.maxTurns
|
|
1165
|
+
};
|
|
1166
|
+
this.bots.set(template.name, new BotRuntime(config));
|
|
1167
|
+
}
|
|
1168
|
+
/** Register a pre-configured BotRuntime directly */
|
|
1169
|
+
registerRuntime(name, runtime) {
|
|
1170
|
+
this.bots.set(name, runtime);
|
|
1171
|
+
}
|
|
1172
|
+
/** Look up a bot by route target name */
|
|
1173
|
+
get(targetName) {
|
|
1174
|
+
return this.bots.get(targetName);
|
|
1175
|
+
}
|
|
1176
|
+
/** List registered bot names */
|
|
1177
|
+
list() {
|
|
1178
|
+
return [...this.bots.keys()];
|
|
1179
|
+
}
|
|
1180
|
+
/** Check if a bot is registered */
|
|
1181
|
+
has(targetName) {
|
|
1182
|
+
return this.bots.has(targetName);
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
1185
|
+
|
|
1186
|
+
export {
|
|
1187
|
+
routeMessage,
|
|
1188
|
+
validateGatewayConfig,
|
|
1189
|
+
retryWithBackoff,
|
|
1190
|
+
CircuitBreaker,
|
|
1191
|
+
Gateway,
|
|
1192
|
+
FULL_ACCESS,
|
|
1193
|
+
READ_ONLY,
|
|
1194
|
+
READ_TOOLS,
|
|
1195
|
+
WRITE_TOOLS,
|
|
1196
|
+
EXECUTE_TOOLS,
|
|
1197
|
+
checkToolPermission,
|
|
1198
|
+
guardToolUseBlocks,
|
|
1199
|
+
buildPermissionContext,
|
|
1200
|
+
FatalBotError,
|
|
1201
|
+
RecoverableBotError,
|
|
1202
|
+
MaxStepsError,
|
|
1203
|
+
classifyError,
|
|
1204
|
+
buildExecAssistantSystemPrompt,
|
|
1205
|
+
BotRuntime,
|
|
1206
|
+
buildExecAssistantTools,
|
|
1207
|
+
BotRegistry
|
|
1208
|
+
};
|