@askexenow/exe-os 0.9.280 → 0.9.281
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.default +2 -1
- package/deploy/compose/.env.example +1 -1
- package/deploy/compose/docker-compose.yml +74 -13
- package/deploy/compose/erp-nginx/nginx.conf +52 -0
- package/deploy/compose/generate-env.ts +1 -1
- package/deploy/compose/init-db.sql +13 -2
- package/deploy/stack-manifests/v0.9.json +11 -31
- package/dist/active-agent-6ZBHGHXF.js +26 -0
- package/dist/active-agent-E23SCYER.js +27 -0
- package/dist/active-agent-KMZT44S4.js +26 -0
- package/dist/active-agent-LFFTVROM.js +27 -0
- package/dist/agentic-ontology-PCZB5HV5.js +25 -0
- package/dist/agentic-ontology-PGGJN2ES.js +25 -0
- package/dist/assets/tmux.conf +2 -0
- package/dist/backfill-metadata-KQ4FEVUR.js +599 -0
- package/dist/backfill-metadata-Y3YWCHKJ.js +599 -0
- package/dist/behaviors-H4DZECKL.js +39 -0
- package/dist/behaviors-WIUTIJF6.js +39 -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 +4 -4
- package/dist/bin/backfill-responses.js +4 -4
- package/dist/bin/backfill-vectors.js +5 -5
- package/dist/bin/bulk-sync-postgres.js +7 -7
- package/dist/bin/cc-doctor.js +4 -4
- package/dist/bin/cleanup-stale-review-tasks.js +10 -10
- package/dist/bin/cli.js +15 -15
- package/dist/bin/exe-agent-config.js +2 -2
- package/dist/bin/exe-agent.js +4 -4
- package/dist/bin/exe-assign.js +5 -5
- package/dist/bin/exe-boot.js +17 -17
- package/dist/bin/exe-call.js +4 -4
- package/dist/bin/exe-cloud.js +5 -5
- package/dist/bin/exe-dispatch.js +10 -10
- package/dist/bin/exe-doctor.js +1 -1
- 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 +4 -4
- package/dist/bin/exe-heartbeat.js +10 -10
- package/dist/bin/exe-kill.js +13 -13
- package/dist/bin/exe-launch-agent.js +17 -17
- package/dist/bin/exe-new-employee.js +7 -7
- 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 +5 -5
- 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-support.js +2 -2
- package/dist/bin/exe-team.js +3 -3
- package/dist/bin/exe-watchdog.js +17 -2
- package/dist/bin/git-sweep.js +11 -11
- package/dist/bin/graph-backfill.js +4 -4
- package/dist/bin/graph-export.js +5 -5
- package/dist/bin/import-history.js +7 -7
- package/dist/bin/install.js +8 -7
- 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/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 +3 -3
- package/dist/bin/vps-health-gate.js +1 -1
- package/dist/capability-cards-F22XWGLB.js +88 -0
- package/dist/capability-cards-KCKITXXZ.js +88 -0
- package/dist/capacity-monitor-CHXGKVEO.js +50 -0
- package/dist/capacity-monitor-L3M5KB6T.js +50 -0
- package/dist/catchup-brief-BVZANLRZ.js +154 -0
- package/dist/catchup-brief-LDUGMC7S.js +154 -0
- package/dist/catchup-brief-NZDLL7SA.js +154 -0
- package/dist/catchup-brief-OOIXVGBA.js +154 -0
- package/dist/catchup-brief-UPXHDYTB.js +154 -0
- package/dist/chunk-2BI3FQKL.js +1876 -0
- package/dist/chunk-2CJUQGHH.js +362 -0
- package/dist/chunk-2EVYBMBJ.js +128 -0
- package/dist/chunk-2NQOZOXG.js +2113 -0
- package/dist/chunk-2QK5E3LB.js +128 -0
- package/dist/chunk-2VTCG4ZU.js +1352 -0
- package/dist/chunk-2YGI36DV.js +1119 -0
- package/dist/chunk-32BQN2QN.js +185 -0
- package/dist/chunk-33JMO4UV.js +157 -0
- package/dist/chunk-3DZAYLXY.js +377 -0
- package/dist/chunk-3MGBE7GR.js +76 -0
- package/dist/chunk-3MTV4FJL.js +271 -0
- package/dist/chunk-3NN7VQ27.js +1352 -0
- package/dist/chunk-4533HNOG.js +70 -0
- package/dist/chunk-456UW3MT.js +731 -0
- package/dist/chunk-4AU56XV2.js +58 -0
- package/dist/chunk-4MRP6EBR.js +280 -0
- package/dist/chunk-4MRWW52U.js +14219 -0
- package/dist/chunk-4WWECNAY.js +50 -0
- package/dist/chunk-4ZJDDR6L.js +171 -0
- package/dist/chunk-522EOPM6.js +382 -0
- package/dist/chunk-52UDAVZE.js +3208 -0
- package/dist/chunk-5FAMUB4X.js +204 -0
- package/dist/chunk-5PGZJQUI.js +58 -0
- package/dist/chunk-67TZJXNZ.js +262 -0
- package/dist/chunk-6FEZ7GN2.js +123 -0
- package/dist/chunk-6MOGND7S.js +14219 -0
- package/dist/chunk-6TRSVY7L.js +181 -0
- package/dist/chunk-6WCS7ZNK.js +85 -0
- package/dist/chunk-6WRYDREW.js +539 -0
- package/dist/chunk-72UA2FB3.js +181 -0
- package/dist/chunk-77DMFEOL.js +30 -0
- package/dist/chunk-7COXVQ5W.js +214 -0
- package/dist/chunk-7HLWBYH7.js +60 -0
- package/dist/chunk-ALSTZCWT.js +204 -0
- package/dist/chunk-ARUTDXZX.js +280 -0
- package/dist/chunk-AU47B4QY.js +129 -0
- package/dist/chunk-AXOREWVL.js +836 -0
- package/dist/chunk-B5MNC54V.js +127 -0
- package/dist/chunk-B74VSMKX.js +1350 -0
- package/dist/chunk-BHWLH44J.js +362 -0
- package/dist/chunk-BUPZ3HD2.js +85 -0
- package/dist/chunk-BWVLMA53.js +2113 -0
- package/dist/chunk-C5WLBKMJ.js +50 -0
- package/dist/chunk-C62T6R2A.js +97 -0
- package/dist/chunk-CV5KBAIK.js +33 -0
- package/dist/chunk-DAYSDWXA.js +1068 -0
- package/dist/chunk-DI4URIUB.js +227 -0
- package/dist/chunk-DNTCYFJ6.js +76 -0
- package/dist/chunk-DPDRRS7T.js +103 -0
- package/dist/chunk-DT3EV6CW.js +103 -0
- package/dist/chunk-DX45HDWY.js +1076 -0
- package/dist/chunk-E72BD6MG.js +284 -0
- package/dist/chunk-EAPUSVKS.js +375 -0
- package/dist/chunk-EFUANRRT.js +85 -0
- package/dist/chunk-EG2SCT5R.js +1352 -0
- package/dist/chunk-EJD2JU77.js +58 -0
- package/dist/chunk-EMXYUAVP.js +81 -0
- package/dist/chunk-ENM2TAAM.js +14219 -0
- package/dist/chunk-EPDRTPVP.js +1876 -0
- package/dist/chunk-EW6XDHID.js +221 -0
- package/dist/chunk-EYEGSAWZ.js +1094 -0
- package/dist/chunk-F6L33PAQ.js +231 -0
- package/dist/chunk-FC2SCTVE.js +38 -0
- package/dist/chunk-FSRKIZGZ.js +630 -0
- package/dist/chunk-FTG7I5CB.js +81 -0
- package/dist/chunk-GVFRLWX7.js +30 -0
- package/dist/chunk-H3XMZOWW.js +1119 -0
- package/dist/chunk-HAKXE6LN.js +123 -0
- package/dist/chunk-HLP3ZDTW.js +448 -0
- package/dist/chunk-HOYWKQAA.js +510 -0
- package/dist/chunk-HWDD64IW.js +712 -0
- package/dist/chunk-HWMCULHY.js +127 -0
- package/dist/chunk-HZZHRZPK.js +210 -0
- package/dist/chunk-ICRWTYNW.js +103 -0
- package/dist/chunk-J5MWPC33.js +167 -0
- package/dist/chunk-JBXANNNB.js +70 -0
- package/dist/chunk-JPBMIYWF.js +1352 -0
- package/dist/chunk-JY6EXBFI.js +373 -0
- package/dist/chunk-JZLIBXI7.js +14219 -0
- package/dist/chunk-JZPTKXJ6.js +668 -0
- package/dist/chunk-KEYMA4ZP.js +510 -0
- package/dist/chunk-KHGNN6GL.js +2078 -0
- package/dist/chunk-KVLB2PD6.js +97 -0
- package/dist/chunk-L3YBRBKL.js +1076 -0
- package/dist/chunk-L7VZ32NA.js +89 -0
- package/dist/chunk-LEHLADW4.js +221 -0
- package/dist/chunk-LPD4HILQ.js +262 -0
- package/dist/chunk-LSJ3ADDI.js +51 -0
- package/dist/chunk-LUMS2MAS.js +58 -0
- package/dist/chunk-LV4SEC6C.js +394 -0
- package/dist/chunk-LXXBEI4A.js +284 -0
- package/dist/chunk-M4NPCAIH.js +456 -0
- package/dist/chunk-MFJ62LQ5.js +157 -0
- package/dist/chunk-MGEZNKOD.js +836 -0
- package/dist/chunk-MJ7X5IBW.js +227 -0
- package/dist/chunk-MLGRWCY4.js +54 -0
- package/dist/chunk-MQZX57IY.js +348 -0
- package/dist/chunk-MY7MFF6J.js +103 -0
- package/dist/chunk-MYA2X5OY.js +185 -0
- package/dist/chunk-MYEABW5Z.js +630 -0
- package/dist/chunk-MZ2TDCAL.js +402 -0
- package/dist/chunk-NF3FRB7Z.js +271 -0
- package/dist/chunk-NXYIFEPV.js +539 -0
- package/dist/chunk-O2GVE5B5.js +58 -0
- package/dist/chunk-O5GUCDR2.js +456 -0
- package/dist/chunk-OBZNRECA.js +128 -0
- package/dist/chunk-OMZ2RLJG.js +214 -0
- package/dist/chunk-ORDHJRWN.js +299 -0
- package/dist/chunk-OSPIJMCD.js +210 -0
- package/dist/chunk-OUGWEH4J.js +240 -0
- package/dist/chunk-OV5MJQGC.js +1876 -0
- package/dist/chunk-PC635OAG.js +4318 -0
- package/dist/chunk-PH46R4J6.js +348 -0
- package/dist/chunk-PJP2EP7P.js +394 -0
- package/dist/chunk-PODFWH3V.js +333 -0
- package/dist/chunk-PPWH3SHR.js +1068 -0
- package/dist/chunk-PWPJK7KB.js +4318 -0
- package/dist/chunk-PXONZVG4.js +377 -0
- package/dist/chunk-QAOGJRZD.js +369 -0
- package/dist/chunk-QC3LAEI7.js +197 -0
- package/dist/chunk-QDWQDUWI.js +668 -0
- package/dist/chunk-QKJFD6BH.js +1350 -0
- package/dist/chunk-QNYVJGFM.js +345 -0
- package/dist/chunk-QQF3XGQ5.js +14219 -0
- package/dist/chunk-QRPFQNI3.js +150 -0
- package/dist/chunk-QSSU5XWD.js +731 -0
- package/dist/chunk-QXZAGVAV.js +2078 -0
- package/dist/chunk-RA54MW64.js +244 -0
- package/dist/chunk-RF7PUWXI.js +197 -0
- package/dist/chunk-S3INDYSO.js +244 -0
- package/dist/chunk-SLQVTHH5.js +369 -0
- package/dist/chunk-SY2B74KL.js +345 -0
- package/dist/chunk-T6QPXXXW.js +712 -0
- package/dist/chunk-TO5M5YCT.js +41 -0
- package/dist/chunk-TQ4VXUAF.js +129 -0
- package/dist/chunk-UAB7RQC4.js +41 -0
- package/dist/chunk-UMEIBDYW.js +97 -0
- package/dist/chunk-UXW5TB7Y.js +240 -0
- package/dist/chunk-VB2N5WOX.js +150 -0
- package/dist/chunk-VLZEMRG3.js +167 -0
- package/dist/chunk-VM3V6VK7.js +230 -0
- package/dist/chunk-VMCGKBHB.js +1352 -0
- package/dist/chunk-VNIYZAR5.js +128 -0
- package/dist/chunk-VYV4KOD2.js +85 -0
- package/dist/chunk-W4SRJBAT.js +171 -0
- package/dist/chunk-W5W3LZ3Q.js +54 -0
- package/dist/chunk-WDNZEOM3.js +38 -0
- package/dist/chunk-WUKEXVOR.js +3208 -0
- package/dist/chunk-X3Z35Q6L.js +373 -0
- package/dist/chunk-X4VOU6BQ.js +382 -0
- package/dist/chunk-X6EEVSVG.js +290 -0
- package/dist/chunk-XFHGWGNB.js +1094 -0
- package/dist/chunk-XKOLRWYA.js +33 -0
- package/dist/chunk-XMMIL3UD.js +402 -0
- package/dist/chunk-XWILC6VA.js +290 -0
- package/dist/chunk-Y4OQCX4C.js +97 -0
- package/dist/chunk-Y67VYYOA.js +231 -0
- package/dist/chunk-YGQCQTQH.js +230 -0
- package/dist/chunk-YGWFBN5A.js +299 -0
- package/dist/chunk-YMKUXZIG.js +379 -0
- package/dist/chunk-YOMLMT7E.js +230 -0
- package/dist/chunk-YPESIZOB.js +14219 -0
- package/dist/chunk-Z2CGCIU2.js +89 -0
- package/dist/chunk-ZLAWNHQR.js +448 -0
- package/dist/chunk-ZME5UQSN.js +333 -0
- package/dist/co-activation-NUEQYXE5.js +73 -0
- package/dist/co-activation-ZG5HLBCZ.js +73 -0
- package/dist/co-occurrence-7S5KWQB2.js +94 -0
- package/dist/co-occurrence-X5SWDXT2.js +94 -0
- package/dist/core-memory-GOPBRGGZ.js +110 -0
- package/dist/core-memory-XLCU6L5M.js +110 -0
- package/dist/crdt-sync-EPKHPGRZ.js +33 -0
- package/dist/crdt-sync-UIQJ5U7T.js +33 -0
- package/dist/crm-webhook-MKN23JNU.js +10 -0
- package/dist/crm-webhook-SM63BPXO.js +10 -0
- package/dist/cto-delegation-gate-PQY5TOVZ.js +279 -0
- package/dist/cto-delegation-gate-V5VVUR3G.js +279 -0
- package/dist/daemon-orchestration-C7AAS67Q.js +138 -0
- package/dist/daemon-orchestration-OBCAJB2H.js +138 -0
- package/dist/db-backup-F7VP4QRH.js +33 -0
- package/dist/db-backup-KVYC57W7.js +33 -0
- package/dist/doc-graph-extractor-H2ETEINP.js +132 -0
- package/dist/doc-graph-extractor-PCUZEYCH.js +132 -0
- package/dist/dreaming-4OZXSLE3.js +33 -0
- package/dist/dreaming-Z2RYEYNT.js +33 -0
- package/dist/exe-drift-GEWNIK7A.js +69 -0
- package/dist/exe-drift-XCGH7AFO.js +69 -0
- package/dist/exe-export-7DKAU5IP.js +75 -0
- package/dist/exe-export-BCHH6OE6.js +75 -0
- package/dist/exe-import-PDRIZVYF.js +78 -0
- package/dist/exe-import-ZCKUDFKL.js +78 -0
- package/dist/exe-key-FUWLLI3U.js +580 -0
- package/dist/exe-key-RKKNVUMP.js +580 -0
- package/dist/exe-snapshot-G4I5FQMK.js +337 -0
- package/dist/exe-snapshot-GWU7QTZK.js +337 -0
- package/dist/fast-db-init-6QG6YQNT.js +7 -0
- package/dist/fast-db-init-X2QDQUA4.js +7 -0
- package/dist/founder-context-TOMNUBGJ.js +96 -0
- package/dist/founder-context-UU3V6MAS.js +96 -0
- package/dist/gateway/index.js +8 -8
- package/dist/git-staleness-FEPFMZKF.js +111 -0
- package/dist/git-staleness-HYVYLCW3.js +111 -0
- package/dist/git-task-sweep-IRV52JIM.js +41 -0
- package/dist/git-task-sweep-T6BSM3GS.js +41 -0
- package/dist/global-procedures-3AURRMKO.js +21 -0
- package/dist/global-procedures-JPCYBZYC.js +21 -0
- package/dist/graph-auto-extract-OC3AOSMW.js +182 -0
- package/dist/graph-auto-extract-PVDYEJBY.js +182 -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.js +6 -6
- package/dist/hooks/instructions-loaded.js +4 -4
- package/dist/hooks/manifest.json +19 -19
- package/dist/hooks/notification.js +4 -4
- package/dist/hooks/post-compact.js +11 -11
- package/dist/hooks/post-tool-combined.js +5 -5
- package/dist/hooks/pre-compact.js +12 -12
- package/dist/hooks/pre-tool-use.js +15 -15
- package/dist/hooks/prompt-submit.js +22 -22
- package/dist/hooks/session-end.js +16 -16
- package/dist/hooks/session-start.js +10 -10
- package/dist/hooks/stop.js +15 -15
- package/dist/hooks/subagent-stop.js +11 -11
- package/dist/hooks/summary-worker.js +15 -15
- package/dist/index.js +18 -18
- package/dist/installer-72XXLBRP.js +39 -0
- package/dist/installer-HDXG2BZN.js +343 -0
- package/dist/installer-JALMKPCS.js +297 -0
- package/dist/installer-Q46SNNLU.js +39 -0
- package/dist/installer-W7PIPRCX.js +343 -0
- package/dist/installer-Z7WQEOS7.js +297 -0
- package/dist/lib/cloud-sync.js +5 -5
- package/dist/lib/consolidation.js +5 -5
- package/dist/lib/database.js +2 -2
- package/dist/lib/db.js +2 -2
- package/dist/lib/employee-templates.js +4 -4
- package/dist/lib/employees.js +2 -2
- package/dist/lib/exe-daemon.js +45 -41
- package/dist/lib/hybrid-search.js +5 -5
- package/dist/lib/identity.js +2 -2
- package/dist/lib/license.js +1 -1
- 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/license-gate-7JZCHOAG.js +14 -0
- package/dist/license-gate-OP4SKL4P.js +14 -0
- package/dist/mcp/register-tools.js +58 -58
- package/dist/mcp/server.js +59 -59
- 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-health-VULNT722.js +17 -0
- package/dist/mcp-health-WDOB6XUB.js +19 -0
- package/dist/mcp-http-config-2OZ7N74D.js +28 -0
- package/dist/mcp-http-config-4VXA5K73.js +28 -0
- package/dist/memory-cards-2K6QRZU6.js +179 -0
- package/dist/memory-cards-KSJF5OH2.js +179 -0
- package/dist/memory-graph-extractor-IJD5HWYT.js +21 -0
- package/dist/memory-graph-extractor-O4GAXOK5.js +21 -0
- package/dist/memory-poisoning-defense-2JRPWT5V.js +223 -0
- package/dist/memory-poisoning-defense-DH4A25NU.js +223 -0
- package/dist/memory-reflection-ISY2BBDB.js +243 -0
- package/dist/memory-reflection-Z5AQRR6H.js +243 -0
- package/dist/notifications-2VSWK2UJ.js +46 -0
- package/dist/notifications-4S253VQM.js +46 -0
- package/dist/oauth-server-D7D4574D.js +437 -0
- package/dist/oauth-server-MACN54SJ.js +437 -0
- package/dist/orchestration-events-BGP5RYQI.js +26 -0
- package/dist/orchestration-events-MDXUEVRZ.js +26 -0
- package/dist/orchestrator-DHK7RSSH.js +34 -0
- package/dist/orchestrator-R75WHQVA.js +34 -0
- package/dist/pipeline-router-4WUKQQEC.js +14 -0
- package/dist/pipeline-router-GWB2XK2Q.js +14 -0
- package/dist/plan-limits-NNJRAESF.js +27 -0
- package/dist/plan-limits-YTQW4UR4.js +27 -0
- package/dist/project-boot-46GZJTEX.js +299 -0
- package/dist/project-boot-PPHBBGIF.js +299 -0
- package/dist/projection-worker-UPAWXI7P.js +1034 -0
- package/dist/projection-worker-ZIKDYBW5.js +1034 -0
- package/dist/reranker-5ZBP2RRN.js +19 -0
- package/dist/reranker-E2MQIMJL.js +19 -0
- package/dist/reranker-GLSDJT3V.js +19 -0
- package/dist/reranker-LBBXWNOD.js +19 -0
- package/dist/reranker-XZ2EF4OH.js +19 -0
- package/dist/retrieval-health-JYRKPSII.js +7 -0
- package/dist/retrieval-health-OUV25J6S.js +7 -0
- package/dist/retrieval-health-U73JUAZL.js +7 -0
- package/dist/retrieval-health-WSZ7TYFF.js +7 -0
- package/dist/review-polling-62JV55ZT.js +125 -0
- package/dist/review-polling-CJXLWFWK.js +125 -0
- package/dist/runtime/index.js +12 -12
- package/dist/session-events-2ADD54VI.js +37 -0
- package/dist/session-events-QIJVBSKS.js +37 -0
- package/dist/session-kill-telemetry-HS6HD2YE.js +30 -0
- package/dist/session-kill-telemetry-MRT5FVSM.js +30 -0
- package/dist/session-scope-7ICYPC33.js +87 -0
- package/dist/session-scope-KMXD6EE6.js +87 -0
- package/dist/setup-wizard-B6GIT7YC.js +12 -0
- package/dist/setup-wizard-JUIJ4UZO.js +12 -0
- package/dist/skill-refinement-HIOX4VMC.js +158 -0
- package/dist/skill-refinement-T7JXRYUW.js +158 -0
- package/dist/stack-update-5KE6BZKQ.js +74 -0
- package/dist/stack-update-OP2RHP7N.js +74 -0
- package/dist/stack-update-VGCWDJEE.js +74 -0
- package/dist/steward-gate-L22WE3SY.js +14 -0
- package/dist/steward-gate-YKD2LUWN.js +14 -0
- package/dist/task-enforcement-5AOKXTY4.js +439 -0
- package/dist/task-enforcement-VO3YEGIO.js +439 -0
- package/dist/task-scope-YV2WPKRD.js +36 -0
- package/dist/task-scope-ZSXDZBRE.js +36 -0
- package/dist/tasks-crud-C6KADACT.js +78 -0
- package/dist/tasks-crud-NV6JEWGL.js +78 -0
- package/dist/tasks-notify-E22HSN6O.js +39 -0
- package/dist/tasks-notify-RPSEQ4WV.js +39 -0
- package/dist/tasks-review-V4ZLXOAZ.js +48 -0
- package/dist/tasks-review-ZVRI73JE.js +48 -0
- package/dist/telemetry-upload-LXUH7SKI.js +740 -0
- package/dist/telemetry-upload-TCDAZTUQ.js +740 -0
- package/dist/token-budget-OFBEZJTA.js +85 -0
- package/dist/token-budget-WAN57V6S.js +85 -0
- package/dist/tool-telemetry-UA3N32PK.js +17 -0
- package/dist/tool-telemetry-XXZJ35RR.js +17 -0
- package/dist/tui/App.js +17 -17
- package/dist/tui-data-46QLCJUE.js +259 -0
- package/dist/tui-data-ZDB7BLP2.js +259 -0
- package/dist/wiki-acl-HHSIBPF3.js +111 -0
- package/dist/wiki-acl-O65GZ2ZF.js +111 -0
- package/dist/worker-gate-27I4GAEZ.js +21 -0
- package/dist/worker-gate-DXU4HEPY.js +21 -0
- package/dist/workflow-engine-63EOEJ5Q.js +28 -0
- package/dist/workflow-engine-C6F2RMPN.js +28 -0
- package/dist/worktree-SFKKOMFD.js +27 -0
- package/dist/worktree-SVCE3S7X.js +27 -0
- package/dist/worktree-sweep-S3JHJTVP.js +20 -0
- package/dist/worktree-sweep-U3TIQ7WL.js +20 -0
- package/package.json +1 -1
- package/release-notes.json +88 -26
|
@@ -0,0 +1,1350 @@
|
|
|
1
|
+
import {
|
|
2
|
+
listWorkflowDefinitions,
|
|
3
|
+
runWorkflow,
|
|
4
|
+
startWorkflow
|
|
5
|
+
} from "./chunk-PODFWH3V.js";
|
|
6
|
+
import {
|
|
7
|
+
ingest
|
|
8
|
+
} from "./chunk-5FAMUB4X.js";
|
|
9
|
+
import {
|
|
10
|
+
initCRMBridge
|
|
11
|
+
} from "./chunk-ONKIWA3R.js";
|
|
12
|
+
import {
|
|
13
|
+
orgBus
|
|
14
|
+
} from "./chunk-MP2AFCGL.js";
|
|
15
|
+
|
|
16
|
+
// src/gateway/router.ts
|
|
17
|
+
function matchesPlatform(msgPlatform, matchPlatform) {
|
|
18
|
+
if (!matchPlatform) return true;
|
|
19
|
+
const platforms = Array.isArray(matchPlatform) ? matchPlatform : [matchPlatform];
|
|
20
|
+
return platforms.includes(msgPlatform);
|
|
21
|
+
}
|
|
22
|
+
function matchesChannel(msgChannel, matchChannel) {
|
|
23
|
+
if (!matchChannel) return true;
|
|
24
|
+
const channels = Array.isArray(matchChannel) ? matchChannel : [matchChannel];
|
|
25
|
+
return channels.includes(msgChannel);
|
|
26
|
+
}
|
|
27
|
+
var MAX_REGEX_LENGTH = 200;
|
|
28
|
+
function safeRegExp(pattern, flags) {
|
|
29
|
+
if (pattern.length > MAX_REGEX_LENGTH) return null;
|
|
30
|
+
try {
|
|
31
|
+
return new RegExp(pattern, flags);
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function matchesSender(msgSender, matchSender) {
|
|
37
|
+
if (!matchSender) return true;
|
|
38
|
+
const re = safeRegExp(matchSender);
|
|
39
|
+
return re ? re.test(msgSender) : false;
|
|
40
|
+
}
|
|
41
|
+
function matchesTextPattern(msgText, matchPattern) {
|
|
42
|
+
if (!matchPattern) return true;
|
|
43
|
+
const re = safeRegExp(matchPattern, "i");
|
|
44
|
+
return re ? re.test(msgText) : false;
|
|
45
|
+
}
|
|
46
|
+
function matchesRoute(msg, match) {
|
|
47
|
+
return matchesPlatform(msg.platform, match.platform) && matchesChannel(msg.channelId, match.channelId) && matchesSender(msg.senderId, match.senderId) && matchesTextPattern(msg.text, match.textPattern);
|
|
48
|
+
}
|
|
49
|
+
function routeMessage(msg, config) {
|
|
50
|
+
for (const route of config.routes) {
|
|
51
|
+
if (matchesRoute(msg, route.match)) {
|
|
52
|
+
return {
|
|
53
|
+
employee: route.target,
|
|
54
|
+
modelTier: route.modelTier,
|
|
55
|
+
permissions: route.permissions,
|
|
56
|
+
routeName: route.name
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
employee: config.defaultRoute,
|
|
62
|
+
modelTier: config.defaultModelTier,
|
|
63
|
+
permissions: config.defaultPermissions,
|
|
64
|
+
routeName: "default"
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function validateGatewayConfig(config) {
|
|
68
|
+
const warnings = [];
|
|
69
|
+
if (!config.routes.length) {
|
|
70
|
+
warnings.push("No routes configured \u2014 all messages will use default route");
|
|
71
|
+
}
|
|
72
|
+
if (!config.defaultRoute) {
|
|
73
|
+
warnings.push("No default route specified");
|
|
74
|
+
}
|
|
75
|
+
const names = /* @__PURE__ */ new Set();
|
|
76
|
+
for (const route of config.routes) {
|
|
77
|
+
if (names.has(route.name)) {
|
|
78
|
+
warnings.push(`Duplicate route name: "${route.name}"`);
|
|
79
|
+
}
|
|
80
|
+
names.add(route.name);
|
|
81
|
+
if (!route.target) {
|
|
82
|
+
warnings.push(`Route "${route.name}" has no target employee`);
|
|
83
|
+
}
|
|
84
|
+
if (route.match.senderId && !safeRegExp(route.match.senderId)) {
|
|
85
|
+
warnings.push(`Route "${route.name}" has invalid senderId regex: ${route.match.senderId}`);
|
|
86
|
+
}
|
|
87
|
+
if (route.match.textPattern && !safeRegExp(route.match.textPattern, "i")) {
|
|
88
|
+
warnings.push(`Route "${route.name}" has invalid textPattern regex: ${route.match.textPattern}`);
|
|
89
|
+
}
|
|
90
|
+
const isEmptyMatch = !route.match.platform && !route.match.channelId && !route.match.senderId && !route.match.textPattern;
|
|
91
|
+
if (isEmptyMatch && config.routes.indexOf(route) !== config.routes.length - 1) {
|
|
92
|
+
warnings.push(
|
|
93
|
+
`Route "${route.name}" matches everything but is not the last route \u2014 routes after it will never match`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return warnings;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/gateway/reliability.ts
|
|
101
|
+
var DEFAULT_RETRY = {
|
|
102
|
+
maxRetries: 3,
|
|
103
|
+
baseDelayMs: 1e3,
|
|
104
|
+
maxDelayMs: 4e3
|
|
105
|
+
};
|
|
106
|
+
async function retryWithBackoff(fn, config = {}) {
|
|
107
|
+
const { maxRetries, baseDelayMs, maxDelayMs } = {
|
|
108
|
+
...DEFAULT_RETRY,
|
|
109
|
+
...config
|
|
110
|
+
};
|
|
111
|
+
let lastError;
|
|
112
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
113
|
+
try {
|
|
114
|
+
return await fn();
|
|
115
|
+
} catch (err) {
|
|
116
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
117
|
+
if (attempt < maxRetries) {
|
|
118
|
+
const delay = Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);
|
|
119
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
throw lastError;
|
|
124
|
+
}
|
|
125
|
+
var DEFAULT_CIRCUIT = {
|
|
126
|
+
windowMs: 6e4,
|
|
127
|
+
failureThreshold: 0.5,
|
|
128
|
+
minimumRequests: 5,
|
|
129
|
+
halfOpenAfterMs: 3e4
|
|
130
|
+
};
|
|
131
|
+
var CircuitBreaker = class {
|
|
132
|
+
state = "closed";
|
|
133
|
+
records = [];
|
|
134
|
+
lastStateChange = Date.now();
|
|
135
|
+
config;
|
|
136
|
+
name;
|
|
137
|
+
constructor(name, config = {}) {
|
|
138
|
+
this.name = name;
|
|
139
|
+
this.config = { ...DEFAULT_CIRCUIT, ...config };
|
|
140
|
+
}
|
|
141
|
+
/** Check if a request should be allowed through */
|
|
142
|
+
canRequest() {
|
|
143
|
+
this.pruneRecords();
|
|
144
|
+
if (this.state === "closed") return true;
|
|
145
|
+
if (this.state === "open") {
|
|
146
|
+
const elapsed = Date.now() - this.lastStateChange;
|
|
147
|
+
if (elapsed >= this.config.halfOpenAfterMs) {
|
|
148
|
+
this.transition("half-open");
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
/** Record a successful request */
|
|
156
|
+
recordSuccess() {
|
|
157
|
+
this.records.push({ timestamp: Date.now(), success: true });
|
|
158
|
+
if (this.state === "half-open") {
|
|
159
|
+
this.transition("closed");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/** Record a failed request */
|
|
163
|
+
recordFailure() {
|
|
164
|
+
this.records.push({ timestamp: Date.now(), success: false });
|
|
165
|
+
if (this.state === "half-open") {
|
|
166
|
+
this.transition("open");
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (this.state === "closed") {
|
|
170
|
+
this.pruneRecords();
|
|
171
|
+
if (this.records.length >= this.config.minimumRequests) {
|
|
172
|
+
const failures = this.records.filter((r) => !r.success).length;
|
|
173
|
+
const rate = failures / this.records.length;
|
|
174
|
+
if (rate >= this.config.failureThreshold) {
|
|
175
|
+
this.transition("open");
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/** Get current circuit state */
|
|
181
|
+
getState() {
|
|
182
|
+
if (this.state === "open") {
|
|
183
|
+
const elapsed = Date.now() - this.lastStateChange;
|
|
184
|
+
if (elapsed >= this.config.halfOpenAfterMs) {
|
|
185
|
+
this.transition("half-open");
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return this.state;
|
|
189
|
+
}
|
|
190
|
+
/** Get failure rate in current window */
|
|
191
|
+
getFailureRate() {
|
|
192
|
+
this.pruneRecords();
|
|
193
|
+
if (this.records.length === 0) return 0;
|
|
194
|
+
const failures = this.records.filter((r) => !r.success).length;
|
|
195
|
+
return failures / this.records.length;
|
|
196
|
+
}
|
|
197
|
+
/** Reset the circuit breaker */
|
|
198
|
+
reset() {
|
|
199
|
+
this.records = [];
|
|
200
|
+
this.transition("closed");
|
|
201
|
+
}
|
|
202
|
+
transition(newState) {
|
|
203
|
+
this.state = newState;
|
|
204
|
+
this.lastStateChange = Date.now();
|
|
205
|
+
}
|
|
206
|
+
pruneRecords() {
|
|
207
|
+
const cutoff = Date.now() - this.config.windowMs;
|
|
208
|
+
while (this.records.length > 0 && this.records[0].timestamp < cutoff) {
|
|
209
|
+
this.records.shift();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
function buildDegradationMessage() {
|
|
214
|
+
return "I'm having trouble right now. Let me get a human to help you.";
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/gateway/types.ts
|
|
218
|
+
var FULL_ACCESS = {
|
|
219
|
+
canRead: true,
|
|
220
|
+
canWrite: true,
|
|
221
|
+
canExecute: true
|
|
222
|
+
};
|
|
223
|
+
var READ_ONLY = {
|
|
224
|
+
canRead: true,
|
|
225
|
+
canWrite: false,
|
|
226
|
+
canExecute: false
|
|
227
|
+
};
|
|
228
|
+
var READ_TOOLS = [
|
|
229
|
+
"ask_team_memory",
|
|
230
|
+
"recall_my_memory",
|
|
231
|
+
"list_tasks",
|
|
232
|
+
"get_session_context",
|
|
233
|
+
"get_memory_by_id",
|
|
234
|
+
"list_reminders",
|
|
235
|
+
"query_conversations"
|
|
236
|
+
];
|
|
237
|
+
var WRITE_TOOLS = [
|
|
238
|
+
"create_task",
|
|
239
|
+
"update_task",
|
|
240
|
+
"send_message",
|
|
241
|
+
"store_memory",
|
|
242
|
+
"store_behavior",
|
|
243
|
+
"create_reminder",
|
|
244
|
+
"complete_reminder"
|
|
245
|
+
];
|
|
246
|
+
var EXECUTE_TOOLS = ["close_task"];
|
|
247
|
+
|
|
248
|
+
// src/gateway/permission-guard.ts
|
|
249
|
+
var TOOL_CLASSIFICATION = /* @__PURE__ */ new Map();
|
|
250
|
+
for (const tool of READ_TOOLS) TOOL_CLASSIFICATION.set(tool, "canRead");
|
|
251
|
+
for (const tool of WRITE_TOOLS) TOOL_CLASSIFICATION.set(tool, "canWrite");
|
|
252
|
+
for (const tool of EXECUTE_TOOLS) TOOL_CLASSIFICATION.set(tool, "canExecute");
|
|
253
|
+
function classifyTool(toolName) {
|
|
254
|
+
return TOOL_CLASSIFICATION.get(toolName) ?? null;
|
|
255
|
+
}
|
|
256
|
+
function checkToolPermission(toolName, permissions) {
|
|
257
|
+
const classification = classifyTool(toolName);
|
|
258
|
+
if (!classification) {
|
|
259
|
+
return {
|
|
260
|
+
allowed: false,
|
|
261
|
+
tool: toolName,
|
|
262
|
+
requiredPermission: "canExecute",
|
|
263
|
+
reason: `Unknown tool "${toolName}" is not in the allowed tool list`
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
const allowed = permissions[classification];
|
|
267
|
+
return {
|
|
268
|
+
allowed,
|
|
269
|
+
tool: toolName,
|
|
270
|
+
requiredPermission: classification,
|
|
271
|
+
reason: allowed ? void 0 : `Tool "${toolName}" requires ${classification} permission, which is not granted on this channel`
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
function guardToolUseBlocks(blocks, permissions) {
|
|
275
|
+
const allowed = [];
|
|
276
|
+
const blocked = [];
|
|
277
|
+
for (const block of blocks) {
|
|
278
|
+
const result = checkToolPermission(block.name, permissions);
|
|
279
|
+
if (result.allowed) {
|
|
280
|
+
allowed.push(block);
|
|
281
|
+
} else {
|
|
282
|
+
blocked.push({ block, check: result });
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return { allowed, blocked };
|
|
286
|
+
}
|
|
287
|
+
function checkSenderAllowlist(senderId, config) {
|
|
288
|
+
if (!config || config.allowlist.length === 0) {
|
|
289
|
+
return { allowed: true, mode: "none" };
|
|
290
|
+
}
|
|
291
|
+
const normalizedSender = senderId.trim().toLowerCase();
|
|
292
|
+
const isAllowed = config.allowlist.some(
|
|
293
|
+
(id) => id.trim().toLowerCase() === normalizedSender
|
|
294
|
+
);
|
|
295
|
+
if (isAllowed) {
|
|
296
|
+
return { allowed: true, mode: config.allowlist_mode };
|
|
297
|
+
}
|
|
298
|
+
if (config.allowlist_mode === "log") {
|
|
299
|
+
console.warn(
|
|
300
|
+
`[sender-allowlist] Unknown sender "${senderId}" \u2014 logging (mode=log)`
|
|
301
|
+
);
|
|
302
|
+
return { allowed: true, mode: "log", reason: "Unknown sender (logged)" };
|
|
303
|
+
}
|
|
304
|
+
return {
|
|
305
|
+
allowed: false,
|
|
306
|
+
mode: "strict",
|
|
307
|
+
reason: config.reject_message ?? "This service is not available to you."
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
var BOUNDARY_MARKER = "--- EXTERNAL USER MESSAGE (untrusted input) ---";
|
|
311
|
+
var INJECTION_PATTERNS = [
|
|
312
|
+
/ignore\s+(all\s+)?previous\s+instructions/gi,
|
|
313
|
+
/ignore\s+(all\s+)?prior\s+instructions/gi,
|
|
314
|
+
/you\s+are\s+now\b/gi,
|
|
315
|
+
/^system\s*:/gim,
|
|
316
|
+
/^ADMIN\s*:/gim,
|
|
317
|
+
/^DEVELOPER\s*:/gim,
|
|
318
|
+
/^ASSISTANT\s*:/gim,
|
|
319
|
+
/^HUMAN\s*:/gim,
|
|
320
|
+
/\bdo\s+not\s+follow\s+(your|the)\s+(rules|instructions|guidelines)\b/gi,
|
|
321
|
+
/\boverride\s+(your|all|the)\s+(rules|instructions|constraints)\b/gi,
|
|
322
|
+
/\bforget\s+(your|all|previous)\s+(rules|instructions|context)\b/gi,
|
|
323
|
+
/\bnew\s+instructions?\s*:/gi,
|
|
324
|
+
/\<\/?system\>/gi,
|
|
325
|
+
/\[\s*SYSTEM\s*\]/gi
|
|
326
|
+
];
|
|
327
|
+
function sanitizeInboundMessage(text) {
|
|
328
|
+
let sanitized = text;
|
|
329
|
+
for (const pattern of INJECTION_PATTERNS) {
|
|
330
|
+
pattern.lastIndex = 0;
|
|
331
|
+
sanitized = sanitized.replace(pattern, "[FILTERED]");
|
|
332
|
+
}
|
|
333
|
+
return `${BOUNDARY_MARKER}
|
|
334
|
+
${sanitized}`;
|
|
335
|
+
}
|
|
336
|
+
var OutboundRateLimiter = class {
|
|
337
|
+
contacts = /* @__PURE__ */ new Map();
|
|
338
|
+
maxPerMinute;
|
|
339
|
+
constructor(maxPerMinute = 5) {
|
|
340
|
+
this.maxPerMinute = maxPerMinute;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Check if an outbound message to this contact is allowed.
|
|
344
|
+
* Returns true if under the rate limit.
|
|
345
|
+
*/
|
|
346
|
+
check(contactId) {
|
|
347
|
+
const now = Date.now();
|
|
348
|
+
const windowMs = 6e4;
|
|
349
|
+
let entry = this.contacts.get(contactId);
|
|
350
|
+
if (!entry) {
|
|
351
|
+
entry = { timestamps: [] };
|
|
352
|
+
this.contacts.set(contactId, entry);
|
|
353
|
+
}
|
|
354
|
+
entry.timestamps = entry.timestamps.filter((t) => now - t < windowMs);
|
|
355
|
+
if (entry.timestamps.length >= this.maxPerMinute) {
|
|
356
|
+
return { allowed: false, remaining: 0 };
|
|
357
|
+
}
|
|
358
|
+
entry.timestamps.push(now);
|
|
359
|
+
return {
|
|
360
|
+
allowed: true,
|
|
361
|
+
remaining: this.maxPerMinute - entry.timestamps.length
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
/** Reset limiter state (for testing) */
|
|
365
|
+
reset() {
|
|
366
|
+
this.contacts.clear();
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
function buildPermissionContext(platform, permissions) {
|
|
370
|
+
if (permissions.canRead && permissions.canWrite && permissions.canExecute) {
|
|
371
|
+
return `[FULL ACCESS \u2014 you can read, write, and execute via ${platform}]`;
|
|
372
|
+
}
|
|
373
|
+
if (permissions.canRead && !permissions.canWrite && !permissions.canExecute) {
|
|
374
|
+
return `[READ-ONLY \u2014 this ${platform} channel cannot create tasks, send messages, or execute commands. Tell the founder to use Signal for commands.]`;
|
|
375
|
+
}
|
|
376
|
+
const parts = [];
|
|
377
|
+
if (permissions.canRead) parts.push("read");
|
|
378
|
+
if (permissions.canWrite) parts.push("write");
|
|
379
|
+
if (permissions.canExecute) parts.push("execute");
|
|
380
|
+
return `[${platform.toUpperCase()} \u2014 allowed: ${parts.join(", ")}]`;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// src/gateway/workflow-triggers.ts
|
|
384
|
+
var _patterns = [];
|
|
385
|
+
function matchPatterns(msg, eventType = "message.inbound") {
|
|
386
|
+
return _patterns.filter((p) => {
|
|
387
|
+
if (!p.enabled) return false;
|
|
388
|
+
if (p.eventType !== eventType && p.eventType !== "*") return false;
|
|
389
|
+
if (p.platformMatch && p.platformMatch !== msg.platform) return false;
|
|
390
|
+
if (p.senderMatch && p.senderMatch !== msg.senderId) return false;
|
|
391
|
+
if (p.textMatch && !msg.text.toLowerCase().includes(p.textMatch.toLowerCase())) return false;
|
|
392
|
+
return true;
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
async function processWorkflowTriggers(msg, eventType = "message.inbound") {
|
|
396
|
+
if (_patterns.length === 0) return 0;
|
|
397
|
+
const matches = matchPatterns(msg, eventType);
|
|
398
|
+
if (matches.length === 0) return 0;
|
|
399
|
+
let started = 0;
|
|
400
|
+
for (const pattern of matches) {
|
|
401
|
+
const defs = listWorkflowDefinitions();
|
|
402
|
+
const def = defs.find((d) => d.id === pattern.workflowDefinitionId);
|
|
403
|
+
if (!def) {
|
|
404
|
+
console.error(
|
|
405
|
+
`[workflow-triggers] Pattern "${pattern.name}" references unknown definition: ${pattern.workflowDefinitionId}`
|
|
406
|
+
);
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
try {
|
|
410
|
+
const instance = startWorkflow(pattern.workflowDefinitionId, {
|
|
411
|
+
platform: msg.platform,
|
|
412
|
+
senderId: msg.senderId,
|
|
413
|
+
senderName: msg.senderName,
|
|
414
|
+
text: msg.text,
|
|
415
|
+
channelId: msg.channelId,
|
|
416
|
+
messageId: msg.messageId,
|
|
417
|
+
timestamp: msg.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
418
|
+
triggerId: pattern.id,
|
|
419
|
+
triggerName: pattern.name
|
|
420
|
+
});
|
|
421
|
+
await runWorkflow(instance.id);
|
|
422
|
+
started++;
|
|
423
|
+
console.log(
|
|
424
|
+
`[workflow-triggers] Started workflow "${def.name}" (${instance.id}) from trigger "${pattern.name}"`
|
|
425
|
+
);
|
|
426
|
+
} catch (err) {
|
|
427
|
+
console.error(
|
|
428
|
+
`[workflow-triggers] Failed to start workflow for trigger "${pattern.name}":`,
|
|
429
|
+
err instanceof Error ? err.message : err
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return started;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// src/gateway/media-r2-bridge.ts
|
|
437
|
+
import { readFileSync } from "fs";
|
|
438
|
+
|
|
439
|
+
// src/lib/r2-media.ts
|
|
440
|
+
import {
|
|
441
|
+
S3Client,
|
|
442
|
+
PutObjectCommand,
|
|
443
|
+
GetObjectCommand,
|
|
444
|
+
DeleteObjectCommand,
|
|
445
|
+
HeadObjectCommand
|
|
446
|
+
} from "@aws-sdk/client-s3";
|
|
447
|
+
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
448
|
+
import { randomUUID } from "crypto";
|
|
449
|
+
import path from "path";
|
|
450
|
+
function loadR2Config() {
|
|
451
|
+
const bucket = process.env.R2_MEDIA_BUCKET?.trim();
|
|
452
|
+
const endpoint = process.env.R2_MEDIA_ENDPOINT?.trim();
|
|
453
|
+
const accessKeyId = process.env.R2_MEDIA_ACCESS_KEY?.trim();
|
|
454
|
+
const secretAccessKey = process.env.R2_MEDIA_SECRET_KEY?.trim();
|
|
455
|
+
if (!bucket || !endpoint || !accessKeyId || !secretAccessKey) return null;
|
|
456
|
+
return {
|
|
457
|
+
bucket,
|
|
458
|
+
endpoint,
|
|
459
|
+
accessKeyId,
|
|
460
|
+
secretAccessKey,
|
|
461
|
+
publicUrl: process.env.R2_MEDIA_PUBLIC_URL?.trim() || void 0
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
var _client = null;
|
|
465
|
+
var _config = null;
|
|
466
|
+
function getClient() {
|
|
467
|
+
if (_client && _config) return { client: _client, config: _config };
|
|
468
|
+
const config = loadR2Config();
|
|
469
|
+
if (!config) return null;
|
|
470
|
+
_client = new S3Client({
|
|
471
|
+
region: "auto",
|
|
472
|
+
endpoint: config.endpoint,
|
|
473
|
+
credentials: {
|
|
474
|
+
accessKeyId: config.accessKeyId,
|
|
475
|
+
secretAccessKey: config.secretAccessKey
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
_config = config;
|
|
479
|
+
return { client: _client, config: _config };
|
|
480
|
+
}
|
|
481
|
+
function isR2MediaConfigured() {
|
|
482
|
+
return loadR2Config() !== null;
|
|
483
|
+
}
|
|
484
|
+
async function uploadMedia(data, filename, product, contentType) {
|
|
485
|
+
const r2 = getClient();
|
|
486
|
+
if (!r2) return null;
|
|
487
|
+
const ext = path.extname(filename) || "";
|
|
488
|
+
const key = `media/${product}/${randomUUID()}${ext}`;
|
|
489
|
+
await r2.client.send(
|
|
490
|
+
new PutObjectCommand({
|
|
491
|
+
Bucket: r2.config.bucket,
|
|
492
|
+
Key: key,
|
|
493
|
+
Body: data,
|
|
494
|
+
ContentType: contentType || guessMimeType(ext)
|
|
495
|
+
})
|
|
496
|
+
);
|
|
497
|
+
const url = r2.config.publicUrl ? `${r2.config.publicUrl}/${key}` : await getPresignedUrl(key, 3600);
|
|
498
|
+
return { key, url, size: data.length };
|
|
499
|
+
}
|
|
500
|
+
async function getPresignedUrl(key, expiresIn = 3600) {
|
|
501
|
+
const r2 = getClient();
|
|
502
|
+
if (!r2) throw new Error("R2 media storage is not configured");
|
|
503
|
+
return getSignedUrl(
|
|
504
|
+
r2.client,
|
|
505
|
+
new GetObjectCommand({
|
|
506
|
+
Bucket: r2.config.bucket,
|
|
507
|
+
Key: key
|
|
508
|
+
}),
|
|
509
|
+
{ expiresIn }
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
function guessMimeType(ext) {
|
|
513
|
+
const map = {
|
|
514
|
+
".jpg": "image/jpeg",
|
|
515
|
+
".jpeg": "image/jpeg",
|
|
516
|
+
".png": "image/png",
|
|
517
|
+
".gif": "image/gif",
|
|
518
|
+
".webp": "image/webp",
|
|
519
|
+
".svg": "image/svg+xml",
|
|
520
|
+
".mp4": "video/mp4",
|
|
521
|
+
".webm": "video/webm",
|
|
522
|
+
".mp3": "audio/mpeg",
|
|
523
|
+
".wav": "audio/wav",
|
|
524
|
+
".pdf": "application/pdf",
|
|
525
|
+
".json": "application/json",
|
|
526
|
+
".txt": "text/plain"
|
|
527
|
+
};
|
|
528
|
+
return map[ext.toLowerCase()] || "application/octet-stream";
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// src/gateway/media-r2-bridge.ts
|
|
532
|
+
async function uploadMediaAttachments(msg) {
|
|
533
|
+
if (!isR2MediaConfigured()) return 0;
|
|
534
|
+
if (!msg.media || msg.media.length === 0) return 0;
|
|
535
|
+
let uploaded = 0;
|
|
536
|
+
for (const attachment of msg.media) {
|
|
537
|
+
if (attachment.url) continue;
|
|
538
|
+
if (!attachment.localPath) continue;
|
|
539
|
+
try {
|
|
540
|
+
const data = readFileSync(attachment.localPath);
|
|
541
|
+
const filename = attachment.fileName ?? `media.${attachment.type}`;
|
|
542
|
+
const result = await uploadMedia(data, filename, msg.platform);
|
|
543
|
+
if (result) {
|
|
544
|
+
attachment.url = result.url;
|
|
545
|
+
uploaded++;
|
|
546
|
+
}
|
|
547
|
+
} catch (err) {
|
|
548
|
+
console.error(
|
|
549
|
+
`[media-r2-bridge] Failed to upload ${attachment.localPath}:`,
|
|
550
|
+
err instanceof Error ? err.message : err
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return uploaded;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// src/gateway/gateway.ts
|
|
558
|
+
var Gateway = class {
|
|
559
|
+
config;
|
|
560
|
+
adapters = /* @__PURE__ */ new Map();
|
|
561
|
+
platformConfigs;
|
|
562
|
+
botRegistry;
|
|
563
|
+
rateLimiter;
|
|
564
|
+
sessionStore;
|
|
565
|
+
analytics;
|
|
566
|
+
alertMonitor;
|
|
567
|
+
failover;
|
|
568
|
+
customerStore;
|
|
569
|
+
startedAt = 0;
|
|
570
|
+
outboundLimiters = /* @__PURE__ */ new Map();
|
|
571
|
+
/** Queued outbound messages awaiting coordinator approval (Tier 3) */
|
|
572
|
+
outboundQueue = [];
|
|
573
|
+
constructor(options) {
|
|
574
|
+
this.config = options.config;
|
|
575
|
+
this.platformConfigs = options.platformConfigs;
|
|
576
|
+
this.botRegistry = options.botRegistry;
|
|
577
|
+
this.rateLimiter = options.rateLimiter;
|
|
578
|
+
this.sessionStore = options.sessionStore;
|
|
579
|
+
this.analytics = options.analytics;
|
|
580
|
+
this.alertMonitor = options.alertMonitor;
|
|
581
|
+
this.failover = options.failover;
|
|
582
|
+
this.customerStore = options.customerStore;
|
|
583
|
+
}
|
|
584
|
+
registerAdapter(adapter) {
|
|
585
|
+
this.adapters.set(adapter.platform, adapter);
|
|
586
|
+
adapter.onMessage((msg) => this.handleMessage(msg));
|
|
587
|
+
}
|
|
588
|
+
async start() {
|
|
589
|
+
this.startedAt = Date.now();
|
|
590
|
+
initCRMBridge();
|
|
591
|
+
const startPromises = [];
|
|
592
|
+
for (const [platform, adapter] of this.adapters) {
|
|
593
|
+
const config = this.platformConfigs.get(platform);
|
|
594
|
+
if (!config) {
|
|
595
|
+
console.error(`[gateway] No config for platform: ${platform}`);
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
console.log(`[gateway] Connecting ${platform}...`);
|
|
599
|
+
startPromises.push(
|
|
600
|
+
adapter.connect(config).then(() => {
|
|
601
|
+
console.log(`[gateway] ${platform} connected`);
|
|
602
|
+
}).catch((err) => {
|
|
603
|
+
console.error(`[gateway] ${platform} connection failed:`, err);
|
|
604
|
+
this.alertMonitor?.alertAdapterDisconnected(
|
|
605
|
+
platform,
|
|
606
|
+
err instanceof Error ? err.message : String(err)
|
|
607
|
+
);
|
|
608
|
+
})
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
await Promise.allSettled(startPromises);
|
|
612
|
+
console.log(
|
|
613
|
+
`[gateway] Started with ${this.botRegistry.list().length} bots: ${this.botRegistry.list().join(", ")}`
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
async stop() {
|
|
617
|
+
const stopPromises = [...this.adapters.values()].map(
|
|
618
|
+
(a) => a.disconnect()
|
|
619
|
+
);
|
|
620
|
+
await Promise.allSettled(stopPromises);
|
|
621
|
+
console.log("[gateway] Stopped");
|
|
622
|
+
}
|
|
623
|
+
async handleMessage(msg) {
|
|
624
|
+
const start = Date.now();
|
|
625
|
+
if (this.rateLimiter) {
|
|
626
|
+
const limitResult = this.rateLimiter.check(msg.senderId);
|
|
627
|
+
if (!limitResult.allowed) {
|
|
628
|
+
const adapter2 = this.adapters.get(msg.platform);
|
|
629
|
+
if (adapter2) {
|
|
630
|
+
await adapter2.sendText(msg.channelId, limitResult.reason ?? "Please slow down.");
|
|
631
|
+
}
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
const platformConfig = this.platformConfigs.get(msg.platform);
|
|
636
|
+
const senderCheck = checkSenderAllowlist(
|
|
637
|
+
msg.senderId,
|
|
638
|
+
platformConfig?.senderAllowlist
|
|
639
|
+
);
|
|
640
|
+
if (!senderCheck.allowed) {
|
|
641
|
+
console.warn(
|
|
642
|
+
`[gateway] Sender rejected by allowlist: ${msg.senderId} on ${msg.platform}`
|
|
643
|
+
);
|
|
644
|
+
const adapter2 = this.adapters.get(msg.platform);
|
|
645
|
+
if (adapter2 && senderCheck.reason) {
|
|
646
|
+
await adapter2.sendText(msg.channelId, senderCheck.reason);
|
|
647
|
+
}
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
msg.text = sanitizeInboundMessage(msg.text);
|
|
651
|
+
this.customerStore?.resolve(msg.platform, msg.senderId);
|
|
652
|
+
uploadMediaAttachments(msg).catch((err) => {
|
|
653
|
+
console.error("[gateway] Media R2 upload error:", err);
|
|
654
|
+
});
|
|
655
|
+
ingest(msg).catch((err) => {
|
|
656
|
+
console.error("[gateway] Pipeline inbound ingest error:", err);
|
|
657
|
+
});
|
|
658
|
+
processWorkflowTriggers(msg).catch((err) => {
|
|
659
|
+
console.error("[gateway] Workflow trigger error:", err);
|
|
660
|
+
});
|
|
661
|
+
const route = routeMessage(msg, this.config);
|
|
662
|
+
console.log(
|
|
663
|
+
`[gateway] ${msg.platform}/${msg.senderId} \u2192 ${route.employee} (${route.routeName})`
|
|
664
|
+
);
|
|
665
|
+
orgBus.emit({
|
|
666
|
+
type: "gateway_message",
|
|
667
|
+
platform: msg.platform,
|
|
668
|
+
senderId: msg.senderId,
|
|
669
|
+
botId: route.employee,
|
|
670
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
671
|
+
});
|
|
672
|
+
const bot = this.botRegistry.get(route.employee);
|
|
673
|
+
if (!bot) {
|
|
674
|
+
console.error(`[gateway] No bot registered for target: ${route.employee}`);
|
|
675
|
+
const adapter2 = this.adapters.get(msg.platform);
|
|
676
|
+
if (adapter2) {
|
|
677
|
+
await adapter2.sendText(msg.channelId, "Sorry, I'm not available right now.");
|
|
678
|
+
}
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
const adapter = this.adapters.get(msg.platform);
|
|
682
|
+
if (!adapter) return;
|
|
683
|
+
await adapter.sendTyping(msg.channelId);
|
|
684
|
+
const session = this.sessionStore?.getOrCreate(
|
|
685
|
+
msg.senderId,
|
|
686
|
+
route.employee,
|
|
687
|
+
msg.platform
|
|
688
|
+
);
|
|
689
|
+
if (session) {
|
|
690
|
+
this.sessionStore.addMessage(session.sessionId, {
|
|
691
|
+
role: "user",
|
|
692
|
+
content: msg.text
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
if (session?.messageCount === 1) {
|
|
696
|
+
this.analytics?.conversationStarted(msg.platform, route.employee);
|
|
697
|
+
}
|
|
698
|
+
try {
|
|
699
|
+
const response = await bot.processMessage(msg, route.permissions);
|
|
700
|
+
const latencyMs = Date.now() - start;
|
|
701
|
+
if (session) {
|
|
702
|
+
this.sessionStore.addMessage(session.sessionId, {
|
|
703
|
+
role: "assistant",
|
|
704
|
+
content: response
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
this.analytics?.responseRecorded(
|
|
708
|
+
msg.platform,
|
|
709
|
+
route.employee,
|
|
710
|
+
latencyMs,
|
|
711
|
+
0,
|
|
712
|
+
// Token counts not available from BotRuntime yet
|
|
713
|
+
0,
|
|
714
|
+
"anthropic",
|
|
715
|
+
true
|
|
716
|
+
);
|
|
717
|
+
this.alertMonitor?.recordEvent(latencyMs, true);
|
|
718
|
+
const outboundConfig = platformConfig?.outboundConfirm;
|
|
719
|
+
if (outboundConfig?.enabled) {
|
|
720
|
+
this.outboundQueue.push({
|
|
721
|
+
platform: msg.platform,
|
|
722
|
+
channelId: msg.channelId,
|
|
723
|
+
text: response,
|
|
724
|
+
replyToMessageId: msg.messageId,
|
|
725
|
+
queuedAt: Date.now()
|
|
726
|
+
});
|
|
727
|
+
console.log(
|
|
728
|
+
`[gateway] Outbound queued for approval (${this.outboundQueue.length} pending): ${msg.platform}/${msg.channelId}`
|
|
729
|
+
);
|
|
730
|
+
} else {
|
|
731
|
+
let limiter = this.outboundLimiters.get(msg.platform);
|
|
732
|
+
if (!limiter) {
|
|
733
|
+
const maxPerMin = outboundConfig?.rate_limit_per_minute ?? 5;
|
|
734
|
+
limiter = new OutboundRateLimiter(maxPerMin);
|
|
735
|
+
this.outboundLimiters.set(msg.platform, limiter);
|
|
736
|
+
}
|
|
737
|
+
const rateCheck = limiter.check(msg.senderId);
|
|
738
|
+
if (!rateCheck.allowed) {
|
|
739
|
+
console.warn(
|
|
740
|
+
`[gateway] Outbound rate-limited for ${msg.senderId} on ${msg.platform}`
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
await adapter.sendText(msg.channelId, response, {
|
|
744
|
+
replyToMessageId: msg.messageId
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
ingest(msg, response, route.employee).catch((err) => {
|
|
748
|
+
console.error("[gateway] Pipeline conversation ingest error:", err);
|
|
749
|
+
});
|
|
750
|
+
} catch (err) {
|
|
751
|
+
const latencyMs = Date.now() - start;
|
|
752
|
+
this.alertMonitor?.recordEvent(latencyMs, false);
|
|
753
|
+
this.analytics?.responseRecorded(
|
|
754
|
+
msg.platform,
|
|
755
|
+
route.employee,
|
|
756
|
+
latencyMs,
|
|
757
|
+
0,
|
|
758
|
+
0,
|
|
759
|
+
"anthropic",
|
|
760
|
+
false
|
|
761
|
+
);
|
|
762
|
+
console.error(`[gateway] Error processing message:`, err);
|
|
763
|
+
await adapter.sendText(
|
|
764
|
+
msg.channelId,
|
|
765
|
+
buildDegradationMessage()
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
/** Get pending outbound messages awaiting approval (Tier 3) */
|
|
770
|
+
getOutboundQueue() {
|
|
771
|
+
return this.outboundQueue.map((item, index) => ({
|
|
772
|
+
platform: item.platform,
|
|
773
|
+
channelId: item.channelId,
|
|
774
|
+
text: item.text,
|
|
775
|
+
queuedAt: item.queuedAt,
|
|
776
|
+
index
|
|
777
|
+
}));
|
|
778
|
+
}
|
|
779
|
+
/** Approve and send a queued outbound message (Tier 3) */
|
|
780
|
+
async approveOutbound(index) {
|
|
781
|
+
if (index < 0 || index >= this.outboundQueue.length) return false;
|
|
782
|
+
const item = this.outboundQueue[index];
|
|
783
|
+
const adapter = this.adapters.get(item.platform);
|
|
784
|
+
if (!adapter) return false;
|
|
785
|
+
await adapter.sendText(item.channelId, item.text, {
|
|
786
|
+
replyToMessageId: item.replyToMessageId
|
|
787
|
+
});
|
|
788
|
+
this.outboundQueue.splice(index, 1);
|
|
789
|
+
return true;
|
|
790
|
+
}
|
|
791
|
+
/** Reject a queued outbound message (Tier 3) */
|
|
792
|
+
rejectOutbound(index) {
|
|
793
|
+
if (index < 0 || index >= this.outboundQueue.length) return false;
|
|
794
|
+
this.outboundQueue.splice(index, 1);
|
|
795
|
+
return true;
|
|
796
|
+
}
|
|
797
|
+
/** Health check all adapters + provider health */
|
|
798
|
+
async healthCheck() {
|
|
799
|
+
const adapterHealth = /* @__PURE__ */ new Map();
|
|
800
|
+
for (const [platform, adapter] of this.adapters) {
|
|
801
|
+
adapterHealth.set(platform, await adapter.healthCheck());
|
|
802
|
+
}
|
|
803
|
+
return {
|
|
804
|
+
adapters: adapterHealth,
|
|
805
|
+
providers: this.failover?.getProviderHealth(),
|
|
806
|
+
bots: this.botRegistry.list(),
|
|
807
|
+
uptime: this.startedAt > 0 ? Date.now() - this.startedAt : 0,
|
|
808
|
+
sessions: this.sessionStore?.stats(),
|
|
809
|
+
alerts: this.alertMonitor?.getActiveAlerts().length
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
// src/gateway/bot-errors.ts
|
|
815
|
+
var FatalBotError = class extends Error {
|
|
816
|
+
constructor(message, cause) {
|
|
817
|
+
super(message);
|
|
818
|
+
this.cause = cause;
|
|
819
|
+
this.name = "FatalBotError";
|
|
820
|
+
}
|
|
821
|
+
cause;
|
|
822
|
+
fatal = true;
|
|
823
|
+
};
|
|
824
|
+
var RecoverableBotError = class extends Error {
|
|
825
|
+
constructor(message, toolName, cause) {
|
|
826
|
+
super(message);
|
|
827
|
+
this.toolName = toolName;
|
|
828
|
+
this.cause = cause;
|
|
829
|
+
this.name = "RecoverableBotError";
|
|
830
|
+
}
|
|
831
|
+
toolName;
|
|
832
|
+
cause;
|
|
833
|
+
recoverable = true;
|
|
834
|
+
};
|
|
835
|
+
var MaxStepsError = class extends Error {
|
|
836
|
+
constructor(stepsTaken, maxSteps) {
|
|
837
|
+
super(
|
|
838
|
+
`Reached maximum steps (${stepsTaken}/${maxSteps}). Returning partial result.`
|
|
839
|
+
);
|
|
840
|
+
this.stepsTaken = stepsTaken;
|
|
841
|
+
this.maxSteps = maxSteps;
|
|
842
|
+
this.name = "MaxStepsError";
|
|
843
|
+
}
|
|
844
|
+
stepsTaken;
|
|
845
|
+
maxSteps;
|
|
846
|
+
};
|
|
847
|
+
function classifyError(err, toolName) {
|
|
848
|
+
if (err instanceof FatalBotError) return err;
|
|
849
|
+
if (err instanceof RecoverableBotError) return err;
|
|
850
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
851
|
+
if (message.includes("401") || message.includes("403") || message.includes("authentication") || message.includes("rate_limit")) {
|
|
852
|
+
return new FatalBotError(message, err);
|
|
853
|
+
}
|
|
854
|
+
return new RecoverableBotError(message, toolName, err);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// src/gateway/bot-runtime.ts
|
|
858
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
859
|
+
var DEFAULT_MODEL = "claude-sonnet-4-20250514";
|
|
860
|
+
var MAX_TURNS = 10;
|
|
861
|
+
var MAX_HISTORY = 50;
|
|
862
|
+
var DEFAULT_PLANNING_INTERVAL = 3;
|
|
863
|
+
function buildExecAssistantSystemPrompt(platform, permissions) {
|
|
864
|
+
const permContext = buildPermissionContext(platform, permissions);
|
|
865
|
+
return `You are the founder's executive assistant (agent_id: "ea").
|
|
866
|
+
|
|
867
|
+
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.
|
|
868
|
+
|
|
869
|
+
${permContext}
|
|
870
|
+
|
|
871
|
+
Your personality:
|
|
872
|
+
- Direct and concise \u2014 the founder is busy
|
|
873
|
+
- Lead with the answer, not the process
|
|
874
|
+
- When summarizing team work, be specific: names, counts, what shipped
|
|
875
|
+
- If you don't know something, say so and suggest who might
|
|
876
|
+
|
|
877
|
+
Your tools:
|
|
878
|
+
- ask_team_memory: Query any employee's memories by their configured name
|
|
879
|
+
- recall_my_memory: Search your own past conversations with the founder
|
|
880
|
+
- list_tasks: See what's in progress across the org
|
|
881
|
+
- get_session_context: Get context from a specific session
|
|
882
|
+
- list_reminders: Check active reminders
|
|
883
|
+
|
|
884
|
+
${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."}`;
|
|
885
|
+
}
|
|
886
|
+
function filterToolsForPermissions(tools, permissions) {
|
|
887
|
+
const allAllowed = /* @__PURE__ */ new Set();
|
|
888
|
+
if (permissions.canRead) {
|
|
889
|
+
for (const t of READ_TOOLS) allAllowed.add(t);
|
|
890
|
+
}
|
|
891
|
+
if (permissions.canWrite) {
|
|
892
|
+
for (const t of WRITE_TOOLS) allAllowed.add(t);
|
|
893
|
+
}
|
|
894
|
+
if (permissions.canExecute) {
|
|
895
|
+
for (const t of EXECUTE_TOOLS) allAllowed.add(t);
|
|
896
|
+
}
|
|
897
|
+
return tools.filter((t) => allAllowed.has(t.name));
|
|
898
|
+
}
|
|
899
|
+
var BotRuntime = class {
|
|
900
|
+
client;
|
|
901
|
+
config;
|
|
902
|
+
conversations = /* @__PURE__ */ new Map();
|
|
903
|
+
constructor(config) {
|
|
904
|
+
this.config = config;
|
|
905
|
+
this.client = new Anthropic({ apiKey: config.apiKey });
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* Process an incoming message and return the text response.
|
|
909
|
+
* Handles multi-turn tool use internally.
|
|
910
|
+
*/
|
|
911
|
+
async processMessage(msg, permissions) {
|
|
912
|
+
const sessionKey = msg.chatType === "group" ? msg.channelId : msg.senderId;
|
|
913
|
+
const history = this.getHistory(sessionKey);
|
|
914
|
+
history.push({ role: "user", content: msg.text, frameType: "task" });
|
|
915
|
+
const systemPrompt = this.config.systemPrompt + "\n\n" + buildPermissionContext(msg.platform, permissions);
|
|
916
|
+
const allowedTools = filterToolsForPermissions(
|
|
917
|
+
this.config.tools,
|
|
918
|
+
permissions
|
|
919
|
+
);
|
|
920
|
+
const model = this.config.model ?? DEFAULT_MODEL;
|
|
921
|
+
const maxTurns = this.config.maxTurns ?? MAX_TURNS;
|
|
922
|
+
const planningInterval = this.config.planningInterval ?? DEFAULT_PLANNING_INTERVAL;
|
|
923
|
+
let turns = 0;
|
|
924
|
+
while (turns < maxTurns) {
|
|
925
|
+
turns++;
|
|
926
|
+
if (planningInterval > 0 && turns > 1 && turns % planningInterval === 1 && maxTurns - turns >= 2) {
|
|
927
|
+
history.push({
|
|
928
|
+
role: "user",
|
|
929
|
+
content: "[Planning checkpoint] Review what you know so far. What facts have you gathered? What is your plan for the remaining steps? Be concise.",
|
|
930
|
+
frameType: "planning"
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
let response;
|
|
934
|
+
try {
|
|
935
|
+
response = await this.client.messages.create({
|
|
936
|
+
model,
|
|
937
|
+
max_tokens: 4096,
|
|
938
|
+
system: systemPrompt,
|
|
939
|
+
messages: history.map((m) => ({
|
|
940
|
+
role: m.role,
|
|
941
|
+
content: m.content
|
|
942
|
+
})),
|
|
943
|
+
tools: allowedTools.map((t) => ({
|
|
944
|
+
name: t.name,
|
|
945
|
+
description: t.description,
|
|
946
|
+
input_schema: t.input_schema
|
|
947
|
+
}))
|
|
948
|
+
});
|
|
949
|
+
} catch (err) {
|
|
950
|
+
const classified = classifyError(err);
|
|
951
|
+
if (classified instanceof FatalBotError) {
|
|
952
|
+
const errorMsg = `Bot error: ${classified.message}`;
|
|
953
|
+
history.push({ role: "assistant", content: errorMsg, frameType: "error" });
|
|
954
|
+
this.trimHistory(sessionKey);
|
|
955
|
+
return errorMsg;
|
|
956
|
+
}
|
|
957
|
+
history.push({
|
|
958
|
+
role: "assistant",
|
|
959
|
+
content: `[API error \u2014 retrying] ${classified.message}`,
|
|
960
|
+
frameType: "error"
|
|
961
|
+
});
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
964
|
+
const toolUseBlocks = response.content.filter(
|
|
965
|
+
(b) => b.type === "tool_use"
|
|
966
|
+
);
|
|
967
|
+
if (toolUseBlocks.length === 0) {
|
|
968
|
+
const textContent = response.content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
|
|
969
|
+
history.push({ role: "assistant", content: textContent, frameType: "assistant" });
|
|
970
|
+
this.trimHistory(sessionKey);
|
|
971
|
+
return textContent;
|
|
972
|
+
}
|
|
973
|
+
const { allowed, blocked } = guardToolUseBlocks(
|
|
974
|
+
toolUseBlocks,
|
|
975
|
+
permissions
|
|
976
|
+
);
|
|
977
|
+
history.push({
|
|
978
|
+
role: "assistant",
|
|
979
|
+
content: response.content,
|
|
980
|
+
frameType: "assistant"
|
|
981
|
+
});
|
|
982
|
+
const toolResults = [];
|
|
983
|
+
let hadFatalError = false;
|
|
984
|
+
for (const block of allowed) {
|
|
985
|
+
try {
|
|
986
|
+
const result = await this.config.toolExecutor(
|
|
987
|
+
block.name,
|
|
988
|
+
block.input
|
|
989
|
+
);
|
|
990
|
+
toolResults.push({
|
|
991
|
+
type: "tool_result",
|
|
992
|
+
tool_use_id: block.id,
|
|
993
|
+
content: result
|
|
994
|
+
});
|
|
995
|
+
} catch (err) {
|
|
996
|
+
const classified = classifyError(err, block.name);
|
|
997
|
+
if (classified instanceof FatalBotError) {
|
|
998
|
+
toolResults.push({
|
|
999
|
+
type: "tool_result",
|
|
1000
|
+
tool_use_id: block.id,
|
|
1001
|
+
content: `Fatal error: ${classified.message}`,
|
|
1002
|
+
is_error: true
|
|
1003
|
+
});
|
|
1004
|
+
hadFatalError = true;
|
|
1005
|
+
} else {
|
|
1006
|
+
toolResults.push({
|
|
1007
|
+
type: "tool_result",
|
|
1008
|
+
tool_use_id: block.id,
|
|
1009
|
+
content: `Error (recoverable): ${classified.message}`,
|
|
1010
|
+
is_error: true
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
for (const { block, check } of blocked) {
|
|
1016
|
+
toolResults.push({
|
|
1017
|
+
type: "tool_result",
|
|
1018
|
+
tool_use_id: block.id,
|
|
1019
|
+
content: `Permission denied: ${check.reason}`,
|
|
1020
|
+
is_error: true
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
history.push({
|
|
1024
|
+
role: "user",
|
|
1025
|
+
content: toolResults,
|
|
1026
|
+
frameType: hadFatalError ? "error" : "tool_result"
|
|
1027
|
+
});
|
|
1028
|
+
if (hadFatalError) {
|
|
1029
|
+
this.trimHistory(sessionKey);
|
|
1030
|
+
return `A fatal error occurred during tool execution. The bot loop has been stopped.`;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
const maxErr = new MaxStepsError(turns, maxTurns);
|
|
1034
|
+
history.push({
|
|
1035
|
+
role: "assistant",
|
|
1036
|
+
content: maxErr.message,
|
|
1037
|
+
frameType: "error"
|
|
1038
|
+
});
|
|
1039
|
+
this.trimHistory(sessionKey);
|
|
1040
|
+
return maxErr.message;
|
|
1041
|
+
}
|
|
1042
|
+
getHistory(sessionKey) {
|
|
1043
|
+
if (!this.conversations.has(sessionKey)) {
|
|
1044
|
+
this.conversations.set(sessionKey, []);
|
|
1045
|
+
}
|
|
1046
|
+
return this.conversations.get(sessionKey);
|
|
1047
|
+
}
|
|
1048
|
+
trimHistory(sessionKey) {
|
|
1049
|
+
const history = this.conversations.get(sessionKey);
|
|
1050
|
+
if (!history || history.length <= MAX_HISTORY) return;
|
|
1051
|
+
const firstTaskIdx = history.findIndex((m) => m.frameType === "task");
|
|
1052
|
+
let trimmed = history.filter(
|
|
1053
|
+
(m, i) => m.frameType !== "planning" || i >= history.length - MAX_HISTORY
|
|
1054
|
+
);
|
|
1055
|
+
if (trimmed.length > MAX_HISTORY) {
|
|
1056
|
+
const tail = trimmed.slice(-MAX_HISTORY);
|
|
1057
|
+
if (firstTaskIdx >= 0 && !tail.includes(history[firstTaskIdx])) {
|
|
1058
|
+
tail[0] = history[firstTaskIdx];
|
|
1059
|
+
}
|
|
1060
|
+
trimmed = tail;
|
|
1061
|
+
}
|
|
1062
|
+
this.conversations.set(sessionKey, trimmed);
|
|
1063
|
+
}
|
|
1064
|
+
/** Clear conversation history for a session */
|
|
1065
|
+
clearHistory(sessionKey) {
|
|
1066
|
+
this.conversations.delete(sessionKey);
|
|
1067
|
+
}
|
|
1068
|
+
};
|
|
1069
|
+
function buildExecAssistantTools() {
|
|
1070
|
+
return [
|
|
1071
|
+
{
|
|
1072
|
+
name: "ask_team_memory",
|
|
1073
|
+
description: "Search another employee's memories. Use this to find what a team member worked on, learned, or solved.",
|
|
1074
|
+
input_schema: {
|
|
1075
|
+
type: "object",
|
|
1076
|
+
properties: {
|
|
1077
|
+
team_member: {
|
|
1078
|
+
type: "string",
|
|
1079
|
+
description: "Configured name of the team member"
|
|
1080
|
+
},
|
|
1081
|
+
query: { type: "string", description: "What to search for" },
|
|
1082
|
+
project_name: {
|
|
1083
|
+
type: "string",
|
|
1084
|
+
description: "Filter by project name"
|
|
1085
|
+
},
|
|
1086
|
+
limit: { type: "number", description: "Max results (default 10)" }
|
|
1087
|
+
},
|
|
1088
|
+
required: ["team_member", "query"]
|
|
1089
|
+
}
|
|
1090
|
+
},
|
|
1091
|
+
{
|
|
1092
|
+
name: "recall_my_memory",
|
|
1093
|
+
description: "Search your own past memories using semantic search. Returns memory IDs; use get_memory_by_id for full long records.",
|
|
1094
|
+
input_schema: {
|
|
1095
|
+
type: "object",
|
|
1096
|
+
properties: {
|
|
1097
|
+
query: { type: "string", description: "What to search for" },
|
|
1098
|
+
project_name: {
|
|
1099
|
+
type: "string",
|
|
1100
|
+
description: "Filter by project name"
|
|
1101
|
+
},
|
|
1102
|
+
limit: { type: "number", description: "Max results (default 10)" },
|
|
1103
|
+
body_chars: { type: "number", description: "Max characters per memory body, up to 50000" }
|
|
1104
|
+
},
|
|
1105
|
+
required: ["query"]
|
|
1106
|
+
}
|
|
1107
|
+
},
|
|
1108
|
+
{
|
|
1109
|
+
name: "get_memory_by_id",
|
|
1110
|
+
description: "Fetch one memory by ID with the full untruncated raw_text body.",
|
|
1111
|
+
input_schema: {
|
|
1112
|
+
type: "object",
|
|
1113
|
+
properties: {
|
|
1114
|
+
id: { type: "string", description: "Memory UUID from recall_my_memory" }
|
|
1115
|
+
},
|
|
1116
|
+
required: ["id"]
|
|
1117
|
+
}
|
|
1118
|
+
},
|
|
1119
|
+
{
|
|
1120
|
+
name: "list_tasks",
|
|
1121
|
+
description: "List tasks across the organization.",
|
|
1122
|
+
input_schema: {
|
|
1123
|
+
type: "object",
|
|
1124
|
+
properties: {
|
|
1125
|
+
assigned_to: {
|
|
1126
|
+
type: "string",
|
|
1127
|
+
description: "Filter by assignee"
|
|
1128
|
+
},
|
|
1129
|
+
status: {
|
|
1130
|
+
type: "string",
|
|
1131
|
+
description: "Filter by status (open, in_progress, done)"
|
|
1132
|
+
},
|
|
1133
|
+
project_name: {
|
|
1134
|
+
type: "string",
|
|
1135
|
+
description: "Filter by project"
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
},
|
|
1140
|
+
{
|
|
1141
|
+
name: "get_session_context",
|
|
1142
|
+
description: "Get context from a specific session.",
|
|
1143
|
+
input_schema: {
|
|
1144
|
+
type: "object",
|
|
1145
|
+
properties: {
|
|
1146
|
+
session_key: {
|
|
1147
|
+
type: "string",
|
|
1148
|
+
description: "Session key to look up"
|
|
1149
|
+
}
|
|
1150
|
+
},
|
|
1151
|
+
required: ["session_key"]
|
|
1152
|
+
}
|
|
1153
|
+
},
|
|
1154
|
+
{
|
|
1155
|
+
name: "list_reminders",
|
|
1156
|
+
description: "List active reminders.",
|
|
1157
|
+
input_schema: {
|
|
1158
|
+
type: "object",
|
|
1159
|
+
properties: {
|
|
1160
|
+
status: {
|
|
1161
|
+
type: "string",
|
|
1162
|
+
description: "Filter by status"
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
},
|
|
1167
|
+
// Write tools (only available on Signal with full access)
|
|
1168
|
+
{
|
|
1169
|
+
name: "create_task",
|
|
1170
|
+
description: "Create a new task and assign it to an employee.",
|
|
1171
|
+
input_schema: {
|
|
1172
|
+
type: "object",
|
|
1173
|
+
properties: {
|
|
1174
|
+
title: { type: "string", description: "Task title" },
|
|
1175
|
+
description: { type: "string", description: "Task description" },
|
|
1176
|
+
assigned_to: {
|
|
1177
|
+
type: "string",
|
|
1178
|
+
description: "Employee to assign to"
|
|
1179
|
+
},
|
|
1180
|
+
priority: {
|
|
1181
|
+
type: "string",
|
|
1182
|
+
enum: ["p0", "p1", "p2"],
|
|
1183
|
+
description: "Task priority"
|
|
1184
|
+
},
|
|
1185
|
+
project_name: { type: "string", description: "Project name" }
|
|
1186
|
+
},
|
|
1187
|
+
required: ["title", "assigned_to"]
|
|
1188
|
+
}
|
|
1189
|
+
},
|
|
1190
|
+
{
|
|
1191
|
+
name: "send_message",
|
|
1192
|
+
description: "Send a message to an employee.",
|
|
1193
|
+
input_schema: {
|
|
1194
|
+
type: "object",
|
|
1195
|
+
properties: {
|
|
1196
|
+
to: { type: "string", description: "Employee name" },
|
|
1197
|
+
message: { type: "string", description: "Message content" }
|
|
1198
|
+
},
|
|
1199
|
+
required: ["to", "message"]
|
|
1200
|
+
}
|
|
1201
|
+
},
|
|
1202
|
+
{
|
|
1203
|
+
name: "store_memory",
|
|
1204
|
+
description: "Store a memory for future reference.",
|
|
1205
|
+
input_schema: {
|
|
1206
|
+
type: "object",
|
|
1207
|
+
properties: {
|
|
1208
|
+
content: { type: "string", description: "Memory content" },
|
|
1209
|
+
project_name: { type: "string", description: "Project name" }
|
|
1210
|
+
},
|
|
1211
|
+
required: ["content"]
|
|
1212
|
+
}
|
|
1213
|
+
},
|
|
1214
|
+
{
|
|
1215
|
+
name: "store_behavior",
|
|
1216
|
+
description: "Store a behavioral directive.",
|
|
1217
|
+
input_schema: {
|
|
1218
|
+
type: "object",
|
|
1219
|
+
properties: {
|
|
1220
|
+
content: {
|
|
1221
|
+
type: "string",
|
|
1222
|
+
description: "Behavioral directive content"
|
|
1223
|
+
}
|
|
1224
|
+
},
|
|
1225
|
+
required: ["content"]
|
|
1226
|
+
}
|
|
1227
|
+
},
|
|
1228
|
+
{
|
|
1229
|
+
name: "update_task",
|
|
1230
|
+
description: "Update a task's status.",
|
|
1231
|
+
input_schema: {
|
|
1232
|
+
type: "object",
|
|
1233
|
+
properties: {
|
|
1234
|
+
task_id: { type: "string", description: "Task ID" },
|
|
1235
|
+
status: {
|
|
1236
|
+
type: "string",
|
|
1237
|
+
enum: ["open", "in_progress", "done", "blocked", "cancelled"],
|
|
1238
|
+
description: "New status"
|
|
1239
|
+
},
|
|
1240
|
+
result: { type: "string", description: "Result summary" }
|
|
1241
|
+
},
|
|
1242
|
+
required: ["task_id", "status"]
|
|
1243
|
+
}
|
|
1244
|
+
},
|
|
1245
|
+
{
|
|
1246
|
+
name: "create_reminder",
|
|
1247
|
+
description: "Create a reminder.",
|
|
1248
|
+
input_schema: {
|
|
1249
|
+
type: "object",
|
|
1250
|
+
properties: {
|
|
1251
|
+
content: { type: "string", description: "Reminder content" },
|
|
1252
|
+
due_at: { type: "string", description: "ISO 8601 due date" }
|
|
1253
|
+
},
|
|
1254
|
+
required: ["content"]
|
|
1255
|
+
}
|
|
1256
|
+
},
|
|
1257
|
+
{
|
|
1258
|
+
name: "complete_reminder",
|
|
1259
|
+
description: "Mark a reminder as complete.",
|
|
1260
|
+
input_schema: {
|
|
1261
|
+
type: "object",
|
|
1262
|
+
properties: {
|
|
1263
|
+
reminder_id: { type: "string", description: "Reminder ID" }
|
|
1264
|
+
},
|
|
1265
|
+
required: ["reminder_id"]
|
|
1266
|
+
}
|
|
1267
|
+
},
|
|
1268
|
+
{
|
|
1269
|
+
name: "close_task",
|
|
1270
|
+
description: "Reviewer-only: finalize a task after review. Only coordinators/reviewers can use this.",
|
|
1271
|
+
input_schema: {
|
|
1272
|
+
type: "object",
|
|
1273
|
+
properties: {
|
|
1274
|
+
task_id: { type: "string", description: "Task ID" },
|
|
1275
|
+
result: { type: "string", description: "Completion summary" }
|
|
1276
|
+
},
|
|
1277
|
+
required: ["task_id"]
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
];
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
// src/gateway/bot-registry.ts
|
|
1284
|
+
var BotRegistry = class {
|
|
1285
|
+
bots = /* @__PURE__ */ new Map();
|
|
1286
|
+
/** Register a bot from a template */
|
|
1287
|
+
register(template, apiKey) {
|
|
1288
|
+
const config = {
|
|
1289
|
+
apiKey,
|
|
1290
|
+
model: template.model,
|
|
1291
|
+
agentId: template.agentId,
|
|
1292
|
+
systemPrompt: template.systemPrompt,
|
|
1293
|
+
tools: template.tools.map((t) => ({
|
|
1294
|
+
name: t.name,
|
|
1295
|
+
description: t.description ?? "",
|
|
1296
|
+
input_schema: t.input_schema
|
|
1297
|
+
})),
|
|
1298
|
+
toolExecutor: async (name, input) => {
|
|
1299
|
+
const handler = template.toolHandlers[name];
|
|
1300
|
+
if (!handler) {
|
|
1301
|
+
throw new Error(`No handler for tool "${name}"`);
|
|
1302
|
+
}
|
|
1303
|
+
const result = await handler(input);
|
|
1304
|
+
return typeof result === "string" ? result : JSON.stringify(result);
|
|
1305
|
+
},
|
|
1306
|
+
maxTurns: template.maxTurns
|
|
1307
|
+
};
|
|
1308
|
+
this.bots.set(template.name, new BotRuntime(config));
|
|
1309
|
+
}
|
|
1310
|
+
/** Register a pre-configured BotRuntime directly */
|
|
1311
|
+
registerRuntime(name, runtime) {
|
|
1312
|
+
this.bots.set(name, runtime);
|
|
1313
|
+
}
|
|
1314
|
+
/** Look up a bot by route target name */
|
|
1315
|
+
get(targetName) {
|
|
1316
|
+
return this.bots.get(targetName);
|
|
1317
|
+
}
|
|
1318
|
+
/** List registered bot names */
|
|
1319
|
+
list() {
|
|
1320
|
+
return [...this.bots.keys()];
|
|
1321
|
+
}
|
|
1322
|
+
/** Check if a bot is registered */
|
|
1323
|
+
has(targetName) {
|
|
1324
|
+
return this.bots.has(targetName);
|
|
1325
|
+
}
|
|
1326
|
+
};
|
|
1327
|
+
|
|
1328
|
+
export {
|
|
1329
|
+
routeMessage,
|
|
1330
|
+
validateGatewayConfig,
|
|
1331
|
+
retryWithBackoff,
|
|
1332
|
+
CircuitBreaker,
|
|
1333
|
+
FULL_ACCESS,
|
|
1334
|
+
READ_ONLY,
|
|
1335
|
+
READ_TOOLS,
|
|
1336
|
+
WRITE_TOOLS,
|
|
1337
|
+
EXECUTE_TOOLS,
|
|
1338
|
+
checkToolPermission,
|
|
1339
|
+
guardToolUseBlocks,
|
|
1340
|
+
buildPermissionContext,
|
|
1341
|
+
Gateway,
|
|
1342
|
+
FatalBotError,
|
|
1343
|
+
RecoverableBotError,
|
|
1344
|
+
MaxStepsError,
|
|
1345
|
+
classifyError,
|
|
1346
|
+
buildExecAssistantSystemPrompt,
|
|
1347
|
+
BotRuntime,
|
|
1348
|
+
buildExecAssistantTools,
|
|
1349
|
+
BotRegistry
|
|
1350
|
+
};
|