stableclaw 2026.4.6 → 2026.4.8
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/README.md +22 -290
- package/package.json +1 -73
- package/dist/.buildstamp +0 -1
- package/dist/abort-C9luPDzM.js +0 -192
- package/dist/abort-cutoff-B1ws4nOb.js +0 -56
- package/dist/abort-cutoff.runtime-DyRUNN6p.js +0 -20
- package/dist/abort-cutoff.runtime.js +0 -1
- package/dist/abort-primitives-CGyFwJ93.js +0 -92
- package/dist/abort-signal-CrrraMAi.js +0 -13
- package/dist/abort.runtime-oBiooAFx.js +0 -2
- package/dist/abort.runtime.js +0 -1
- package/dist/access-DIqrGWVo.js +0 -81
- package/dist/access-control-DfV6HWO9.js +0 -152
- package/dist/account-action-gate-eHi2MB4J.js +0 -12
- package/dist/account-core-DOelA7Wl.js +0 -26
- package/dist/account-helpers-CafpxP7V.js +0 -1
- package/dist/account-helpers-jJOgsae3.js +0 -102
- package/dist/account-id-DXal1kRJ.js +0 -1
- package/dist/account-inspect-CfagweUE.js +0 -158
- package/dist/account-inspect-DOt4IDX6.js +0 -92
- package/dist/account-inspect-pnXre-7b.js +0 -81
- package/dist/account-lookup-CLJxpfHr.js +0 -17
- package/dist/account-resolution-CbKUJkzG.js +0 -30
- package/dist/account-selection-B8a_DqXo.js +0 -222
- package/dist/account-snapshot-fields-mr-1u321.js +0 -130
- package/dist/account-summary-DA6ds0Hk.js +0 -36
- package/dist/accounts-CO-lK8jJ.js +0 -150
- package/dist/accounts-D419O6Gq.js +0 -2
- package/dist/accounts-DKMLcV4g.js +0 -132
- package/dist/accounts-DdiZwn14.js +0 -57
- package/dist/accounts-SWhOQhoA.js +0 -58
- package/dist/accounts-iR2U_Vs4.js +0 -41
- package/dist/accounts-jsSym1md.js +0 -102
- package/dist/accounts-p4Eir-wB.js +0 -107
- package/dist/accounts-uAWNlC7q.js +0 -94
- package/dist/accounts-x23pAGjm.js +0 -137
- package/dist/accounts.runtime-PJOhs60R.js +0 -2
- package/dist/accounts.runtime.js +0 -1
- package/dist/ack-reactions-fcKs7CQ_.js +0 -43
- package/dist/acp-cli-ByWTIhFx.js +0 -2207
- package/dist/acp-runtime-D7wrReOP.js +0 -8
- package/dist/acp-stateful-target-driver-DY2EfkQt.js +0 -60
- package/dist/action-runtime-BdRtmydq.js +0 -318
- package/dist/action-runtime-DvNB5A61.js +0 -72
- package/dist/action-runtime-api-BnVs42oX.js +0 -1028
- package/dist/actions-CUYHtUqe.js +0 -531
- package/dist/actions.runtime-BBrSlYkH.js +0 -18
- package/dist/actions.runtime-DB4aKmBO.js +0 -5
- package/dist/actions.runtime-bdKprwSE.js +0 -2
- package/dist/actions.runtime.js +0 -1
- package/dist/active-listener-SB9ihcaC.js +0 -46
- package/dist/agent-command-C_MGil8o.js +0 -1457
- package/dist/agent-list-MRXQwyo0.js +0 -52
- package/dist/agent-media-payload-DDTSvr-n.js +0 -17
- package/dist/agent-paths-D711oayD.js +0 -12
- package/dist/agent-runner-utils-DEYzwZHa.js +0 -182
- package/dist/agent-runner.runtime-v2uIsAVy.js +0 -2218
- package/dist/agent-runner.runtime.js +0 -1
- package/dist/agent-runtime-Cz19IsmJ.js +0 -142
- package/dist/agent-s4m0Xp6V.js +0 -2
- package/dist/agent-scope-rWWUivuC.js +0 -215
- package/dist/agent-tools-login-DU1gQC9-.js +0 -57
- package/dist/agents/auth-profiles.runtime.js +0 -7
- package/dist/agents/pi-model-discovery-runtime.js +0 -2
- package/dist/agents-B2W3IoEe.js +0 -888
- package/dist/agents-DmE3LRim.js +0 -5
- package/dist/agents.config-D6XoJFQ-.js +0 -3
- package/dist/agents.config-Ui_tQc3C.js +0 -121
- package/dist/allow-from-Bj0HDNYh.js +0 -20
- package/dist/allow-from-CJwSUt_K.js +0 -9
- package/dist/allow-from-xOap1t02.js +0 -62
- package/dist/allow-list-DeRUbyj4.js +0 -319
- package/dist/allowed-updates-aOgPxM0g.js +0 -31
- package/dist/allowlist-config-edit-Ch2EvAYo.js +0 -279
- package/dist/allowlist-match-Dm6AqFEs.js +0 -63
- package/dist/ansi-B_0KjIJj.js +0 -54
- package/dist/anthropic-vertex-Stff_3_3.js +0 -21
- package/dist/anthropic-vertex-stream-DgR8-m-G.js +0 -653
- package/dist/api-7Pf2fGBp.js +0 -11
- package/dist/api-B80kCRiV.js +0 -11
- package/dist/api-B8_Wwelx.js +0 -2
- package/dist/api-BLI24uxW.js +0 -2
- package/dist/api-BLSg8PM8.js +0 -2
- package/dist/api-BRy2h4ZA.js +0 -2
- package/dist/api-BU_YBaOK.js +0 -3
- package/dist/api-BXQjqvPC.js +0 -2
- package/dist/api-BY9iT-AK.js +0 -106
- package/dist/api-BipiQQoS.js +0 -2
- package/dist/api-Bk5mFOju.js +0 -10
- package/dist/api-BrsLZRXn.js +0 -3
- package/dist/api-C16j1Xno.js +0 -2
- package/dist/api-CGtiPyfv.js +0 -8358
- package/dist/api-CHIsf5Wl.js +0 -21
- package/dist/api-CP9q4gi6.js +0 -19
- package/dist/api-CWPDpJCM.js +0 -2
- package/dist/api-CaMdAP9T.js +0 -2
- package/dist/api-CftMlodx.js +0 -5
- package/dist/api-Cpj_QQAF.js +0 -5
- package/dist/api-D13mhDzE.js +0 -33
- package/dist/api-DGcbqpSh.js +0 -2
- package/dist/api-DIGFhGqm.js +0 -30
- package/dist/api-DNxTJAiA.js +0 -5
- package/dist/api-DasNTWRo.js +0 -2
- package/dist/api-Dn7n2sWC.js +0 -74
- package/dist/api-Drq06O1M2.js +0 -6
- package/dist/api-OOJHubHS.js +0 -77
- package/dist/api-XH46JouJ.js +0 -2
- package/dist/api-builder-uuKvRs_O.js +0 -66
- package/dist/api-e3cIcFaV.js +0 -1
- package/dist/api-fetch-CvBBI8q8.js +0 -35
- package/dist/api-key-rotation-BCssmYGp.js +0 -150
- package/dist/api-rPpH8518.js +0 -322
- package/dist/api-w-46PN0B.js +0 -2
- package/dist/apply-DfjUTAkv.js +0 -485
- package/dist/apply.runtime-B-srtEUl.js +0 -2
- package/dist/apply.runtime-DOG87kW8.js +0 -166
- package/dist/apply.runtime.js +0 -1
- package/dist/approval-native-CI9-iVJa.js +0 -79
- package/dist/approval-native-runtime-2AVlvrdL.js +0 -399
- package/dist/approval-renderers-BTKeDNRo.js +0 -87
- package/dist/approval-runtime-DIA8ohXa.js +0 -262
- package/dist/archive-BaCqqQP1.js +0 -537
- package/dist/arg-split-get0R_Yr.js +0 -38
- package/dist/artifacts-DXKlQp1z.js +0 -52
- package/dist/assistant-error-format-CFSPB8qt.js +0 -109
- package/dist/async-lock-QqhPYwhW.js +0 -19
- package/dist/attachment-normalize-CMMyswNa.js +0 -921
- package/dist/audio-BxqNDSFq.js +0 -53
- package/dist/audio-DZWA6M9B.js +0 -41
- package/dist/audit-86J-sIQq.js +0 -54
- package/dist/audit-D9O3O-41.js +0 -86
- package/dist/audit-DwAT-Kzg.js +0 -892
- package/dist/audit-channel.allow-from.runtime-DmuUvr2f.js +0 -5
- package/dist/audit-channel.allow-from.runtime.js +0 -1
- package/dist/audit-channel.collect.runtime-B76LAkrF.js +0 -528
- package/dist/audit-channel.collect.runtime.js +0 -1
- package/dist/audit-channel.discord.runtime-qM5u1Hnl.js +0 -5
- package/dist/audit-channel.discord.runtime.js +0 -1
- package/dist/audit-channel.telegram.runtime-DpwmRUPy.js +0 -8
- package/dist/audit-channel.telegram.runtime.js +0 -1
- package/dist/audit-channel.zalouser.runtime-CzlFZYsv.js +0 -5
- package/dist/audit-channel.zalouser.runtime.js +0 -1
- package/dist/audit-extra.async-BupKW2BB.js +0 -820
- package/dist/audit-fs-B3GdTjId.js +0 -375
- package/dist/audit-membership-runtime-n195y1jy.js +0 -55
- package/dist/audit.deep.runtime-2gZPpwj8.js +0 -2
- package/dist/audit.deep.runtime.js +0 -1
- package/dist/audit.nondeep.runtime-6MT8p_25.js +0 -831
- package/dist/audit.nondeep.runtime.js +0 -1
- package/dist/audit.runtime-jdG0-1Bq.js +0 -7
- package/dist/audit.runtime.js +0 -1
- package/dist/auth-CmL8nlqP.js +0 -371
- package/dist/auth-Zya_1BCc.js +0 -181
- package/dist/auth-choice-CSSk-2VN.js +0 -69
- package/dist/auth-choice-Uyll2O8p.js +0 -341
- package/dist/auth-choice-legacy-5XgU4yCZ.js +0 -44
- package/dist/auth-choice-options-DTRb6fq4.js +0 -98
- package/dist/auth-choice-prompt-BAQTSD2R.js +0 -36
- package/dist/auth-choice-prompt-D6qkTw8h.js +0 -2
- package/dist/auth-choice-x5Cm9EXp.js +0 -3
- package/dist/auth-choice.apply-helpers-CtKCiOT4.js +0 -2
- package/dist/auth-choice.plugin-providers.runtime-BVH7Kq_W.js +0 -11
- package/dist/auth-choice.plugin-providers.runtime.js +0 -1
- package/dist/auth-health-BBleirOY.js +0 -165
- package/dist/auth-mode-policy-BD7_M2AG.js +0 -18
- package/dist/auth-profiles-B0SpzBJv.js +0 -440
- package/dist/auth-rate-limit-OkMcM1wH.js +0 -119
- package/dist/auth-store-Bro-SY1-.js +0 -242
- package/dist/avatar-policy-pho6U35i.js +0 -67
- package/dist/backend-config-CNAjcjho.js +0 -311
- package/dist/backoff-nvVV-tHk.js +0 -18
- package/dist/backup-create-CkkM1HNg.js +0 -462
- package/dist/backup-health-BWSliymu.js +0 -59
- package/dist/banner-DPWatUan.js +0 -370
- package/dist/banner-lDKgB6uE.js +0 -2
- package/dist/base-session-key-CRSigEPk.js +0 -14
- package/dist/binaries-CWBYavgU.js +0 -2
- package/dist/binaries-sPf-9aLM.js +0 -11
- package/dist/binding-registry-BkSvI4jO.js +0 -520
- package/dist/binding-targets-CdIq3brX.js +0 -99
- package/dist/bindings-AuReyzvX.js +0 -21
- package/dist/bindings-CYhLRcr8.js +0 -69
- package/dist/block-reply-pipeline-GSCCinHx.js +0 -266
- package/dist/block-streaming-4outFa6R.js +0 -107
- package/dist/bluebubbles-CorTCHc9.js +0 -64
- package/dist/bluebubbles-D2WymlOg.js +0 -235
- package/dist/bluebubbles-policy-C3d8eqhQ.js +0 -13
- package/dist/bonjour-cache-cleanup-BZJ-GrdX.js +0 -126
- package/dist/boolean-BEhDlAk_.js +0 -29
- package/dist/boolean-param-B807dWHx.js +0 -13
- package/dist/bot-BM1bybV7.js +0 -4268
- package/dist/bot-message-context-CdedyAq_.js +0 -2
- package/dist/bot-message-context-DKGx6N8p.js +0 -1175
- package/dist/bot-message-context.runtime-ClW1x1V5.js +0 -7
- package/dist/bot-message-context.runtime.js +0 -1
- package/dist/bot-message-context.session.runtime-BWjdkRuf.js +0 -12
- package/dist/bot-message-context.session.runtime.js +0 -1
- package/dist/bot-native-commands.delivery.runtime-CgfbR-Nj.js +0 -3
- package/dist/bot-native-commands.delivery.runtime.js +0 -1
- package/dist/bot-native-commands.runtime-DCz7w0aK.js +0 -11
- package/dist/bot-native-commands.runtime.js +0 -1
- package/dist/boundary-file-read-DB4ibbUg.js +0 -192
- package/dist/brave-web-search-provider-B7m_Mprt.js +0 -441
- package/dist/brew-B9JEV5Jj.js +0 -44
- package/dist/browser-config-D_OlwMzh.js +0 -226
- package/dist/browser-open-C9FOO4U_.js +0 -94
- package/dist/browser-runtime-CnWZ_eLQ.js +0 -185
- package/dist/browser-support-Cdp2Gr_6.js +0 -34
- package/dist/btw-command-CPUGSdlD.js +0 -16
- package/dist/build-info.json +0 -5
- package/dist/bundle-lsp-CCIAK1od.js +0 -93
- package/dist/bundled/boot-md/HOOK.md +0 -20
- package/dist/bundled/boot-md/handler.js +0 -180
- package/dist/bundled/bootstrap-extra-files/HOOK.md +0 -53
- package/dist/bundled/bootstrap-extra-files/handler.js +0 -42
- package/dist/bundled/command-logger/HOOK.md +0 -122
- package/dist/bundled/command-logger/handler.js +0 -56
- package/dist/bundled/session-memory/HOOK.md +0 -108
- package/dist/bundled/session-memory/handler.js +0 -196
- package/dist/bundled/workflow-summary/HOOK.md +0 -123
- package/dist/bundled/workflow-summary/handler.js +0 -115
- package/dist/bundled-capability-metadata-BpPgIy_5.js +0 -40
- package/dist/bundled-compat-BLlVmkDi.js +0 -64
- package/dist/bundled-sources-BQTCt2mJ.js +0 -53
- package/dist/button-types-B3_mzf8P.js +0 -54
- package/dist/cache-controls-DBdT4I-H.js +0 -40
- package/dist/call-dy-GwrXS.js +0 -649
- package/dist/call-ti-nPnzT.js +0 -2
- package/dist/canvas-host/a2ui/.bundle.hash +0 -1
- package/dist/canvas-host/a2ui/a2ui.bundle.js +0 -14896
- package/dist/canvas-host/a2ui/index.html +0 -307
- package/dist/capability-provider-runtime-CFUC5NbJ.js +0 -40
- package/dist/catalog-5PPtilI5.js +0 -274
- package/dist/channel-2EW4xzvV.js +0 -788
- package/dist/channel-BiRtGLF8.js +0 -1135
- package/dist/channel-C3AVw0Nl.js +0 -883
- package/dist/channel-CPe2XmyD.js +0 -1217
- package/dist/channel-CVFWRvjf.js +0 -337
- package/dist/channel-Cs0onQ6d.js +0 -4976
- package/dist/channel-D18v9r53.js +0 -1661
- package/dist/channel-D2n2s8Le.js +0 -208
- package/dist/channel-DWKApPW2.js +0 -454
- package/dist/channel-Depm5C-f.js +0 -800
- package/dist/channel-DwTcZxFU.js +0 -1129
- package/dist/channel-LT5vI3EK.js +0 -361
- package/dist/channel-XgThIGmf.js +0 -698
- package/dist/channel-account-context-CWd3uN_m.js +0 -104
- package/dist/channel-actions-DSvaYD1T.js +0 -39
- package/dist/channel-bQKtZIt6.js +0 -599
- package/dist/channel-catalog.json +0 -3
- package/dist/channel-config-D0eO5f5m.js +0 -115
- package/dist/channel-config-helpers-CLFL8FLU.js +0 -482
- package/dist/channel-config-primitives-DwsP66Ak.js +0 -3
- package/dist/channel-config-schema-DsguzRfd.js +0 -6
- package/dist/channel-config-writes-DQZOOZZ8.js +0 -1
- package/dist/channel-configured-B6WgvouH.js +0 -108
- package/dist/channel-feedback-DiLm9hqS.js +0 -257
- package/dist/channel-inbound-CXD3aSAK.js +0 -30
- package/dist/channel-issues-C-49e607.js +0 -13
- package/dist/channel-lifecycle-BnjeZxB6.js +0 -286
- package/dist/channel-lifecycle.core-Cn3zeU0h.js +0 -69
- package/dist/channel-options-DXJ7b7Ye.js +0 -39
- package/dist/channel-pairing-Byr3MFbN.js +0 -68
- package/dist/channel-plugin-common-D2EFcTg8.js +0 -4
- package/dist/channel-plugin-ids-DiOSWlnO.js +0 -56
- package/dist/channel-plugin-resolution-SMHmHx5v.js +0 -144
- package/dist/channel-policy-B-XKjrjK.js +0 -32
- package/dist/channel-reply-pipeline-BtBcBCNu.js +0 -16
- package/dist/channel-resolution-Du3EtWh1.js +0 -60
- package/dist/channel-runtime-F8_51GPp.js +0 -9
- package/dist/channel-selection-Bz3pl14y.js +0 -125
- package/dist/channel-send-result-CyfDCXJG.js +0 -40
- package/dist/channel-setup-yWpuHwhm.js +0 -51
- package/dist/channel-shared-gHcJ8n-f.js +0 -58
- package/dist/channel-status-CyoeLEnj.js +0 -2
- package/dist/channel-summary-5zHt-z1E.js +0 -139
- package/dist/channel-summary-B_fbXZ3l.js +0 -2
- package/dist/channel-target-DHO43h-I.js +0 -157
- package/dist/channel-targets-C8ZPfJmV.js +0 -18
- package/dist/channel-y-bXwYi3.js +0 -783
- package/dist/channel.runtime-B2SEUCf0.js +0 -14
- package/dist/channel.runtime-BF5qdBeA.js +0 -5
- package/dist/channel.runtime-BPaq6EO_.js +0 -108
- package/dist/channel.runtime-BV8FGxyb.js +0 -373
- package/dist/channel.runtime-CGnamSTC.js +0 -125
- package/dist/channel.runtime-CRNf20Ct.js +0 -80
- package/dist/channel.runtime-CRSWVexY.js +0 -335
- package/dist/channel.runtime-Cg6DmoTp.js +0 -783
- package/dist/channel.runtime-CpHTHeFR.js +0 -309
- package/dist/channel.runtime-ZdhCn8XI.js +0 -3315
- package/dist/channel.runtime-cXufT2kW.js +0 -61
- package/dist/channel.runtime.js +0 -1
- package/dist/channel.setup-Bq1vgxXZ.js +0 -279
- package/dist/channels-BZC2kp_G.js +0 -409
- package/dist/channels-CMeYB3bY.js +0 -1134
- package/dist/channels-cli-BIzqRmuu.js +0 -246
- package/dist/channels-status-issues-DqTdy1lq.js +0 -17
- package/dist/chat-envelope-DlGbh8k2.js +0 -158
- package/dist/chat-meta-wVbKo1sw.js +0 -700
- package/dist/chat-target-prefixes-DCF8a_Lp.js +0 -156
- package/dist/chat-type-DXjGtE-S.js +0 -10
- package/dist/chunk-CW-a7Cfh.js +0 -334
- package/dist/chunk-CrysgU_F.js +0 -36
- package/dist/chutes-oauth-Bh3BTMMG.js +0 -127
- package/dist/cjk-chars-Dl_Ko7yI.js +0 -50
- package/dist/clack-prompter-C5PtKpUH.js +0 -112
- package/dist/clawbot-cli-CgHyabtp.js +0 -9
- package/dist/clawhub-CDs2LE11.js +0 -263
- package/dist/clawhub-CO6YnoQZ.js +0 -163
- package/dist/cli/daemon-cli.js +0 -9
- package/dist/cli-CoOwN-V4.js +0 -164
- package/dist/cli-CpOL_jWZ.js +0 -152
- package/dist/cli-DCp06hI2.js +0 -2
- package/dist/cli-DVSDSUQm.js +0 -695
- package/dist/cli-backend-B20k67rv.js +0 -44
- package/dist/cli-backend-CcuyEMrq.js +0 -1
- package/dist/cli-backend-DMTxL8mq.js +0 -40
- package/dist/cli-backend-M4CTb3mq.js +0 -49
- package/dist/cli-migration-BwqQvUer.js +0 -100
- package/dist/cli-name-C3PA6Rhs.js +0 -21
- package/dist/cli-runner-8SNo1pKS.js +0 -1014
- package/dist/cli-runtime-ChUqI2f-.js +0 -3
- package/dist/cli-shared-01J1LZRE.js +0 -72
- package/dist/cli-startup-metadata.json +0 -14
- package/dist/cli-utils-eYXt_xAg.js +0 -40
- package/dist/cli-watchdog-defaults-Dff-MFUz.js +0 -14
- package/dist/cli.runtime-WWGaX6X6.js +0 -547
- package/dist/cli.runtime.js +0 -1
- package/dist/client-BFxjGELJ.js +0 -5
- package/dist/client-C8_DsF-r.js +0 -7
- package/dist/client-bootstrap-BiVps8T6.js +0 -2
- package/dist/client-bootstrap-CFGCltAu.js +0 -121
- package/dist/client-lEilgam1.js +0 -87
- package/dist/cmd-argv-BbyQhzAC.js +0 -68
- package/dist/code-execution-CPcuE0sX.js +0 -142
- package/dist/codex-native-web-search-CyrsDBZ2.js +0 -155
- package/dist/codex-native-web-search-PFJu8fcM.js +0 -2
- package/dist/collection-runtime-DJjFReXJ.js +0 -1
- package/dist/command-auth-BAkZv3cP.js +0 -356
- package/dist/command-auth-native-Bsp5Doyd.js +0 -3
- package/dist/command-auth-tqFmaIaJ.js +0 -64
- package/dist/command-detection-BV7Ipg_0.js +0 -49
- package/dist/command-detection-DyqWl5Nq.js +0 -2
- package/dist/command-format-CX1xXtgB.js +0 -22
- package/dist/command-format-DwYoemR9.js +0 -2
- package/dist/command-gating-NyDk7t84.js +0 -40
- package/dist/command-options-BUoAEXAi.js +0 -25
- package/dist/command-poll-backoff-B7RiSvcV.js +0 -56
- package/dist/command-poll-backoff.runtime-DX9gobB0.js +0 -7
- package/dist/command-poll-backoff.runtime.js +0 -1
- package/dist/command-queue-CxEmvSGT.js +0 -253
- package/dist/command-registry-CaJXKNoO.js +0 -3
- package/dist/command-registry-Ct0qor9d.js +0 -199
- package/dist/command-secret-gateway-43O-1Hnf.js +0 -2
- package/dist/command-secret-gateway-C39SFGHd.js +0 -442
- package/dist/command-secret-targets-Bns-l1xV.js +0 -2
- package/dist/command-secret-targets-CTrFghcj.js +0 -87
- package/dist/command-surface-CfP1vask.js +0 -2
- package/dist/commands/status.summary.runtime.js +0 -146
- package/dist/commands-BxWe27p0.js +0 -42
- package/dist/commands-CiyPyNRY.js +0 -194
- package/dist/commands-acp-BeCm2Wnq.js +0 -990
- package/dist/commands-context-DbfMrVv4.js +0 -33
- package/dist/commands-core-CEmQydIV.js +0 -241
- package/dist/commands-core.runtime-BYooAQbc.js +0 -2
- package/dist/commands-core.runtime.js +0 -1
- package/dist/commands-handlers.runtime-CEAP4Yv2.js +0 -3707
- package/dist/commands-handlers.runtime.js +0 -1
- package/dist/commands-info-COwAtIWb.js +0 -651
- package/dist/commands-models-B4EJh5Fy.js +0 -395
- package/dist/commands-o21Y9Xck.js +0 -4
- package/dist/commands-registry-DgMc5_jk.js +0 -295
- package/dist/commands-registry.data-5O2FOeZl.js +0 -935
- package/dist/commands-registry.runtime-CT_YXpwM.js +0 -2
- package/dist/commands-registry.runtime.js +0 -1
- package/dist/commands-status.runtime-D4F4iVUh.js +0 -2
- package/dist/commands-status.runtime.js +0 -1
- package/dist/commands.runtime-_ALsMFcS.js +0 -4
- package/dist/commands.runtime.js +0 -1
- package/dist/common-Dbmo0I1E.js +0 -206
- package/dist/compact.runtime-CBfyBJbR.js +0 -7
- package/dist/compact.runtime.js +0 -1
- package/dist/completion-cli-COhEd9CF.js +0 -2
- package/dist/completion-cli-DHe4Rit5.js +0 -466
- package/dist/components-CamLdbbq.js +0 -797
- package/dist/components-DPbtLC3x.js +0 -2
- package/dist/config-CKJA2kAO.js +0 -53
- package/dist/config-D7-9dK3i.js +0 -138
- package/dist/config-DJ29sg5U.js +0 -475
- package/dist/config-DJNI2tTK.js +0 -136
- package/dist/config-Dr2GRT3y.js +0 -135
- package/dist/config-DsqKYjeo.js +0 -421
- package/dist/config-Q9kc32gz.js +0 -41
- package/dist/config-RUefx-_1.js +0 -8
- package/dist/config-cli-BqlYHmPA.js +0 -945
- package/dist/config-error-handler-BKRR0_j_.js +0 -197
- package/dist/config-error-handler-CT1igiX6.js +0 -2
- package/dist/config-eval-hTWq4IVy.js +0 -100
- package/dist/config-guard-Bk3_wSTH.js +0 -113
- package/dist/config-helpers-DLVbtn1B.js +0 -117
- package/dist/config-paths-BKYh__Ga.js +0 -8
- package/dist/config-paths-DP5yXXo2.js +0 -102
- package/dist/config-presence-omlqvhAQ.js +0 -79
- package/dist/config-regex-CvZFnWkO.js +0 -39
- package/dist/config-runtime-ngoccXmP.js +0 -13
- package/dist/config-schema-B3sQY5B_.js +0 -133
- package/dist/config-schema-B_2MhEQY.js +0 -71
- package/dist/config-schema-BpIXBNbc.js +0 -202
- package/dist/config-schema-BtfQOmdV.js +0 -115
- package/dist/config-schema-Bw-Xbo_T.js +0 -85
- package/dist/config-schema-Cqj-Mugi.js +0 -105
- package/dist/config-schema-DQSj_ZL_.js +0 -29
- package/dist/config-schema-Pe1Vn9kb.js +0 -17
- package/dist/config-schema-f7nntJMh.js +0 -25
- package/dist/config-schema-lBqODOnr.js +0 -25
- package/dist/config-schema-mfJ91WLf.js +0 -65
- package/dist/config-secret-input.runtime-B_HyRtgE.js +0 -3
- package/dist/config-secret-input.runtime.js +0 -1
- package/dist/config-state-DfIk9ME6.js +0 -289
- package/dist/config-status-Bnobp1-p.js +0 -2
- package/dist/config-status-Db93sFQB.js +0 -109
- package/dist/config-update-B_S0YQ_Y.js +0 -3
- package/dist/config-update-CK4EDDeD.js +0 -127
- package/dist/config-validation-Bh_sg7RW.js +0 -276
- package/dist/config-value-CMTdBw5q.js +0 -25
- package/dist/configure-CGDY1YR7.js +0 -1140
- package/dist/configure-V8jejRHr.js +0 -2
- package/dist/configured-provider-fallback-DWYDg0L7.js +0 -15
- package/dist/connection-auth-CKM60o55.js +0 -30
- package/dist/connection-details-DyVOoGG6.js +0 -56
- package/dist/constants-Bq55qi82.js +0 -71
- package/dist/constants-CimYBSye.js +0 -4
- package/dist/content-blocks-DuKJNUtm.js +0 -785
- package/dist/context-D-9a98Zk.js +0 -227
- package/dist/context-visibility-DTMZdcAH.js +0 -14
- package/dist/context-visibility-vOvLMUrZ.js +0 -35
- package/dist/context-window-guard-WE0ENAeQ.js +0 -46
- package/dist/control-ui-assets-DVYAF-2p.js +0 -240
- package/dist/control-ui-shared-BKXkFsA7.js +0 -29
- package/dist/conversation-binding-C2nuZ3Eg.js +0 -520
- package/dist/conversation-binding-input-CBwdWDC4.js +0 -250
- package/dist/conversation-bindings-Cq4z4vhr.js +0 -210
- package/dist/conversation-id-4JeHag0a.js +0 -168
- package/dist/conversation-id-CXuzm-Ub.js +0 -116
- package/dist/conversation-id-DCTwJCZF.js +0 -43
- package/dist/conversation-label-C2xTELop.js +0 -32
- package/dist/conversation-runtime-bArk0IjJ.js +0 -85
- package/dist/core-DaMVvavf.js +0 -195
- package/dist/core-api-D0U2iGRK.js +0 -670
- package/dist/core-command-descriptors-DxRm2H1p.js +0 -91
- package/dist/create-client-BOKOxyiu.js +0 -58
- package/dist/create-client-Bzn467Jo.js +0 -2
- package/dist/credentials-BGPs1gIX.js +0 -242
- package/dist/credentials-DUoYFiLk.js +0 -22
- package/dist/credentials-read-CDYtvkBt.js +0 -74
- package/dist/credentials-read-KB395CbD.js +0 -2
- package/dist/credentials-write.runtime-Bx7mKj4E.js +0 -9
- package/dist/credentials-write.runtime.js +0 -1
- package/dist/creds-files-CTtTs6zG.js +0 -19
- package/dist/cron-cli-Di3TakVa.js +0 -644
- package/dist/crypto-node.runtime-DI0fQ6wI.js +0 -5
- package/dist/crypto-node.runtime.js +0 -1
- package/dist/crypto-runtime-Bc-jKPlv.js +0 -1036
- package/dist/current-time--aIDbABZ.js +0 -20
- package/dist/daemon-cli-Cnry1ft1.js +0 -370
- package/dist/daemon-install-de_bWPuS.js +0 -63
- package/dist/daemon-install-plan.shared-u00bwv47.js +0 -222
- package/dist/daemon-runtime-DPejcJWG.js +0 -12
- package/dist/dangerous-name-matching-DnCDaXzP.js +0 -48
- package/dist/dangerous-name-runtime-Dinw00TZ.js +0 -1
- package/dist/dangerous-tools-C3RlQe7u.js +0 -38
- package/dist/date-time-DRQ0-oaw.js +0 -118
- package/dist/ddg-search-provider-B0zLjcDN.js +0 -182
- package/dist/dedupe-DZbQYOSD.js +0 -59
- package/dist/default-account-warnings-BCrmijcz.js +0 -15
- package/dist/default-models-Br0v8QOH.js +0 -35
- package/dist/defaults-DAt--D9n.js +0 -6
- package/dist/defaults-D_Y_lvUP.js +0 -7
- package/dist/defaults-MkMfnGzi.js +0 -7
- package/dist/delegate-CLP7zDS_.js +0 -43
- package/dist/deliver-CIUEKe4q.js +0 -954
- package/dist/deliver-LN-Yg8wS.js +0 -3
- package/dist/deliver-reply-DL-CSF43.js +0 -238
- package/dist/deliver-runtime-CoqXhxlS.js +0 -2
- package/dist/delivery-CL_3vFSl.js +0 -834
- package/dist/delivery-context-CoAL-jTI.js +0 -106
- package/dist/delivery-info-DTAUApso.js +0 -40
- package/dist/delivery-queue-DBW69jH4.js +0 -2
- package/dist/delivery-queue-DMubgaLa.js +0 -289
- package/dist/deps-34N0Nmbl.js +0 -67
- package/dist/deps-BVogrCia.js +0 -2
- package/dist/deps-DZJpA4XG.js +0 -153
- package/dist/detect-Zrcrs8SP.js +0 -12
- package/dist/detect-binary-CEX186X6.js +0 -30
- package/dist/device-auth-D_0GUMYY.js +0 -19
- package/dist/device-bootstrap-Bnn4K3fQ.js +0 -3
- package/dist/device-bootstrap-bwpHYQhY.js +0 -176
- package/dist/device-health-B3q7Cmqn.js +0 -2
- package/dist/device-health-DhmPD0gq.js +0 -16
- package/dist/device-identity-Cak311si.js +0 -2
- package/dist/device-identity-Doxo0HJx.js +0 -140
- package/dist/device-metadata-normalization-B5_zplC_.js +0 -21
- package/dist/device-pairing-BCpR-Vtb.js +0 -520
- package/dist/devices-cli-DYPkfzsG.js +0 -328
- package/dist/diagnostic-DfKz60Xk.js +0 -311
- package/dist/diagnostic-events-CUSmY-rn.js +0 -48
- package/dist/diagnostic-flags-DLDDycUv.js +0 -64
- package/dist/diagnostic-runtime-ZQC4AswI.js +0 -1
- package/dist/diagnostics-Cxl1bFGj.js +0 -33
- package/dist/diagnostics-GjF7wpY9.js +0 -2
- package/dist/diagnostics-otel-C2RQUlQT.js +0 -3
- package/dist/diffs-DsHt689X.js +0 -2
- package/dist/direct-dm-ByuWacLn.js +0 -139
- package/dist/direct-management-DEtuCcDL.js +0 -2
- package/dist/direct-management-DMp7MOnt.js +0 -300
- package/dist/direct-room-ARYWsCbX.js +0 -2
- package/dist/direct-room-DYlSJOU_.js +0 -67
- package/dist/directive-handling.defaults-BMh2DXTA.js +0 -19
- package/dist/directive-handling.fast-lane-CBX4jbax.js +0 -2
- package/dist/directive-handling.fast-lane-DRvycpgg.js +0 -64
- package/dist/directive-handling.impl-BKJf5Ltu.js +0 -672
- package/dist/directive-handling.impl-BkdGnm-T.js +0 -2
- package/dist/directive-handling.levels-BHOBnnn8.js +0 -2
- package/dist/directive-handling.levels-CecoBNlK.js +0 -12
- package/dist/directive-handling.parse-BuTiWmrT.js +0 -375
- package/dist/directive-handling.persist-D9RYevoA.js +0 -133
- package/dist/directive-handling.persist.runtime-Cb_unTVH.js +0 -2
- package/dist/directive-handling.persist.runtime.js +0 -1
- package/dist/directive-handling.shared-_Zvt3-ej.js +0 -158
- package/dist/directive-tags-Cm0H6Dn_.js +0 -109
- package/dist/directives-f-UxcP8D.js +0 -209
- package/dist/directory-cache-CM_EUchr.js +0 -62
- package/dist/directory-cli-D6N59tzr.js +0 -238
- package/dist/directory-config-helpers-BCrssKbI.js +0 -141
- package/dist/directory-runtime-CAabZ_XW.js +0 -20
- package/dist/directory.static-TKo2MeNR.js +0 -45
- package/dist/discord-BOpl5w4U.js +0 -5
- package/dist/discord-account-wyFuLDq_.js +0 -11
- package/dist/discord-core-BpzKxDZ7.js +0 -5
- package/dist/discord-lOPvIxp4.js +0 -11
- package/dist/discord-runtime-surface-Dm5WUtCV.js +0 -67
- package/dist/discord-session-key-DaPto1yr.js +0 -11
- package/dist/discord-surface-Cg_Mu9MI.js +0 -30
- package/dist/discord-thread-bindings-zIOp1Tj4.js +0 -29
- package/dist/discord-timeouts-Di5n5LAU.js +0 -12
- package/dist/discovery-DLbFJLD_.js +0 -171
- package/dist/dispatch-C4gGGYZt.js +0 -858
- package/dist/dispatch-acp.runtime-DUHj6bP-.js +0 -884
- package/dist/dispatch-acp.runtime.js +0 -1
- package/dist/dist-7JNqoGV7.js +0 -3
- package/dist/dist-BqJPEiV8.js +0 -14245
- package/dist/dist-DdE56Pmi.js +0 -503
- package/dist/dist-shQCTuJB.js +0 -1239
- package/dist/dm-policy-shared-C99GNVxp.js +0 -188
- package/dist/dns-cli-vQBAzTmg.js +0 -192
- package/dist/docker-BN7sQKCb.js +0 -789
- package/dist/docs-cli-Br6cfKoS.js +0 -156
- package/dist/doctor-completion-Cf0GcTXR.js +0 -90
- package/dist/doctor-config-preflight-C2cKYiKe.js +0 -2
- package/dist/doctor-config-preflight-DNbO-_N8.js +0 -148
- package/dist/doctor-state-migrations-BXkbeSFy.js +0 -2
- package/dist/doctor-state-migrations-G3shZ-W_.js +0 -3
- package/dist/dotenv-CelghjA4.js +0 -11
- package/dist/dotenv-Csg1Xj8n.js +0 -113
- package/dist/embedding-provider-Aey9VsDb.js +0 -129
- package/dist/enable-BgSxsKp_.js +0 -57
- package/dist/encryption-guidance-62xAQFP4.js +0 -33
- package/dist/entry-status-3ymZYX2l.js +0 -172
- package/dist/entry.js +0 -209
- package/dist/env-CK3eZNIX.js +0 -40
- package/dist/env-overrides-CcDOWo0Y.js +0 -346
- package/dist/env-overrides.runtime-CKb7-Q72.js +0 -7
- package/dist/env-overrides.runtime.js +0 -1
- package/dist/env-substitution-UhJwb0ys.js +0 -136
- package/dist/envelope-CrC9AdBL.js +0 -137
- package/dist/error-runtime-D-gKufd_.js +0 -2
- package/dist/error-text-BC9WfvfX.js +0 -24
- package/dist/errors-Bs2h5H8p.js +0 -71
- package/dist/errors-CjFsF-QY.js +0 -9
- package/dist/exa-web-search-provider-DUUlmDcV.js +0 -355
- package/dist/exec-BO6vYjRf.js +0 -368
- package/dist/exec-CD7C9g-b.js +0 -2
- package/dist/exec-approval-reply-CFOEcmIv.js +0 -189
- package/dist/exec-approval-session-target-BbsAlD1Q.js +0 -159
- package/dist/exec-approvals-3UDrE7Q9.js +0 -1565
- package/dist/exec-approvals-CW5axvaY.js +0 -121
- package/dist/exec-approvals-cli-BPYvtJv-.js +0 -483
- package/dist/exec-approvals-effective-DbLJfkpe.js +0 -140
- package/dist/exec-safe-bin-runtime-policy-218hHVyc.js +0 -90
- package/dist/exec-safety-XzKkUSvE.js +0 -24
- package/dist/export-html/template.css +0 -1060
- package/dist/export-html/template.html +0 -88
- package/dist/export-html/template.js +0 -1875
- package/dist/export-html/vendor/highlight.min.js +0 -1213
- package/dist/export-html/vendor/marked.min.js +0 -6
- package/dist/extension-shared-V_4IXpRU.js +0 -74
- package/dist/extensionAPI.js +0 -17
- package/dist/extensions/acpx/index.js +0 -1855
- package/dist/extensions/acpx/mcp-proxy.mjs +0 -151
- package/dist/extensions/acpx/openclaw.plugin.json +0 -132
- package/dist/extensions/acpx/package.json +0 -14
- package/dist/extensions/acpx/runtime-api.js +0 -5
- package/dist/extensions/acpx/skills/acp-router/SKILL.md +0 -245
- package/dist/extensions/aicq-chat/index.js +0 -284
- package/dist/extensions/aicq-chat/openclaw.plugin.json +0 -30
- package/dist/extensions/amazon-bedrock/api.js +0 -3
- package/dist/extensions/amazon-bedrock/discovery.js +0 -2
- package/dist/extensions/amazon-bedrock/index.js +0 -65
- package/dist/extensions/amazon-bedrock/openclaw.plugin.json +0 -44
- package/dist/extensions/amazon-bedrock/package.json +0 -23
- package/dist/extensions/anthropic/api.js +0 -3
- package/dist/extensions/anthropic/cli-backend.js +0 -2
- package/dist/extensions/anthropic/cli-migration.js +0 -2
- package/dist/extensions/anthropic/cli-shared.js +0 -2
- package/dist/extensions/anthropic/index.js +0 -357
- package/dist/extensions/anthropic/media-understanding-provider.js +0 -2
- package/dist/extensions/anthropic/openclaw.plugin.json +0 -64
- package/dist/extensions/anthropic/package.json +0 -12
- package/dist/extensions/anthropic/stream-wrappers.js +0 -2
- package/dist/extensions/anthropic/test-api.js +0 -2
- package/dist/extensions/anthropic-vertex/api.js +0 -4
- package/dist/extensions/anthropic-vertex/index.js +0 -36
- package/dist/extensions/anthropic-vertex/openclaw.plugin.json +0 -12
- package/dist/extensions/anthropic-vertex/package.json +0 -12
- package/dist/extensions/anthropic-vertex/provider-catalog.js +0 -2
- package/dist/extensions/anthropic-vertex/region.js +0 -2
- package/dist/extensions/bluebubbles/api.js +0 -5
- package/dist/extensions/bluebubbles/channel-config-api.js +0 -2
- package/dist/extensions/bluebubbles/index.js +0 -13
- package/dist/extensions/bluebubbles/openclaw.plugin.json +0 -11
- package/dist/extensions/bluebubbles/package.json +0 -48
- package/dist/extensions/bluebubbles/runtime-api.js +0 -2
- package/dist/extensions/bluebubbles/setup-entry.js +0 -26
- package/dist/extensions/brave/index.js +0 -13
- package/dist/extensions/brave/openclaw.plugin.json +0 -50
- package/dist/extensions/brave/package.json +0 -12
- package/dist/extensions/brave/test-api.js +0 -2
- package/dist/extensions/brave/web-search-provider.js +0 -2
- package/dist/extensions/browser/browser-runtime-api.js +0 -4
- package/dist/extensions/browser/index.js +0 -20
- package/dist/extensions/browser/openclaw.plugin.json +0 -9
- package/dist/extensions/browser/package.json +0 -12
- package/dist/extensions/browser/runtime-api.js +0 -6
- package/dist/extensions/browser/test-support.js +0 -50
- package/dist/extensions/byteplus/api.js +0 -3
- package/dist/extensions/byteplus/index.js +0 -63
- package/dist/extensions/byteplus/models.js +0 -2
- package/dist/extensions/byteplus/openclaw.plugin.json +0 -33
- package/dist/extensions/byteplus/package.json +0 -12
- package/dist/extensions/byteplus/provider-catalog.js +0 -2
- package/dist/extensions/chutes/api.js +0 -3
- package/dist/extensions/chutes/index.js +0 -143
- package/dist/extensions/chutes/models.js +0 -2
- package/dist/extensions/chutes/onboard.js +0 -3
- package/dist/extensions/chutes/openclaw.plugin.json +0 -44
- package/dist/extensions/chutes/package.json +0 -12
- package/dist/extensions/chutes/provider-catalog.js +0 -2
- package/dist/extensions/cloudflare-ai-gateway/api.js +0 -3
- package/dist/extensions/cloudflare-ai-gateway/index.js +0 -188
- package/dist/extensions/cloudflare-ai-gateway/models.js +0 -2
- package/dist/extensions/cloudflare-ai-gateway/onboard.js +0 -2
- package/dist/extensions/cloudflare-ai-gateway/openclaw.plugin.json +0 -33
- package/dist/extensions/cloudflare-ai-gateway/package.json +0 -12
- package/dist/extensions/copilot-proxy/index.js +0 -125
- package/dist/extensions/copilot-proxy/openclaw.plugin.json +0 -27
- package/dist/extensions/copilot-proxy/package.json +0 -12
- package/dist/extensions/copilot-proxy/runtime-api.js +0 -3
- package/dist/extensions/deepgram/audio.js +0 -2
- package/dist/extensions/deepgram/index.js +0 -13
- package/dist/extensions/deepgram/media-understanding-provider.js +0 -2
- package/dist/extensions/deepgram/openclaw.plugin.json +0 -13
- package/dist/extensions/deepgram/package.json +0 -12
- package/dist/extensions/deepseek/api.js +0 -3
- package/dist/extensions/deepseek/index.js +0 -33
- package/dist/extensions/deepseek/models.js +0 -2
- package/dist/extensions/deepseek/onboard.js +0 -2
- package/dist/extensions/deepseek/openclaw.plugin.json +0 -32
- package/dist/extensions/deepseek/package.json +0 -12
- package/dist/extensions/deepseek/provider-catalog.js +0 -2
- package/dist/extensions/device-pair/api.js +0 -10
- package/dist/extensions/device-pair/index.js +0 -483
- package/dist/extensions/device-pair/notify.js +0 -2
- package/dist/extensions/device-pair/openclaw.plugin.json +0 -21
- package/dist/extensions/device-pair/qr-image.js +0 -3
- package/dist/extensions/diagnostics-otel/api.js +0 -6
- package/dist/extensions/diagnostics-otel/index.js +0 -485
- package/dist/extensions/diagnostics-otel/openclaw.plugin.json +0 -8
- package/dist/extensions/diagnostics-otel/package.json +0 -27
- package/dist/extensions/diffs/api.js +0 -4
- package/dist/extensions/diffs/assets/viewer-runtime.js +0 -1888
- package/dist/extensions/diffs/index.js +0 -2058
- package/dist/extensions/diffs/openclaw.plugin.json +0 -226
- package/dist/extensions/diffs/package.json +0 -24
- package/dist/extensions/diffs/runtime-api.js +0 -3
- package/dist/extensions/diffs/skills/diffs/SKILL.md +0 -23
- package/dist/extensions/discord/action-runtime-api.js +0 -2
- package/dist/extensions/discord/api.js +0 -14
- package/dist/extensions/discord/channel-config-api.js +0 -2
- package/dist/extensions/discord/index.js +0 -612
- package/dist/extensions/discord/openclaw.plugin.json +0 -11
- package/dist/extensions/discord/package.json +0 -52
- package/dist/extensions/discord/runtime-api.js +0 -230
- package/dist/extensions/discord/session-key-api.js +0 -2
- package/dist/extensions/discord/setup-entry.js +0 -10
- package/dist/extensions/discord/test-api.js +0 -45
- package/dist/extensions/discord/timeouts.js +0 -2
- package/dist/extensions/duckduckgo/index.js +0 -13
- package/dist/extensions/duckduckgo/openclaw.plugin.json +0 -41
- package/dist/extensions/duckduckgo/package.json +0 -12
- package/dist/extensions/duckduckgo/web-search-provider.js +0 -2
- package/dist/extensions/elevenlabs/index.js +0 -13
- package/dist/extensions/elevenlabs/openclaw.plugin.json +0 -13
- package/dist/extensions/elevenlabs/package.json +0 -12
- package/dist/extensions/elevenlabs/speech-provider.js +0 -2
- package/dist/extensions/elevenlabs/test-api.js +0 -2
- package/dist/extensions/elevenlabs/tts.js +0 -2
- package/dist/extensions/exa/index.js +0 -13
- package/dist/extensions/exa/openclaw.plugin.json +0 -39
- package/dist/extensions/exa/package.json +0 -12
- package/dist/extensions/exa/web-search-provider.js +0 -2
- package/dist/extensions/fal/image-generation-provider.js +0 -2
- package/dist/extensions/fal/index.js +0 -45
- package/dist/extensions/fal/onboard.js +0 -2
- package/dist/extensions/fal/openclaw.plugin.json +0 -40
- package/dist/extensions/fal/package.json +0 -12
- package/dist/extensions/feishu/api.js +0 -7
- package/dist/extensions/feishu/index.js +0 -2531
- package/dist/extensions/feishu/openclaw.plugin.json +0 -14
- package/dist/extensions/feishu/package.json +0 -52
- package/dist/extensions/feishu/runtime-api.js +0 -34
- package/dist/extensions/feishu/session-key-api.js +0 -2
- package/dist/extensions/feishu/setup-api.js +0 -2
- package/dist/extensions/feishu/setup-entry.js +0 -6
- package/dist/extensions/feishu/skills/feishu-doc/SKILL.md +0 -211
- package/dist/extensions/feishu/skills/feishu-doc/references/block-types.md +0 -103
- package/dist/extensions/feishu/skills/feishu-drive/SKILL.md +0 -97
- package/dist/extensions/feishu/skills/feishu-perm/SKILL.md +0 -119
- package/dist/extensions/feishu/skills/feishu-wiki/SKILL.md +0 -111
- package/dist/extensions/firecrawl/api.js +0 -33
- package/dist/extensions/firecrawl/index.js +0 -184
- package/dist/extensions/firecrawl/openclaw.plugin.json +0 -87
- package/dist/extensions/firecrawl/package.json +0 -12
- package/dist/extensions/firecrawl/web-search-provider.js +0 -2
- package/dist/extensions/github-copilot/api.js +0 -2
- package/dist/extensions/github-copilot/index.js +0 -138
- package/dist/extensions/github-copilot/login.js +0 -2
- package/dist/extensions/github-copilot/models-defaults.js +0 -38
- package/dist/extensions/github-copilot/models.js +0 -2
- package/dist/extensions/github-copilot/openclaw.plugin.json +0 -31
- package/dist/extensions/github-copilot/package.json +0 -12
- package/dist/extensions/github-copilot/token.js +0 -3
- package/dist/extensions/github-copilot/usage.js +0 -2
- package/dist/extensions/google/api.js +0 -3
- package/dist/extensions/google/cli-backend.js +0 -2
- package/dist/extensions/google/gemini-cli-provider.js +0 -110
- package/dist/extensions/google/google-shared.test-helpers.js +0 -92
- package/dist/extensions/google/image-generation-provider.js +0 -162
- package/dist/extensions/google/index.js +0 -213
- package/dist/extensions/google/media-understanding-provider.js +0 -104
- package/dist/extensions/google/model-id.js +0 -2
- package/dist/extensions/google/oauth.credentials.js +0 -2
- package/dist/extensions/google/oauth.flow.js +0 -2
- package/dist/extensions/google/oauth.http.js +0 -2
- package/dist/extensions/google/oauth.js +0 -3
- package/dist/extensions/google/oauth.project.js +0 -2
- package/dist/extensions/google/oauth.runtime.js +0 -2
- package/dist/extensions/google/oauth.shared.js +0 -2
- package/dist/extensions/google/oauth.token.js +0 -2
- package/dist/extensions/google/openclaw.plugin.json +0 -89
- package/dist/extensions/google/package.json +0 -12
- package/dist/extensions/google/provider-models.js +0 -2
- package/dist/extensions/google/runtime-api.js +0 -4
- package/dist/extensions/google/test-api.js +0 -2
- package/dist/extensions/google/web-search-provider.js +0 -2
- package/dist/extensions/googlechat/api.js +0 -2
- package/dist/extensions/googlechat/channel-config-api.js +0 -7
- package/dist/extensions/googlechat/index.js +0 -13
- package/dist/extensions/googlechat/openclaw.plugin.json +0 -11
- package/dist/extensions/googlechat/package.json +0 -48
- package/dist/extensions/googlechat/runtime-api.js +0 -39
- package/dist/extensions/googlechat/setup-entry.js +0 -6
- package/dist/extensions/googlechat/test-api.js +0 -3
- package/dist/extensions/groq/index.js +0 -13
- package/dist/extensions/groq/media-understanding-provider.js +0 -2
- package/dist/extensions/groq/openclaw.plugin.json +0 -13
- package/dist/extensions/groq/package.json +0 -12
- package/dist/extensions/huggingface/api.js +0 -4
- package/dist/extensions/huggingface/index.js +0 -54
- package/dist/extensions/huggingface/models.js +0 -2
- package/dist/extensions/huggingface/onboard.js +0 -2
- package/dist/extensions/huggingface/openclaw.plugin.json +0 -34
- package/dist/extensions/huggingface/package.json +0 -12
- package/dist/extensions/huggingface/provider-catalog.js +0 -3
- package/dist/extensions/imessage/api.js +0 -4
- package/dist/extensions/imessage/channel-config-api.js +0 -2
- package/dist/extensions/imessage/index.js +0 -211
- package/dist/extensions/imessage/openclaw.plugin.json +0 -11
- package/dist/extensions/imessage/package.json +0 -26
- package/dist/extensions/imessage/runtime-api.js +0 -11
- package/dist/extensions/imessage/setup-entry.js +0 -13
- package/dist/extensions/irc/api.js +0 -2
- package/dist/extensions/irc/channel-config-api.js +0 -2
- package/dist/extensions/irc/index.js +0 -12
- package/dist/extensions/irc/openclaw.plugin.json +0 -11
- package/dist/extensions/irc/package.json +0 -28
- package/dist/extensions/irc/setup-entry.js +0 -6
- package/dist/extensions/kilocode/api.js +0 -3
- package/dist/extensions/kilocode/index.js +0 -37
- package/dist/extensions/kilocode/onboard.js +0 -3
- package/dist/extensions/kilocode/openclaw.plugin.json +0 -33
- package/dist/extensions/kilocode/package.json +0 -12
- package/dist/extensions/kilocode/provider-catalog.js +0 -2
- package/dist/extensions/kilocode/provider-models.js +0 -2
- package/dist/extensions/kilocode/shared.js +0 -2
- package/dist/extensions/kimi-coding/api.js +0 -3
- package/dist/extensions/kimi-coding/index.js +0 -81
- package/dist/extensions/kimi-coding/onboard.js +0 -2
- package/dist/extensions/kimi-coding/openclaw.plugin.json +0 -38
- package/dist/extensions/kimi-coding/package.json +0 -12
- package/dist/extensions/kimi-coding/provider-catalog.js +0 -2
- package/dist/extensions/kimi-coding/stream.js +0 -2
- package/dist/extensions/line/api.js +0 -13
- package/dist/extensions/line/index.js +0 -815
- package/dist/extensions/line/openclaw.plugin.json +0 -11
- package/dist/extensions/line/package.json +0 -41
- package/dist/extensions/line/runtime-api.js +0 -11
- package/dist/extensions/line/setup-api.js +0 -3
- package/dist/extensions/line/setup-entry.js +0 -15
- package/dist/extensions/litellm/api.js +0 -2
- package/dist/extensions/litellm/index.js +0 -36
- package/dist/extensions/litellm/onboard.js +0 -2
- package/dist/extensions/litellm/openclaw.plugin.json +0 -33
- package/dist/extensions/litellm/package.json +0 -12
- package/dist/extensions/litellm/provider-catalog.js +0 -2
- package/dist/extensions/llm-task/api.js +0 -6
- package/dist/extensions/llm-task/index.js +0 -156
- package/dist/extensions/llm-task/openclaw.plugin.json +0 -33
- package/dist/extensions/llm-task/package.json +0 -16
- package/dist/extensions/lobster/index.js +0 -232
- package/dist/extensions/lobster/openclaw.plugin.json +0 -10
- package/dist/extensions/lobster/package.json +0 -17
- package/dist/extensions/lobster/runtime-api.js +0 -4
- package/dist/extensions/matrix/api.js +0 -10
- package/dist/extensions/matrix/helper-api.js +0 -3
- package/dist/extensions/matrix/index.js +0 -44
- package/dist/extensions/matrix/legacy-crypto-inspector.js +0 -41
- package/dist/extensions/matrix/openclaw.plugin.json +0 -11
- package/dist/extensions/matrix/package.json +0 -53
- package/dist/extensions/matrix/plugin-entry.handlers.runtime.js +0 -48
- package/dist/extensions/matrix/runtime-api.js +0 -25
- package/dist/extensions/matrix/setup-entry.js +0 -6
- package/dist/extensions/matrix/test-api.js +0 -3
- package/dist/extensions/matrix/thread-bindings-runtime.js +0 -2
- package/dist/extensions/mattermost/api.js +0 -2
- package/dist/extensions/mattermost/index.js +0 -15
- package/dist/extensions/mattermost/openclaw.plugin.json +0 -11
- package/dist/extensions/mattermost/package.json +0 -41
- package/dist/extensions/mattermost/runtime-api.js +0 -41
- package/dist/extensions/mattermost/setup-entry.js +0 -6
- package/dist/extensions/memory-core/api.js +0 -1
- package/dist/extensions/memory-core/index.js +0 -440
- package/dist/extensions/memory-core/openclaw.plugin.json +0 -9
- package/dist/extensions/memory-core/package.json +0 -23
- package/dist/extensions/memory-core/runtime-api.js +0 -3
- package/dist/extensions/memory-lancedb/api.js +0 -4
- package/dist/extensions/memory-lancedb/config.js +0 -2
- package/dist/extensions/memory-lancedb/index.js +0 -7037
- package/dist/extensions/memory-lancedb/lancedb-runtime.js +0 -2
- package/dist/extensions/memory-lancedb/openclaw.plugin.json +0 -92
- package/dist/extensions/memory-lancedb/package.json +0 -24
- package/dist/extensions/microsoft/index.js +0 -13
- package/dist/extensions/microsoft/openclaw.plugin.json +0 -13
- package/dist/extensions/microsoft/package.json +0 -15
- package/dist/extensions/microsoft/speech-provider.js +0 -2
- package/dist/extensions/microsoft/test-api.js +0 -2
- package/dist/extensions/microsoft/tts.js +0 -2
- package/dist/extensions/microsoft-foundry/auth.js +0 -2
- package/dist/extensions/microsoft-foundry/cli.js +0 -2
- package/dist/extensions/microsoft-foundry/index.js +0 -13
- package/dist/extensions/microsoft-foundry/onboard.js +0 -3
- package/dist/extensions/microsoft-foundry/openclaw.plugin.json +0 -39
- package/dist/extensions/microsoft-foundry/package.json +0 -12
- package/dist/extensions/microsoft-foundry/provider.js +0 -2
- package/dist/extensions/microsoft-foundry/runtime.js +0 -2
- package/dist/extensions/microsoft-foundry/shared-runtime.js +0 -3
- package/dist/extensions/microsoft-foundry/shared.js +0 -2
- package/dist/extensions/minimax/api.js +0 -6
- package/dist/extensions/minimax/image-generation-provider.js +0 -2
- package/dist/extensions/minimax/index.js +0 -219
- package/dist/extensions/minimax/media-understanding-provider.js +0 -2
- package/dist/extensions/minimax/model-definitions.js +0 -2
- package/dist/extensions/minimax/oauth.js +0 -2
- package/dist/extensions/minimax/oauth.runtime.js +0 -2
- package/dist/extensions/minimax/onboard.js +0 -2
- package/dist/extensions/minimax/openclaw.plugin.json +0 -98
- package/dist/extensions/minimax/package.json +0 -12
- package/dist/extensions/minimax/provider-catalog.js +0 -2
- package/dist/extensions/minimax/provider-models.js +0 -2
- package/dist/extensions/mistral/api.js +0 -5
- package/dist/extensions/mistral/index.js +0 -79
- package/dist/extensions/mistral/media-understanding-provider.js +0 -2
- package/dist/extensions/mistral/model-definitions.js +0 -2
- package/dist/extensions/mistral/onboard.js +0 -2
- package/dist/extensions/mistral/openclaw.plugin.json +0 -37
- package/dist/extensions/mistral/package.json +0 -12
- package/dist/extensions/mistral/provider-catalog.js +0 -2
- package/dist/extensions/modelstudio/api.js +0 -4
- package/dist/extensions/modelstudio/index.js +0 -111
- package/dist/extensions/modelstudio/model-definitions.js +0 -2
- package/dist/extensions/modelstudio/models.js +0 -2
- package/dist/extensions/modelstudio/onboard.js +0 -3
- package/dist/extensions/modelstudio/openclaw.plugin.json +0 -75
- package/dist/extensions/modelstudio/package.json +0 -12
- package/dist/extensions/modelstudio/provider-catalog.js +0 -2
- package/dist/extensions/moonshot/api.js +0 -4
- package/dist/extensions/moonshot/index.js +0 -58
- package/dist/extensions/moonshot/media-understanding-provider.js +0 -2
- package/dist/extensions/moonshot/onboard.js +0 -2
- package/dist/extensions/moonshot/openclaw.plugin.json +0 -87
- package/dist/extensions/moonshot/package.json +0 -12
- package/dist/extensions/moonshot/provider-catalog.js +0 -2
- package/dist/extensions/moonshot/test-api.js +0 -2
- package/dist/extensions/moonshot/web-search-provider.js +0 -2
- package/dist/extensions/msteams/api.js +0 -2
- package/dist/extensions/msteams/channel-config-api.js +0 -2
- package/dist/extensions/msteams/index.js +0 -13
- package/dist/extensions/msteams/openclaw.plugin.json +0 -11
- package/dist/extensions/msteams/package.json +0 -48
- package/dist/extensions/msteams/runtime-api.js +0 -47
- package/dist/extensions/msteams/setup-entry.js +0 -6
- package/dist/extensions/msteams/test-api.js +0 -2
- package/dist/extensions/nextcloud-talk/api.js +0 -2
- package/dist/extensions/nextcloud-talk/index.js +0 -12
- package/dist/extensions/nextcloud-talk/openclaw.plugin.json +0 -11
- package/dist/extensions/nextcloud-talk/package.json +0 -45
- package/dist/extensions/nextcloud-talk/runtime-api.js +0 -35
- package/dist/extensions/nextcloud-talk/setup-entry.js +0 -6
- package/dist/extensions/nostr/api.js +0 -15
- package/dist/extensions/nostr/index.js +0 -559
- package/dist/extensions/nostr/openclaw.plugin.json +0 -11
- package/dist/extensions/nostr/package.json +0 -44
- package/dist/extensions/nostr/runtime-api.js +0 -15
- package/dist/extensions/nostr/setup-api.js +0 -2
- package/dist/extensions/nostr/setup-entry.js +0 -6
- package/dist/extensions/nostr/test-api.js +0 -2
- package/dist/extensions/nvidia/api.js +0 -2
- package/dist/extensions/nvidia/index.js +0 -16
- package/dist/extensions/nvidia/openclaw.plugin.json +0 -17
- package/dist/extensions/nvidia/package.json +0 -12
- package/dist/extensions/nvidia/provider-catalog.js +0 -2
- package/dist/extensions/ollama/api.js +0 -3
- package/dist/extensions/ollama/index.js +0 -139
- package/dist/extensions/ollama/openclaw.plugin.json +0 -29
- package/dist/extensions/ollama/package.json +0 -12
- package/dist/extensions/ollama/runtime-api.js +0 -3
- package/dist/extensions/open-prose/index.js +0 -10
- package/dist/extensions/open-prose/openclaw.plugin.json +0 -13
- package/dist/extensions/open-prose/package.json +0 -12
- package/dist/extensions/open-prose/runtime-api.js +0 -3
- package/dist/extensions/open-prose/skills/prose/LICENSE +0 -21
- package/dist/extensions/open-prose/skills/prose/SKILL.md +0 -323
- package/dist/extensions/open-prose/skills/prose/alt-borges.md +0 -141
- package/dist/extensions/open-prose/skills/prose/alts/arabian-nights.md +0 -229
- package/dist/extensions/open-prose/skills/prose/alts/borges.md +0 -360
- package/dist/extensions/open-prose/skills/prose/alts/folk.md +0 -322
- package/dist/extensions/open-prose/skills/prose/alts/homer.md +0 -217
- package/dist/extensions/open-prose/skills/prose/alts/kafka.md +0 -373
- package/dist/extensions/open-prose/skills/prose/alts/shared-appendix.md +0 -35
- package/dist/extensions/open-prose/skills/prose/compiler.md +0 -2971
- package/dist/extensions/open-prose/skills/prose/examples/01-hello-world.prose +0 -4
- package/dist/extensions/open-prose/skills/prose/examples/02-research-and-summarize.prose +0 -6
- package/dist/extensions/open-prose/skills/prose/examples/03-code-review.prose +0 -17
- package/dist/extensions/open-prose/skills/prose/examples/04-write-and-refine.prose +0 -14
- package/dist/extensions/open-prose/skills/prose/examples/05-debug-issue.prose +0 -20
- package/dist/extensions/open-prose/skills/prose/examples/06-explain-codebase.prose +0 -17
- package/dist/extensions/open-prose/skills/prose/examples/07-refactor.prose +0 -20
- package/dist/extensions/open-prose/skills/prose/examples/08-blog-post.prose +0 -20
- package/dist/extensions/open-prose/skills/prose/examples/09-research-with-agents.prose +0 -25
- package/dist/extensions/open-prose/skills/prose/examples/10-code-review-agents.prose +0 -32
- package/dist/extensions/open-prose/skills/prose/examples/11-skills-and-imports.prose +0 -27
- package/dist/extensions/open-prose/skills/prose/examples/12-secure-agent-permissions.prose +0 -43
- package/dist/extensions/open-prose/skills/prose/examples/13-variables-and-context.prose +0 -51
- package/dist/extensions/open-prose/skills/prose/examples/14-composition-blocks.prose +0 -48
- package/dist/extensions/open-prose/skills/prose/examples/15-inline-sequences.prose +0 -23
- package/dist/extensions/open-prose/skills/prose/examples/16-parallel-reviews.prose +0 -19
- package/dist/extensions/open-prose/skills/prose/examples/17-parallel-research.prose +0 -19
- package/dist/extensions/open-prose/skills/prose/examples/18-mixed-parallel-sequential.prose +0 -36
- package/dist/extensions/open-prose/skills/prose/examples/19-advanced-parallel.prose +0 -71
- package/dist/extensions/open-prose/skills/prose/examples/20-fixed-loops.prose +0 -20
- package/dist/extensions/open-prose/skills/prose/examples/21-pipeline-operations.prose +0 -35
- package/dist/extensions/open-prose/skills/prose/examples/22-error-handling.prose +0 -51
- package/dist/extensions/open-prose/skills/prose/examples/23-retry-with-backoff.prose +0 -63
- package/dist/extensions/open-prose/skills/prose/examples/24-choice-blocks.prose +0 -86
- package/dist/extensions/open-prose/skills/prose/examples/25-conditionals.prose +0 -114
- package/dist/extensions/open-prose/skills/prose/examples/26-parameterized-blocks.prose +0 -100
- package/dist/extensions/open-prose/skills/prose/examples/27-string-interpolation.prose +0 -105
- package/dist/extensions/open-prose/skills/prose/examples/28-automated-pr-review.prose +0 -37
- package/dist/extensions/open-prose/skills/prose/examples/28-gas-town.prose +0 -1572
- package/dist/extensions/open-prose/skills/prose/examples/29-captains-chair.prose +0 -218
- package/dist/extensions/open-prose/skills/prose/examples/30-captains-chair-simple.prose +0 -42
- package/dist/extensions/open-prose/skills/prose/examples/31-captains-chair-with-memory.prose +0 -145
- package/dist/extensions/open-prose/skills/prose/examples/33-pr-review-autofix.prose +0 -168
- package/dist/extensions/open-prose/skills/prose/examples/34-content-pipeline.prose +0 -204
- package/dist/extensions/open-prose/skills/prose/examples/35-feature-factory.prose +0 -296
- package/dist/extensions/open-prose/skills/prose/examples/36-bug-hunter.prose +0 -237
- package/dist/extensions/open-prose/skills/prose/examples/37-the-forge.prose +0 -1474
- package/dist/extensions/open-prose/skills/prose/examples/38-skill-scan.prose +0 -455
- package/dist/extensions/open-prose/skills/prose/examples/39-architect-by-simulation.prose +0 -277
- package/dist/extensions/open-prose/skills/prose/examples/40-rlm-self-refine.prose +0 -32
- package/dist/extensions/open-prose/skills/prose/examples/41-rlm-divide-conquer.prose +0 -38
- package/dist/extensions/open-prose/skills/prose/examples/42-rlm-filter-recurse.prose +0 -46
- package/dist/extensions/open-prose/skills/prose/examples/43-rlm-pairwise.prose +0 -50
- package/dist/extensions/open-prose/skills/prose/examples/44-run-endpoint-ux-test.prose +0 -261
- package/dist/extensions/open-prose/skills/prose/examples/45-plugin-release.prose +0 -159
- package/dist/extensions/open-prose/skills/prose/examples/45-run-endpoint-ux-test-with-remediation.prose +0 -637
- package/dist/extensions/open-prose/skills/prose/examples/46-run-endpoint-ux-test-fast.prose +0 -148
- package/dist/extensions/open-prose/skills/prose/examples/46-workflow-crystallizer.prose +0 -225
- package/dist/extensions/open-prose/skills/prose/examples/47-language-self-improvement.prose +0 -356
- package/dist/extensions/open-prose/skills/prose/examples/48-habit-miner.prose +0 -445
- package/dist/extensions/open-prose/skills/prose/examples/49-prose-run-retrospective.prose +0 -210
- package/dist/extensions/open-prose/skills/prose/examples/README.md +0 -391
- package/dist/extensions/open-prose/skills/prose/examples/roadmap/README.md +0 -22
- package/dist/extensions/open-prose/skills/prose/examples/roadmap/iterative-refinement.prose +0 -20
- package/dist/extensions/open-prose/skills/prose/examples/roadmap/parallel-review.prose +0 -18
- package/dist/extensions/open-prose/skills/prose/examples/roadmap/simple-pipeline.prose +0 -17
- package/dist/extensions/open-prose/skills/prose/examples/roadmap/syntax/open-prose-syntax.prose +0 -223
- package/dist/extensions/open-prose/skills/prose/guidance/antipatterns.md +0 -951
- package/dist/extensions/open-prose/skills/prose/guidance/patterns.md +0 -700
- package/dist/extensions/open-prose/skills/prose/guidance/system-prompt.md +0 -180
- package/dist/extensions/open-prose/skills/prose/help.md +0 -144
- package/dist/extensions/open-prose/skills/prose/lib/README.md +0 -108
- package/dist/extensions/open-prose/skills/prose/lib/calibrator.prose +0 -215
- package/dist/extensions/open-prose/skills/prose/lib/cost-analyzer.prose +0 -174
- package/dist/extensions/open-prose/skills/prose/lib/error-forensics.prose +0 -250
- package/dist/extensions/open-prose/skills/prose/lib/inspector.prose +0 -196
- package/dist/extensions/open-prose/skills/prose/lib/profiler.prose +0 -460
- package/dist/extensions/open-prose/skills/prose/lib/program-improver.prose +0 -275
- package/dist/extensions/open-prose/skills/prose/lib/project-memory.prose +0 -118
- package/dist/extensions/open-prose/skills/prose/lib/user-memory.prose +0 -93
- package/dist/extensions/open-prose/skills/prose/lib/vm-improver.prose +0 -243
- package/dist/extensions/open-prose/skills/prose/primitives/session.md +0 -593
- package/dist/extensions/open-prose/skills/prose/prose.md +0 -1237
- package/dist/extensions/open-prose/skills/prose/state/filesystem.md +0 -498
- package/dist/extensions/open-prose/skills/prose/state/in-context.md +0 -384
- package/dist/extensions/open-prose/skills/prose/state/postgres.md +0 -880
- package/dist/extensions/open-prose/skills/prose/state/sqlite.md +0 -531
- package/dist/extensions/openai/api.js +0 -4
- package/dist/extensions/openai/cli-backend.js +0 -2
- package/dist/extensions/openai/default-models.js +0 -2
- package/dist/extensions/openai/image-generation-provider.js +0 -2
- package/dist/extensions/openai/index.js +0 -24
- package/dist/extensions/openai/media-understanding-provider.js +0 -2
- package/dist/extensions/openai/openai-codex-auth-identity.js +0 -2
- package/dist/extensions/openai/openai-codex-catalog.js +0 -2
- package/dist/extensions/openai/openai-codex-provider.js +0 -2
- package/dist/extensions/openai/openai-codex-provider.runtime.js +0 -14
- package/dist/extensions/openai/openai-provider.js +0 -2
- package/dist/extensions/openai/openclaw.plugin.json +0 -61
- package/dist/extensions/openai/package.json +0 -12
- package/dist/extensions/openai/shared.js +0 -4
- package/dist/extensions/openai/speech-provider.js +0 -2
- package/dist/extensions/openai/test-api.js +0 -5
- package/dist/extensions/openai/tts.js +0 -2
- package/dist/extensions/opencode/api.js +0 -3
- package/dist/extensions/opencode/index.js +0 -63
- package/dist/extensions/opencode/onboard.js +0 -2
- package/dist/extensions/opencode/openclaw.plugin.json +0 -33
- package/dist/extensions/opencode/package.json +0 -12
- package/dist/extensions/opencode-go/api.js +0 -3
- package/dist/extensions/opencode-go/index.js +0 -54
- package/dist/extensions/opencode-go/onboard.js +0 -2
- package/dist/extensions/opencode-go/openclaw.plugin.json +0 -33
- package/dist/extensions/opencode-go/package.json +0 -12
- package/dist/extensions/openrouter/api.js +0 -3
- package/dist/extensions/openrouter/index.js +0 -121
- package/dist/extensions/openrouter/media-understanding-provider.js +0 -2
- package/dist/extensions/openrouter/onboard.js +0 -2
- package/dist/extensions/openrouter/openclaw.plugin.json +0 -37
- package/dist/extensions/openrouter/package.json +0 -12
- package/dist/extensions/openrouter/provider-catalog.js +0 -2
- package/dist/extensions/openshell/index.js +0 -919
- package/dist/extensions/openshell/openclaw.plugin.json +0 -118
- package/dist/extensions/openshell/package.json +0 -12
- package/dist/extensions/perplexity/index.js +0 -13
- package/dist/extensions/perplexity/openclaw.plugin.json +0 -54
- package/dist/extensions/perplexity/package.json +0 -12
- package/dist/extensions/perplexity/test-api.js +0 -2
- package/dist/extensions/perplexity/web-search-provider.js +0 -2
- package/dist/extensions/phone-control/index.js +0 -283
- package/dist/extensions/phone-control/openclaw.plugin.json +0 -11
- package/dist/extensions/phone-control/runtime-api.js +0 -3
- package/dist/extensions/qianfan/api.js +0 -3
- package/dist/extensions/qianfan/index.js +0 -26
- package/dist/extensions/qianfan/onboard.js +0 -2
- package/dist/extensions/qianfan/openclaw.plugin.json +0 -32
- package/dist/extensions/qianfan/package.json +0 -12
- package/dist/extensions/qianfan/provider-catalog.js +0 -2
- package/dist/extensions/qqbot/api.js +0 -173
- package/dist/extensions/qqbot/index.js +0 -445
- package/dist/extensions/qqbot/openclaw.plugin.json +0 -257
- package/dist/extensions/qqbot/package.json +0 -52
- package/dist/extensions/qqbot/runtime-api.js +0 -2
- package/dist/extensions/qqbot/setup-entry.js +0 -6
- package/dist/extensions/qqbot/skills/qqbot-channel/SKILL.md +0 -262
- package/dist/extensions/qqbot/skills/qqbot-channel/references/api_references.md +0 -521
- package/dist/extensions/qqbot/skills/qqbot-media/SKILL.md +0 -36
- package/dist/extensions/qqbot/skills/qqbot-remind/SKILL.md +0 -149
- package/dist/extensions/searxng/index.js +0 -13
- package/dist/extensions/searxng/openclaw.plugin.json +0 -46
- package/dist/extensions/searxng/package.json +0 -12
- package/dist/extensions/searxng/web-search-provider.js +0 -2
- package/dist/extensions/sglang/api.js +0 -4
- package/dist/extensions/sglang/defaults.js +0 -2
- package/dist/extensions/sglang/index.js +0 -77
- package/dist/extensions/sglang/models.js +0 -2
- package/dist/extensions/sglang/openclaw.plugin.json +0 -29
- package/dist/extensions/sglang/package.json +0 -12
- package/dist/extensions/signal/api.js +0 -7
- package/dist/extensions/signal/channel-config-api.js +0 -2
- package/dist/extensions/signal/index.js +0 -319
- package/dist/extensions/signal/openclaw.plugin.json +0 -11
- package/dist/extensions/signal/package.json +0 -24
- package/dist/extensions/signal/reaction-runtime-api.js +0 -2
- package/dist/extensions/signal/runtime-api.js +0 -24
- package/dist/extensions/signal/setup-entry.js +0 -13
- package/dist/extensions/signal/test-api.js +0 -111
- package/dist/extensions/slack/api.js +0 -10
- package/dist/extensions/slack/channel-config-api.js +0 -2
- package/dist/extensions/slack/index.js +0 -12
- package/dist/extensions/slack/openclaw.plugin.json +0 -11
- package/dist/extensions/slack/package.json +0 -31
- package/dist/extensions/slack/runtime-api.js +0 -110
- package/dist/extensions/slack/setup-entry.js +0 -12
- package/dist/extensions/slack/test-api.js +0 -47
- package/dist/extensions/stepfun/index.js +0 -163
- package/dist/extensions/stepfun/onboard.js +0 -3
- package/dist/extensions/stepfun/openclaw.plugin.json +0 -83
- package/dist/extensions/stepfun/package.json +0 -12
- package/dist/extensions/stepfun/provider-catalog.js +0 -2
- package/dist/extensions/synology-chat/index.js +0 -12
- package/dist/extensions/synology-chat/openclaw.plugin.json +0 -11
- package/dist/extensions/synology-chat/package.json +0 -26
- package/dist/extensions/synology-chat/setup-api.js +0 -2
- package/dist/extensions/synology-chat/setup-entry.js +0 -6
- package/dist/extensions/synthetic/api.js +0 -4
- package/dist/extensions/synthetic/index.js +0 -27
- package/dist/extensions/synthetic/models.js +0 -2
- package/dist/extensions/synthetic/onboard.js +0 -3
- package/dist/extensions/synthetic/openclaw.plugin.json +0 -32
- package/dist/extensions/synthetic/package.json +0 -12
- package/dist/extensions/synthetic/provider-catalog.js +0 -2
- package/dist/extensions/talk-voice/api.js +0 -3
- package/dist/extensions/talk-voice/index.js +0 -160
- package/dist/extensions/talk-voice/openclaw.plugin.json +0 -11
- package/dist/extensions/tavily/index.js +0 -128
- package/dist/extensions/tavily/openclaw.plugin.json +0 -53
- package/dist/extensions/tavily/package.json +0 -12
- package/dist/extensions/tavily/skills/tavily/SKILL.md +0 -94
- package/dist/extensions/tavily/web-search-provider.js +0 -2
- package/dist/extensions/telegram/allow-from.js +0 -2
- package/dist/extensions/telegram/api.js +0 -14
- package/dist/extensions/telegram/channel-config-api.js +0 -2
- package/dist/extensions/telegram/index.js +0 -12
- package/dist/extensions/telegram/openclaw.plugin.json +0 -11
- package/dist/extensions/telegram/package.json +0 -37
- package/dist/extensions/telegram/runtime-api.js +0 -25
- package/dist/extensions/telegram/session-key-api.js +0 -2
- package/dist/extensions/telegram/setup-entry.js +0 -13
- package/dist/extensions/telegram/test-api.js +0 -88
- package/dist/extensions/telegram/test-support.js +0 -46
- package/dist/extensions/telegram/update-offset-runtime-api.js +0 -2
- package/dist/extensions/thread-ownership/api.js +0 -5
- package/dist/extensions/thread-ownership/index.js +0 -82
- package/dist/extensions/thread-ownership/openclaw.plugin.json +0 -30
- package/dist/extensions/tlon/api.js +0 -14
- package/dist/extensions/tlon/index.js +0 -150
- package/dist/extensions/tlon/openclaw.plugin.json +0 -12
- package/dist/extensions/tlon/package.json +0 -44
- package/dist/extensions/tlon/runtime-api.js +0 -14
- package/dist/extensions/tlon/setup-api.js +0 -3
- package/dist/extensions/tlon/setup-entry.js +0 -6
- package/dist/extensions/tlon/test-api.js +0 -2
- package/dist/extensions/together/api.js +0 -3
- package/dist/extensions/together/index.js +0 -26
- package/dist/extensions/together/models.js +0 -2
- package/dist/extensions/together/onboard.js +0 -2
- package/dist/extensions/together/openclaw.plugin.json +0 -32
- package/dist/extensions/together/package.json +0 -12
- package/dist/extensions/together/provider-catalog.js +0 -2
- package/dist/extensions/twitch/api.js +0 -9
- package/dist/extensions/twitch/index.js +0 -1090
- package/dist/extensions/twitch/openclaw.plugin.json +0 -11
- package/dist/extensions/twitch/package.json +0 -29
- package/dist/extensions/twitch/runtime-api.js +0 -9
- package/dist/extensions/venice/api.js +0 -3
- package/dist/extensions/venice/index.js +0 -55
- package/dist/extensions/venice/models.js +0 -2
- package/dist/extensions/venice/onboard.js +0 -3
- package/dist/extensions/venice/openclaw.plugin.json +0 -32
- package/dist/extensions/venice/package.json +0 -12
- package/dist/extensions/venice/provider-catalog.js +0 -2
- package/dist/extensions/vercel-ai-gateway/api.js +0 -4
- package/dist/extensions/vercel-ai-gateway/index.js +0 -30
- package/dist/extensions/vercel-ai-gateway/models.js +0 -2
- package/dist/extensions/vercel-ai-gateway/onboard.js +0 -2
- package/dist/extensions/vercel-ai-gateway/openclaw.plugin.json +0 -32
- package/dist/extensions/vercel-ai-gateway/package.json +0 -12
- package/dist/extensions/vercel-ai-gateway/provider-catalog.js +0 -2
- package/dist/extensions/vllm/api.js +0 -4
- package/dist/extensions/vllm/defaults.js +0 -2
- package/dist/extensions/vllm/index.js +0 -78
- package/dist/extensions/vllm/models.js +0 -2
- package/dist/extensions/vllm/openclaw.plugin.json +0 -29
- package/dist/extensions/vllm/package.json +0 -12
- package/dist/extensions/voice-call/api.js +0 -7
- package/dist/extensions/voice-call/index.js +0 -698
- package/dist/extensions/voice-call/openclaw.plugin.json +0 -700
- package/dist/extensions/voice-call/package.json +0 -33
- package/dist/extensions/voice-call/runtime-api.js +0 -7
- package/dist/extensions/voice-call/runtime-entry.js +0 -2
- package/dist/extensions/volcengine/api.js +0 -3
- package/dist/extensions/volcengine/index.js +0 -63
- package/dist/extensions/volcengine/models.js +0 -2
- package/dist/extensions/volcengine/openclaw.plugin.json +0 -33
- package/dist/extensions/volcengine/package.json +0 -12
- package/dist/extensions/volcengine/provider-catalog.js +0 -2
- package/dist/extensions/whatsapp/action-runtime-api.js +0 -2
- package/dist/extensions/whatsapp/action-runtime.runtime.js +0 -2
- package/dist/extensions/whatsapp/api.js +0 -8
- package/dist/extensions/whatsapp/auth-presence.js +0 -2
- package/dist/extensions/whatsapp/channel-config-api.js +0 -2
- package/dist/extensions/whatsapp/constants.js +0 -2
- package/dist/extensions/whatsapp/index.js +0 -12
- package/dist/extensions/whatsapp/light-runtime-api.js +0 -5
- package/dist/extensions/whatsapp/login-qr-api.js +0 -2
- package/dist/extensions/whatsapp/openclaw.plugin.json +0 -11
- package/dist/extensions/whatsapp/package.json +0 -45
- package/dist/extensions/whatsapp/runtime-api.js +0 -334
- package/dist/extensions/whatsapp/setup-entry.js +0 -23
- package/dist/extensions/whatsapp/test-api.js +0 -76
- package/dist/extensions/xai/api.js +0 -6
- package/dist/extensions/xai/code-execution.js +0 -2
- package/dist/extensions/xai/index.js +0 -122
- package/dist/extensions/xai/model-definitions.js +0 -2
- package/dist/extensions/xai/model-id.js +0 -2
- package/dist/extensions/xai/onboard.js +0 -2
- package/dist/extensions/xai/openclaw.plugin.json +0 -157
- package/dist/extensions/xai/package.json +0 -12
- package/dist/extensions/xai/provider-catalog.js +0 -2
- package/dist/extensions/xai/provider-models.js +0 -2
- package/dist/extensions/xai/stream.js +0 -2
- package/dist/extensions/xai/web-search.js +0 -2
- package/dist/extensions/xai/x-search.js +0 -2
- package/dist/extensions/xiaomi/api.js +0 -3
- package/dist/extensions/xiaomi/index.js +0 -37
- package/dist/extensions/xiaomi/onboard.js +0 -2
- package/dist/extensions/xiaomi/openclaw.plugin.json +0 -32
- package/dist/extensions/xiaomi/package.json +0 -12
- package/dist/extensions/xiaomi/provider-catalog.js +0 -2
- package/dist/extensions/zai/api.js +0 -3
- package/dist/extensions/zai/detect.js +0 -2
- package/dist/extensions/zai/index.js +0 -239
- package/dist/extensions/zai/media-understanding-provider.js +0 -2
- package/dist/extensions/zai/model-definitions.js +0 -2
- package/dist/extensions/zai/onboard.js +0 -2
- package/dist/extensions/zai/openclaw.plugin.json +0 -94
- package/dist/extensions/zai/package.json +0 -12
- package/dist/extensions/zai/runtime-api.js +0 -3
- package/dist/extensions/zalo/api.js +0 -3
- package/dist/extensions/zalo/index.js +0 -13
- package/dist/extensions/zalo/openclaw.plugin.json +0 -11
- package/dist/extensions/zalo/package.json +0 -47
- package/dist/extensions/zalo/runtime-api.js +0 -38
- package/dist/extensions/zalo/setup-entry.js +0 -6
- package/dist/extensions/zalo/test-api.js +0 -2
- package/dist/extensions/zalouser/api.js +0 -2
- package/dist/extensions/zalouser/index.js +0 -599
- package/dist/extensions/zalouser/openclaw.plugin.json +0 -11
- package/dist/extensions/zalouser/package.json +0 -48
- package/dist/extensions/zalouser/runtime-api.js +0 -30
- package/dist/extensions/zalouser/setup-entry.js +0 -13
- package/dist/extensions/zalouser/test-api.js +0 -5
- package/dist/external-content-DtoRY-d1.js +0 -242
- package/dist/facade-runtime-Bpi4Gy-u.js +0 -263
- package/dist/failover-error-BrdBIFg2.js +0 -180
- package/dist/feishu-81Iv8tqF.js +0 -18
- package/dist/feishu-conversation-NmEqAxSA.js +0 -17
- package/dist/feishu-setup-DXAK6GqE.js +0 -12
- package/dist/fetch-7dbKQPT5.js +0 -256
- package/dist/fetch-BjiGl865.js +0 -418
- package/dist/fetch-Bp23aO5S.js +0 -65
- package/dist/fetch-guard-BnhVFBtf.js +0 -150
- package/dist/fetch-mock-WghRRNpK.js +0 -6
- package/dist/fetch-runtime-DfFmkkZH.js +0 -4
- package/dist/fetch-timeout-BKMGkHu9.js +0 -58
- package/dist/file-identity-DPm-6kp0.js +0 -567
- package/dist/file-lock-CH0V9dhL.js +0 -141
- package/dist/file-lock-D292sbfa.js +0 -2
- package/dist/firecrawl-client-GOGPuEeg.js +0 -338
- package/dist/firecrawl-search-provider-ptp7GNAt.js +0 -47
- package/dist/format-BwzSM9vY.js +0 -24
- package/dist/format-D38w0aKT.js +0 -817
- package/dist/format-DCFsK8oa.js +0 -19
- package/dist/format-datetime-Bl6-b7G1.js +0 -73
- package/dist/format-duration-DqcpDYkZ.js +0 -57
- package/dist/format-relative-CYrRyrT7.js +0 -54
- package/dist/frontmatter-Ba2Hn11b.js +0 -45
- package/dist/frontmatter-Cjxy4vX8.js +0 -210
- package/dist/fs-safe-BQ59ttjX.js +0 -999
- package/dist/gateway-CWPyH-P7.js +0 -2575
- package/dist/gateway-cli-BmPBPCcJ.js +0 -1572
- package/dist/gateway-discovery-targets--oNzWOVw.js +0 -430
- package/dist/gateway-install-token-tyQyR2ED.js +0 -230
- package/dist/gateway-lock-BbUQuUet.js +0 -292
- package/dist/gateway-presence-BVCIm1hy.js +0 -15
- package/dist/gateway-registry-tQcIThkV.js +0 -74
- package/dist/gateway-rpc-BXdoSPN7.js +0 -26
- package/dist/gateway-runtime-C7EhkU_c.js +0 -12
- package/dist/gaxios-fetch-compat-D4gl5582.js +0 -180
- package/dist/gemini-auth-Dlpcd9MB.js +0 -29
- package/dist/gemini-web-search-provider-CkNEl2du.js +0 -162
- package/dist/get-reply-from-config.runtime-vBmBttxw.js +0 -2
- package/dist/get-reply-from-config.runtime.js +0 -1
- package/dist/git-commit-BS28SZVT.js +0 -2
- package/dist/git-commit-ChiOoWyK.js +0 -177
- package/dist/github-copilot-login-B8s4OVQ4.js +0 -11
- package/dist/github-copilot-token-BwXtH-ZJ.js +0 -2
- package/dist/github-copilot-token-D81Wvm8b.js +0 -2
- package/dist/github-copilot-token-beHjQGOp.js +0 -91
- package/dist/global-singleton-54zT3rCn.js +0 -13
- package/dist/global-singleton-iOq2_-p8.js +0 -1
- package/dist/global-state-DUuMGgts.js +0 -17
- package/dist/globals-CDtV0ht8.js +0 -25
- package/dist/gmail-setup-utils-CxXfU7cy.js +0 -421
- package/dist/google-Cx1_z3Q3.js +0 -24
- package/dist/googlechat-CoiMNDC2.js +0 -37
- package/dist/graph-users-BmZWavVH.js +0 -917
- package/dist/group-access-BZZ3FNmb.js +0 -32
- package/dist/group-access-BjveEAjz.js +0 -113
- package/dist/group-access-owHLoCWX.js +0 -119
- package/dist/group-activation-ClEb7qwj.js +0 -22
- package/dist/group-id-CcsUNOct.js +0 -11
- package/dist/group-policy-Bb8dxOpM.js +0 -25
- package/dist/group-policy-G9GcSyph.js +0 -201
- package/dist/group-policy-wS9F8Y_i.js +0 -51
- package/dist/group-policy-warnings-CL2gHsXM.js +0 -196
- package/dist/groups.runtime-K758xQFV.js +0 -6
- package/dist/groups.runtime.js +0 -1
- package/dist/handle-action-CE4vpa7G.js +0 -500
- package/dist/health-D_KQbqQY.js +0 -577
- package/dist/health-a1VPtSyV.js +0 -2
- package/dist/health-format-COR93f8W.js +0 -26
- package/dist/heartbeat-C_TrErgd.js +0 -122
- package/dist/heartbeat-reply-payload-C7Ac3fOZ.js +0 -13
- package/dist/heartbeat-summary-v5gPj5bd.js +0 -57
- package/dist/heartbeat-visibility-CHMO4pmJ.js +0 -102
- package/dist/help-CyR1Oo4X.js +0 -81
- package/dist/help-format-l1phKNA8.js +0 -15
- package/dist/helpers-COHnQbWX.js +0 -32
- package/dist/helpers-DNpD7VMo.js +0 -24
- package/dist/history-B4mW8GfY.js +0 -102
- package/dist/home-dir-n9onX5PG.js +0 -83
- package/dist/hook-runner-global-C9GhdBtf.js +0 -632
- package/dist/hook-runner-global-Cr_Pthp4.js +0 -2
- package/dist/hook-runtime-DK8QF-2x.js +0 -3
- package/dist/hooks-cli-XCo-7nz9.js +0 -353
- package/dist/hooks-policy-BmULXBcp.js +0 -20
- package/dist/hooks-status-D_ZxRD0H.js +0 -86
- package/dist/host-env-security-SlXI58bc.js +0 -290
- package/dist/host-runtime-BGHb06HN.js +0 -1
- package/dist/hostname-BRYLr_mh.js +0 -8
- package/dist/http-body-CfdQSEp0.js +0 -238
- package/dist/http-client-_3i4-nw5.js +0 -294
- package/dist/http-registry-ZGSBfYSe.js +0 -54
- package/dist/http-route-overlap-C8lbolN5.js +0 -101
- package/dist/identity-BoQQh6Y8.js +0 -26
- package/dist/identity-D64Wfv_s.js +0 -29
- package/dist/identity-DvwF6Yix.js +0 -84
- package/dist/identity-avatar-DPBgVwpU.js +0 -86
- package/dist/identity-file-BtIJI4dn.js +0 -60
- package/dist/image-C4V19x9x.js +0 -152
- package/dist/image-generation-BbOnazXi.js +0 -1
- package/dist/image-generation-provider-CQK8HA0O.js +0 -136
- package/dist/image-generation-provider-CWUvRxmv.js +0 -131
- package/dist/image-generation-provider-F2sfNjcr.js +0 -281
- package/dist/image-ops-BJKjhMAu.js +0 -476
- package/dist/image-runtime-DodXcK2e.js +0 -7
- package/dist/imessage-2ukkzufG.js +0 -19
- package/dist/inbound-context-Cxvd3JHZ.js +0 -59
- package/dist/inbound-context-Czd3FdNb.js +0 -52
- package/dist/inbound-debounce-CI7l3R36.js +0 -159
- package/dist/inbound-envelope-LyHTUTue.js +0 -61
- package/dist/inbound-reply-dispatch-BdeY6N-k.js +0 -73
- package/dist/inbound-text-CacimZtw.js +0 -14
- package/dist/inbound.runtime-CqX3n132.js +0 -3
- package/dist/inbound.runtime.js +0 -1
- package/dist/includes-Ccr-Q5DB.js +0 -188
- package/dist/includes-scan-DVvYLj4S.js +0 -55
- package/dist/index.js +0 -56
- package/dist/infra/warning-filter.js +0 -2
- package/dist/infra-runtime-Cl0iZpRS.js +0 -22
- package/dist/input-files-PZC6ZR4W.js +0 -251
- package/dist/input-provenance-8GBFuk0Q.js +0 -44
- package/dist/inspect-B8dw6NVJ.js +0 -286
- package/dist/install-Bk7XyvtC.js +0 -590
- package/dist/install-package-dir-C2ba2toB.js +0 -284
- package/dist/install-safe-path-CkCd0rWS.js +0 -69
- package/dist/install-security-scan-C02VaEU9.js +0 -22
- package/dist/install-security-scan.runtime-CDUuS6Dl.js +0 -299
- package/dist/install-security-scan.runtime.js +0 -1
- package/dist/install-source-utils-2tdWQmwI.js +0 -167
- package/dist/install-target-CBNysuiP.js +0 -175
- package/dist/install.runtime-D_zKCpnB.js +0 -11
- package/dist/install.runtime-DjQozvMe.js +0 -27
- package/dist/install.runtime.js +0 -1
- package/dist/installs-BnAbQ70v.js +0 -28
- package/dist/interactive-CEnsu1RT.js +0 -8
- package/dist/interactive-runtime-Bxq5ztle.js +0 -1
- package/dist/internal-Lk0wFTNq.js +0 -411
- package/dist/internal-hooks-C3ziA7w_.js +0 -155
- package/dist/io-BUxKr33U.js +0 -2236
- package/dist/io-BuMkfjp3.js +0 -4
- package/dist/ip-BZP18dDl.js +0 -208
- package/dist/ipv4-Xiyu1rvD.js +0 -82
- package/dist/irc-DXfYh-Sd.js +0 -14
- package/dist/irc-surface-Di-joPuc.js +0 -15
- package/dist/is-main-Bv5ej4lF.js +0 -27
- package/dist/issue-format-8Eylt-2O.js +0 -2
- package/dist/issue-format-DLZczJ0U.js +0 -31
- package/dist/json-file-5xJqyXrK.js +0 -23
- package/dist/json-files-C4lsZ_HR.js +0 -72
- package/dist/json-mode-4O1xL2fT.js +0 -34
- package/dist/json-pointer-C30cknnZ.js +0 -43
- package/dist/json-store-FUgu1pyj.js +0 -37
- package/dist/keyed-async-queue-BTOTBE8n.js +0 -32
- package/dist/kill-tree-CIfAbzdN.js +0 -2
- package/dist/kill-tree-CUPpLbeq.js +0 -82
- package/dist/kimi-web-search-provider-Cb3F9_Da.js +0 -224
- package/dist/known-users-DjerpGCB.js +0 -167
- package/dist/lancedb-runtime-DTb_mDUG.js +0 -201
- package/dist/launchd-cp98MUhz.js +0 -517
- package/dist/lazy-runtime-C6LCAM-L.js +0 -29
- package/dist/lazy-runtime-yh_WT4on.js +0 -1
- package/dist/lazy-service-module-CeifO9oo.js +0 -24
- package/dist/legacy-crypto-restore-CsHmLVjY.js +0 -85
- package/dist/legacy-names-CFo8Oe-2.js +0 -9
- package/dist/legacy-web-search-Bn8F2jrN.js +0 -140
- package/dist/legacy.shared-oZM32end.js +0 -45
- package/dist/level-overrides-gqc8xfh2.js +0 -36
- package/dist/library-ChjjeniJ.js +0 -44
- package/dist/lifecycle-core-Ce_ZEvjl.js +0 -402
- package/dist/line-Be0jY3CU.js +0 -4
- package/dist/line-runtime-B0ZGTK81.js +0 -60
- package/dist/line-surface-BTopinac.js +0 -27
- package/dist/links-D7o22Ygt.js +0 -17
- package/dist/listeners-_47QWN8-.js +0 -14
- package/dist/llm-slug-generator-Dv-sLOW9.js +0 -68
- package/dist/llm-slug-generator.js +0 -2
- package/dist/llm-task-BDA3i86A.js +0 -3
- package/dist/load-BXSzQ8YA.js +0 -28
- package/dist/load-options-BqszWkkN.js +0 -36
- package/dist/loader-DsZDoLQ6.js +0 -2519
- package/dist/local-file-access-PajXzDdK.js +0 -49
- package/dist/local-roots-DrpNBv4b.js +0 -46
- package/dist/location-CtMHXE-d.js +0 -42
- package/dist/logger-Bc2lC3X3.js +0 -79
- package/dist/logger-CIV9WBoa.js +0 -11
- package/dist/logger-DdGvppaE.js +0 -75
- package/dist/logger-MWEZrHUL.js +0 -582
- package/dist/logging-B1QD0DRq.js +0 -2
- package/dist/logging-CAfgK7sj.js +0 -3
- package/dist/logging-ColPHIDT.js +0 -16
- package/dist/logging-D4xyCEzZ.js +0 -85
- package/dist/logging-Dt_aiHur.js +0 -13
- package/dist/logging-Dywye6yZ.js +0 -2
- package/dist/logging-core-hw7Df6Hl.js +0 -3
- package/dist/login-BrqdQcNh.js +0 -108
- package/dist/login-DSqrZNW-.js +0 -2333
- package/dist/login-qr-Dm3iC25_.js +0 -212
- package/dist/login-qr-api-BxRDJKkp.js +0 -16
- package/dist/logs-cli-CxjV80As.js +0 -221
- package/dist/magic-string.es-CVHNyqW1.js +0 -1011
- package/dist/main-session-BbO0A-No.js +0 -38
- package/dist/manager-c_fEco8X.js +0 -2773
- package/dist/manager-runtime-Vy_F8ORR.js +0 -2
- package/dist/manager.runtime-Bm1Knq-3.js +0 -670
- package/dist/manager.runtime.js +0 -1
- package/dist/manifest-f_gihArl.js +0 -237
- package/dist/manifest-registry-Kx5Z2lJL.js +0 -1069
- package/dist/map-size-BBQq2RPq.js +0 -15
- package/dist/markdown-table-runtime-Dx12DQTq.js +0 -2
- package/dist/markdown-tables-CSlyhQVP.js +0 -31
- package/dist/mask-api-key-BYVFDQdo.js +0 -10
- package/dist/matrix-NDa-FoaE.js +0 -351
- package/dist/matrix-config-helpers-D0D-oL7I.js +0 -153
- package/dist/matrix-helper-DioFIiaO.js +0 -20
- package/dist/matrix-migration-snapshot-DAOFFSgM.js +0 -714
- package/dist/matrix-ouiFsVI1.js +0 -39
- package/dist/matrix-runtime-heavy-KUEg_MfD.js +0 -4
- package/dist/matrix-runtime-shared-B6aE5Uu6.js +0 -1
- package/dist/matrix-runtime-surface-R78Hde6Q.js +0 -12
- package/dist/matrix-surface-B-nU-e0B.js +0 -13
- package/dist/matrix-thread-bindings-B77Krs8r.js +0 -12
- package/dist/mattermost-Cg2PI71s.js +0 -18
- package/dist/mcp/plugin-tools-serve.js +0 -105
- package/dist/mcp-cli-BMNT4RIm.js +0 -768
- package/dist/mcp-config-3y1z3N2x.js +0 -118
- package/dist/media-CUdiVhEt.js +0 -1691
- package/dist/media-limits-DmcT5KnO.js +0 -14
- package/dist/media-runtime-8QR_uTs0.js +0 -295
- package/dist/media-understanding-CpMkSmKj.js +0 -51
- package/dist/media-understanding-provider-C0xVBaas.js +0 -11
- package/dist/media-understanding-provider-C8RSrdEy.js +0 -16
- package/dist/media-understanding-provider-CCB6_bwc2.js +0 -9
- package/dist/media-understanding-provider-CZ83ep6L.js +0 -16
- package/dist/media-understanding-provider-Cmcg82YY.js +0 -28
- package/dist/media-understanding-provider-D1_z5Kwx.js +0 -11
- package/dist/media-understanding-provider-DC9bhcFj.js +0 -11
- package/dist/media-understanding-provider-DbMV-bHu.js +0 -17
- package/dist/media-understanding-provider-Drg3Vyoe.js +0 -86
- package/dist/media-understanding-runtime-JIhXSD7e.js +0 -15
- package/dist/media-understanding.runtime-2Muvvak4.js +0 -7
- package/dist/media-understanding.runtime.js +0 -1
- package/dist/memory-DPYsR3Cn.js +0 -228
- package/dist/memory-core-engine-runtime-DP_nzKMm.js +0 -14
- package/dist/memory-core-host-engine-embeddings-BhsmnP-6.js +0 -1370
- package/dist/memory-core-host-engine-foundation-CtFD8jpc.js +0 -10
- package/dist/memory-core-host-engine-qmd-DCvmid82.js +0 -375
- package/dist/memory-core-host-engine-storage-N3YflxjF.js +0 -109
- package/dist/memory-core-host-multimodal-BVd6eEE3.js +0 -1
- package/dist/memory-core-host-query-Bxzu0mAD.js +0 -2
- package/dist/memory-core-host-runtime-cli-nz5fxbbn.js +0 -10
- package/dist/memory-core-host-runtime-core-BcGQFQUw.js +0 -8
- package/dist/memory-core-host-runtime-files-DO5A0pY5.js +0 -3
- package/dist/memory-core-host-secret-CQAiTACg.js +0 -1
- package/dist/memory-core-host-status-0g2XexDn.js +0 -44
- package/dist/memory-embedding-providers-BOMS0u3X.js +0 -34
- package/dist/memory-lancedb-kZBcB07O.js +0 -2
- package/dist/memory-runtime-BIGGpmaC.js +0 -2
- package/dist/memory-runtime-XyiDon5w.js +0 -34
- package/dist/memory-search-CQFSugEg.js +0 -2
- package/dist/memory-search-DpAYXyjc.js +0 -202
- package/dist/memory-state-DDJh6L4-.js +0 -41
- package/dist/mention-gating-_kzByrvM.js +0 -25
- package/dist/mentions-BCBF5Klg.js +0 -65
- package/dist/mentions-BKYgZd-C.js +0 -155
- package/dist/merge-patch-CtvE2OP0.js +0 -65
- package/dist/message-Dna6Vsdg.js +0 -205
- package/dist/message-action-discovery-CPTCAl8K.js +0 -133
- package/dist/message-action-runner-Byldnjex.js +0 -1703
- package/dist/message-channel-bEWdw4ul.js +0 -105
- package/dist/message-handler-C9TRy9hy.js +0 -1771
- package/dist/message-hook-mappers-CzwFvq_c.js +0 -255
- package/dist/messages-CQT2FDZ3.js +0 -270
- package/dist/method-scopes-B6e_KuRU.js +0 -2663
- package/dist/migrate-cli-C_iDle77.js +0 -500
- package/dist/migration-snapshot.runtime-DAzTUgU-.js +0 -3
- package/dist/migration-snapshot.runtime.js +0 -1
- package/dist/mime-DkaKoCVP.js +0 -156
- package/dist/min-host-version-CIhgRq0c.js +0 -349
- package/dist/minimax-vlm-Dh6g-MLh.js +0 -512
- package/dist/model-auth-DcCyGs_6.js +0 -377
- package/dist/model-auth-env-OT6ucjoP.js +0 -63
- package/dist/model-auth-label-fGtOs2u-.js +0 -45
- package/dist/model-auth-markers-D2z_AIuC.js +0 -70
- package/dist/model-auth-runtime-shared-DnhseQ_3.js +0 -18
- package/dist/model-buttons-zQ-cRhqo.js +0 -187
- package/dist/model-catalog-f9zO4b6C.js +0 -208
- package/dist/model-catalog.runtime-iRu_OMJS.js +0 -2
- package/dist/model-catalog.runtime.js +0 -1
- package/dist/model-definitions-BGWurJeM.js +0 -49
- package/dist/model-definitions-C-e07VgA.js +0 -255
- package/dist/model-definitions-DUANiLUG.js +0 -126
- package/dist/model-definitions-DrxdjAkB.js +0 -202
- package/dist/model-id-9W_sHLHy.js +0 -12
- package/dist/model-id-DEv7HEyl.js +0 -20
- package/dist/model-input-DwbpFRYm.js +0 -20
- package/dist/model-overrides-BaecXmMv.js +0 -84
- package/dist/model-overrides-UUODi1G1.js +0 -91
- package/dist/model-param-b-BHlbSl5R.js +0 -15
- package/dist/model-picker-BgVMnja3.js +0 -439
- package/dist/model-picker-D8elZqN_.js +0 -3
- package/dist/model-picker.runtime-BUfgRpvT.js +0 -15
- package/dist/model-picker.runtime.js +0 -1
- package/dist/model-runtime-CkjgxQmB.js +0 -1772
- package/dist/model-selection-DXY5nwui.js +0 -407
- package/dist/model-selection-Dx6Ag-ZE.js +0 -505
- package/dist/model-suppression-C8XSEnsv.js +0 -24
- package/dist/model-suppression.runtime-Dqra9AAF.js +0 -7
- package/dist/model-suppression.runtime.js +0 -1
- package/dist/models--Ze_o5z6.js +0 -193
- package/dist/models-7_w_ynp7.js +0 -35
- package/dist/models-BMmB_oQc.js +0 -130
- package/dist/models-B_TKM4gG.js +0 -18
- package/dist/models-B_UROL5b.js +0 -3
- package/dist/models-BmDGSZCK.js +0 -31
- package/dist/models-BywIyTzh.js +0 -126
- package/dist/models-C5GB-E7D.js +0 -154
- package/dist/models-DU3Lv10g.js +0 -754
- package/dist/models-VgpjMiqM.js +0 -138
- package/dist/models-_DZVZ2Na.js +0 -18
- package/dist/models-bBe0FFJe.js +0 -2550
- package/dist/models-cli-kZR11PgM.js +0 -197
- package/dist/models-config-D6j7jkCN.js +0 -2
- package/dist/models-config-lYKxSxLo.js +0 -1106
- package/dist/models-config.runtime-D2tdVUsi.js +0 -2
- package/dist/models-config.runtime.js +0 -1
- package/dist/models-emJUoDsF.js +0 -40
- package/dist/models-gPCm0Fco.js +0 -122
- package/dist/models-mi7nu17U.js +0 -99
- package/dist/models-nUP9ZPV8.js +0 -552
- package/dist/models-provider-runtime-DfIZ0cvV.js +0 -2
- package/dist/monitor-BEjwRY59.js +0 -3622
- package/dist/monitor-BiwufaF7.js +0 -718
- package/dist/monitor-BqwlHm94.js +0 -3660
- package/dist/monitor-Cj6Atk8I.js +0 -676
- package/dist/monitor-D2qBnOYz.js +0 -666
- package/dist/monitor-DZmBV1Nj2.js +0 -2
- package/dist/monitor-RMoB6sGQ.js +0 -4067
- package/dist/monitor-auth-EvlH1zbN.js +0 -192
- package/dist/monitor-lbUydC5_.js +0 -1525
- package/dist/monitor-polling.runtime-O6ZG6yYv.js +0 -307
- package/dist/monitor-polling.runtime.js +0 -1
- package/dist/monitor-provider-x4TUmYNN.js +0 -1782
- package/dist/monitor-vE0skDM1.js +0 -687
- package/dist/monitor-webhook.runtime-DoTv_oZv.js +0 -297
- package/dist/monitor-webhook.runtime.js +0 -1
- package/dist/moonshot-stream-wrappers-Csssd6kW.js +0 -24
- package/dist/moonshot-thinking-stream-wrappers-D3j6Q9Yk.js +0 -69
- package/dist/msteams-DsFLaFEV.js +0 -33
- package/dist/multimodal-DoXQtvBl.js +0 -66
- package/dist/mutable-allowlist-detectors-CbFK2eTK.js +0 -62
- package/dist/native-command-registry-B4BAqmAW.js +0 -2
- package/dist/native-command-session-targets-DfUCBY63.js +0 -10
- package/dist/net-DjzoSxQo.js +0 -265
- package/dist/network-discovery-display-h8MqTMCI.js +0 -48
- package/dist/network-mode-QjJNOXMa.js +0 -17
- package/dist/nextcloud-talk-COZ5YrPY.js +0 -18
- package/dist/node-cli-z8LHYl1w.js +0 -2323
- package/dist/node-command-policy-DoeXKD1V.js +0 -205
- package/dist/node-commands-ycnRypMq.js +0 -11
- package/dist/node-require-BgDD9bTi.js +0 -14
- package/dist/node-resolve-Drr1RIcN.js +0 -110
- package/dist/node-service-Nyf8eLc4.js +0 -68
- package/dist/node-startup-env-CVMjxAUF.js +0 -50
- package/dist/nodes-cli-BMyafV_R.js +0 -1044
- package/dist/nodes-screen-oyGHNlY5.js +0 -219
- package/dist/nodes.helpers-DKBA9twS.js +0 -203
- package/dist/normalize-C46CugdT.js +0 -35
- package/dist/normalize-reply-VrMa_PA2.js +0 -500
- package/dist/normalize-secret-input-CC9iFcnK.js +0 -32
- package/dist/nostr-DGfd-Skj.js +0 -19
- package/dist/note-B5R9zt2K.js +0 -109
- package/dist/notify-CKEt6MKY.js +0 -298
- package/dist/npm-registry-spec-dWUFCqDk.js +0 -100
- package/dist/oauth-BBIy_OBl.js +0 -152
- package/dist/oauth-DzMUt9ZO.js +0 -56
- package/dist/oauth.credentials-CKrXIXPf.js +0 -119
- package/dist/oauth.flow-Dd6MZcHS.js +0 -122
- package/dist/oauth.http-CZlbXoZD.js +0 -23
- package/dist/oauth.project-DAVNbct7.js +0 -125
- package/dist/oauth.shared-D1_wwlyt.js +0 -26
- package/dist/oauth.token-CnB-w_v2.js +0 -42
- package/dist/ollama-EE_SvO8P.js +0 -26
- package/dist/onboard-2otvb4-n.js +0 -307
- package/dist/onboard-72nGEzpd.js +0 -25
- package/dist/onboard-B14oDS4M.js +0 -23
- package/dist/onboard-BH5hcOyK.js +0 -39
- package/dist/onboard-BRkQIFt_.js +0 -25
- package/dist/onboard-BSArlkkq.js +0 -73
- package/dist/onboard-Bau_HSmO.js +0 -36
- package/dist/onboard-BsqpQhXj.js +0 -591
- package/dist/onboard-CBa7u1iZ.js +0 -25
- package/dist/onboard-CLKZrJow.js +0 -29
- package/dist/onboard-COaMJ5wS.js +0 -28
- package/dist/onboard-CbTQZti9.js +0 -48
- package/dist/onboard-Ce3rcBNR.js +0 -29
- package/dist/onboard-CiMzgSvO.js +0 -67
- package/dist/onboard-DNuWjUOg.js +0 -17
- package/dist/onboard-DY3JtOcX.js +0 -47
- package/dist/onboard-Dd_eFJ7g.js +0 -25
- package/dist/onboard-DgMI-C3P.js +0 -24
- package/dist/onboard-Dj3Kkl9C.js +0 -49
- package/dist/onboard-Djne8OY8.js +0 -34
- package/dist/onboard-DnNU28xK.js +0 -48
- package/dist/onboard-MFS79j1I.js +0 -23
- package/dist/onboard-REHqRyQm.js +0 -26
- package/dist/onboard-VVnzyvuh.js +0 -46
- package/dist/onboard-channels-B8UocRdo.js +0 -705
- package/dist/onboard-channels-Ci5QQyQ0.js +0 -2
- package/dist/onboard-config-7edS4yYd.js +0 -2
- package/dist/onboard-config-C-6p2HNL.js +0 -29
- package/dist/onboard-custom-BREC7K0s.js +0 -2
- package/dist/onboard-custom-D7oXiV6u.js +0 -646
- package/dist/onboard-helpers-BCIfy96O.js +0 -4
- package/dist/onboard-helpers-CLedS83g.js +0 -227
- package/dist/onboard-hooks-DW0gav3i.js +0 -52
- package/dist/onboard-lh38uUOa.js +0 -25
- package/dist/onboard-rH_HA9v-.js +0 -34
- package/dist/onboard-remote-DG5Rb2y7.js +0 -2
- package/dist/onboard-remote-DgDwHLCW.js +0 -193
- package/dist/onboard-search-Bz78SAON.js +0 -339
- package/dist/onboard-skills-BK1iF6NI.js +0 -2
- package/dist/onboard-skills-Cfs4Gz-d.js +0 -134
- package/dist/openai-B3atWWiZ.js +0 -21
- package/dist/openai-codex-auth-identity-blYgqX5I.js +0 -40
- package/dist/openai-codex-catalog-DQoW5ntP.js +0 -11
- package/dist/openai-codex-provider-BeX3_fQL.js +0 -254
- package/dist/openai-provider-DfSRc815.js +0 -272
- package/dist/openai-stream-wrappers-Cmn9nSYj.js +0 -338
- package/dist/openclaw-exec-env-DnTrPIdx.js +0 -17
- package/dist/openclaw-root-F1SOdmJ0.js +0 -88
- package/dist/openclaw-tools.runtime-Dx73yE1V.js +0 -2
- package/dist/openclaw-tools.runtime.js +0 -1
- package/dist/operator-approvals-client-NQtGN5k_.js +0 -33
- package/dist/order-DNBhjW6F.js +0 -744
- package/dist/origin-routing-BC2EzQqM.js +0 -15
- package/dist/origin-routing-BVUTLJGu.js +0 -2
- package/dist/os-summary-CdlXkpL1.js +0 -26
- package/dist/outbound-adapter-D6WGzS7P.js +0 -183
- package/dist/outbound-ek4CqhT2.js +0 -2
- package/dist/outbound-hgMEZIU9.js +0 -2739
- package/dist/outbound-media-B2ZrkOxl.js +0 -15
- package/dist/outbound-runtime-CtDmQy3z.js +0 -2
- package/dist/outbound-send-deps-COPf3lwV.js +0 -7
- package/dist/outbound-session-DHKvttsz.js +0 -41
- package/dist/pairing-challenge-O7jo1Q5R.js +0 -33
- package/dist/pairing-cli-BEUxC5EH.js +0 -116
- package/dist/pairing-labels-Cj1fFxG9.js +0 -7
- package/dist/pairing-message-FaqHPEFk.js +0 -4
- package/dist/pairing-messages-DxLpe0GE.js +0 -23
- package/dist/pairing-pending-DGyUpmjC.js +0 -14
- package/dist/pairing-store-B171dUVa.js +0 -617
- package/dist/pairing-token-CnyXOU__.js +0 -88
- package/dist/param-readers-CqAoT2n2.js +0 -2
- package/dist/parse-duration-D9q1V0xx.js +0 -41
- package/dist/parse-finite-number-CzdLC_gQ.js +0 -30
- package/dist/parse-json-compat-DfjRYtKX.js +0 -11
- package/dist/parse-log-line-Cd0JHPGg.js +0 -43
- package/dist/parse-port-BFmNhrtE.js +0 -8
- package/dist/parse-timeout-bktz0495.js +0 -27
- package/dist/path-alias-guards-BeOn9PZR.js +0 -40
- package/dist/path-env-C2F1kvna.js +0 -87
- package/dist/path-safety-2mfHCe0n.js +0 -12
- package/dist/paths-Bw8Ci57u.js +0 -2
- package/dist/paths-GWMNxnBn.js +0 -201
- package/dist/paths-xpks599B.js +0 -180
- package/dist/payload-S8niBCaz.js +0 -90
- package/dist/pdf-extract-Cm83kt2M.js +0 -73
- package/dist/perplexity-web-search-provider-Cgr2BYVk.js +0 -423
- package/dist/persistent-bindings.lifecycle-CAOm8v8I.js +0 -159
- package/dist/persistent-dedupe-DoNHVMWs.js +0 -116
- package/dist/pi-embedded-DEAtCIdT.js +0 -40616
- package/dist/pi-embedded-helpers-BVjvCsor.js +0 -1733
- package/dist/pi-embedded.runtime-Ctj3QFs-.js +0 -4
- package/dist/pi-embedded.runtime.js +0 -1
- package/dist/pi-model-discovery-KjI5xf8m.js +0 -188
- package/dist/pi-settings-BJzoHYF9.js +0 -57
- package/dist/pi-tools.before-tool-call.runtime-DJ6s0GDV.js +0 -354
- package/dist/pi-tools.before-tool-call.runtime.js +0 -1
- package/dist/plugin-auto-enable-BZvicayR.js +0 -400
- package/dist/plugin-entry-D0vhddM4.js +0 -25
- package/dist/plugin-entry.runtime-Xd644UDA.js +0 -112
- package/dist/plugin-entry.runtime.js +0 -1
- package/dist/plugin-error-boundary-RI4ByQoA.js +0 -55
- package/dist/plugin-error-handler-DWUTf_UF.js +0 -109
- package/dist/plugin-health-checker-2c6LyJwr.js +0 -125
- package/dist/plugin-hot-reload-CA3__dZN.js +0 -215
- package/dist/plugin-install-BjVLwj7-.js +0 -230
- package/dist/plugin-install-CjwMt13j.js +0 -2
- package/dist/plugin-install-config-policy-MApev-w-.js +0 -93
- package/dist/plugin-install-path-warnings-BkBH-k4n.js +0 -40
- package/dist/plugin-install-plan-0fWhyVBN.js +0 -50
- package/dist/plugin-registry-CORVyKHv.js +0 -74
- package/dist/plugin-registry-Dl1iIh7W.js +0 -2
- package/dist/plugin-runtime-Dd1YN95W.js +0 -6
- package/dist/plugin-sdk/account-core.js +0 -8
- package/dist/plugin-sdk/account-helpers.js +0 -4
- package/dist/plugin-sdk/account-id.js +0 -3
- package/dist/plugin-sdk/account-resolution.js +0 -12
- package/dist/plugin-sdk/acp-runtime.js +0 -3
- package/dist/plugin-sdk/agent-config-primitives.js +0 -3
- package/dist/plugin-sdk/agent-runtime.js +0 -29
- package/dist/plugin-sdk/allow-from.js +0 -5
- package/dist/plugin-sdk/allowlist-config-edit.js +0 -2
- package/dist/plugin-sdk/amazon-bedrock.js +0 -15
- package/dist/plugin-sdk/anthropic-vertex.js +0 -2
- package/dist/plugin-sdk/approval-runtime.js +0 -8
- package/dist/plugin-sdk/bluebubbles-policy.js +0 -2
- package/dist/plugin-sdk/bluebubbles.js +0 -39
- package/dist/plugin-sdk/boolean-param.js +0 -2
- package/dist/plugin-sdk/browser-runtime.js +0 -3
- package/dist/plugin-sdk/browser-support.js +0 -44
- package/dist/plugin-sdk/browser.js +0 -15
- package/dist/plugin-sdk/byteplus.js +0 -17
- package/dist/plugin-sdk/channel-actions.js +0 -3
- package/dist/plugin-sdk/channel-config-helpers.js +0 -2
- package/dist/plugin-sdk/channel-config-primitives.js +0 -4
- package/dist/plugin-sdk/channel-config-schema.js +0 -7
- package/dist/plugin-sdk/channel-config-writes.js +0 -3
- package/dist/plugin-sdk/channel-contract.js +0 -1
- package/dist/plugin-sdk/channel-feedback.js +0 -5
- package/dist/plugin-sdk/channel-inbound.js +0 -10
- package/dist/plugin-sdk/channel-lifecycle.js +0 -3
- package/dist/plugin-sdk/channel-pairing.js +0 -2
- package/dist/plugin-sdk/channel-policy.js +0 -6
- package/dist/plugin-sdk/channel-reply-pipeline.js +0 -2
- package/dist/plugin-sdk/channel-runtime.js +0 -16
- package/dist/plugin-sdk/channel-send-result.js +0 -2
- package/dist/plugin-sdk/channel-setup.js +0 -5
- package/dist/plugin-sdk/channel-status.js +0 -5
- package/dist/plugin-sdk/channel-targets.js +0 -5
- package/dist/plugin-sdk/chutes.js +0 -20
- package/dist/plugin-sdk/cli-backend.js +0 -3
- package/dist/plugin-sdk/cli-runtime.js +0 -7
- package/dist/plugin-sdk/cloudflare-ai-gateway.js +0 -18
- package/dist/plugin-sdk/collection-runtime.js +0 -3
- package/dist/plugin-sdk/command-auth-native.js +0 -6
- package/dist/plugin-sdk/command-auth.js +0 -14
- package/dist/plugin-sdk/command-detection.js +0 -3
- package/dist/plugin-sdk/command-surface.js +0 -3
- package/dist/plugin-sdk/compat.js +0 -35
- package/dist/plugin-sdk/config-runtime.js +0 -25
- package/dist/plugin-sdk/conversation-runtime.js +0 -15
- package/dist/plugin-sdk/core.js +0 -22
- package/dist/plugin-sdk/dangerous-name-runtime.js +0 -3
- package/dist/plugin-sdk/deepseek.js +0 -14
- package/dist/plugin-sdk/device-bootstrap.js +0 -4
- package/dist/plugin-sdk/diagnostic-runtime.js +0 -4
- package/dist/plugin-sdk/diagnostics-otel.js +0 -6
- package/dist/plugin-sdk/diffs.js +0 -4
- package/dist/plugin-sdk/direct-dm.js +0 -2
- package/dist/plugin-sdk/directory-runtime.js +0 -5
- package/dist/plugin-sdk/discord-account.js +0 -2
- package/dist/plugin-sdk/discord-core.js +0 -9
- package/dist/plugin-sdk/discord-runtime-surface.js +0 -2
- package/dist/plugin-sdk/discord-session-key.js +0 -2
- package/dist/plugin-sdk/discord-surface.js +0 -2
- package/dist/plugin-sdk/discord-thread-bindings.js +0 -2
- package/dist/plugin-sdk/discord-timeouts.js +0 -2
- package/dist/plugin-sdk/discord.js +0 -21
- package/dist/plugin-sdk/error-runtime.js +0 -3
- package/dist/plugin-sdk/extension-shared.js +0 -3
- package/dist/plugin-sdk/feishu-conversation.js +0 -2
- package/dist/plugin-sdk/feishu-setup.js +0 -2
- package/dist/plugin-sdk/feishu.js +0 -34
- package/dist/plugin-sdk/fetch-runtime.js +0 -7
- package/dist/plugin-sdk/file-lock.js +0 -2
- package/dist/plugin-sdk/gateway-runtime.js +0 -4
- package/dist/plugin-sdk/github-copilot-login.js +0 -2
- package/dist/plugin-sdk/github-copilot-token.js +0 -3
- package/dist/plugin-sdk/global-singleton.js +0 -4
- package/dist/plugin-sdk/google.js +0 -2
- package/dist/plugin-sdk/googlechat.js +0 -38
- package/dist/plugin-sdk/group-access.js +0 -2
- package/dist/plugin-sdk/hook-runtime.js +0 -4
- package/dist/plugin-sdk/host-runtime.js +0 -4
- package/dist/plugin-sdk/huggingface.js +0 -18
- package/dist/plugin-sdk/image-generation-core.js +0 -63
- package/dist/plugin-sdk/image-generation.js +0 -2
- package/dist/plugin-sdk/index.js +0 -5
- package/dist/plugin-sdk/infra-runtime.js +0 -45
- package/dist/plugin-sdk/interactive-runtime.js +0 -4
- package/dist/plugin-sdk/irc-surface.js +0 -2
- package/dist/plugin-sdk/irc.js +0 -30
- package/dist/plugin-sdk/json-store.js +0 -3
- package/dist/plugin-sdk/keyed-async-queue.js +0 -2
- package/dist/plugin-sdk/kilocode.js +0 -23
- package/dist/plugin-sdk/kimi-coding.js +0 -11
- package/dist/plugin-sdk/lazy-runtime.js +0 -3
- package/dist/plugin-sdk/line-core.js +0 -6
- package/dist/plugin-sdk/line-runtime.js +0 -2
- package/dist/plugin-sdk/line-surface.js +0 -2
- package/dist/plugin-sdk/line.js +0 -9
- package/dist/plugin-sdk/llm-task.js +0 -6
- package/dist/plugin-sdk/logging-core.js +0 -5
- package/dist/plugin-sdk/markdown-table-runtime.js +0 -3
- package/dist/plugin-sdk/matrix-helper.js +0 -2
- package/dist/plugin-sdk/matrix-runtime-heavy.js +0 -5
- package/dist/plugin-sdk/matrix-runtime-shared.js +0 -3
- package/dist/plugin-sdk/matrix-runtime-surface.js +0 -2
- package/dist/plugin-sdk/matrix-surface.js +0 -2
- package/dist/plugin-sdk/matrix-thread-bindings.js +0 -2
- package/dist/plugin-sdk/matrix.js +0 -53
- package/dist/plugin-sdk/mattermost-policy.js +0 -11
- package/dist/plugin-sdk/mattermost.js +0 -41
- package/dist/plugin-sdk/media-runtime.js +0 -17
- package/dist/plugin-sdk/media-understanding-runtime.js +0 -2
- package/dist/plugin-sdk/media-understanding.js +0 -3
- package/dist/plugin-sdk/memory-core-engine-runtime.js +0 -2
- package/dist/plugin-sdk/memory-core-host-engine-embeddings.js +0 -6
- package/dist/plugin-sdk/memory-core-host-engine-foundation.js +0 -17
- package/dist/plugin-sdk/memory-core-host-engine-qmd.js +0 -3
- package/dist/plugin-sdk/memory-core-host-engine-storage.js +0 -4
- package/dist/plugin-sdk/memory-core-host-multimodal.js +0 -3
- package/dist/plugin-sdk/memory-core-host-query.js +0 -3
- package/dist/plugin-sdk/memory-core-host-runtime-cli.js +0 -12
- package/dist/plugin-sdk/memory-core-host-runtime-core.js +0 -14
- package/dist/plugin-sdk/memory-core-host-runtime-files.js +0 -4
- package/dist/plugin-sdk/memory-core-host-secret.js +0 -3
- package/dist/plugin-sdk/memory-core-host-status.js +0 -2
- package/dist/plugin-sdk/memory-core.js +0 -29
- package/dist/plugin-sdk/memory-lancedb.js +0 -4
- package/dist/plugin-sdk/minimax.js +0 -24
- package/dist/plugin-sdk/mistral.js +0 -17
- package/dist/plugin-sdk/models-provider-runtime.js +0 -3
- package/dist/plugin-sdk/modelstudio-definitions.js +0 -19
- package/dist/plugin-sdk/modelstudio.js +0 -24
- package/dist/plugin-sdk/moonshot.js +0 -17
- package/dist/plugin-sdk/msteams.js +0 -46
- package/dist/plugin-sdk/native-command-registry.js +0 -3
- package/dist/plugin-sdk/nextcloud-talk.js +0 -35
- package/dist/plugin-sdk/nostr.js +0 -14
- package/dist/plugin-sdk/nvidia.js +0 -11
- package/dist/plugin-sdk/ollama-surface.js +0 -24
- package/dist/plugin-sdk/ollama.js +0 -2
- package/dist/plugin-sdk/openai.js +0 -2
- package/dist/plugin-sdk/opencode-go.js +0 -14
- package/dist/plugin-sdk/opencode.js +0 -15
- package/dist/plugin-sdk/outbound-runtime.js +0 -5
- package/dist/plugin-sdk/param-readers.js +0 -3
- package/dist/plugin-sdk/plugin-entry.js +0 -3
- package/dist/plugin-sdk/plugin-runtime.js +0 -8
- package/dist/plugin-sdk/process-runtime.js +0 -3
- package/dist/plugin-sdk/provider-auth-api-key.js +0 -9
- package/dist/plugin-sdk/provider-auth-login.js +0 -2
- package/dist/plugin-sdk/provider-auth-result.js +0 -2
- package/dist/plugin-sdk/provider-auth-runtime.js +0 -5
- package/dist/plugin-sdk/provider-auth.js +0 -17
- package/dist/plugin-sdk/provider-catalog-shared.js +0 -2
- package/dist/plugin-sdk/provider-entry.js +0 -2
- package/dist/plugin-sdk/provider-env-vars.js +0 -3
- package/dist/plugin-sdk/provider-http.js +0 -6
- package/dist/plugin-sdk/provider-model-shared.js +0 -5
- package/dist/plugin-sdk/provider-moonshot.js +0 -3
- package/dist/plugin-sdk/provider-onboard.js +0 -3
- package/dist/plugin-sdk/provider-setup.js +0 -3
- package/dist/plugin-sdk/provider-stream.js +0 -4
- package/dist/plugin-sdk/provider-tools.js +0 -2
- package/dist/plugin-sdk/provider-usage.js +0 -3
- package/dist/plugin-sdk/provider-web-fetch.js +0 -6
- package/dist/plugin-sdk/provider-web-search.js +0 -7
- package/dist/plugin-sdk/provider-zai-endpoint.js +0 -2
- package/dist/plugin-sdk/qianfan.js +0 -13
- package/dist/plugin-sdk/reply-chunking.js +0 -4
- package/dist/plugin-sdk/reply-dispatch-runtime.js +0 -5
- package/dist/plugin-sdk/reply-history.js +0 -3
- package/dist/plugin-sdk/reply-payload.js +0 -2
- package/dist/plugin-sdk/reply-runtime.js +0 -14
- package/dist/plugin-sdk/request-url.js +0 -2
- package/dist/plugin-sdk/retry-runtime.js +0 -4
- package/dist/plugin-sdk/root-alias.cjs +0 -347
- package/dist/plugin-sdk/routing.js +0 -9
- package/dist/plugin-sdk/runtime-config-snapshot.js +0 -3
- package/dist/plugin-sdk/runtime-env.js +0 -17
- package/dist/plugin-sdk/runtime-group-policy.js +0 -3
- package/dist/plugin-sdk/runtime-store.js +0 -2
- package/dist/plugin-sdk/runtime.js +0 -9
- package/dist/plugin-sdk/sandbox.js +0 -6
- package/dist/plugin-sdk/secret-input.js +0 -4
- package/dist/plugin-sdk/security-runtime.js +0 -6
- package/dist/plugin-sdk/self-hosted-provider-setup.js +0 -2
- package/dist/plugin-sdk/session-store-runtime.js +0 -4
- package/dist/plugin-sdk/setup-adapter-runtime.js +0 -3
- package/dist/plugin-sdk/setup-runtime.js +0 -6
- package/dist/plugin-sdk/setup-tools.js +0 -6
- package/dist/plugin-sdk/setup.js +0 -13
- package/dist/plugin-sdk/sglang.js +0 -15
- package/dist/plugin-sdk/signal-account.js +0 -2
- package/dist/plugin-sdk/signal-surface.js +0 -2
- package/dist/plugin-sdk/signal.js +0 -24
- package/dist/plugin-sdk/skill-commands-runtime.js +0 -3
- package/dist/plugin-sdk/slack-account.js +0 -2
- package/dist/plugin-sdk/slack-core.js +0 -7
- package/dist/plugin-sdk/slack-runtime-surface.js +0 -2
- package/dist/plugin-sdk/slack-surface.js +0 -2
- package/dist/plugin-sdk/slack-target-parser.js +0 -2
- package/dist/plugin-sdk/slack-targets.js +0 -3
- package/dist/plugin-sdk/slack.js +0 -20
- package/dist/plugin-sdk/speech-core.js +0 -4
- package/dist/plugin-sdk/speech-runtime.js +0 -2
- package/dist/plugin-sdk/speech.js +0 -5
- package/dist/plugin-sdk/ssrf-runtime.js +0 -6
- package/dist/plugin-sdk/state-paths.js +0 -3
- package/dist/plugin-sdk/status-helpers.js +0 -4
- package/dist/plugin-sdk/string-normalization-runtime.js +0 -3
- package/dist/plugin-sdk/synthetic.js +0 -17
- package/dist/plugin-sdk/telegram-account.js +0 -2
- package/dist/plugin-sdk/telegram-allow-from.js +0 -12
- package/dist/plugin-sdk/telegram-command-config.js +0 -3
- package/dist/plugin-sdk/telegram-core.js +0 -18
- package/dist/plugin-sdk/telegram-surface.js +0 -42
- package/dist/plugin-sdk/temp-path.js +0 -4
- package/dist/plugin-sdk/testing.js +0 -11
- package/dist/plugin-sdk/text-runtime.js +0 -19
- package/dist/plugin-sdk/thread-bindings-runtime.js +0 -4
- package/dist/plugin-sdk/thread-ownership.js +0 -5
- package/dist/plugin-sdk/tlon.js +0 -13
- package/dist/plugin-sdk/together.js +0 -16
- package/dist/plugin-sdk/tool-send.js +0 -2
- package/dist/plugin-sdk/twitch.js +0 -8
- package/dist/plugin-sdk/venice.js +0 -16
- package/dist/plugin-sdk/vllm.js +0 -15
- package/dist/plugin-sdk/voice-call.js +0 -7
- package/dist/plugin-sdk/volcengine.js +0 -17
- package/dist/plugin-sdk/web-media.js +0 -4
- package/dist/plugin-sdk/webhook-ingress.js +0 -9
- package/dist/plugin-sdk/webhook-path.js +0 -2
- package/dist/plugin-sdk/webhook-request-guards.js +0 -3
- package/dist/plugin-sdk/whatsapp-auth-presence.js +0 -2
- package/dist/plugin-sdk/whatsapp-core.js +0 -11
- package/dist/plugin-sdk/whatsapp-shared.js +0 -5
- package/dist/plugin-sdk/whatsapp-surface.js +0 -2
- package/dist/plugin-sdk/whatsapp-targets.js +0 -2
- package/dist/plugin-sdk/windows-spawn.js +0 -2
- package/dist/plugin-sdk/xai.js +0 -27
- package/dist/plugin-sdk/zalo-setup.js +0 -2
- package/dist/plugin-sdk/zalo.js +0 -38
- package/dist/plugin-sdk/zalouser.js +0 -29
- package/dist/plugin-sdk/zod.js +0 -3
- package/dist/plugins/build-smoke-entry.js +0 -4
- package/dist/plugins/provider-runtime.runtime.js +0 -2
- package/dist/plugins/runtime/index.js +0 -2
- package/dist/plugins/runtime/runtime-line.contract.js +0 -23
- package/dist/plugins-DcjzAZWt.js +0 -5
- package/dist/plugins-allowlist-DoZlIC4p.js +0 -14
- package/dist/plugins-cli-G6GhHHD0.js +0 -561
- package/dist/plugins-command-helpers-CIYzoC65.js +0 -116
- package/dist/plugins-install-persist-C5-bJdRb.js +0 -121
- package/dist/plugins-update-command-CsT_saJm.js +0 -974
- package/dist/plugins-w6xUzHby.js +0 -40
- package/dist/pluralkit-B_DWQnc5.js +0 -21
- package/dist/pluralkit-Bez5QVFC.js +0 -2
- package/dist/policy-D6OQdw73.js +0 -173
- package/dist/policy-DXeeH8B2.js +0 -147
- package/dist/poll-params-DTVSSuok.js +0 -52
- package/dist/polls-CMmT0F_y.js +0 -35
- package/dist/port-defaults-Dajs4Obu.js +0 -30
- package/dist/ports-DuU-A86K.js +0 -262
- package/dist/ports-ZA1n2It7.js +0 -432
- package/dist/ports-lsof-Cw_6T7ru.js +0 -25
- package/dist/ports-probe-DeuK48Pq.js +0 -14
- package/dist/preflight-audio-DTSBExi8.js +0 -48
- package/dist/preflight-audio.runtime-BJRLyBvf.js +0 -7
- package/dist/preflight-audio.runtime.js +0 -1
- package/dist/prepare-BQBndJUC.js +0 -1301
- package/dist/probe-BO4hbHvs.js +0 -435
- package/dist/probe-DPv2pfj5.js +0 -1868
- package/dist/probe-DlO2Q9rN.js +0 -170
- package/dist/probe-RbbTQOHx.js +0 -178
- package/dist/probe-auth-Bl9MHtlP.js +0 -2
- package/dist/probe-auth-CUfEH7di.js +0 -63
- package/dist/probe-eYLpFww_.js +0 -137
- package/dist/probe-kfBMuOWF.js +0 -2
- package/dist/probe-wktb4WTP.js +0 -349
- package/dist/probe.runtime-BhbUesak.js +0 -2
- package/dist/probe.runtime.js +0 -1
- package/dist/process-runtime-CBFMzhIk.js +0 -2
- package/dist/process-scoped-map-C7IoOrV-.js +0 -61
- package/dist/profile-B5n9yLlB.js +0 -2
- package/dist/profile-CmsqnPsu.js +0 -107
- package/dist/profile-update-D6f10CZk.js +0 -67
- package/dist/profile-utils-C7uzTwgJ.js +0 -15
- package/dist/profiles-BoNergZo.js +0 -79
- package/dist/program-BuKG0Ntx.js +0 -158
- package/dist/program-context-DDmlbmbl.js +0 -2
- package/dist/program-context-DQOnR5c_.js +0 -10
- package/dist/progress-DQvXYKne.js +0 -132
- package/dist/prompt-COzf5LIJ.js +0 -2
- package/dist/prompt-OSmU-o7R.js +0 -20
- package/dist/prompt-select-styled-yYL3el77.js +0 -6432
- package/dist/prompt-style-DekFM6h7.js +0 -7
- package/dist/prompts-MtRTsZpw.js +0 -9
- package/dist/prototype-keys-DrjhYNcz.js +0 -11
- package/dist/provider-BWTXmDq3.js +0 -3685
- package/dist/provider-BrmcrkEe.js +0 -70
- package/dist/provider-ByBRbhW5.js +0 -7216
- package/dist/provider-api-key-auth-BcyipSdy.js +0 -109
- package/dist/provider-api-key-auth.runtime-BRu_8L4m.js +0 -14
- package/dist/provider-api-key-auth.runtime.js +0 -1
- package/dist/provider-attribution-DMVujx9B.js +0 -285
- package/dist/provider-auth-DK2UmYsZ.js +0 -43
- package/dist/provider-auth-api-key-99KxscjW.js +0 -5
- package/dist/provider-auth-choice-GbHta8Bp.js +0 -134
- package/dist/provider-auth-choice-helpers-CO2MnSGR.js +0 -58
- package/dist/provider-auth-choice-preference-DvAqSMGK.js +0 -44
- package/dist/provider-auth-choice.runtime-B8R63sLq.js +0 -14
- package/dist/provider-auth-choice.runtime-DypTKgMQ.js +0 -2
- package/dist/provider-auth-choice.runtime.js +0 -1
- package/dist/provider-auth-choices-n-JV1XgU.js +0 -65
- package/dist/provider-auth-guidance-DAB9sGGR.js +0 -35
- package/dist/provider-auth-helpers-gmTAji1W.js +0 -92
- package/dist/provider-auth-input-GgY_tjJh.js +0 -116
- package/dist/provider-auth-login-BYbccnB1.js +0 -8
- package/dist/provider-auth-login.runtime-DnSxCwqU.js +0 -196
- package/dist/provider-auth-login.runtime.js +0 -1
- package/dist/provider-auth-mode-Dbja5LLX.js +0 -20
- package/dist/provider-auth-ref-BErMfwCd.js +0 -171
- package/dist/provider-auth-ref-DEF_hE2R.js +0 -2
- package/dist/provider-auth-result-DiUxDF1G.js +0 -31
- package/dist/provider-auth-runtime-BwMq9oMD.js +0 -32
- package/dist/provider-catalog--Qc0q0I-.js +0 -26
- package/dist/provider-catalog-A_s8JQaq.js +0 -18
- package/dist/provider-catalog-B-EcLvBI.js +0 -11
- package/dist/provider-catalog-Bte-HucL.js +0 -44
- package/dist/provider-catalog-C63Rfat7.js +0 -11
- package/dist/provider-catalog-C7q0Vb3S.js +0 -95
- package/dist/provider-catalog-CCnuf8d3.js +0 -49
- package/dist/provider-catalog-CYQ7k_Db.js +0 -48
- package/dist/provider-catalog-CnlB2mMZ.js +0 -11
- package/dist/provider-catalog-CoyAbDZX.js +0 -56
- package/dist/provider-catalog-CtqhFopw.js +0 -48
- package/dist/provider-catalog-CyGxB_aR.js +0 -18
- package/dist/provider-catalog-D1pmWiDQ.js +0 -48
- package/dist/provider-catalog-DGgsalJv.js +0 -12
- package/dist/provider-catalog-DLdTTMmW.js +0 -11
- package/dist/provider-catalog-DZzX7Vwk.js +0 -54
- package/dist/provider-catalog-J2dZciKS.js +0 -11
- package/dist/provider-catalog-LVrcrz-W.js +0 -36
- package/dist/provider-catalog-aIN0QojF.js +0 -11
- package/dist/provider-catalog-shared-Db9nbr1B.js +0 -28
- package/dist/provider-discovery.runtime-BCUAsMMz.js +0 -10
- package/dist/provider-discovery.runtime.js +0 -1
- package/dist/provider-dispatcher-COxXbow_.js +0 -22
- package/dist/provider-entry-CT_YeII_.js +0 -86
- package/dist/provider-env-vars-4Bon7Y2X.js +0 -121
- package/dist/provider-env-vars-Np7qkDjC.js +0 -2
- package/dist/provider-flow-Dlg1GcXQ.js +0 -93
- package/dist/provider-http-DQQ317hc.js +0 -3
- package/dist/provider-id-CHzLB538.js +0 -30
- package/dist/provider-model-defaults-dSPzPas0.js +0 -5
- package/dist/provider-model-primary-D8SgLcOd.js +0 -26
- package/dist/provider-model-shared-GO1WT2fY.js +0 -104
- package/dist/provider-models-BG8YwJaY.js +0 -117
- package/dist/provider-models-BVVCdM8F.js +0 -22
- package/dist/provider-models-D2UZvTo3.js +0 -46
- package/dist/provider-moonshot-Cl8urWHL.js +0 -2
- package/dist/provider-oauth-flow-Bu2gIeX6.js +0 -33
- package/dist/provider-onboard-CTEHTjfw.js +0 -264
- package/dist/provider-openai-codex-oauth-tls-DdLUDuIM.js +0 -101
- package/dist/provider-registry-Ljtt6CKE.js +0 -49
- package/dist/provider-request-config-CIrbwAnR.js +0 -209
- package/dist/provider-runtime-BBFXlsU0.js +0 -2
- package/dist/provider-runtime-D_itmv9t.js +0 -291
- package/dist/provider-runtime.runtime-B_oC992d.js +0 -26
- package/dist/provider-runtime.runtime.js +0 -1
- package/dist/provider-self-hosted-setup-BlNrSREL.js +0 -272
- package/dist/provider-session.runtime-DQyRV-t3.js +0 -7
- package/dist/provider-session.runtime.js +0 -1
- package/dist/provider-setup-CSrOyU04.js +0 -2
- package/dist/provider-stream-m5D8QZPz.js +0 -289
- package/dist/provider-tools-NuJzTqa1.js +0 -37
- package/dist/provider-usage-BB5mQ8Fg.js +0 -3
- package/dist/provider-usage-CbPs3eFQ.js +0 -2
- package/dist/provider-usage-DG1gff_M.js +0 -375
- package/dist/provider-usage.fetch-DHWQ4YZu.js +0 -633
- package/dist/provider-web-fetch-QzED2vth.js +0 -5
- package/dist/provider-web-search-CVMEZcl8.js +0 -306
- package/dist/provider-wizard-B0ocsg3q.js +0 -209
- package/dist/provider-zai-endpoint-DoIy5HQf.js +0 -116
- package/dist/provider.runtime-jHt6447e.js +0 -2
- package/dist/provider.runtime.js +0 -1
- package/dist/providers-DUy2TaM-.js +0 -59
- package/dist/providers.runtime-Bz0X8DZ2.js +0 -60
- package/dist/providers.runtime.js +0 -1
- package/dist/proxy-env-D4H9qI5l.js +0 -40
- package/dist/proxy-fetch-DiyTLcAR.js +0 -55
- package/dist/proxy-hcRRmsDd.js +0 -121
- package/dist/pw-ai-Cp64-UGh.js +0 -2037
- package/dist/qmd-manager-D2rMomsU.js +0 -1740
- package/dist/qr-cli-DuANCi-n.js +0 -2
- package/dist/qr-cli-lr5CcLr8.js +0 -367
- package/dist/qr-image-DOcoqAUw.js +0 -2
- package/dist/query-expansion-85HlYbKD.js +0 -633
- package/dist/queue-CtO0M_P1.js +0 -663
- package/dist/reaction-runtime-api-B4KmfKtJ.js +0 -114
- package/dist/reactions-5N2KSGJ2.js +0 -258
- package/dist/read-only-account-inspect-Y_ijOKJ3.js +0 -9
- package/dist/read-only-account-inspect.telegram-BSjjDWnM.js +0 -202
- package/dist/reasoning-tags-CAmbBXLr.js +0 -77
- package/dist/recovery-key-store-DqgdfMc7.js +0 -283
- package/dist/redact-BDinS1q9.js +0 -102
- package/dist/redact-identifier-S69DDJn1.js +0 -13
- package/dist/redact-sensitive-url-CAJSZ_7g.js +0 -50
- package/dist/redirect-headers-CGxONG_L.js +0 -25
- package/dist/ref-contract-CC5BLDoS.js +0 -53
- package/dist/region-BFGEM88n.js +0 -62
- package/dist/register-lazy-command-D6EEI-zz.js +0 -43
- package/dist/register.agent-D1eKwVU7.js +0 -239
- package/dist/register.backup-S8hwHpZL.js +0 -236
- package/dist/register.configure-CJrQ3u9y.js +0 -15
- package/dist/register.maintenance-BrKWl7_D.js +0 -449
- package/dist/register.message-BCWJfH2h.js +0 -642
- package/dist/register.onboard-zzNq0pJS.js +0 -79
- package/dist/register.setup-BLQwDKzc.js +0 -104
- package/dist/register.status-health-sessions-B4TUEudf.js +0 -1206
- package/dist/register.subclis-B0pG22H2.js +0 -295
- package/dist/register.subclis-BEM0mzJb.js +0 -3
- package/dist/registry-ByjnKj3u.js +0 -219
- package/dist/registry-CuFNdbH5.js +0 -60
- package/dist/registry-DvqOrcbF.js +0 -675
- package/dist/registry-mVzzlPf7.js +0 -50
- package/dist/repair-IMBZnz-L.js +0 -112
- package/dist/replies-BCoZ2HxY.js +0 -118
- package/dist/reply-D8bUQd-Q.js +0 -3176
- package/dist/reply-blocks-CjCUTZru.js +0 -103
- package/dist/reply-chunking-CxFd5izq.js +0 -3
- package/dist/reply-dispatch-runtime-Bk9RDzB9.js +0 -3
- package/dist/reply-history-DtTaPEgt.js +0 -2
- package/dist/reply-payload-CRYI4jIB.js +0 -232
- package/dist/reply-payloads-CQXH7ury.js +0 -116
- package/dist/reply-payloads-dedupe-BKObDjVe.js +0 -135
- package/dist/reply-payloads-dedupe.runtime-SbVNMvEs.js +0 -2
- package/dist/reply-payloads-dedupe.runtime.js +0 -1
- package/dist/reply-prefix-Eema_xXG.js +0 -39
- package/dist/reply-runtime-DUrpJTzu.js +0 -123
- package/dist/reply.runtime-Cdb_6NI1.js +0 -2
- package/dist/reply.runtime.js +0 -1
- package/dist/request-url-B8k-YRRH.js +0 -10
- package/dist/resolve-C3pJ2gj_.js +0 -174
- package/dist/resolve-C_pCWwC0.js +0 -619
- package/dist/resolve-allowlist-HrJ_3icS.js +0 -147
- package/dist/resolve-cbCi7mbs.js +0 -2
- package/dist/resolve-channels-BTyErRev.js +0 -226
- package/dist/resolve-channels-DvV-fUQv.js +0 -263
- package/dist/resolve-configured-secret-input-string-BasQGOd9.js +0 -112
- package/dist/resolve-route-EE8E7OAF.js +0 -486
- package/dist/resolve-system-bin-TC907QYo.js +0 -120
- package/dist/resolve-targets-B1MvBWpT.js +0 -280
- package/dist/resolve-users-BfVoBZG_.js +0 -145
- package/dist/resolve-utils-ClmKSkEQ.js +0 -102
- package/dist/response-generator-ACP21F1j.js +0 -153
- package/dist/response-prefix-template-Cp4tg6Qw.js +0 -50
- package/dist/restart-sentinel-B3EigRIb.js +0 -70
- package/dist/restart-stale-pids-dsB3eE6_.js +0 -187
- package/dist/retry-CIAqcw7C.js +0 -76
- package/dist/retry-policy-BJ5Q68It.js +0 -58
- package/dist/retry-runtime-Cbu5zDnX.js +0 -3
- package/dist/rooms-B1IyUfuR.js +0 -325
- package/dist/root-help-Bwa-vlhy.js +0 -53
- package/dist/route-reply-bC07YRDb.js +0 -133
- package/dist/route-reply.runtime-dS2XMwkv.js +0 -2
- package/dist/route-reply.runtime.js +0 -1
- package/dist/route-resolution-DQHmIfWN.js +0 -461
- package/dist/routes-CWXihJFR.js +0 -6884
- package/dist/routing-BVt9nMN-.js +0 -16
- package/dist/rpc-5eaI-FsF.js +0 -67
- package/dist/rpc-context-Dimdo4io.js +0 -155
- package/dist/run-command-2jxKF1a6.js +0 -32
- package/dist/run-main-BKeNUaXq.js +0 -425
- package/dist/run-with-concurrency-DGLkItan.js +0 -41
- package/dist/runner-COmi-bB0.js +0 -1479
- package/dist/runs-CX-70k4f.js +0 -616
- package/dist/runtime-1phb9iVo.js +0 -6
- package/dist/runtime-B8hqEtu6.js +0 -181
- package/dist/runtime-BpHrcXgA.js +0 -5
- package/dist/runtime-BscSIjIL.js +0 -126
- package/dist/runtime-C8qvwPYG.js +0 -286
- package/dist/runtime-CBX_rq4O.js +0 -18
- package/dist/runtime-CC1YDOst.js +0 -5
- package/dist/runtime-CIE7xJwu.js +0 -5
- package/dist/runtime-CNZi_czi.js +0 -42
- package/dist/runtime-CPw-0dWW.js +0 -5
- package/dist/runtime-DXdxDbJm.js +0 -102
- package/dist/runtime-DofOSZ5U.js +0 -2153
- package/dist/runtime-api-B4Zb5p1_.js +0 -2
- package/dist/runtime-api-BB2RZPnq.js +0 -2
- package/dist/runtime-api-BJnw1_pK.js +0 -2
- package/dist/runtime-api-BTf2rLCn.js +0 -2
- package/dist/runtime-api-BTgAUySz.js +0 -2
- package/dist/runtime-api-BVftCkYc.js +0 -2484
- package/dist/runtime-api-BWaVsvsK.js +0 -14
- package/dist/runtime-api-BXlE282E.js +0 -2
- package/dist/runtime-api-Ba7wWBWO.js +0 -1
- package/dist/runtime-api-BfZqvwkY.js +0 -2
- package/dist/runtime-api-Bja3afzd.js +0 -3676
- package/dist/runtime-api-C7cX3f2r.js +0 -2
- package/dist/runtime-api-CCKblcQO.js +0 -81
- package/dist/runtime-api-CEmQSooZ.js +0 -2
- package/dist/runtime-api-CWQMIahu.js +0 -241
- package/dist/runtime-api-Ch1E1gvT.js +0 -2
- package/dist/runtime-api-CjgyJEjn.js +0 -2
- package/dist/runtime-api-CuqnElJG.js +0 -3
- package/dist/runtime-api-DOyNJNHi.js +0 -2
- package/dist/runtime-api-DUbp7oPz.js +0 -2
- package/dist/runtime-api-DZDDizAj.js +0 -1
- package/dist/runtime-api-DZcQIyI0.js +0 -1
- package/dist/runtime-api-Di-XE7J4.js +0 -3
- package/dist/runtime-api-Dq7rdsm6.js +0 -2
- package/dist/runtime-api-DxO7sIvA.js +0 -120
- package/dist/runtime-api-Dzz1aWyq.js +0 -29
- package/dist/runtime-api-_dg5Me1l.js +0 -2
- package/dist/runtime-config-snapshot-DqWNFIej.js +0 -2
- package/dist/runtime-d8L2hZ57.js +0 -5
- package/dist/runtime-discord-ops.runtime-C2o0995d.js +0 -25
- package/dist/runtime-discord-ops.runtime.js +0 -1
- package/dist/runtime-embedded-pi.runtime-Bk8vHdbc.js +0 -2
- package/dist/runtime-embedded-pi.runtime.js +0 -1
- package/dist/runtime-entry-BypP0hpR.js +0 -4925
- package/dist/runtime-env-SHKgcATc.js +0 -9
- package/dist/runtime-forwarders-ev8FdVyV.js +0 -44
- package/dist/runtime-group-policy-DXJJCWpk.js +0 -1
- package/dist/runtime-group-policy-zTp8fKRK.js +0 -59
- package/dist/runtime-guard-78ESz4Qy.js +0 -70
- package/dist/runtime-kS8e4c6-.js +0 -125
- package/dist/runtime-media-understanding.runtime-CpPWzWdQ.js +0 -2
- package/dist/runtime-media-understanding.runtime.js +0 -1
- package/dist/runtime-model-auth.runtime-1Gsp1j1n.js +0 -2
- package/dist/runtime-model-auth.runtime.js +0 -1
- package/dist/runtime-parse-vvG9x7Ps.js +0 -83
- package/dist/runtime-paths-NiQSNveu.js +0 -305
- package/dist/runtime-plugin-boundary-CzRNh2jW.js +0 -65
- package/dist/runtime-schema-uKXWjWrD.js +0 -14440
- package/dist/runtime-slack-ops.runtime-MQwSRdp3.js +0 -15
- package/dist/runtime-slack-ops.runtime.js +0 -1
- package/dist/runtime-status-2omGgKaM.js +0 -109
- package/dist/runtime-status-Cc383GkB.js +0 -15
- package/dist/runtime-store-CT3yRBe_.js +0 -22
- package/dist/runtime-tts.runtime-DTfPYn3H.js +0 -2
- package/dist/runtime-tts.runtime.js +0 -1
- package/dist/runtime-uvckmk15.js +0 -68
- package/dist/runtime-web-tools-Cf6BXnCm.js +0 -2042
- package/dist/runtime-whatsapp-boundary-Bs8RrKxP.js +0 -139
- package/dist/runtime-whatsapp-boundary-C5mx67ac.js +0 -2
- package/dist/safe-regex-tLlDZYfM.js +0 -244
- package/dist/safe-text-DhIQbZid.js +0 -16
- package/dist/sandbox-DkZ4-LBj.js +0 -4
- package/dist/sandbox-cli-Cho_k3dA.js +0 -443
- package/dist/sandbox-f4-hZNCj.js +0 -2710
- package/dist/sandbox-paths-Br54mtAJ.js +0 -146
- package/dist/sanitize-env-vars-BpNLWkzd.js +0 -82
- package/dist/scan-paths-Bln7l19-.js +0 -28
- package/dist/schema-validator-DofqBvJi.js +0 -176
- package/dist/scoped-expiring-id-cache-C8wyKEV-.js +0 -35
- package/dist/scp-host-B9NPcVwn.js +0 -62
- package/dist/sdk-Ba91_ru8.js +0 -1108
- package/dist/searxng-search-provider-CUEJoeaQ.js +0 -220
- package/dist/secret-equal-fMR1DjSl.js +0 -9
- package/dist/secret-file-BNKBIds0.js +0 -11
- package/dist/secret-file-DwDmPLSX.js +0 -92
- package/dist/secret-input-4MqmO__N.js +0 -13
- package/dist/secret-input-Bqf5ho4v.js +0 -38
- package/dist/secret-input-DyzcNLLY.js +0 -2
- package/dist/secrets-cli-DAP4hi4r.js +0 -2112
- package/dist/secure-random-Diy9rj1m.js +0 -20
- package/dist/security-cli-2WQVKZ3w.js +0 -477
- package/dist/security-runtime-BSQDlFPi.js +0 -55
- package/dist/semver-compare-DkYCMcwG.js +0 -53
- package/dist/send--GwzHbbI.js +0 -100
- package/dist/send-4ZItW1GB.js +0 -3
- package/dist/send-B1YVZR_q.js +0 -1076
- package/dist/send-B6AgLKhw.js +0 -178
- package/dist/send-CAosrV3E.js +0 -2
- package/dist/send-CTHiX-WN.js +0 -442
- package/dist/send-CywIIzow.js +0 -318
- package/dist/send-DZ2VaVTD.js +0 -856
- package/dist/send-deps-Cuc-dTy5.js +0 -19
- package/dist/send-policy-Dz5lteoO.js +0 -60
- package/dist/send-q09VUMe3.js +0 -532
- package/dist/send.components-Cbi5ixnJ.js +0 -844
- package/dist/send.components-dLe6ppiS.js +0 -2
- package/dist/server-BRW1HQ2M.js +0 -67
- package/dist/server-Clq20rV8.js +0 -27990
- package/dist/server-middleware-Ubw3OFd1.js +0 -108
- package/dist/server-node-events-kgWhdE2I.js +0 -454
- package/dist/server-startup-matrix-migration-DJZgJiUa.js +0 -1348
- package/dist/service-2O78fADl.js +0 -867
- package/dist/service-BCUDr0mF.js +0 -2
- package/dist/session-BIwHn1FL.js +0 -137
- package/dist/session-BJudz6Rb.js +0 -50
- package/dist/session-archive.runtime-8FrIp7MP.js +0 -2
- package/dist/session-archive.runtime.js +0 -1
- package/dist/session-binding-service-D7bUc7mS.js +0 -421
- package/dist/session-context-DbS6R7x0.js +0 -23
- package/dist/session-conversation-5Z-WApq_.js +0 -15
- package/dist/session-conversation-BC-SPGma.js +0 -135
- package/dist/session-conversation-D-Olnomz.js +0 -27
- package/dist/session-cost-usage-5iS5N1Rs.js +0 -651
- package/dist/session-cost-usage-ibiGeH7Z.js +0 -2
- package/dist/session-envelope-RE_1D3fZ.js +0 -18
- package/dist/session-errors-CoC2qhLC.js +0 -63
- package/dist/session-file-CnTnoAyl.js +0 -120
- package/dist/session-fork.runtime-Bb9nNLwX.js +0 -50
- package/dist/session-fork.runtime.js +0 -1
- package/dist/session-hooks-D4zcaOKV.js +0 -109
- package/dist/session-key-CQMUjsSp.js +0 -52
- package/dist/session-key-Do__tq1E.js +0 -279
- package/dist/session-key-normalization-BamCBtxj.js +0 -22
- package/dist/session-override-CHm5auQy.js +0 -100
- package/dist/session-reset-model.runtime-DD5SooaH.js +0 -119
- package/dist/session-reset-model.runtime.js +0 -1
- package/dist/session-route-O1Pb7Oid.js +0 -91
- package/dist/session-store-runtime-ePmbOWO1.js +0 -2
- package/dist/session-subagent-reactivation.runtime-BwUDVflj.js +0 -2
- package/dist/session-subagent-reactivation.runtime.js +0 -1
- package/dist/session-transcript-files.fs-CyBs2u56.js +0 -149
- package/dist/session-updates-C33bkLCm.js +0 -222
- package/dist/session-updates.runtime-BHLQJ71D.js +0 -2
- package/dist/session-updates.runtime.js +0 -1
- package/dist/session-utils-C0HJP9m_.js +0 -1743
- package/dist/session-write-lock-C7svKLqX.js +0 -343
- package/dist/sessions-CkaONOVi.js +0 -224
- package/dist/sessions-Dqj9fL_x.js +0 -2
- package/dist/sessions-j2hbBGOS.js +0 -443
- package/dist/setup-0GrDtpRN.js +0 -427
- package/dist/setup-BrOJEPVN.js +0 -15
- package/dist/setup-adapter-runtime-CyIIvoms.js +0 -2
- package/dist/setup-api-1P88M5pV.js +0 -2
- package/dist/setup-binary-CXmsVXWb.js +0 -2
- package/dist/setup-bootstrap-QdrIAyis.js +0 -54
- package/dist/setup-core-BqPbreVF.js +0 -256
- package/dist/setup-core-CFzSqpD7.js +0 -256
- package/dist/setup-core-DJHWTJKX.js +0 -383
- package/dist/setup-core-DT1fK9yD.js +0 -146
- package/dist/setup-core-kjYyNYvv.js +0 -48
- package/dist/setup-core-t03VZdcn.js +0 -160
- package/dist/setup-group-access-BQbnTk8B.js +0 -70
- package/dist/setup-helpers-0yWIq2cW.js +0 -390
- package/dist/setup-runtime-BChXvJOO.js +0 -4
- package/dist/setup-surface-B8loJ-nJ.js +0 -272
- package/dist/setup-surface-BKizDP2g.js +0 -237
- package/dist/setup-surface-BOd4V0w5.js +0 -261
- package/dist/setup-surface-BaLQtvFW.js +0 -353
- package/dist/setup-surface-Bha7U86Z.js +0 -72
- package/dist/setup-surface-BkHkduRm.js +0 -450
- package/dist/setup-surface-CDfudYpT2.js +0 -261
- package/dist/setup-surface-CT-Erp53.js +0 -346
- package/dist/setup-surface-Co1jSLDk.js +0 -656
- package/dist/setup-surface-D0joW4q1.js +0 -333
- package/dist/setup-surface-D9rWJb4Z.js +0 -1447
- package/dist/setup-surface-D_CJfyJ1.js +0 -381
- package/dist/setup-surface-DbVJ8CAj.js +0 -484
- package/dist/setup-surface-Di1q12ha.js +0 -97
- package/dist/setup-surface-LWupQ_eL.js +0 -304
- package/dist/setup-tools-Cb6PLN3L.js +0 -4
- package/dist/setup-wizard-helpers-CA5yiprq.js +0 -841
- package/dist/setup-wizard-proxy-bmMo6OPA.js +0 -127
- package/dist/setup.finalize-DvGlwfbU.js +0 -443
- package/dist/setup.gateway-config-D_eSo_Jz.js +0 -246
- package/dist/setup.secret-input-BAQnay16.js +0 -25
- package/dist/shared-1nXP7RAM.js +0 -78
- package/dist/shared-6u4-kP5J.js +0 -221
- package/dist/shared-BnSR7joQ.js +0 -144
- package/dist/shared-BsLpgVLV.js +0 -37
- package/dist/shared-C-PSF7Rv.js +0 -64
- package/dist/shared-C1MrVu4M.js +0 -180
- package/dist/shared-CA8QQ_O5.js +0 -94
- package/dist/shared-CCV62W5K.js +0 -2
- package/dist/shared-CHJjz6Zz.js +0 -72
- package/dist/shared-CK49TKED.js +0 -50
- package/dist/shared-CL7I4ij0.js +0 -160
- package/dist/shared-Cr7ijuS4.js +0 -198
- package/dist/shared-D61IX915.js +0 -10
- package/dist/shared-Dr9OQu4c.js +0 -64
- package/dist/shared-DwxV-O-g.js +0 -136
- package/dist/shared-dWHdeoyy.js +0 -126
- package/dist/shared-runtime-yV5uP0IO.js +0 -7
- package/dist/shell-argv-B9p91CMW.js +0 -72
- package/dist/shell-env-BTztt4a1.js +0 -181
- package/dist/signal-B1kD3iF3.js +0 -46
- package/dist/signal-DP8FUJOa.js +0 -6
- package/dist/signal-Dvmeol77.js +0 -13
- package/dist/signal-account-CYKnyxZK.js +0 -11
- package/dist/signal-cli-install-DcmsRDbB.js +0 -188
- package/dist/signal-surface-AbofIH7s.js +0 -21
- package/dist/simple-completion-transport-Dl5DzRWP.js +0 -24
- package/dist/skill-commands-CH4lfyW8.js +0 -77
- package/dist/skill-commands-base-CvVjZByy.js +0 -60
- package/dist/skill-commands-runtime-zpWkVBay.js +0 -2
- package/dist/skill-commands.runtime-BKa5oyH-.js +0 -2
- package/dist/skill-commands.runtime.js +0 -1
- package/dist/skill-scanner-bqroDZTs.js +0 -354
- package/dist/skills-D0K7gu2V.js +0 -972
- package/dist/skills-DFTLIGCd.js +0 -4
- package/dist/skills-clawhub-KJGw0uGT.js +0 -274
- package/dist/skills-cli-DziAPxba.js +0 -369
- package/dist/skills-install-CQeO3Sll.js +0 -819
- package/dist/skills-remote-BojLLDxp.js +0 -584
- package/dist/skills-status-BINZ_svK.js +0 -2
- package/dist/skills-status-CdQUttuP.js +0 -170
- package/dist/slack-BuMSbx3k.js +0 -6
- package/dist/slack-DMg3O5YT.js +0 -9
- package/dist/slack-account-LEbfbXys.js +0 -11
- package/dist/slack-core-oqCiyGw8.js +0 -4
- package/dist/slack-runtime-surface-CyaeT7yk.js +0 -18
- package/dist/slack-surface-1yTIS5Q8.js +0 -44
- package/dist/slack-target-parser-CruIJ5w5.js +0 -12
- package/dist/slack-targets-ukxuKR1I.js +0 -45
- package/dist/slash-commands-CRuXC74L.js +0 -672
- package/dist/slash-commands.runtime-F6b9KUnh.js +0 -20
- package/dist/slash-commands.runtime.js +0 -1
- package/dist/slash-dispatch.runtime-CItGyoBF.js +0 -38
- package/dist/slash-dispatch.runtime.js +0 -1
- package/dist/slash-skill-commands.runtime-Caw0oNvV.js +0 -8
- package/dist/slash-skill-commands.runtime.js +0 -1
- package/dist/slots-Dq-2eIRb.js +0 -93
- package/dist/speech-Ceb4ISD4.js +0 -46
- package/dist/speech-provider-BHjDbut-.js +0 -190
- package/dist/speech-provider-D45m9grJ.js +0 -154
- package/dist/speech-provider-DM1ZXsz2.js +0 -385
- package/dist/speech-runtime-CNvOkPw4.js +0 -34
- package/dist/src-Dn1TYXp6.js +0 -2739
- package/dist/ssh-config-C1ipLHgq.js +0 -77
- package/dist/ssh-tunnel-BwMCLc2e.js +0 -159
- package/dist/ssh-tunnel-D5YdH2SQ.js +0 -2
- package/dist/ssrf-Duye12M_.js +0 -246
- package/dist/ssrf-policy-5L7qS2kK.js +0 -69
- package/dist/ssrf-runtime-bpDM5Bgo.js +0 -5
- package/dist/stable-node-path-n7TARbHZ.js +0 -32
- package/dist/stage-sandbox-media.runtime-Tlgx4F6M.js +0 -229
- package/dist/stage-sandbox-media.runtime.js +0 -1
- package/dist/stagger-Cs-6sySM.js +0 -54
- package/dist/startup-verification-DS8Rlwfj.js +0 -131
- package/dist/state-migrations-ssGF0Trg.js +0 -845
- package/dist/state-paths-LoYjHd5L.js +0 -2
- package/dist/status-B7yRr4OV.js +0 -3
- package/dist/status-BM1Lyqo6.js +0 -261
- package/dist/status-BekOC_tW.js +0 -650
- package/dist/status-CxodlqzZ.js +0 -2
- package/dist/status-DHbl5eIj.js +0 -608
- package/dist/status-PsQV2sfU.js +0 -44
- package/dist/status-all-C53a3881.js +0 -798
- package/dist/status-helpers-CCJSSrVJ.js +0 -170
- package/dist/status-issues-M-RUxHvm.js +0 -336
- package/dist/status-issues-qWKbW6r2.js +0 -187
- package/dist/status-json-B66tF12J.js +0 -86
- package/dist/status.agent-local-B8Sw_p6M.js +0 -59
- package/dist/status.command.text-runtime-csZS5yHm.js +0 -18
- package/dist/status.daemon-D0GzvHft.js +0 -25
- package/dist/status.format-C1VNrTBi.js +0 -44
- package/dist/status.gateway-probe-4INUEsUo.js +0 -15
- package/dist/status.link-channel-CJtR5UX6.js +0 -35
- package/dist/status.node-mode-Bva3wbBI.js +0 -2
- package/dist/status.node-mode-CneoXHPW.js +0 -32
- package/dist/status.scan-t1kfm4e0.js +0 -264
- package/dist/status.scan.deps.runtime-CCaTJlzV.js +0 -18
- package/dist/status.scan.deps.runtime.js +0 -1
- package/dist/status.scan.fast-json-DmzN3HuU.js +0 -97
- package/dist/status.scan.fast-json-gu6uL_BR.js +0 -2
- package/dist/status.scan.json-core-JjMIy9YZ.js +0 -254
- package/dist/status.scan.runtime-r_5mTC8f.js +0 -9
- package/dist/status.scan.runtime.js +0 -1
- package/dist/status.service-summary-DnbDBXMs.js +0 -32
- package/dist/status.summary-CbEhZE0h.js +0 -210
- package/dist/status.summary-SLm6uT22.js +0 -2
- package/dist/status.update-Bw9CvcCc.js +0 -2
- package/dist/status.update-CLnevxJb.js +0 -79
- package/dist/sticker-cache-C_FrWgaA.js +0 -1573
- package/dist/sticker-vision.runtime-DR7e08nD.js +0 -16
- package/dist/sticker-vision.runtime.js +0 -1
- package/dist/storage-CPQLpmgN.js +0 -234
- package/dist/storage-paths-DvC4vQo1.js +0 -51
- package/dist/store-8ONMiMTI.js +0 -365
- package/dist/store-DQJZr0Yf.js +0 -767
- package/dist/store-Dxcm2vsw.js +0 -140
- package/dist/store-_OZh8RrM.js +0 -1343
- package/dist/store-read-JHUhrL0Z.js +0 -16
- package/dist/store.runtime-Dmqv-8M7.js +0 -2
- package/dist/store.runtime.js +0 -1
- package/dist/stream-CGrFe0j0.js +0 -123
- package/dist/stream-D9HOuZmn.js +0 -633
- package/dist/stream-Di_JOWoE.js +0 -180
- package/dist/stream-wrappers-D-DlMIYG.js +0 -136
- package/dist/string-normalization-Ddc4JXRe.js +0 -19
- package/dist/string-normalization-runtime-CCsvs1ds.js +0 -1
- package/dist/string-sample-BIWt11jp.js +0 -11
- package/dist/strip-inbound-meta-DvHTlpJG.js +0 -155
- package/dist/subagent-orphan-recovery-Do8Az2RD.js +0 -206
- package/dist/subagent-registry-runtime-D5PNyCsh.js +0 -2
- package/dist/subcli-descriptors-Dasenv4J.js +0 -151
- package/dist/subsystem-C9VSlEcV.js +0 -582
- package/dist/supervisor-log.runtime-CZe76kAj.js +0 -8
- package/dist/supervisor-log.runtime.js +0 -1
- package/dist/system-cli-2-HDh_Ev.js +0 -58
- package/dist/system-events-5SDw3lix.js +0 -93
- package/dist/system-events-BuGmSpuG.js +0 -34
- package/dist/system-message-0oem5MWV.js +0 -16
- package/dist/system-run-command-BmlO5dn2.js +0 -301
- package/dist/systemd-B2HBHMpF.js +0 -588
- package/dist/systemd-hints-DkRlAuPR.js +0 -325
- package/dist/systemd-linger-BBPBJSm0.js +0 -68
- package/dist/systemd-linger-DiWO9n2E.js +0 -2
- package/dist/table-CY2XIg3l.js +0 -305
- package/dist/tables-D6qL6k4d.js +0 -832
- package/dist/tailnet-CZMxMuGK.js +0 -76
- package/dist/tailscale-mpS_cTve.js +0 -238
- package/dist/tailscale-status-Dg2Iov5f.js +0 -71
- package/dist/target-errors-ThNFqo8X.js +0 -26
- package/dist/target-ids-O3O6piTN.js +0 -76
- package/dist/target-parsing-Boyj_2v0.js +0 -41
- package/dist/target-registry-BHJcVSo1.js +0 -1269
- package/dist/targets-C7I_wjs2.js +0 -283
- package/dist/targets-CZA632lq.js +0 -39
- package/dist/targets-Dz1dx4G8.js +0 -78
- package/dist/targets-Zd0-pJu7.js +0 -748
- package/dist/targets-krmeAAxz.js +0 -70
- package/dist/task-registry-delivery-runtime-BnkUa6Mp.js +0 -2
- package/dist/task-registry.audit-DbCRt5MR.js +0 -320
- package/dist/task-registry.audit.shared-SH4MOLKM.js +0 -18
- package/dist/task-registry.maintenance-DfZmDWog.js +0 -2
- package/dist/task-registry.summary-SoFiECCc.js +0 -44
- package/dist/tavily-search-provider-BCY1kfQB.js +0 -227
- package/dist/telegram/audit.js +0 -2
- package/dist/telegram/token.js +0 -2
- package/dist/telegram-Cq20PSxw.js +0 -23
- package/dist/telegram-account-vRRwyfuA.js +0 -11
- package/dist/telegram-command-config-BiHhXScJ.js +0 -1
- package/dist/telegram-core-DTJayyAj.js +0 -6
- package/dist/telegram-custom-commands-C-nv8yoB.js +0 -75
- package/dist/temp-download-Ccatr7zI.js +0 -62
- package/dist/temp-path-JU2kkd64.js +0 -2
- package/dist/templating-Bu436Euy.js +0 -24
- package/dist/testing-BrrgubIp.js +0 -690
- package/dist/text-chunking-BTs2yTSd.js +0 -12
- package/dist/text-chunking-ByXwyFNP.js +0 -20
- package/dist/text-format-lEYRGxj5.js +0 -8
- package/dist/text-runtime-D9ApM_8O.js +0 -398
- package/dist/theme-Bb-qluq6.js +0 -2
- package/dist/theme-wBMZmJzz.js +0 -34
- package/dist/thinking-Dga2zdtd.js +0 -68
- package/dist/thinking.shared-D6IkoLJU.js +0 -243
- package/dist/thread-bindings-BMnz7w-V.js +0 -219
- package/dist/thread-bindings-BbQWOcMr.js +0 -352
- package/dist/thread-bindings-Bv0I9sBY.js +0 -534
- package/dist/thread-bindings-C3jMrVvp.js +0 -778
- package/dist/thread-bindings-policy-Br8s4hQ6.js +0 -172
- package/dist/thread-bindings-runtime-CbG63N4N.js +0 -2
- package/dist/thread-bindings-shared-DjSYaoHi.js +0 -95
- package/dist/thread-bindings.discord-api-NFR5qsWt.js +0 -487
- package/dist/thread-ownership-DG810AQi.js +0 -3
- package/dist/threading-1Z_3y_Dp.js +0 -2
- package/dist/threading-Hl8nWez6.js +0 -832
- package/dist/threading-helpers-BpqyeyAU.js +0 -14
- package/dist/threading-tool-context-DxV30qIW.js +0 -157
- package/dist/timeouts-B1NiHSGi.js +0 -74
- package/dist/tlon-C54Qojl0.js +0 -20
- package/dist/tmp-openclaw-dir-B44_vmJv.js +0 -102
- package/dist/token-BlUJQi_X.js +0 -2
- package/dist/token-CDYFJvj8.js +0 -82
- package/dist/token-kCjtGElJ.js +0 -45
- package/dist/tokens-C2f9Klbt.js +0 -66
- package/dist/tool-actions.runtime-aB0DgLk7.js +0 -532
- package/dist/tool-actions.runtime.js +0 -1
- package/dist/tool-display-C7h9IZa_.js +0 -1607
- package/dist/tool-images-BDUvc_pd.js +0 -270
- package/dist/tool-mutation-DIBpIegg.js +0 -140
- package/dist/tool-policy-FUm_F19C.js +0 -515
- package/dist/tool-policy-match-CCFHTsu9.js +0 -49
- package/dist/tool-send-DClM4vsZ.js +0 -16
- package/dist/tools-54j1hYBv.js +0 -123
- package/dist/tools-effective-inventory-C09suj6-.js +0 -148
- package/dist/tools.runtime-VBFxIXC8.js +0 -4
- package/dist/tools.runtime.js +0 -1
- package/dist/transcript-events-Dc7U5QmQ.js +0 -29
- package/dist/transport-ready-Cje3USX4.js +0 -36
- package/dist/tts-CTM9C09A.js +0 -2
- package/dist/tts-Ck_K3tIi.js +0 -84
- package/dist/tts-auto-mode-bD-u2biE.js +0 -14
- package/dist/tts-bTuorllO.js +0 -102
- package/dist/tts-eOLiJF6d.js +0 -29
- package/dist/tts.runtime-ChTwy0yH.js +0 -3
- package/dist/tts.runtime.js +0 -1
- package/dist/tui-CrM-Bz3i.js +0 -3899
- package/dist/tui-cli-aVL5aPGw.js +0 -31
- package/dist/twitch-CFFlkhYG.js +0 -15
- package/dist/typebox-DmerPHaM.js +0 -22
- package/dist/types-BzNSmNk3.js +0 -12
- package/dist/types-DAzqpvqD.js +0 -83
- package/dist/types-DVJVELY7.js +0 -471
- package/dist/types-DqNppUoh.js +0 -15
- package/dist/types.secrets-BpcqHPAv.js +0 -80
- package/dist/types.tools-BTQL6INB.js +0 -22
- package/dist/typing-BnCchGtL.js +0 -8
- package/dist/typing-Bugg5LPP.js +0 -68
- package/dist/typing-J_ktpWeA.js +0 -2
- package/dist/typing-mode-DDYL_0TG.js +0 -75
- package/dist/typing-policy-CaZhrM-F.js +0 -11
- package/dist/typing-start-guard-Cgjgzy5e.js +0 -73
- package/dist/ui-BLDY9-7R.js +0 -212
- package/dist/undici-global-dispatcher-CcNdQ7Dt.js +0 -99
- package/dist/unhandled-rejections-CA_e1R-O.js +0 -2
- package/dist/unhandled-rejections-V7U9keRG.js +0 -241
- package/dist/update-CcALvtl_.js +0 -1253
- package/dist/update-channels-Dn1F57Kf.js +0 -78
- package/dist/update-check-CoDVxy35.js +0 -354
- package/dist/update-cli-BWOYy_Mp.js +0 -1447
- package/dist/update-offset-store-BLZmshZE.js +0 -75
- package/dist/usage-CC7-wLUi.js +0 -39
- package/dist/usage-format-C7nMV1jc.js +0 -513
- package/dist/utils-CN_F_3Qg.js +0 -243
- package/dist/utils-Cgw9Y0-Q.js +0 -2
- package/dist/validation-BNNAUmDO.js +0 -18521
- package/dist/verification-B4ogZ29M.js +0 -130
- package/dist/verification-B99uVIxo.js +0 -2
- package/dist/version-BNzJ37Re.js +0 -79
- package/dist/version-Dw0eJ-1F.js +0 -2
- package/dist/voice-call-Dt6m6oA3.js +0 -4
- package/dist/wait-YXJvtMmp.js +0 -7
- package/dist/warning-filter-GX8LpGKz.js +0 -57
- package/dist/web-fetch-utils-CoVia2ez.js +0 -413
- package/dist/web-media-D9opLz21.js +0 -2
- package/dist/web-media-DHlguFej.js +0 -339
- package/dist/web-search-BcSDWJK9.js +0 -169
- package/dist/web-search-providers-CqY6CVp3.js +0 -21
- package/dist/web-search-providers.runtime-BHjzPQW-.js +0 -113
- package/dist/web-search-providers.runtime.js +0 -1
- package/dist/web-search-providers.shared-CMeQec8y.js +0 -421
- package/dist/web-search-shared-B5mHm7QD.js +0 -87
- package/dist/webhook-ingress-CTl81NUR.js +0 -164
- package/dist/webhook-memory-guards-B6k3g-po.js +0 -129
- package/dist/webhook-path-CklCw5kp.js +0 -22
- package/dist/webhook-request-guards-CxDPmU9e.js +0 -178
- package/dist/webhook-shared-Bw9wLe6L.js +0 -370
- package/dist/webhooks-cli-DPRITERU.js +0 -326
- package/dist/whatsapp-CSzf1Nu0.js +0 -5
- package/dist/whatsapp-D18Otn45.js +0 -30
- package/dist/whatsapp-auth-presence-B5GZUgTA.js +0 -41
- package/dist/whatsapp-core-Be0P_p4R.js +0 -7
- package/dist/whatsapp-heartbeat-a2-OB01n.js +0 -85
- package/dist/whatsapp-shared-ChjLiMDw.js +0 -56
- package/dist/whatsapp-shared-FR_Jw-NH.js +0 -4
- package/dist/whatsapp-surface-Dwrfi_SD.js +0 -26
- package/dist/whatsapp-targets-59A3BDBi.js +0 -13
- package/dist/widearea-dns-CfQt6b3q.js +0 -125
- package/dist/windows-argv-DDAf3waz.js +0 -345
- package/dist/windows-install-roots-XI3CvO9O.js +0 -149
- package/dist/windows-spawn-DKvkg_66.js +0 -154
- package/dist/with-timeout-B2smrGj0.js +0 -214
- package/dist/with-timeout-BmJKiOMd.js +0 -13
- package/dist/workflow-summary-BMvffn8D.js +0 -2
- package/dist/workflow-summary-NvN5lUvK.js +0 -167
- package/dist/workspace-D5P9nuJd.js +0 -479
- package/dist/workspace-D5UiPYKk.js +0 -306
- package/dist/workspace-dirs-CRv8Jwfz.js +0 -13
- package/dist/ws-97FEIc7w.js +0 -11
- package/dist/ws-log-BaAOgG9m.js +0 -281
- package/dist/ws-logging-M92fgmyp.js +0 -10
- package/dist/wsl-ytdRZp54.js +0 -57
- package/dist/x-search-FsMNZ7d0.js +0 -166
- package/dist/x-search-shared-D0tVMJk9.js +0 -124
- package/dist/zalo-CNoa7XH7.js +0 -13
- package/dist/zalo-js-HktBGDbg.js +0 -1156
- package/dist/zalo-setup-BT-xGElf.js +0 -14
- package/dist/zalouser-TwyfhIYz.js +0 -22
- package/dist/zod-1Wegy5nx.js +0 -7
- package/dist/zod-parse-HlGwTZP3.js +0 -14
- package/dist/zod-schema.agent-runtime-DFC7a3eV.js +0 -602
- package/dist/zod-schema.core-C1Ewlj9j.js +0 -520
- package/dist/zod-schema.providers-core-Bmbs-R4l.js +0 -1541
- package/dist/zod-schema.providers-whatsapp-CanJPkvb.js +0 -132
- package/dist/zod-schema.sensitive-DEzKzMxI.js +0 -5
|
@@ -1,4925 +0,0 @@
|
|
|
1
|
-
import { j as TtsConfigSchema } from "./zod-schema.core-C1Ewlj9j.js";
|
|
2
|
-
import { t as safeEqualSecret } from "./secret-equal-fMR1DjSl.js";
|
|
3
|
-
import { n as fetchWithSsrFGuard } from "./fetch-guard-BnhVFBtf.js";
|
|
4
|
-
import { a as isRequestBodyLimitError, c as requestBodyErrorToText, s as readRequestBodyWithLimit } from "./http-body-CfdQSEp0.js";
|
|
5
|
-
import { a as createWebhookInFlightLimiter, t as WEBHOOK_BODY_READ_DEFAULTS } from "./webhook-request-guards-CxDPmU9e.js";
|
|
6
|
-
import "./webhook-ingress-CTl81NUR.js";
|
|
7
|
-
import "./browser-support-Cdp2Gr_6.js";
|
|
8
|
-
import { t as zod_exports } from "./zod-1Wegy5nx.js";
|
|
9
|
-
import "./api-C16j1Xno.js";
|
|
10
|
-
import { URL as URL$1 } from "node:url";
|
|
11
|
-
import fsSync from "node:fs";
|
|
12
|
-
import path from "node:path";
|
|
13
|
-
import { spawn } from "node:child_process";
|
|
14
|
-
import os from "node:os";
|
|
15
|
-
import fs from "node:fs/promises";
|
|
16
|
-
import crypto from "node:crypto";
|
|
17
|
-
import WebSocket$1, { WebSocket, WebSocketServer } from "ws";
|
|
18
|
-
import http from "node:http";
|
|
19
|
-
//#region extensions/voice-call/src/deep-merge.ts
|
|
20
|
-
const BLOCKED_MERGE_KEYS = new Set([
|
|
21
|
-
"__proto__",
|
|
22
|
-
"prototype",
|
|
23
|
-
"constructor"
|
|
24
|
-
]);
|
|
25
|
-
function deepMergeDefined(base, override) {
|
|
26
|
-
if (!isPlainObject(base) || !isPlainObject(override)) return override === void 0 ? base : override;
|
|
27
|
-
const result = { ...base };
|
|
28
|
-
for (const [key, value] of Object.entries(override)) {
|
|
29
|
-
if (BLOCKED_MERGE_KEYS.has(key) || value === void 0) continue;
|
|
30
|
-
const existing = result[key];
|
|
31
|
-
result[key] = key in result ? deepMergeDefined(existing, value) : value;
|
|
32
|
-
}
|
|
33
|
-
return result;
|
|
34
|
-
}
|
|
35
|
-
function isPlainObject(value) {
|
|
36
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
37
|
-
}
|
|
38
|
-
//#endregion
|
|
39
|
-
//#region extensions/voice-call/src/config.ts
|
|
40
|
-
/**
|
|
41
|
-
* E.164 phone number format: +[country code][number]
|
|
42
|
-
* Examples use 555 prefix (reserved for fictional numbers)
|
|
43
|
-
*/
|
|
44
|
-
const E164Schema = zod_exports.z.string().regex(/^\+[1-9]\d{1,14}$/, "Expected E.164 format, e.g. +15550001234");
|
|
45
|
-
/**
|
|
46
|
-
* Controls how inbound calls are handled:
|
|
47
|
-
* - "disabled": Block all inbound calls (outbound only)
|
|
48
|
-
* - "allowlist": Only accept calls from numbers in allowFrom
|
|
49
|
-
* - "pairing": Unknown callers can request pairing (future)
|
|
50
|
-
* - "open": Accept all inbound calls (dangerous!)
|
|
51
|
-
*/
|
|
52
|
-
const InboundPolicySchema = zod_exports.z.enum([
|
|
53
|
-
"disabled",
|
|
54
|
-
"allowlist",
|
|
55
|
-
"pairing",
|
|
56
|
-
"open"
|
|
57
|
-
]);
|
|
58
|
-
const TelnyxConfigSchema = zod_exports.z.object({
|
|
59
|
-
apiKey: zod_exports.z.string().min(1).optional(),
|
|
60
|
-
connectionId: zod_exports.z.string().min(1).optional(),
|
|
61
|
-
publicKey: zod_exports.z.string().min(1).optional()
|
|
62
|
-
}).strict();
|
|
63
|
-
const TwilioConfigSchema = zod_exports.z.object({
|
|
64
|
-
accountSid: zod_exports.z.string().min(1).optional(),
|
|
65
|
-
authToken: zod_exports.z.string().min(1).optional()
|
|
66
|
-
}).strict();
|
|
67
|
-
const PlivoConfigSchema = zod_exports.z.object({
|
|
68
|
-
authId: zod_exports.z.string().min(1).optional(),
|
|
69
|
-
authToken: zod_exports.z.string().min(1).optional()
|
|
70
|
-
}).strict();
|
|
71
|
-
const SttConfigSchema = zod_exports.z.object({
|
|
72
|
-
provider: zod_exports.z.literal("openai").default("openai"),
|
|
73
|
-
model: zod_exports.z.string().min(1).default("whisper-1")
|
|
74
|
-
}).strict().default({
|
|
75
|
-
provider: "openai",
|
|
76
|
-
model: "whisper-1"
|
|
77
|
-
});
|
|
78
|
-
const VoiceCallServeConfigSchema = zod_exports.z.object({
|
|
79
|
-
port: zod_exports.z.number().int().positive().default(3334),
|
|
80
|
-
bind: zod_exports.z.string().default("127.0.0.1"),
|
|
81
|
-
path: zod_exports.z.string().min(1).default("/voice/webhook")
|
|
82
|
-
}).strict().default({
|
|
83
|
-
port: 3334,
|
|
84
|
-
bind: "127.0.0.1",
|
|
85
|
-
path: "/voice/webhook"
|
|
86
|
-
});
|
|
87
|
-
const VoiceCallTailscaleConfigSchema = zod_exports.z.object({
|
|
88
|
-
mode: zod_exports.z.enum([
|
|
89
|
-
"off",
|
|
90
|
-
"serve",
|
|
91
|
-
"funnel"
|
|
92
|
-
]).default("off"),
|
|
93
|
-
path: zod_exports.z.string().min(1).default("/voice/webhook")
|
|
94
|
-
}).strict().default({
|
|
95
|
-
mode: "off",
|
|
96
|
-
path: "/voice/webhook"
|
|
97
|
-
});
|
|
98
|
-
const VoiceCallTunnelConfigSchema = zod_exports.z.object({
|
|
99
|
-
provider: zod_exports.z.enum([
|
|
100
|
-
"none",
|
|
101
|
-
"ngrok",
|
|
102
|
-
"tailscale-serve",
|
|
103
|
-
"tailscale-funnel"
|
|
104
|
-
]).default("none"),
|
|
105
|
-
ngrokAuthToken: zod_exports.z.string().min(1).optional(),
|
|
106
|
-
ngrokDomain: zod_exports.z.string().min(1).optional(),
|
|
107
|
-
allowNgrokFreeTierLoopbackBypass: zod_exports.z.boolean().default(false)
|
|
108
|
-
}).strict().default({
|
|
109
|
-
provider: "none",
|
|
110
|
-
allowNgrokFreeTierLoopbackBypass: false
|
|
111
|
-
});
|
|
112
|
-
const VoiceCallWebhookSecurityConfigSchema = zod_exports.z.object({
|
|
113
|
-
allowedHosts: zod_exports.z.array(zod_exports.z.string().min(1)).default([]),
|
|
114
|
-
trustForwardingHeaders: zod_exports.z.boolean().default(false),
|
|
115
|
-
trustedProxyIPs: zod_exports.z.array(zod_exports.z.string().min(1)).default([])
|
|
116
|
-
}).strict().default({
|
|
117
|
-
allowedHosts: [],
|
|
118
|
-
trustForwardingHeaders: false,
|
|
119
|
-
trustedProxyIPs: []
|
|
120
|
-
});
|
|
121
|
-
/**
|
|
122
|
-
* Call mode determines how outbound calls behave:
|
|
123
|
-
* - "notify": Deliver message and auto-hangup after delay (one-way notification)
|
|
124
|
-
* - "conversation": Stay open for back-and-forth until explicit end or timeout
|
|
125
|
-
*/
|
|
126
|
-
const CallModeSchema = zod_exports.z.enum(["notify", "conversation"]);
|
|
127
|
-
const OutboundConfigSchema = zod_exports.z.object({
|
|
128
|
-
defaultMode: CallModeSchema.default("notify"),
|
|
129
|
-
notifyHangupDelaySec: zod_exports.z.number().int().nonnegative().default(3)
|
|
130
|
-
}).strict().default({
|
|
131
|
-
defaultMode: "notify",
|
|
132
|
-
notifyHangupDelaySec: 3
|
|
133
|
-
});
|
|
134
|
-
const VoiceCallStreamingConfigSchema = zod_exports.z.object({
|
|
135
|
-
enabled: zod_exports.z.boolean().default(false),
|
|
136
|
-
sttProvider: zod_exports.z.enum(["openai-realtime"]).default("openai-realtime"),
|
|
137
|
-
openaiApiKey: zod_exports.z.string().min(1).optional(),
|
|
138
|
-
sttModel: zod_exports.z.string().min(1).default("gpt-4o-transcribe"),
|
|
139
|
-
silenceDurationMs: zod_exports.z.number().int().positive().default(800),
|
|
140
|
-
vadThreshold: zod_exports.z.number().min(0).max(1).default(.5),
|
|
141
|
-
streamPath: zod_exports.z.string().min(1).default("/voice/stream"),
|
|
142
|
-
preStartTimeoutMs: zod_exports.z.number().int().positive().default(5e3),
|
|
143
|
-
maxPendingConnections: zod_exports.z.number().int().positive().default(32),
|
|
144
|
-
maxPendingConnectionsPerIp: zod_exports.z.number().int().positive().default(4),
|
|
145
|
-
maxConnections: zod_exports.z.number().int().positive().default(128)
|
|
146
|
-
}).strict().default({
|
|
147
|
-
enabled: false,
|
|
148
|
-
sttProvider: "openai-realtime",
|
|
149
|
-
sttModel: "gpt-4o-transcribe",
|
|
150
|
-
silenceDurationMs: 800,
|
|
151
|
-
vadThreshold: .5,
|
|
152
|
-
streamPath: "/voice/stream",
|
|
153
|
-
preStartTimeoutMs: 5e3,
|
|
154
|
-
maxPendingConnections: 32,
|
|
155
|
-
maxPendingConnectionsPerIp: 4,
|
|
156
|
-
maxConnections: 128
|
|
157
|
-
});
|
|
158
|
-
const VoiceCallConfigSchema = zod_exports.z.object({
|
|
159
|
-
enabled: zod_exports.z.boolean().default(false),
|
|
160
|
-
provider: zod_exports.z.enum([
|
|
161
|
-
"telnyx",
|
|
162
|
-
"twilio",
|
|
163
|
-
"plivo",
|
|
164
|
-
"mock"
|
|
165
|
-
]).optional(),
|
|
166
|
-
telnyx: TelnyxConfigSchema.optional(),
|
|
167
|
-
twilio: TwilioConfigSchema.optional(),
|
|
168
|
-
plivo: PlivoConfigSchema.optional(),
|
|
169
|
-
fromNumber: E164Schema.optional(),
|
|
170
|
-
toNumber: E164Schema.optional(),
|
|
171
|
-
inboundPolicy: InboundPolicySchema.default("disabled"),
|
|
172
|
-
allowFrom: zod_exports.z.array(E164Schema).default([]),
|
|
173
|
-
inboundGreeting: zod_exports.z.string().optional(),
|
|
174
|
-
outbound: OutboundConfigSchema,
|
|
175
|
-
maxDurationSeconds: zod_exports.z.number().int().positive().default(300),
|
|
176
|
-
staleCallReaperSeconds: zod_exports.z.number().int().nonnegative().default(0),
|
|
177
|
-
silenceTimeoutMs: zod_exports.z.number().int().positive().default(800),
|
|
178
|
-
transcriptTimeoutMs: zod_exports.z.number().int().positive().default(18e4),
|
|
179
|
-
ringTimeoutMs: zod_exports.z.number().int().positive().default(3e4),
|
|
180
|
-
maxConcurrentCalls: zod_exports.z.number().int().positive().default(1),
|
|
181
|
-
serve: VoiceCallServeConfigSchema,
|
|
182
|
-
tailscale: VoiceCallTailscaleConfigSchema,
|
|
183
|
-
tunnel: VoiceCallTunnelConfigSchema,
|
|
184
|
-
webhookSecurity: VoiceCallWebhookSecurityConfigSchema,
|
|
185
|
-
streaming: VoiceCallStreamingConfigSchema,
|
|
186
|
-
publicUrl: zod_exports.z.string().url().optional(),
|
|
187
|
-
skipSignatureVerification: zod_exports.z.boolean().default(false),
|
|
188
|
-
stt: SttConfigSchema,
|
|
189
|
-
tts: TtsConfigSchema,
|
|
190
|
-
store: zod_exports.z.string().optional(),
|
|
191
|
-
responseModel: zod_exports.z.string().default("openai/gpt-4o-mini"),
|
|
192
|
-
responseSystemPrompt: zod_exports.z.string().optional(),
|
|
193
|
-
responseTimeoutMs: zod_exports.z.number().int().positive().default(3e4)
|
|
194
|
-
}).strict();
|
|
195
|
-
const DEFAULT_VOICE_CALL_CONFIG = VoiceCallConfigSchema.parse({});
|
|
196
|
-
function cloneDefaultVoiceCallConfig() {
|
|
197
|
-
return structuredClone(DEFAULT_VOICE_CALL_CONFIG);
|
|
198
|
-
}
|
|
199
|
-
function normalizeVoiceCallTtsConfig(defaults, overrides) {
|
|
200
|
-
if (!defaults && !overrides) return;
|
|
201
|
-
return TtsConfigSchema.parse(deepMergeDefined(defaults ?? {}, overrides ?? {}));
|
|
202
|
-
}
|
|
203
|
-
function normalizeVoiceCallConfig(config) {
|
|
204
|
-
const defaults = cloneDefaultVoiceCallConfig();
|
|
205
|
-
return {
|
|
206
|
-
...defaults,
|
|
207
|
-
...config,
|
|
208
|
-
allowFrom: config.allowFrom ?? defaults.allowFrom,
|
|
209
|
-
outbound: {
|
|
210
|
-
...defaults.outbound,
|
|
211
|
-
...config.outbound
|
|
212
|
-
},
|
|
213
|
-
serve: {
|
|
214
|
-
...defaults.serve,
|
|
215
|
-
...config.serve
|
|
216
|
-
},
|
|
217
|
-
tailscale: {
|
|
218
|
-
...defaults.tailscale,
|
|
219
|
-
...config.tailscale
|
|
220
|
-
},
|
|
221
|
-
tunnel: {
|
|
222
|
-
...defaults.tunnel,
|
|
223
|
-
...config.tunnel
|
|
224
|
-
},
|
|
225
|
-
webhookSecurity: {
|
|
226
|
-
...defaults.webhookSecurity,
|
|
227
|
-
...config.webhookSecurity,
|
|
228
|
-
allowedHosts: config.webhookSecurity?.allowedHosts ?? defaults.webhookSecurity.allowedHosts,
|
|
229
|
-
trustedProxyIPs: config.webhookSecurity?.trustedProxyIPs ?? defaults.webhookSecurity.trustedProxyIPs
|
|
230
|
-
},
|
|
231
|
-
streaming: {
|
|
232
|
-
...defaults.streaming,
|
|
233
|
-
...config.streaming
|
|
234
|
-
},
|
|
235
|
-
stt: {
|
|
236
|
-
...defaults.stt,
|
|
237
|
-
...config.stt
|
|
238
|
-
},
|
|
239
|
-
tts: normalizeVoiceCallTtsConfig(defaults.tts, config.tts)
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* Resolves the configuration by merging environment variables into missing fields.
|
|
244
|
-
* Returns a new configuration object with environment variables applied.
|
|
245
|
-
*/
|
|
246
|
-
function resolveVoiceCallConfig(config) {
|
|
247
|
-
const resolved = normalizeVoiceCallConfig(config);
|
|
248
|
-
if (resolved.provider === "telnyx") {
|
|
249
|
-
resolved.telnyx = resolved.telnyx ?? {};
|
|
250
|
-
resolved.telnyx.apiKey = resolved.telnyx.apiKey ?? process.env.TELNYX_API_KEY;
|
|
251
|
-
resolved.telnyx.connectionId = resolved.telnyx.connectionId ?? process.env.TELNYX_CONNECTION_ID;
|
|
252
|
-
resolved.telnyx.publicKey = resolved.telnyx.publicKey ?? process.env.TELNYX_PUBLIC_KEY;
|
|
253
|
-
}
|
|
254
|
-
if (resolved.provider === "twilio") {
|
|
255
|
-
resolved.twilio = resolved.twilio ?? {};
|
|
256
|
-
resolved.twilio.accountSid = resolved.twilio.accountSid ?? process.env.TWILIO_ACCOUNT_SID;
|
|
257
|
-
resolved.twilio.authToken = resolved.twilio.authToken ?? process.env.TWILIO_AUTH_TOKEN;
|
|
258
|
-
}
|
|
259
|
-
if (resolved.provider === "plivo") {
|
|
260
|
-
resolved.plivo = resolved.plivo ?? {};
|
|
261
|
-
resolved.plivo.authId = resolved.plivo.authId ?? process.env.PLIVO_AUTH_ID;
|
|
262
|
-
resolved.plivo.authToken = resolved.plivo.authToken ?? process.env.PLIVO_AUTH_TOKEN;
|
|
263
|
-
}
|
|
264
|
-
resolved.tunnel = resolved.tunnel ?? {
|
|
265
|
-
provider: "none",
|
|
266
|
-
allowNgrokFreeTierLoopbackBypass: false
|
|
267
|
-
};
|
|
268
|
-
resolved.tunnel.allowNgrokFreeTierLoopbackBypass = resolved.tunnel.allowNgrokFreeTierLoopbackBypass ?? false;
|
|
269
|
-
resolved.tunnel.ngrokAuthToken = resolved.tunnel.ngrokAuthToken ?? process.env.NGROK_AUTHTOKEN;
|
|
270
|
-
resolved.tunnel.ngrokDomain = resolved.tunnel.ngrokDomain ?? process.env.NGROK_DOMAIN;
|
|
271
|
-
resolved.webhookSecurity = resolved.webhookSecurity ?? {
|
|
272
|
-
allowedHosts: [],
|
|
273
|
-
trustForwardingHeaders: false,
|
|
274
|
-
trustedProxyIPs: []
|
|
275
|
-
};
|
|
276
|
-
resolved.webhookSecurity.allowedHosts = resolved.webhookSecurity.allowedHosts ?? [];
|
|
277
|
-
resolved.webhookSecurity.trustForwardingHeaders = resolved.webhookSecurity.trustForwardingHeaders ?? false;
|
|
278
|
-
resolved.webhookSecurity.trustedProxyIPs = resolved.webhookSecurity.trustedProxyIPs ?? [];
|
|
279
|
-
return normalizeVoiceCallConfig(resolved);
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Validate that the configuration has all required fields for the selected provider.
|
|
283
|
-
*/
|
|
284
|
-
function validateProviderConfig(config) {
|
|
285
|
-
const errors = [];
|
|
286
|
-
if (!config.enabled) return {
|
|
287
|
-
valid: true,
|
|
288
|
-
errors: []
|
|
289
|
-
};
|
|
290
|
-
if (!config.provider) errors.push("plugins.entries.voice-call.config.provider is required");
|
|
291
|
-
if (!config.fromNumber && config.provider !== "mock") errors.push("plugins.entries.voice-call.config.fromNumber is required");
|
|
292
|
-
if (config.provider === "telnyx") {
|
|
293
|
-
if (!config.telnyx?.apiKey) errors.push("plugins.entries.voice-call.config.telnyx.apiKey is required (or set TELNYX_API_KEY env)");
|
|
294
|
-
if (!config.telnyx?.connectionId) errors.push("plugins.entries.voice-call.config.telnyx.connectionId is required (or set TELNYX_CONNECTION_ID env)");
|
|
295
|
-
if (!config.skipSignatureVerification && !config.telnyx?.publicKey) errors.push("plugins.entries.voice-call.config.telnyx.publicKey is required (or set TELNYX_PUBLIC_KEY env)");
|
|
296
|
-
}
|
|
297
|
-
if (config.provider === "twilio") {
|
|
298
|
-
if (!config.twilio?.accountSid) errors.push("plugins.entries.voice-call.config.twilio.accountSid is required (or set TWILIO_ACCOUNT_SID env)");
|
|
299
|
-
if (!config.twilio?.authToken) errors.push("plugins.entries.voice-call.config.twilio.authToken is required (or set TWILIO_AUTH_TOKEN env)");
|
|
300
|
-
}
|
|
301
|
-
if (config.provider === "plivo") {
|
|
302
|
-
if (!config.plivo?.authId) errors.push("plugins.entries.voice-call.config.plivo.authId is required (or set PLIVO_AUTH_ID env)");
|
|
303
|
-
if (!config.plivo?.authToken) errors.push("plugins.entries.voice-call.config.plivo.authToken is required (or set PLIVO_AUTH_TOKEN env)");
|
|
304
|
-
}
|
|
305
|
-
return {
|
|
306
|
-
valid: errors.length === 0,
|
|
307
|
-
errors
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
//#endregion
|
|
311
|
-
//#region extensions/voice-call/src/allowlist.ts
|
|
312
|
-
function normalizePhoneNumber(input) {
|
|
313
|
-
if (!input) return "";
|
|
314
|
-
return input.replace(/\D/g, "");
|
|
315
|
-
}
|
|
316
|
-
function isAllowlistedCaller(normalizedFrom, allowFrom) {
|
|
317
|
-
if (!normalizedFrom) return false;
|
|
318
|
-
return (allowFrom ?? []).some((num) => {
|
|
319
|
-
const normalizedAllow = normalizePhoneNumber(num);
|
|
320
|
-
return normalizedAllow !== "" && normalizedAllow === normalizedFrom;
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
//#endregion
|
|
324
|
-
//#region extensions/voice-call/src/types.ts
|
|
325
|
-
const ProviderNameSchema = zod_exports.z.enum([
|
|
326
|
-
"telnyx",
|
|
327
|
-
"twilio",
|
|
328
|
-
"plivo",
|
|
329
|
-
"mock"
|
|
330
|
-
]);
|
|
331
|
-
const CallStateSchema = zod_exports.z.enum([
|
|
332
|
-
"initiated",
|
|
333
|
-
"ringing",
|
|
334
|
-
"answered",
|
|
335
|
-
"active",
|
|
336
|
-
"speaking",
|
|
337
|
-
"listening",
|
|
338
|
-
"completed",
|
|
339
|
-
"hangup-user",
|
|
340
|
-
"hangup-bot",
|
|
341
|
-
"timeout",
|
|
342
|
-
"error",
|
|
343
|
-
"failed",
|
|
344
|
-
"no-answer",
|
|
345
|
-
"busy",
|
|
346
|
-
"voicemail"
|
|
347
|
-
]);
|
|
348
|
-
const TerminalStates = new Set([
|
|
349
|
-
"completed",
|
|
350
|
-
"hangup-user",
|
|
351
|
-
"hangup-bot",
|
|
352
|
-
"timeout",
|
|
353
|
-
"error",
|
|
354
|
-
"failed",
|
|
355
|
-
"no-answer",
|
|
356
|
-
"busy",
|
|
357
|
-
"voicemail"
|
|
358
|
-
]);
|
|
359
|
-
const EndReasonSchema = zod_exports.z.enum([
|
|
360
|
-
"completed",
|
|
361
|
-
"hangup-user",
|
|
362
|
-
"hangup-bot",
|
|
363
|
-
"timeout",
|
|
364
|
-
"error",
|
|
365
|
-
"failed",
|
|
366
|
-
"no-answer",
|
|
367
|
-
"busy",
|
|
368
|
-
"voicemail"
|
|
369
|
-
]);
|
|
370
|
-
const BaseEventSchema = zod_exports.z.object({
|
|
371
|
-
id: zod_exports.z.string(),
|
|
372
|
-
dedupeKey: zod_exports.z.string().optional(),
|
|
373
|
-
callId: zod_exports.z.string(),
|
|
374
|
-
providerCallId: zod_exports.z.string().optional(),
|
|
375
|
-
timestamp: zod_exports.z.number(),
|
|
376
|
-
turnToken: zod_exports.z.string().optional(),
|
|
377
|
-
direction: zod_exports.z.enum(["inbound", "outbound"]).optional(),
|
|
378
|
-
from: zod_exports.z.string().optional(),
|
|
379
|
-
to: zod_exports.z.string().optional()
|
|
380
|
-
});
|
|
381
|
-
zod_exports.z.discriminatedUnion("type", [
|
|
382
|
-
BaseEventSchema.extend({ type: zod_exports.z.literal("call.initiated") }),
|
|
383
|
-
BaseEventSchema.extend({ type: zod_exports.z.literal("call.ringing") }),
|
|
384
|
-
BaseEventSchema.extend({ type: zod_exports.z.literal("call.answered") }),
|
|
385
|
-
BaseEventSchema.extend({ type: zod_exports.z.literal("call.active") }),
|
|
386
|
-
BaseEventSchema.extend({
|
|
387
|
-
type: zod_exports.z.literal("call.speaking"),
|
|
388
|
-
text: zod_exports.z.string()
|
|
389
|
-
}),
|
|
390
|
-
BaseEventSchema.extend({
|
|
391
|
-
type: zod_exports.z.literal("call.speech"),
|
|
392
|
-
transcript: zod_exports.z.string(),
|
|
393
|
-
isFinal: zod_exports.z.boolean(),
|
|
394
|
-
confidence: zod_exports.z.number().min(0).max(1).optional()
|
|
395
|
-
}),
|
|
396
|
-
BaseEventSchema.extend({
|
|
397
|
-
type: zod_exports.z.literal("call.silence"),
|
|
398
|
-
durationMs: zod_exports.z.number()
|
|
399
|
-
}),
|
|
400
|
-
BaseEventSchema.extend({
|
|
401
|
-
type: zod_exports.z.literal("call.dtmf"),
|
|
402
|
-
digits: zod_exports.z.string()
|
|
403
|
-
}),
|
|
404
|
-
BaseEventSchema.extend({
|
|
405
|
-
type: zod_exports.z.literal("call.ended"),
|
|
406
|
-
reason: EndReasonSchema
|
|
407
|
-
}),
|
|
408
|
-
BaseEventSchema.extend({
|
|
409
|
-
type: zod_exports.z.literal("call.error"),
|
|
410
|
-
error: zod_exports.z.string(),
|
|
411
|
-
retryable: zod_exports.z.boolean().optional()
|
|
412
|
-
})
|
|
413
|
-
]);
|
|
414
|
-
const CallDirectionSchema = zod_exports.z.enum(["outbound", "inbound"]);
|
|
415
|
-
const TranscriptEntrySchema = zod_exports.z.object({
|
|
416
|
-
timestamp: zod_exports.z.number(),
|
|
417
|
-
speaker: zod_exports.z.enum(["bot", "user"]),
|
|
418
|
-
text: zod_exports.z.string(),
|
|
419
|
-
isFinal: zod_exports.z.boolean().default(true)
|
|
420
|
-
});
|
|
421
|
-
const CallRecordSchema = zod_exports.z.object({
|
|
422
|
-
callId: zod_exports.z.string(),
|
|
423
|
-
providerCallId: zod_exports.z.string().optional(),
|
|
424
|
-
provider: ProviderNameSchema,
|
|
425
|
-
direction: CallDirectionSchema,
|
|
426
|
-
state: CallStateSchema,
|
|
427
|
-
from: zod_exports.z.string(),
|
|
428
|
-
to: zod_exports.z.string(),
|
|
429
|
-
sessionKey: zod_exports.z.string().optional(),
|
|
430
|
-
startedAt: zod_exports.z.number(),
|
|
431
|
-
answeredAt: zod_exports.z.number().optional(),
|
|
432
|
-
endedAt: zod_exports.z.number().optional(),
|
|
433
|
-
endReason: EndReasonSchema.optional(),
|
|
434
|
-
transcript: zod_exports.z.array(TranscriptEntrySchema).default([]),
|
|
435
|
-
processedEventIds: zod_exports.z.array(zod_exports.z.string()).default([]),
|
|
436
|
-
metadata: zod_exports.z.record(zod_exports.z.string(), zod_exports.z.unknown()).optional()
|
|
437
|
-
});
|
|
438
|
-
//#endregion
|
|
439
|
-
//#region extensions/voice-call/src/manager/state.ts
|
|
440
|
-
const ConversationStates = new Set(["speaking", "listening"]);
|
|
441
|
-
const StateOrder = [
|
|
442
|
-
"initiated",
|
|
443
|
-
"ringing",
|
|
444
|
-
"answered",
|
|
445
|
-
"active",
|
|
446
|
-
"speaking",
|
|
447
|
-
"listening"
|
|
448
|
-
];
|
|
449
|
-
function transitionState(call, newState) {
|
|
450
|
-
if (call.state === newState || TerminalStates.has(call.state)) return;
|
|
451
|
-
if (TerminalStates.has(newState)) {
|
|
452
|
-
call.state = newState;
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
if (ConversationStates.has(call.state) && ConversationStates.has(newState)) {
|
|
456
|
-
call.state = newState;
|
|
457
|
-
return;
|
|
458
|
-
}
|
|
459
|
-
const currentIndex = StateOrder.indexOf(call.state);
|
|
460
|
-
if (StateOrder.indexOf(newState) > currentIndex) call.state = newState;
|
|
461
|
-
}
|
|
462
|
-
function addTranscriptEntry(call, speaker, text) {
|
|
463
|
-
const entry = {
|
|
464
|
-
timestamp: Date.now(),
|
|
465
|
-
speaker,
|
|
466
|
-
text,
|
|
467
|
-
isFinal: true
|
|
468
|
-
};
|
|
469
|
-
call.transcript.push(entry);
|
|
470
|
-
}
|
|
471
|
-
//#endregion
|
|
472
|
-
//#region extensions/voice-call/src/manager/store.ts
|
|
473
|
-
function persistCallRecord(storePath, call) {
|
|
474
|
-
const logPath = path.join(storePath, "calls.jsonl");
|
|
475
|
-
const line = `${JSON.stringify(call)}\n`;
|
|
476
|
-
fs.appendFile(logPath, line).catch((err) => {
|
|
477
|
-
console.error("[voice-call] Failed to persist call record:", err);
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
function loadActiveCallsFromStore(storePath) {
|
|
481
|
-
const logPath = path.join(storePath, "calls.jsonl");
|
|
482
|
-
if (!fsSync.existsSync(logPath)) return {
|
|
483
|
-
activeCalls: /* @__PURE__ */ new Map(),
|
|
484
|
-
providerCallIdMap: /* @__PURE__ */ new Map(),
|
|
485
|
-
processedEventIds: /* @__PURE__ */ new Set(),
|
|
486
|
-
rejectedProviderCallIds: /* @__PURE__ */ new Set()
|
|
487
|
-
};
|
|
488
|
-
const lines = fsSync.readFileSync(logPath, "utf-8").split("\n");
|
|
489
|
-
const callMap = /* @__PURE__ */ new Map();
|
|
490
|
-
for (const line of lines) {
|
|
491
|
-
if (!line.trim()) continue;
|
|
492
|
-
try {
|
|
493
|
-
const call = CallRecordSchema.parse(JSON.parse(line));
|
|
494
|
-
callMap.set(call.callId, call);
|
|
495
|
-
} catch {}
|
|
496
|
-
}
|
|
497
|
-
const activeCalls = /* @__PURE__ */ new Map();
|
|
498
|
-
const providerCallIdMap = /* @__PURE__ */ new Map();
|
|
499
|
-
const processedEventIds = /* @__PURE__ */ new Set();
|
|
500
|
-
const rejectedProviderCallIds = /* @__PURE__ */ new Set();
|
|
501
|
-
for (const [callId, call] of callMap) {
|
|
502
|
-
if (TerminalStates.has(call.state)) continue;
|
|
503
|
-
activeCalls.set(callId, call);
|
|
504
|
-
if (call.providerCallId) providerCallIdMap.set(call.providerCallId, callId);
|
|
505
|
-
for (const eventId of call.processedEventIds) processedEventIds.add(eventId);
|
|
506
|
-
}
|
|
507
|
-
return {
|
|
508
|
-
activeCalls,
|
|
509
|
-
providerCallIdMap,
|
|
510
|
-
processedEventIds,
|
|
511
|
-
rejectedProviderCallIds
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
async function getCallHistoryFromStore(storePath, limit = 50) {
|
|
515
|
-
const logPath = path.join(storePath, "calls.jsonl");
|
|
516
|
-
try {
|
|
517
|
-
await fs.access(logPath);
|
|
518
|
-
} catch {
|
|
519
|
-
return [];
|
|
520
|
-
}
|
|
521
|
-
const lines = (await fs.readFile(logPath, "utf-8")).trim().split("\n").filter(Boolean);
|
|
522
|
-
const calls = [];
|
|
523
|
-
for (const line of lines.slice(-limit)) try {
|
|
524
|
-
const parsed = CallRecordSchema.parse(JSON.parse(line));
|
|
525
|
-
calls.push(parsed);
|
|
526
|
-
} catch {}
|
|
527
|
-
return calls;
|
|
528
|
-
}
|
|
529
|
-
//#endregion
|
|
530
|
-
//#region extensions/voice-call/src/manager/timers.ts
|
|
531
|
-
function clearMaxDurationTimer(ctx, callId) {
|
|
532
|
-
const timer = ctx.maxDurationTimers.get(callId);
|
|
533
|
-
if (timer) {
|
|
534
|
-
clearTimeout(timer);
|
|
535
|
-
ctx.maxDurationTimers.delete(callId);
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
function startMaxDurationTimer(params) {
|
|
539
|
-
clearMaxDurationTimer(params.ctx, params.callId);
|
|
540
|
-
const maxDurationMs = params.ctx.config.maxDurationSeconds * 1e3;
|
|
541
|
-
console.log(`[voice-call] Starting max duration timer (${params.ctx.config.maxDurationSeconds}s) for call ${params.callId}`);
|
|
542
|
-
const timer = setTimeout(async () => {
|
|
543
|
-
params.ctx.maxDurationTimers.delete(params.callId);
|
|
544
|
-
const call = params.ctx.activeCalls.get(params.callId);
|
|
545
|
-
if (call && !TerminalStates.has(call.state)) {
|
|
546
|
-
console.log(`[voice-call] Max duration reached (${params.ctx.config.maxDurationSeconds}s), ending call ${params.callId}`);
|
|
547
|
-
call.endReason = "timeout";
|
|
548
|
-
persistCallRecord(params.ctx.storePath, call);
|
|
549
|
-
await params.onTimeout(params.callId);
|
|
550
|
-
}
|
|
551
|
-
}, maxDurationMs);
|
|
552
|
-
params.ctx.maxDurationTimers.set(params.callId, timer);
|
|
553
|
-
}
|
|
554
|
-
function clearTranscriptWaiter(ctx, callId) {
|
|
555
|
-
const waiter = ctx.transcriptWaiters.get(callId);
|
|
556
|
-
if (!waiter) return;
|
|
557
|
-
clearTimeout(waiter.timeout);
|
|
558
|
-
ctx.transcriptWaiters.delete(callId);
|
|
559
|
-
}
|
|
560
|
-
function rejectTranscriptWaiter(ctx, callId, reason) {
|
|
561
|
-
const waiter = ctx.transcriptWaiters.get(callId);
|
|
562
|
-
if (!waiter) return;
|
|
563
|
-
clearTranscriptWaiter(ctx, callId);
|
|
564
|
-
waiter.reject(new Error(reason));
|
|
565
|
-
}
|
|
566
|
-
function resolveTranscriptWaiter(ctx, callId, transcript, turnToken) {
|
|
567
|
-
const waiter = ctx.transcriptWaiters.get(callId);
|
|
568
|
-
if (!waiter) return false;
|
|
569
|
-
if (waiter.turnToken && waiter.turnToken !== turnToken) return false;
|
|
570
|
-
clearTranscriptWaiter(ctx, callId);
|
|
571
|
-
waiter.resolve(transcript);
|
|
572
|
-
return true;
|
|
573
|
-
}
|
|
574
|
-
function waitForFinalTranscript(ctx, callId, turnToken) {
|
|
575
|
-
if (ctx.transcriptWaiters.has(callId)) return Promise.reject(/* @__PURE__ */ new Error("Already waiting for transcript"));
|
|
576
|
-
const timeoutMs = ctx.config.transcriptTimeoutMs;
|
|
577
|
-
return new Promise((resolve, reject) => {
|
|
578
|
-
const timeout = setTimeout(() => {
|
|
579
|
-
ctx.transcriptWaiters.delete(callId);
|
|
580
|
-
reject(/* @__PURE__ */ new Error(`Timed out waiting for transcript after ${timeoutMs}ms`));
|
|
581
|
-
}, timeoutMs);
|
|
582
|
-
ctx.transcriptWaiters.set(callId, {
|
|
583
|
-
resolve,
|
|
584
|
-
reject,
|
|
585
|
-
timeout,
|
|
586
|
-
turnToken
|
|
587
|
-
});
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
|
-
//#endregion
|
|
591
|
-
//#region extensions/voice-call/src/manager/lifecycle.ts
|
|
592
|
-
function removeProviderCallMapping(providerCallIdMap, call) {
|
|
593
|
-
if (!call.providerCallId) return;
|
|
594
|
-
if (providerCallIdMap.get(call.providerCallId) === call.callId) providerCallIdMap.delete(call.providerCallId);
|
|
595
|
-
}
|
|
596
|
-
function finalizeCall(params) {
|
|
597
|
-
const { ctx, call, endReason } = params;
|
|
598
|
-
call.endedAt = params.endedAt ?? Date.now();
|
|
599
|
-
call.endReason = endReason;
|
|
600
|
-
transitionState(call, endReason);
|
|
601
|
-
persistCallRecord(ctx.storePath, call);
|
|
602
|
-
if (ctx.maxDurationTimers) clearMaxDurationTimer({ maxDurationTimers: ctx.maxDurationTimers }, call.callId);
|
|
603
|
-
if (ctx.transcriptWaiters) rejectTranscriptWaiter({ transcriptWaiters: ctx.transcriptWaiters }, call.callId, params.transcriptRejectReason ?? `Call ended: ${endReason}`);
|
|
604
|
-
ctx.activeCalls.delete(call.callId);
|
|
605
|
-
removeProviderCallMapping(ctx.providerCallIdMap, call);
|
|
606
|
-
}
|
|
607
|
-
//#endregion
|
|
608
|
-
//#region extensions/voice-call/src/manager/lookup.ts
|
|
609
|
-
function getCallByProviderCallId(params) {
|
|
610
|
-
const callId = params.providerCallIdMap.get(params.providerCallId);
|
|
611
|
-
if (callId) return params.activeCalls.get(callId);
|
|
612
|
-
for (const call of params.activeCalls.values()) if (call.providerCallId === params.providerCallId) return call;
|
|
613
|
-
}
|
|
614
|
-
function findCall(params) {
|
|
615
|
-
const directCall = params.activeCalls.get(params.callIdOrProviderCallId);
|
|
616
|
-
if (directCall) return directCall;
|
|
617
|
-
return getCallByProviderCallId({
|
|
618
|
-
activeCalls: params.activeCalls,
|
|
619
|
-
providerCallIdMap: params.providerCallIdMap,
|
|
620
|
-
providerCallId: params.callIdOrProviderCallId
|
|
621
|
-
});
|
|
622
|
-
}
|
|
623
|
-
//#endregion
|
|
624
|
-
//#region extensions/voice-call/src/voice-mapping.ts
|
|
625
|
-
/**
|
|
626
|
-
* Voice mapping and XML utilities for voice call providers.
|
|
627
|
-
*/
|
|
628
|
-
/**
|
|
629
|
-
* Escape XML special characters for TwiML and other XML responses.
|
|
630
|
-
*/
|
|
631
|
-
function escapeXml(text) {
|
|
632
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
633
|
-
}
|
|
634
|
-
/**
|
|
635
|
-
* Map of OpenAI voice names to similar Twilio Polly voices.
|
|
636
|
-
*/
|
|
637
|
-
const OPENAI_TO_POLLY_MAP = {
|
|
638
|
-
alloy: "Polly.Joanna",
|
|
639
|
-
echo: "Polly.Matthew",
|
|
640
|
-
fable: "Polly.Amy",
|
|
641
|
-
onyx: "Polly.Brian",
|
|
642
|
-
nova: "Polly.Salli",
|
|
643
|
-
shimmer: "Polly.Kimberly"
|
|
644
|
-
};
|
|
645
|
-
/**
|
|
646
|
-
* Default Polly voice when no mapping is found.
|
|
647
|
-
*/
|
|
648
|
-
const DEFAULT_POLLY_VOICE = "Polly.Joanna";
|
|
649
|
-
/**
|
|
650
|
-
* Map OpenAI voice names to Twilio Polly equivalents.
|
|
651
|
-
* Falls through if already a valid Polly/Google voice.
|
|
652
|
-
*
|
|
653
|
-
* @param voice - OpenAI voice name (alloy, echo, etc.) or Polly voice name
|
|
654
|
-
* @returns Polly voice name suitable for Twilio TwiML
|
|
655
|
-
*/
|
|
656
|
-
function mapVoiceToPolly(voice) {
|
|
657
|
-
if (!voice) return DEFAULT_POLLY_VOICE;
|
|
658
|
-
if (voice.startsWith("Polly.") || voice.startsWith("Google.")) return voice;
|
|
659
|
-
return OPENAI_TO_POLLY_MAP[voice.toLowerCase()] || "Polly.Joanna";
|
|
660
|
-
}
|
|
661
|
-
//#endregion
|
|
662
|
-
//#region extensions/voice-call/src/manager/twiml.ts
|
|
663
|
-
function generateNotifyTwiml(message, voice) {
|
|
664
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
665
|
-
<Response>
|
|
666
|
-
<Say voice="${voice}">${escapeXml(message)}</Say>
|
|
667
|
-
<Hangup/>
|
|
668
|
-
</Response>`;
|
|
669
|
-
}
|
|
670
|
-
//#endregion
|
|
671
|
-
//#region extensions/voice-call/src/manager/outbound.ts
|
|
672
|
-
function lookupConnectedCall(ctx, callId) {
|
|
673
|
-
const call = ctx.activeCalls.get(callId);
|
|
674
|
-
if (!call) return {
|
|
675
|
-
kind: "error",
|
|
676
|
-
error: "Call not found"
|
|
677
|
-
};
|
|
678
|
-
if (!ctx.provider || !call.providerCallId) return {
|
|
679
|
-
kind: "error",
|
|
680
|
-
error: "Call not connected"
|
|
681
|
-
};
|
|
682
|
-
if (TerminalStates.has(call.state)) return {
|
|
683
|
-
kind: "ended",
|
|
684
|
-
call
|
|
685
|
-
};
|
|
686
|
-
return {
|
|
687
|
-
kind: "ok",
|
|
688
|
-
call,
|
|
689
|
-
providerCallId: call.providerCallId,
|
|
690
|
-
provider: ctx.provider
|
|
691
|
-
};
|
|
692
|
-
}
|
|
693
|
-
function requireConnectedCall(ctx, callId) {
|
|
694
|
-
const lookup = lookupConnectedCall(ctx, callId);
|
|
695
|
-
if (lookup.kind === "error") return {
|
|
696
|
-
ok: false,
|
|
697
|
-
error: lookup.error
|
|
698
|
-
};
|
|
699
|
-
if (lookup.kind === "ended") return {
|
|
700
|
-
ok: false,
|
|
701
|
-
error: "Call has ended"
|
|
702
|
-
};
|
|
703
|
-
return {
|
|
704
|
-
ok: true,
|
|
705
|
-
call: lookup.call,
|
|
706
|
-
providerCallId: lookup.providerCallId,
|
|
707
|
-
provider: lookup.provider
|
|
708
|
-
};
|
|
709
|
-
}
|
|
710
|
-
function resolveOpenAITtsVoice(config) {
|
|
711
|
-
const providerConfig = config.tts?.providers?.openai;
|
|
712
|
-
return providerConfig && typeof providerConfig === "object" ? providerConfig.voice : void 0;
|
|
713
|
-
}
|
|
714
|
-
async function initiateCall(ctx, to, sessionKey, options) {
|
|
715
|
-
const opts = typeof options === "string" ? { message: options } : options ?? {};
|
|
716
|
-
const initialMessage = opts.message;
|
|
717
|
-
const mode = opts.mode ?? ctx.config.outbound.defaultMode;
|
|
718
|
-
if (!ctx.provider) return {
|
|
719
|
-
callId: "",
|
|
720
|
-
success: false,
|
|
721
|
-
error: "Provider not initialized"
|
|
722
|
-
};
|
|
723
|
-
if (!ctx.webhookUrl) return {
|
|
724
|
-
callId: "",
|
|
725
|
-
success: false,
|
|
726
|
-
error: "Webhook URL not configured"
|
|
727
|
-
};
|
|
728
|
-
if (ctx.activeCalls.size >= ctx.config.maxConcurrentCalls) return {
|
|
729
|
-
callId: "",
|
|
730
|
-
success: false,
|
|
731
|
-
error: `Maximum concurrent calls (${ctx.config.maxConcurrentCalls}) reached`
|
|
732
|
-
};
|
|
733
|
-
const callId = crypto.randomUUID();
|
|
734
|
-
const from = ctx.config.fromNumber || (ctx.provider?.name === "mock" ? "+15550000000" : void 0);
|
|
735
|
-
if (!from) return {
|
|
736
|
-
callId: "",
|
|
737
|
-
success: false,
|
|
738
|
-
error: "fromNumber not configured"
|
|
739
|
-
};
|
|
740
|
-
const callRecord = {
|
|
741
|
-
callId,
|
|
742
|
-
provider: ctx.provider.name,
|
|
743
|
-
direction: "outbound",
|
|
744
|
-
state: "initiated",
|
|
745
|
-
from,
|
|
746
|
-
to,
|
|
747
|
-
sessionKey,
|
|
748
|
-
startedAt: Date.now(),
|
|
749
|
-
transcript: [],
|
|
750
|
-
processedEventIds: [],
|
|
751
|
-
metadata: {
|
|
752
|
-
...initialMessage && { initialMessage },
|
|
753
|
-
mode
|
|
754
|
-
}
|
|
755
|
-
};
|
|
756
|
-
ctx.activeCalls.set(callId, callRecord);
|
|
757
|
-
persistCallRecord(ctx.storePath, callRecord);
|
|
758
|
-
try {
|
|
759
|
-
let inlineTwiml;
|
|
760
|
-
if (mode === "notify" && initialMessage) {
|
|
761
|
-
const pollyVoice = mapVoiceToPolly(resolveOpenAITtsVoice(ctx.config));
|
|
762
|
-
inlineTwiml = generateNotifyTwiml(initialMessage, pollyVoice);
|
|
763
|
-
console.log(`[voice-call] Using inline TwiML for notify mode (voice: ${pollyVoice})`);
|
|
764
|
-
}
|
|
765
|
-
const result = await ctx.provider.initiateCall({
|
|
766
|
-
callId,
|
|
767
|
-
from,
|
|
768
|
-
to,
|
|
769
|
-
webhookUrl: ctx.webhookUrl,
|
|
770
|
-
inlineTwiml
|
|
771
|
-
});
|
|
772
|
-
callRecord.providerCallId = result.providerCallId;
|
|
773
|
-
ctx.providerCallIdMap.set(result.providerCallId, callId);
|
|
774
|
-
persistCallRecord(ctx.storePath, callRecord);
|
|
775
|
-
return {
|
|
776
|
-
callId,
|
|
777
|
-
success: true
|
|
778
|
-
};
|
|
779
|
-
} catch (err) {
|
|
780
|
-
finalizeCall({
|
|
781
|
-
ctx,
|
|
782
|
-
call: callRecord,
|
|
783
|
-
endReason: "failed"
|
|
784
|
-
});
|
|
785
|
-
return {
|
|
786
|
-
callId,
|
|
787
|
-
success: false,
|
|
788
|
-
error: err instanceof Error ? err.message : String(err)
|
|
789
|
-
};
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
async function speak(ctx, callId, text) {
|
|
793
|
-
const connected = requireConnectedCall(ctx, callId);
|
|
794
|
-
if (!connected.ok) return {
|
|
795
|
-
success: false,
|
|
796
|
-
error: connected.error
|
|
797
|
-
};
|
|
798
|
-
const { call, providerCallId, provider } = connected;
|
|
799
|
-
try {
|
|
800
|
-
transitionState(call, "speaking");
|
|
801
|
-
persistCallRecord(ctx.storePath, call);
|
|
802
|
-
const voice = provider.name === "twilio" ? resolveOpenAITtsVoice(ctx.config) : void 0;
|
|
803
|
-
await provider.playTts({
|
|
804
|
-
callId,
|
|
805
|
-
providerCallId,
|
|
806
|
-
text,
|
|
807
|
-
voice
|
|
808
|
-
});
|
|
809
|
-
addTranscriptEntry(call, "bot", text);
|
|
810
|
-
persistCallRecord(ctx.storePath, call);
|
|
811
|
-
return { success: true };
|
|
812
|
-
} catch (err) {
|
|
813
|
-
transitionState(call, "listening");
|
|
814
|
-
persistCallRecord(ctx.storePath, call);
|
|
815
|
-
return {
|
|
816
|
-
success: false,
|
|
817
|
-
error: err instanceof Error ? err.message : String(err)
|
|
818
|
-
};
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
async function speakInitialMessage(ctx, providerCallId) {
|
|
822
|
-
const call = getCallByProviderCallId({
|
|
823
|
-
activeCalls: ctx.activeCalls,
|
|
824
|
-
providerCallIdMap: ctx.providerCallIdMap,
|
|
825
|
-
providerCallId
|
|
826
|
-
});
|
|
827
|
-
if (!call) {
|
|
828
|
-
console.warn(`[voice-call] speakInitialMessage: no call found for ${providerCallId}`);
|
|
829
|
-
return;
|
|
830
|
-
}
|
|
831
|
-
const initialMessage = call.metadata?.initialMessage;
|
|
832
|
-
const mode = call.metadata?.mode ?? "conversation";
|
|
833
|
-
if (!initialMessage) {
|
|
834
|
-
console.log(`[voice-call] speakInitialMessage: no initial message for ${call.callId}`);
|
|
835
|
-
return;
|
|
836
|
-
}
|
|
837
|
-
if (ctx.initialMessageInFlight.has(call.callId)) {
|
|
838
|
-
console.log(`[voice-call] speakInitialMessage: initial message already in flight for ${call.callId}`);
|
|
839
|
-
return;
|
|
840
|
-
}
|
|
841
|
-
ctx.initialMessageInFlight.add(call.callId);
|
|
842
|
-
try {
|
|
843
|
-
console.log(`[voice-call] Speaking initial message for call ${call.callId} (mode: ${mode})`);
|
|
844
|
-
const result = await speak(ctx, call.callId, initialMessage);
|
|
845
|
-
if (!result.success) {
|
|
846
|
-
console.warn(`[voice-call] Failed to speak initial message: ${result.error}`);
|
|
847
|
-
return;
|
|
848
|
-
}
|
|
849
|
-
if (call.metadata) {
|
|
850
|
-
delete call.metadata.initialMessage;
|
|
851
|
-
persistCallRecord(ctx.storePath, call);
|
|
852
|
-
}
|
|
853
|
-
if (mode === "notify") {
|
|
854
|
-
const delaySec = ctx.config.outbound.notifyHangupDelaySec;
|
|
855
|
-
console.log(`[voice-call] Notify mode: auto-hangup in ${delaySec}s for call ${call.callId}`);
|
|
856
|
-
setTimeout(async () => {
|
|
857
|
-
const currentCall = ctx.activeCalls.get(call.callId);
|
|
858
|
-
if (currentCall && !TerminalStates.has(currentCall.state)) {
|
|
859
|
-
console.log(`[voice-call] Notify mode: hanging up call ${call.callId}`);
|
|
860
|
-
await endCall(ctx, call.callId);
|
|
861
|
-
}
|
|
862
|
-
}, delaySec * 1e3);
|
|
863
|
-
}
|
|
864
|
-
} finally {
|
|
865
|
-
ctx.initialMessageInFlight.delete(call.callId);
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
async function continueCall(ctx, callId, prompt) {
|
|
869
|
-
const connected = requireConnectedCall(ctx, callId);
|
|
870
|
-
if (!connected.ok) return {
|
|
871
|
-
success: false,
|
|
872
|
-
error: connected.error
|
|
873
|
-
};
|
|
874
|
-
const { call, providerCallId, provider } = connected;
|
|
875
|
-
if (ctx.activeTurnCalls.has(callId) || ctx.transcriptWaiters.has(callId)) return {
|
|
876
|
-
success: false,
|
|
877
|
-
error: "Already waiting for transcript"
|
|
878
|
-
};
|
|
879
|
-
ctx.activeTurnCalls.add(callId);
|
|
880
|
-
const turnStartedAt = Date.now();
|
|
881
|
-
const turnToken = provider.name === "twilio" ? crypto.randomUUID() : void 0;
|
|
882
|
-
try {
|
|
883
|
-
await speak(ctx, callId, prompt);
|
|
884
|
-
transitionState(call, "listening");
|
|
885
|
-
persistCallRecord(ctx.storePath, call);
|
|
886
|
-
const listenStartedAt = Date.now();
|
|
887
|
-
await provider.startListening({
|
|
888
|
-
callId,
|
|
889
|
-
providerCallId,
|
|
890
|
-
turnToken
|
|
891
|
-
});
|
|
892
|
-
const transcript = await waitForFinalTranscript(ctx, callId, turnToken);
|
|
893
|
-
const transcriptReceivedAt = Date.now();
|
|
894
|
-
await provider.stopListening({
|
|
895
|
-
callId,
|
|
896
|
-
providerCallId
|
|
897
|
-
});
|
|
898
|
-
const lastTurnLatencyMs = transcriptReceivedAt - turnStartedAt;
|
|
899
|
-
const lastTurnListenWaitMs = transcriptReceivedAt - listenStartedAt;
|
|
900
|
-
const turnCount = call.metadata && typeof call.metadata.turnCount === "number" ? call.metadata.turnCount + 1 : 1;
|
|
901
|
-
call.metadata = {
|
|
902
|
-
...call.metadata ?? {},
|
|
903
|
-
turnCount,
|
|
904
|
-
lastTurnLatencyMs,
|
|
905
|
-
lastTurnListenWaitMs,
|
|
906
|
-
lastTurnCompletedAt: transcriptReceivedAt
|
|
907
|
-
};
|
|
908
|
-
persistCallRecord(ctx.storePath, call);
|
|
909
|
-
console.log("[voice-call] continueCall latency call=" + call.callId + " totalMs=" + String(lastTurnLatencyMs) + " listenWaitMs=" + String(lastTurnListenWaitMs));
|
|
910
|
-
return {
|
|
911
|
-
success: true,
|
|
912
|
-
transcript
|
|
913
|
-
};
|
|
914
|
-
} catch (err) {
|
|
915
|
-
return {
|
|
916
|
-
success: false,
|
|
917
|
-
error: err instanceof Error ? err.message : String(err)
|
|
918
|
-
};
|
|
919
|
-
} finally {
|
|
920
|
-
ctx.activeTurnCalls.delete(callId);
|
|
921
|
-
clearTranscriptWaiter(ctx, callId);
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
async function endCall(ctx, callId, options) {
|
|
925
|
-
const lookup = lookupConnectedCall(ctx, callId);
|
|
926
|
-
if (lookup.kind === "error") return {
|
|
927
|
-
success: false,
|
|
928
|
-
error: lookup.error
|
|
929
|
-
};
|
|
930
|
-
if (lookup.kind === "ended") return { success: true };
|
|
931
|
-
const { call, providerCallId, provider } = lookup;
|
|
932
|
-
const reason = options?.reason ?? "hangup-bot";
|
|
933
|
-
try {
|
|
934
|
-
await provider.hangupCall({
|
|
935
|
-
callId,
|
|
936
|
-
providerCallId,
|
|
937
|
-
reason
|
|
938
|
-
});
|
|
939
|
-
finalizeCall({
|
|
940
|
-
ctx,
|
|
941
|
-
call,
|
|
942
|
-
endReason: reason
|
|
943
|
-
});
|
|
944
|
-
return { success: true };
|
|
945
|
-
} catch (err) {
|
|
946
|
-
return {
|
|
947
|
-
success: false,
|
|
948
|
-
error: err instanceof Error ? err.message : String(err)
|
|
949
|
-
};
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
//#endregion
|
|
953
|
-
//#region extensions/voice-call/src/manager/events.ts
|
|
954
|
-
function shouldAcceptInbound(config, from) {
|
|
955
|
-
const { inboundPolicy: policy, allowFrom } = config;
|
|
956
|
-
switch (policy) {
|
|
957
|
-
case "disabled":
|
|
958
|
-
console.log("[voice-call] Inbound call rejected: policy is disabled");
|
|
959
|
-
return false;
|
|
960
|
-
case "open":
|
|
961
|
-
console.log("[voice-call] Inbound call accepted: policy is open");
|
|
962
|
-
return true;
|
|
963
|
-
case "allowlist":
|
|
964
|
-
case "pairing": {
|
|
965
|
-
const normalized = normalizePhoneNumber(from);
|
|
966
|
-
if (!normalized) {
|
|
967
|
-
console.log("[voice-call] Inbound call rejected: missing caller ID");
|
|
968
|
-
return false;
|
|
969
|
-
}
|
|
970
|
-
const allowed = isAllowlistedCaller(normalized, allowFrom);
|
|
971
|
-
console.log(`[voice-call] Inbound call ${allowed ? "accepted" : "rejected"}: ${from} ${allowed ? "is in" : "not in"} allowlist`);
|
|
972
|
-
return allowed;
|
|
973
|
-
}
|
|
974
|
-
default: return false;
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
function createWebhookCall(params) {
|
|
978
|
-
const callId = crypto.randomUUID();
|
|
979
|
-
const callRecord = {
|
|
980
|
-
callId,
|
|
981
|
-
providerCallId: params.providerCallId,
|
|
982
|
-
provider: params.ctx.provider?.name || "twilio",
|
|
983
|
-
direction: params.direction,
|
|
984
|
-
state: "ringing",
|
|
985
|
-
from: params.from,
|
|
986
|
-
to: params.to,
|
|
987
|
-
startedAt: Date.now(),
|
|
988
|
-
transcript: [],
|
|
989
|
-
processedEventIds: [],
|
|
990
|
-
metadata: { initialMessage: params.direction === "inbound" ? params.ctx.config.inboundGreeting || "Hello! How can I help you today?" : void 0 }
|
|
991
|
-
};
|
|
992
|
-
params.ctx.activeCalls.set(callId, callRecord);
|
|
993
|
-
params.ctx.providerCallIdMap.set(params.providerCallId, callId);
|
|
994
|
-
persistCallRecord(params.ctx.storePath, callRecord);
|
|
995
|
-
console.log(`[voice-call] Created ${params.direction} call record: ${callId} from ${params.from}`);
|
|
996
|
-
return callRecord;
|
|
997
|
-
}
|
|
998
|
-
function processEvent(ctx, event) {
|
|
999
|
-
const dedupeKey = event.dedupeKey || event.id;
|
|
1000
|
-
if (ctx.processedEventIds.has(dedupeKey)) return;
|
|
1001
|
-
ctx.processedEventIds.add(dedupeKey);
|
|
1002
|
-
let call = findCall({
|
|
1003
|
-
activeCalls: ctx.activeCalls,
|
|
1004
|
-
providerCallIdMap: ctx.providerCallIdMap,
|
|
1005
|
-
callIdOrProviderCallId: event.callId
|
|
1006
|
-
});
|
|
1007
|
-
const providerCallId = event.providerCallId;
|
|
1008
|
-
const eventDirection = event.direction === "inbound" || event.direction === "outbound" ? event.direction : void 0;
|
|
1009
|
-
if (!call && providerCallId && eventDirection) {
|
|
1010
|
-
if (eventDirection === "inbound" && !shouldAcceptInbound(ctx.config, event.from)) {
|
|
1011
|
-
const pid = providerCallId;
|
|
1012
|
-
if (!ctx.provider) {
|
|
1013
|
-
console.warn(`[voice-call] Inbound call rejected by policy but no provider to hang up (providerCallId: ${pid}, from: ${event.from}); call will time out on provider side.`);
|
|
1014
|
-
return;
|
|
1015
|
-
}
|
|
1016
|
-
if (ctx.rejectedProviderCallIds.has(pid)) return;
|
|
1017
|
-
ctx.rejectedProviderCallIds.add(pid);
|
|
1018
|
-
const callId = event.callId ?? pid;
|
|
1019
|
-
console.log(`[voice-call] Rejecting inbound call by policy: ${pid}`);
|
|
1020
|
-
ctx.provider.hangupCall({
|
|
1021
|
-
callId,
|
|
1022
|
-
providerCallId: pid,
|
|
1023
|
-
reason: "hangup-bot"
|
|
1024
|
-
}).catch((err) => {
|
|
1025
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1026
|
-
console.warn(`[voice-call] Failed to reject inbound call ${pid}:`, message);
|
|
1027
|
-
});
|
|
1028
|
-
return;
|
|
1029
|
-
}
|
|
1030
|
-
call = createWebhookCall({
|
|
1031
|
-
ctx,
|
|
1032
|
-
providerCallId,
|
|
1033
|
-
direction: eventDirection === "outbound" ? "outbound" : "inbound",
|
|
1034
|
-
from: event.from || "unknown",
|
|
1035
|
-
to: event.to || ctx.config.fromNumber || "unknown"
|
|
1036
|
-
});
|
|
1037
|
-
event.callId = call.callId;
|
|
1038
|
-
}
|
|
1039
|
-
if (!call) return;
|
|
1040
|
-
if (event.providerCallId && event.providerCallId !== call.providerCallId) {
|
|
1041
|
-
const previousProviderCallId = call.providerCallId;
|
|
1042
|
-
call.providerCallId = event.providerCallId;
|
|
1043
|
-
ctx.providerCallIdMap.set(event.providerCallId, call.callId);
|
|
1044
|
-
if (previousProviderCallId) {
|
|
1045
|
-
if (ctx.providerCallIdMap.get(previousProviderCallId) === call.callId) ctx.providerCallIdMap.delete(previousProviderCallId);
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
call.processedEventIds.push(dedupeKey);
|
|
1049
|
-
switch (event.type) {
|
|
1050
|
-
case "call.initiated":
|
|
1051
|
-
transitionState(call, "initiated");
|
|
1052
|
-
break;
|
|
1053
|
-
case "call.ringing":
|
|
1054
|
-
transitionState(call, "ringing");
|
|
1055
|
-
break;
|
|
1056
|
-
case "call.answered":
|
|
1057
|
-
call.answeredAt = event.timestamp;
|
|
1058
|
-
transitionState(call, "answered");
|
|
1059
|
-
startMaxDurationTimer({
|
|
1060
|
-
ctx,
|
|
1061
|
-
callId: call.callId,
|
|
1062
|
-
onTimeout: async (callId) => {
|
|
1063
|
-
await endCall(ctx, callId, { reason: "timeout" });
|
|
1064
|
-
}
|
|
1065
|
-
});
|
|
1066
|
-
ctx.onCallAnswered?.(call);
|
|
1067
|
-
break;
|
|
1068
|
-
case "call.active":
|
|
1069
|
-
transitionState(call, "active");
|
|
1070
|
-
break;
|
|
1071
|
-
case "call.speaking":
|
|
1072
|
-
transitionState(call, "speaking");
|
|
1073
|
-
break;
|
|
1074
|
-
case "call.speech":
|
|
1075
|
-
if (event.isFinal) {
|
|
1076
|
-
const hadWaiter = ctx.transcriptWaiters.has(call.callId);
|
|
1077
|
-
const resolved = resolveTranscriptWaiter(ctx, call.callId, event.transcript, event.turnToken);
|
|
1078
|
-
if (hadWaiter && !resolved) {
|
|
1079
|
-
console.warn(`[voice-call] Ignoring speech event with mismatched turn token for ${call.callId}`);
|
|
1080
|
-
break;
|
|
1081
|
-
}
|
|
1082
|
-
addTranscriptEntry(call, "user", event.transcript);
|
|
1083
|
-
}
|
|
1084
|
-
transitionState(call, "listening");
|
|
1085
|
-
break;
|
|
1086
|
-
case "call.ended":
|
|
1087
|
-
finalizeCall({
|
|
1088
|
-
ctx,
|
|
1089
|
-
call,
|
|
1090
|
-
endReason: event.reason,
|
|
1091
|
-
endedAt: event.timestamp
|
|
1092
|
-
});
|
|
1093
|
-
return;
|
|
1094
|
-
case "call.error":
|
|
1095
|
-
if (!event.retryable) {
|
|
1096
|
-
finalizeCall({
|
|
1097
|
-
ctx,
|
|
1098
|
-
call,
|
|
1099
|
-
endReason: "error",
|
|
1100
|
-
endedAt: event.timestamp,
|
|
1101
|
-
transcriptRejectReason: `Call error: ${event.error}`
|
|
1102
|
-
});
|
|
1103
|
-
return;
|
|
1104
|
-
}
|
|
1105
|
-
break;
|
|
1106
|
-
}
|
|
1107
|
-
persistCallRecord(ctx.storePath, call);
|
|
1108
|
-
}
|
|
1109
|
-
//#endregion
|
|
1110
|
-
//#region extensions/voice-call/src/utils.ts
|
|
1111
|
-
function resolveUserPath(input) {
|
|
1112
|
-
const trimmed = input.trim();
|
|
1113
|
-
if (!trimmed) return trimmed;
|
|
1114
|
-
if (trimmed.startsWith("~")) {
|
|
1115
|
-
const expanded = trimmed.replace(/^~(?=$|[\\/])/, os.homedir());
|
|
1116
|
-
return path.resolve(expanded);
|
|
1117
|
-
}
|
|
1118
|
-
return path.resolve(trimmed);
|
|
1119
|
-
}
|
|
1120
|
-
//#endregion
|
|
1121
|
-
//#region extensions/voice-call/src/manager.ts
|
|
1122
|
-
function resolveDefaultStoreBase(config, storePath) {
|
|
1123
|
-
const rawOverride = storePath?.trim() || config.store?.trim();
|
|
1124
|
-
if (rawOverride) return resolveUserPath(rawOverride);
|
|
1125
|
-
const preferred = path.join(os.homedir(), ".openclaw", "voice-calls");
|
|
1126
|
-
return [preferred].map((dir) => resolveUserPath(dir)).find((dir) => {
|
|
1127
|
-
try {
|
|
1128
|
-
return fsSync.existsSync(path.join(dir, "calls.jsonl")) || fsSync.existsSync(dir);
|
|
1129
|
-
} catch {
|
|
1130
|
-
return false;
|
|
1131
|
-
}
|
|
1132
|
-
}) ?? resolveUserPath(preferred);
|
|
1133
|
-
}
|
|
1134
|
-
/**
|
|
1135
|
-
* Manages voice calls: state ownership and delegation to manager helper modules.
|
|
1136
|
-
*/
|
|
1137
|
-
var CallManager = class {
|
|
1138
|
-
constructor(config, storePath) {
|
|
1139
|
-
this.activeCalls = /* @__PURE__ */ new Map();
|
|
1140
|
-
this.providerCallIdMap = /* @__PURE__ */ new Map();
|
|
1141
|
-
this.processedEventIds = /* @__PURE__ */ new Set();
|
|
1142
|
-
this.rejectedProviderCallIds = /* @__PURE__ */ new Set();
|
|
1143
|
-
this.provider = null;
|
|
1144
|
-
this.webhookUrl = null;
|
|
1145
|
-
this.activeTurnCalls = /* @__PURE__ */ new Set();
|
|
1146
|
-
this.transcriptWaiters = /* @__PURE__ */ new Map();
|
|
1147
|
-
this.maxDurationTimers = /* @__PURE__ */ new Map();
|
|
1148
|
-
this.initialMessageInFlight = /* @__PURE__ */ new Set();
|
|
1149
|
-
this.config = config;
|
|
1150
|
-
this.storePath = resolveDefaultStoreBase(config, storePath);
|
|
1151
|
-
}
|
|
1152
|
-
/**
|
|
1153
|
-
* Initialize the call manager with a provider.
|
|
1154
|
-
* Verifies persisted calls with the provider and restarts timers.
|
|
1155
|
-
*/
|
|
1156
|
-
async initialize(provider, webhookUrl) {
|
|
1157
|
-
this.provider = provider;
|
|
1158
|
-
this.webhookUrl = webhookUrl;
|
|
1159
|
-
fsSync.mkdirSync(this.storePath, { recursive: true });
|
|
1160
|
-
const persisted = loadActiveCallsFromStore(this.storePath);
|
|
1161
|
-
this.processedEventIds = persisted.processedEventIds;
|
|
1162
|
-
this.rejectedProviderCallIds = persisted.rejectedProviderCallIds;
|
|
1163
|
-
const verified = await this.verifyRestoredCalls(provider, persisted.activeCalls);
|
|
1164
|
-
this.activeCalls = verified;
|
|
1165
|
-
this.providerCallIdMap = /* @__PURE__ */ new Map();
|
|
1166
|
-
for (const [callId, call] of verified) if (call.providerCallId) this.providerCallIdMap.set(call.providerCallId, callId);
|
|
1167
|
-
for (const [callId, call] of verified) if (call.answeredAt && !TerminalStates.has(call.state)) {
|
|
1168
|
-
if (Date.now() - call.answeredAt >= this.config.maxDurationSeconds * 1e3) {
|
|
1169
|
-
verified.delete(callId);
|
|
1170
|
-
if (call.providerCallId) this.providerCallIdMap.delete(call.providerCallId);
|
|
1171
|
-
console.log(`[voice-call] Skipping restored call ${callId} (max duration already elapsed)`);
|
|
1172
|
-
continue;
|
|
1173
|
-
}
|
|
1174
|
-
startMaxDurationTimer({
|
|
1175
|
-
ctx: this.getContext(),
|
|
1176
|
-
callId,
|
|
1177
|
-
onTimeout: async (id) => {
|
|
1178
|
-
await endCall(this.getContext(), id, { reason: "timeout" });
|
|
1179
|
-
}
|
|
1180
|
-
});
|
|
1181
|
-
console.log(`[voice-call] Restarted max-duration timer for restored call ${callId}`);
|
|
1182
|
-
}
|
|
1183
|
-
if (verified.size > 0) console.log(`[voice-call] Restored ${verified.size} active call(s) from store`);
|
|
1184
|
-
}
|
|
1185
|
-
/**
|
|
1186
|
-
* Verify persisted calls with the provider before restoring.
|
|
1187
|
-
* Calls without providerCallId or older than maxDurationSeconds are skipped.
|
|
1188
|
-
* Transient provider errors keep the call (rely on timer fallback).
|
|
1189
|
-
*/
|
|
1190
|
-
async verifyRestoredCalls(provider, candidates) {
|
|
1191
|
-
if (candidates.size === 0) return /* @__PURE__ */ new Map();
|
|
1192
|
-
const maxAgeMs = this.config.maxDurationSeconds * 1e3;
|
|
1193
|
-
const now = Date.now();
|
|
1194
|
-
const verified = /* @__PURE__ */ new Map();
|
|
1195
|
-
const verifyTasks = [];
|
|
1196
|
-
for (const [callId, call] of candidates) {
|
|
1197
|
-
if (!call.providerCallId) {
|
|
1198
|
-
console.log(`[voice-call] Skipping restored call ${callId} (no providerCallId)`);
|
|
1199
|
-
continue;
|
|
1200
|
-
}
|
|
1201
|
-
if (now - call.startedAt > maxAgeMs) {
|
|
1202
|
-
console.log(`[voice-call] Skipping restored call ${callId} (older than maxDurationSeconds)`);
|
|
1203
|
-
continue;
|
|
1204
|
-
}
|
|
1205
|
-
const task = {
|
|
1206
|
-
callId,
|
|
1207
|
-
call,
|
|
1208
|
-
promise: provider.getCallStatus({ providerCallId: call.providerCallId }).then((result) => {
|
|
1209
|
-
if (result.isTerminal) console.log(`[voice-call] Skipping restored call ${callId} (provider status: ${result.status})`);
|
|
1210
|
-
else if (result.isUnknown) {
|
|
1211
|
-
console.log(`[voice-call] Keeping restored call ${callId} (provider status unknown, relying on timer)`);
|
|
1212
|
-
verified.set(callId, call);
|
|
1213
|
-
} else verified.set(callId, call);
|
|
1214
|
-
}).catch(() => {
|
|
1215
|
-
console.log(`[voice-call] Keeping restored call ${callId} (verification failed, relying on timer)`);
|
|
1216
|
-
verified.set(callId, call);
|
|
1217
|
-
})
|
|
1218
|
-
};
|
|
1219
|
-
verifyTasks.push(task);
|
|
1220
|
-
}
|
|
1221
|
-
await Promise.allSettled(verifyTasks.map((t) => t.promise));
|
|
1222
|
-
return verified;
|
|
1223
|
-
}
|
|
1224
|
-
/**
|
|
1225
|
-
* Get the current provider.
|
|
1226
|
-
*/
|
|
1227
|
-
getProvider() {
|
|
1228
|
-
return this.provider;
|
|
1229
|
-
}
|
|
1230
|
-
/**
|
|
1231
|
-
* Initiate an outbound call.
|
|
1232
|
-
*/
|
|
1233
|
-
async initiateCall(to, sessionKey, options) {
|
|
1234
|
-
return initiateCall(this.getContext(), to, sessionKey, options);
|
|
1235
|
-
}
|
|
1236
|
-
/**
|
|
1237
|
-
* Speak to user in an active call.
|
|
1238
|
-
*/
|
|
1239
|
-
async speak(callId, text) {
|
|
1240
|
-
return speak(this.getContext(), callId, text);
|
|
1241
|
-
}
|
|
1242
|
-
/**
|
|
1243
|
-
* Speak the initial message for a call (called when media stream connects).
|
|
1244
|
-
*/
|
|
1245
|
-
async speakInitialMessage(providerCallId) {
|
|
1246
|
-
return speakInitialMessage(this.getContext(), providerCallId);
|
|
1247
|
-
}
|
|
1248
|
-
/**
|
|
1249
|
-
* Continue call: speak prompt, then wait for user's final transcript.
|
|
1250
|
-
*/
|
|
1251
|
-
async continueCall(callId, prompt) {
|
|
1252
|
-
return continueCall(this.getContext(), callId, prompt);
|
|
1253
|
-
}
|
|
1254
|
-
/**
|
|
1255
|
-
* End an active call.
|
|
1256
|
-
*/
|
|
1257
|
-
async endCall(callId) {
|
|
1258
|
-
return endCall(this.getContext(), callId);
|
|
1259
|
-
}
|
|
1260
|
-
getContext() {
|
|
1261
|
-
return {
|
|
1262
|
-
activeCalls: this.activeCalls,
|
|
1263
|
-
providerCallIdMap: this.providerCallIdMap,
|
|
1264
|
-
processedEventIds: this.processedEventIds,
|
|
1265
|
-
rejectedProviderCallIds: this.rejectedProviderCallIds,
|
|
1266
|
-
provider: this.provider,
|
|
1267
|
-
config: this.config,
|
|
1268
|
-
storePath: this.storePath,
|
|
1269
|
-
webhookUrl: this.webhookUrl,
|
|
1270
|
-
activeTurnCalls: this.activeTurnCalls,
|
|
1271
|
-
transcriptWaiters: this.transcriptWaiters,
|
|
1272
|
-
maxDurationTimers: this.maxDurationTimers,
|
|
1273
|
-
initialMessageInFlight: this.initialMessageInFlight,
|
|
1274
|
-
onCallAnswered: (call) => {
|
|
1275
|
-
this.maybeSpeakInitialMessageOnAnswered(call);
|
|
1276
|
-
}
|
|
1277
|
-
};
|
|
1278
|
-
}
|
|
1279
|
-
/**
|
|
1280
|
-
* Process a webhook event.
|
|
1281
|
-
*/
|
|
1282
|
-
processEvent(event) {
|
|
1283
|
-
processEvent(this.getContext(), event);
|
|
1284
|
-
}
|
|
1285
|
-
shouldDeferConversationInitialMessageUntilStreamConnect() {
|
|
1286
|
-
if (!this.provider || this.provider.name !== "twilio" || !this.config.streaming.enabled) return false;
|
|
1287
|
-
const streamAwareProvider = this.provider;
|
|
1288
|
-
if (typeof streamAwareProvider.isConversationStreamConnectEnabled !== "function") return false;
|
|
1289
|
-
return streamAwareProvider.isConversationStreamConnectEnabled();
|
|
1290
|
-
}
|
|
1291
|
-
maybeSpeakInitialMessageOnAnswered(call) {
|
|
1292
|
-
if (!(typeof call.metadata?.initialMessage === "string" ? call.metadata.initialMessage.trim() : "")) return;
|
|
1293
|
-
const mode = call.metadata?.mode ?? "conversation";
|
|
1294
|
-
if (mode === "conversation") {
|
|
1295
|
-
if (this.shouldDeferConversationInitialMessageUntilStreamConnect()) return;
|
|
1296
|
-
} else if (mode !== "notify") return;
|
|
1297
|
-
if (!this.provider || !call.providerCallId) return;
|
|
1298
|
-
this.speakInitialMessage(call.providerCallId);
|
|
1299
|
-
}
|
|
1300
|
-
/**
|
|
1301
|
-
* Get an active call by ID.
|
|
1302
|
-
*/
|
|
1303
|
-
getCall(callId) {
|
|
1304
|
-
return this.activeCalls.get(callId);
|
|
1305
|
-
}
|
|
1306
|
-
/**
|
|
1307
|
-
* Get an active call by provider call ID (e.g., Twilio CallSid).
|
|
1308
|
-
*/
|
|
1309
|
-
getCallByProviderCallId(providerCallId) {
|
|
1310
|
-
return getCallByProviderCallId({
|
|
1311
|
-
activeCalls: this.activeCalls,
|
|
1312
|
-
providerCallIdMap: this.providerCallIdMap,
|
|
1313
|
-
providerCallId
|
|
1314
|
-
});
|
|
1315
|
-
}
|
|
1316
|
-
/**
|
|
1317
|
-
* Get all active calls.
|
|
1318
|
-
*/
|
|
1319
|
-
getActiveCalls() {
|
|
1320
|
-
return Array.from(this.activeCalls.values());
|
|
1321
|
-
}
|
|
1322
|
-
/**
|
|
1323
|
-
* Get call history (from persisted logs).
|
|
1324
|
-
*/
|
|
1325
|
-
async getCallHistory(limit = 50) {
|
|
1326
|
-
return getCallHistoryFromStore(this.storePath, limit);
|
|
1327
|
-
}
|
|
1328
|
-
};
|
|
1329
|
-
//#endregion
|
|
1330
|
-
//#region extensions/voice-call/src/providers/mock.ts
|
|
1331
|
-
/**
|
|
1332
|
-
* Mock voice call provider for local testing.
|
|
1333
|
-
*
|
|
1334
|
-
* Events are driven via webhook POST with JSON body:
|
|
1335
|
-
* - { events: NormalizedEvent[] } for bulk events
|
|
1336
|
-
* - { event: NormalizedEvent } for single event
|
|
1337
|
-
*/
|
|
1338
|
-
var MockProvider = class {
|
|
1339
|
-
constructor() {
|
|
1340
|
-
this.name = "mock";
|
|
1341
|
-
}
|
|
1342
|
-
verifyWebhook(_ctx) {
|
|
1343
|
-
return { ok: true };
|
|
1344
|
-
}
|
|
1345
|
-
parseWebhookEvent(ctx, _options) {
|
|
1346
|
-
try {
|
|
1347
|
-
const payload = JSON.parse(ctx.rawBody);
|
|
1348
|
-
const events = [];
|
|
1349
|
-
if (Array.isArray(payload.events)) for (const evt of payload.events) {
|
|
1350
|
-
const normalized = this.normalizeEvent(evt);
|
|
1351
|
-
if (normalized) events.push(normalized);
|
|
1352
|
-
}
|
|
1353
|
-
else if (payload.event) {
|
|
1354
|
-
const normalized = this.normalizeEvent(payload.event);
|
|
1355
|
-
if (normalized) events.push(normalized);
|
|
1356
|
-
}
|
|
1357
|
-
return {
|
|
1358
|
-
events,
|
|
1359
|
-
statusCode: 200
|
|
1360
|
-
};
|
|
1361
|
-
} catch {
|
|
1362
|
-
return {
|
|
1363
|
-
events: [],
|
|
1364
|
-
statusCode: 400
|
|
1365
|
-
};
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
normalizeEvent(evt) {
|
|
1369
|
-
if (!evt.type || !evt.callId) return null;
|
|
1370
|
-
const base = {
|
|
1371
|
-
id: evt.id ?? crypto.randomUUID(),
|
|
1372
|
-
callId: evt.callId,
|
|
1373
|
-
providerCallId: evt.providerCallId,
|
|
1374
|
-
timestamp: evt.timestamp ?? Date.now()
|
|
1375
|
-
};
|
|
1376
|
-
switch (evt.type) {
|
|
1377
|
-
case "call.initiated":
|
|
1378
|
-
case "call.ringing":
|
|
1379
|
-
case "call.answered":
|
|
1380
|
-
case "call.active": return {
|
|
1381
|
-
...base,
|
|
1382
|
-
type: evt.type
|
|
1383
|
-
};
|
|
1384
|
-
case "call.speaking": {
|
|
1385
|
-
const payload = evt;
|
|
1386
|
-
return {
|
|
1387
|
-
...base,
|
|
1388
|
-
type: evt.type,
|
|
1389
|
-
text: payload.text ?? ""
|
|
1390
|
-
};
|
|
1391
|
-
}
|
|
1392
|
-
case "call.speech": {
|
|
1393
|
-
const payload = evt;
|
|
1394
|
-
return {
|
|
1395
|
-
...base,
|
|
1396
|
-
type: evt.type,
|
|
1397
|
-
transcript: payload.transcript ?? "",
|
|
1398
|
-
isFinal: payload.isFinal ?? true,
|
|
1399
|
-
confidence: payload.confidence
|
|
1400
|
-
};
|
|
1401
|
-
}
|
|
1402
|
-
case "call.silence": {
|
|
1403
|
-
const payload = evt;
|
|
1404
|
-
return {
|
|
1405
|
-
...base,
|
|
1406
|
-
type: evt.type,
|
|
1407
|
-
durationMs: payload.durationMs ?? 0
|
|
1408
|
-
};
|
|
1409
|
-
}
|
|
1410
|
-
case "call.dtmf": {
|
|
1411
|
-
const payload = evt;
|
|
1412
|
-
return {
|
|
1413
|
-
...base,
|
|
1414
|
-
type: evt.type,
|
|
1415
|
-
digits: payload.digits ?? ""
|
|
1416
|
-
};
|
|
1417
|
-
}
|
|
1418
|
-
case "call.ended": {
|
|
1419
|
-
const payload = evt;
|
|
1420
|
-
return {
|
|
1421
|
-
...base,
|
|
1422
|
-
type: evt.type,
|
|
1423
|
-
reason: payload.reason ?? "completed"
|
|
1424
|
-
};
|
|
1425
|
-
}
|
|
1426
|
-
case "call.error": {
|
|
1427
|
-
const payload = evt;
|
|
1428
|
-
return {
|
|
1429
|
-
...base,
|
|
1430
|
-
type: evt.type,
|
|
1431
|
-
error: payload.error ?? "unknown error",
|
|
1432
|
-
retryable: payload.retryable
|
|
1433
|
-
};
|
|
1434
|
-
}
|
|
1435
|
-
default: return null;
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
async initiateCall(input) {
|
|
1439
|
-
return {
|
|
1440
|
-
providerCallId: `mock-${input.callId}`,
|
|
1441
|
-
status: "initiated"
|
|
1442
|
-
};
|
|
1443
|
-
}
|
|
1444
|
-
async hangupCall(_input) {}
|
|
1445
|
-
async playTts(_input) {}
|
|
1446
|
-
async startListening(_input) {}
|
|
1447
|
-
async stopListening(_input) {}
|
|
1448
|
-
async getCallStatus(input) {
|
|
1449
|
-
const id = input.providerCallId.toLowerCase();
|
|
1450
|
-
if (id.includes("stale") || id.includes("ended") || id.includes("completed")) return {
|
|
1451
|
-
status: "completed",
|
|
1452
|
-
isTerminal: true
|
|
1453
|
-
};
|
|
1454
|
-
return {
|
|
1455
|
-
status: "in-progress",
|
|
1456
|
-
isTerminal: false
|
|
1457
|
-
};
|
|
1458
|
-
}
|
|
1459
|
-
};
|
|
1460
|
-
//#endregion
|
|
1461
|
-
//#region extensions/voice-call/src/http-headers.ts
|
|
1462
|
-
function getHeader(headers, name) {
|
|
1463
|
-
const target = name.toLowerCase();
|
|
1464
|
-
const value = headers[target] ?? Object.entries(headers).find(([key]) => key.toLowerCase() === target)?.[1];
|
|
1465
|
-
if (Array.isArray(value)) return value[0];
|
|
1466
|
-
return value;
|
|
1467
|
-
}
|
|
1468
|
-
//#endregion
|
|
1469
|
-
//#region extensions/voice-call/src/webhook-security.ts
|
|
1470
|
-
const REPLAY_WINDOW_MS = 600 * 1e3;
|
|
1471
|
-
const REPLAY_CACHE_MAX_ENTRIES = 1e4;
|
|
1472
|
-
const REPLAY_CACHE_PRUNE_INTERVAL = 64;
|
|
1473
|
-
const twilioReplayCache = {
|
|
1474
|
-
seenUntil: /* @__PURE__ */ new Map(),
|
|
1475
|
-
calls: 0
|
|
1476
|
-
};
|
|
1477
|
-
const plivoReplayCache = {
|
|
1478
|
-
seenUntil: /* @__PURE__ */ new Map(),
|
|
1479
|
-
calls: 0
|
|
1480
|
-
};
|
|
1481
|
-
const telnyxReplayCache = {
|
|
1482
|
-
seenUntil: /* @__PURE__ */ new Map(),
|
|
1483
|
-
calls: 0
|
|
1484
|
-
};
|
|
1485
|
-
function sha256Hex(input) {
|
|
1486
|
-
return crypto.createHash("sha256").update(input).digest("hex");
|
|
1487
|
-
}
|
|
1488
|
-
function createSkippedVerificationReplayKey(provider, ctx) {
|
|
1489
|
-
return `${provider}:skip:${sha256Hex(`${ctx.method}\n${ctx.url}\n${ctx.rawBody}`)}`;
|
|
1490
|
-
}
|
|
1491
|
-
function pruneReplayCache(cache, now) {
|
|
1492
|
-
for (const [key, expiresAt] of cache.seenUntil) if (expiresAt <= now) cache.seenUntil.delete(key);
|
|
1493
|
-
while (cache.seenUntil.size > REPLAY_CACHE_MAX_ENTRIES) {
|
|
1494
|
-
const oldest = cache.seenUntil.keys().next().value;
|
|
1495
|
-
if (!oldest) break;
|
|
1496
|
-
cache.seenUntil.delete(oldest);
|
|
1497
|
-
}
|
|
1498
|
-
}
|
|
1499
|
-
function markReplay(cache, replayKey) {
|
|
1500
|
-
const now = Date.now();
|
|
1501
|
-
cache.calls += 1;
|
|
1502
|
-
if (cache.calls % REPLAY_CACHE_PRUNE_INTERVAL === 0) pruneReplayCache(cache, now);
|
|
1503
|
-
const existing = cache.seenUntil.get(replayKey);
|
|
1504
|
-
if (existing && existing > now) return true;
|
|
1505
|
-
cache.seenUntil.set(replayKey, now + REPLAY_WINDOW_MS);
|
|
1506
|
-
if (cache.seenUntil.size > REPLAY_CACHE_MAX_ENTRIES) pruneReplayCache(cache, now);
|
|
1507
|
-
return false;
|
|
1508
|
-
}
|
|
1509
|
-
/**
|
|
1510
|
-
* Validate Twilio webhook signature using HMAC-SHA1.
|
|
1511
|
-
*
|
|
1512
|
-
* Twilio signs requests by concatenating the URL with sorted POST params,
|
|
1513
|
-
* then computing HMAC-SHA1 with the auth token.
|
|
1514
|
-
*
|
|
1515
|
-
* @see https://www.twilio.com/docs/usage/webhooks/webhooks-security
|
|
1516
|
-
*/
|
|
1517
|
-
function validateTwilioSignature(authToken, signature, url, params) {
|
|
1518
|
-
if (!signature) return false;
|
|
1519
|
-
const dataToSign = buildTwilioDataToSign(url, params);
|
|
1520
|
-
return timingSafeEqual$1(signature, crypto.createHmac("sha1", authToken).update(dataToSign).digest("base64"));
|
|
1521
|
-
}
|
|
1522
|
-
function buildTwilioDataToSign(url, params) {
|
|
1523
|
-
let dataToSign = url;
|
|
1524
|
-
const sortedParams = Array.from(params.entries()).toSorted((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
|
|
1525
|
-
for (const [key, value] of sortedParams) dataToSign += key + value;
|
|
1526
|
-
return dataToSign;
|
|
1527
|
-
}
|
|
1528
|
-
function buildCanonicalTwilioParamString(params) {
|
|
1529
|
-
return Array.from(params.entries()).toSorted((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0).map(([key, value]) => `${key}=${value}`).join("&");
|
|
1530
|
-
}
|
|
1531
|
-
/**
|
|
1532
|
-
* Timing-safe string comparison to prevent timing attacks.
|
|
1533
|
-
*/
|
|
1534
|
-
function timingSafeEqual$1(a, b) {
|
|
1535
|
-
return safeEqualSecret(a, b);
|
|
1536
|
-
}
|
|
1537
|
-
/**
|
|
1538
|
-
* Validate that a hostname matches RFC 1123 format.
|
|
1539
|
-
* Prevents injection of malformed hostnames.
|
|
1540
|
-
*/
|
|
1541
|
-
function isValidHostname(hostname) {
|
|
1542
|
-
if (!hostname || hostname.length > 253) return false;
|
|
1543
|
-
return /^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/.test(hostname);
|
|
1544
|
-
}
|
|
1545
|
-
/**
|
|
1546
|
-
* Safely extract hostname from a host header value.
|
|
1547
|
-
* Handles IPv6 addresses and prevents injection via malformed values.
|
|
1548
|
-
*/
|
|
1549
|
-
function extractHostname(hostHeader) {
|
|
1550
|
-
if (!hostHeader) return null;
|
|
1551
|
-
let hostname;
|
|
1552
|
-
if (hostHeader.startsWith("[")) {
|
|
1553
|
-
const endBracket = hostHeader.indexOf("]");
|
|
1554
|
-
if (endBracket === -1) return null;
|
|
1555
|
-
hostname = hostHeader.substring(1, endBracket);
|
|
1556
|
-
return hostname.toLowerCase();
|
|
1557
|
-
}
|
|
1558
|
-
if (hostHeader.includes("@")) return null;
|
|
1559
|
-
hostname = hostHeader.split(":")[0];
|
|
1560
|
-
if (!isValidHostname(hostname)) return null;
|
|
1561
|
-
return hostname.toLowerCase();
|
|
1562
|
-
}
|
|
1563
|
-
function extractHostnameFromHeader(headerValue) {
|
|
1564
|
-
const first = headerValue.split(",")[0]?.trim();
|
|
1565
|
-
if (!first) return null;
|
|
1566
|
-
return extractHostname(first);
|
|
1567
|
-
}
|
|
1568
|
-
function normalizeAllowedHosts(allowedHosts) {
|
|
1569
|
-
if (!allowedHosts || allowedHosts.length === 0) return null;
|
|
1570
|
-
const normalized = /* @__PURE__ */ new Set();
|
|
1571
|
-
for (const host of allowedHosts) {
|
|
1572
|
-
const extracted = extractHostname(host.trim());
|
|
1573
|
-
if (extracted) normalized.add(extracted);
|
|
1574
|
-
}
|
|
1575
|
-
return normalized.size > 0 ? normalized : null;
|
|
1576
|
-
}
|
|
1577
|
-
/**
|
|
1578
|
-
* Reconstruct the public webhook URL from request headers.
|
|
1579
|
-
*
|
|
1580
|
-
* SECURITY: This function validates host headers to prevent host header
|
|
1581
|
-
* injection attacks. When using forwarding headers (X-Forwarded-Host, etc.),
|
|
1582
|
-
* always provide allowedHosts to whitelist valid hostnames.
|
|
1583
|
-
*
|
|
1584
|
-
* When behind a reverse proxy (Tailscale, nginx, ngrok), the original URL
|
|
1585
|
-
* used by Twilio differs from the local request URL. We use standard
|
|
1586
|
-
* forwarding headers to reconstruct it.
|
|
1587
|
-
*
|
|
1588
|
-
* Priority order:
|
|
1589
|
-
* 1. X-Forwarded-Proto + X-Forwarded-Host (standard proxy headers)
|
|
1590
|
-
* 2. X-Original-Host (nginx)
|
|
1591
|
-
* 3. Ngrok-Forwarded-Host (ngrok specific)
|
|
1592
|
-
* 4. Host header (direct connection)
|
|
1593
|
-
*/
|
|
1594
|
-
function reconstructWebhookUrl(ctx, options) {
|
|
1595
|
-
const { headers } = ctx;
|
|
1596
|
-
const allowedHosts = normalizeAllowedHosts(options?.allowedHosts);
|
|
1597
|
-
const hasAllowedHosts = allowedHosts !== null;
|
|
1598
|
-
const explicitlyTrusted = options?.trustForwardingHeaders === true;
|
|
1599
|
-
const trustedProxyIPs = options?.trustedProxyIPs?.filter(Boolean) ?? [];
|
|
1600
|
-
const hasTrustedProxyIPs = trustedProxyIPs.length > 0;
|
|
1601
|
-
const remoteIP = options?.remoteIP ?? ctx.remoteAddress;
|
|
1602
|
-
const fromTrustedProxy = !hasTrustedProxyIPs || (remoteIP ? trustedProxyIPs.includes(remoteIP) : false);
|
|
1603
|
-
const shouldTrustForwardingHeaders = (hasAllowedHosts || explicitlyTrusted) && fromTrustedProxy;
|
|
1604
|
-
const isAllowedForwardedHost = (host) => !allowedHosts || allowedHosts.has(host);
|
|
1605
|
-
let proto = "https";
|
|
1606
|
-
if (shouldTrustForwardingHeaders) {
|
|
1607
|
-
const forwardedProto = getHeader(headers, "x-forwarded-proto");
|
|
1608
|
-
if (forwardedProto === "http" || forwardedProto === "https") proto = forwardedProto;
|
|
1609
|
-
}
|
|
1610
|
-
let host = null;
|
|
1611
|
-
if (shouldTrustForwardingHeaders) for (const headerName of [
|
|
1612
|
-
"x-forwarded-host",
|
|
1613
|
-
"x-original-host",
|
|
1614
|
-
"ngrok-forwarded-host"
|
|
1615
|
-
]) {
|
|
1616
|
-
const headerValue = getHeader(headers, headerName);
|
|
1617
|
-
if (headerValue) {
|
|
1618
|
-
const extracted = extractHostnameFromHeader(headerValue);
|
|
1619
|
-
if (extracted && isAllowedForwardedHost(extracted)) {
|
|
1620
|
-
host = extracted;
|
|
1621
|
-
break;
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
if (!host) {
|
|
1626
|
-
const hostHeader = getHeader(headers, "host");
|
|
1627
|
-
if (hostHeader) {
|
|
1628
|
-
const extracted = extractHostnameFromHeader(hostHeader);
|
|
1629
|
-
if (extracted) host = extracted;
|
|
1630
|
-
}
|
|
1631
|
-
}
|
|
1632
|
-
if (!host) try {
|
|
1633
|
-
const extracted = extractHostname(new URL(ctx.url).host);
|
|
1634
|
-
if (extracted) host = extracted;
|
|
1635
|
-
} catch {
|
|
1636
|
-
host = "";
|
|
1637
|
-
}
|
|
1638
|
-
if (!host) host = "";
|
|
1639
|
-
let path = "/";
|
|
1640
|
-
try {
|
|
1641
|
-
const parsed = new URL(ctx.url);
|
|
1642
|
-
path = parsed.pathname + parsed.search;
|
|
1643
|
-
} catch {}
|
|
1644
|
-
return `${proto}://${host}${path}`;
|
|
1645
|
-
}
|
|
1646
|
-
function buildTwilioVerificationUrl(ctx, publicUrl, urlOptions) {
|
|
1647
|
-
if (!publicUrl) return reconstructWebhookUrl(ctx, urlOptions);
|
|
1648
|
-
try {
|
|
1649
|
-
const base = new URL(publicUrl);
|
|
1650
|
-
const requestUrl = new URL(ctx.url);
|
|
1651
|
-
base.pathname = requestUrl.pathname;
|
|
1652
|
-
base.search = requestUrl.search;
|
|
1653
|
-
return base.toString();
|
|
1654
|
-
} catch {
|
|
1655
|
-
return publicUrl;
|
|
1656
|
-
}
|
|
1657
|
-
}
|
|
1658
|
-
function isLoopbackAddress(address) {
|
|
1659
|
-
if (!address) return false;
|
|
1660
|
-
if (address === "127.0.0.1" || address === "::1") return true;
|
|
1661
|
-
if (address.startsWith("::ffff:127.")) return true;
|
|
1662
|
-
return false;
|
|
1663
|
-
}
|
|
1664
|
-
function stripPortFromUrl(url) {
|
|
1665
|
-
try {
|
|
1666
|
-
const parsed = new URL(url);
|
|
1667
|
-
if (!parsed.port) return url;
|
|
1668
|
-
parsed.port = "";
|
|
1669
|
-
return parsed.toString();
|
|
1670
|
-
} catch {
|
|
1671
|
-
return url;
|
|
1672
|
-
}
|
|
1673
|
-
}
|
|
1674
|
-
function setPortOnUrl(url, port) {
|
|
1675
|
-
try {
|
|
1676
|
-
const parsed = new URL(url);
|
|
1677
|
-
parsed.port = port;
|
|
1678
|
-
return parsed.toString();
|
|
1679
|
-
} catch {
|
|
1680
|
-
return url;
|
|
1681
|
-
}
|
|
1682
|
-
}
|
|
1683
|
-
function extractPortFromHostHeader(hostHeader) {
|
|
1684
|
-
if (!hostHeader) return;
|
|
1685
|
-
try {
|
|
1686
|
-
return new URL(`https://${hostHeader}`).port || void 0;
|
|
1687
|
-
} catch {
|
|
1688
|
-
return;
|
|
1689
|
-
}
|
|
1690
|
-
}
|
|
1691
|
-
function createTwilioReplayKey(params) {
|
|
1692
|
-
const canonicalParams = buildCanonicalTwilioParamString(params.requestParams);
|
|
1693
|
-
return `twilio:req:${sha256Hex(`${params.verificationUrl}\n${canonicalParams}\n${params.signature}`)}`;
|
|
1694
|
-
}
|
|
1695
|
-
function decodeBase64OrBase64Url(input) {
|
|
1696
|
-
const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
1697
|
-
const padLen = (4 - normalized.length % 4) % 4;
|
|
1698
|
-
const padded = normalized + "=".repeat(padLen);
|
|
1699
|
-
return Buffer.from(padded, "base64");
|
|
1700
|
-
}
|
|
1701
|
-
function base64UrlEncode(buf) {
|
|
1702
|
-
return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
1703
|
-
}
|
|
1704
|
-
function importEd25519PublicKey(publicKey) {
|
|
1705
|
-
const trimmed = publicKey.trim();
|
|
1706
|
-
if (trimmed.startsWith("-----BEGIN")) return trimmed;
|
|
1707
|
-
const decoded = decodeBase64OrBase64Url(trimmed);
|
|
1708
|
-
if (decoded.length === 32) return crypto.createPublicKey({
|
|
1709
|
-
key: {
|
|
1710
|
-
kty: "OKP",
|
|
1711
|
-
crv: "Ed25519",
|
|
1712
|
-
x: base64UrlEncode(decoded)
|
|
1713
|
-
},
|
|
1714
|
-
format: "jwk"
|
|
1715
|
-
});
|
|
1716
|
-
return crypto.createPublicKey({
|
|
1717
|
-
key: decoded,
|
|
1718
|
-
format: "der",
|
|
1719
|
-
type: "spki"
|
|
1720
|
-
});
|
|
1721
|
-
}
|
|
1722
|
-
/**
|
|
1723
|
-
* Verify Telnyx webhook signature using Ed25519.
|
|
1724
|
-
*
|
|
1725
|
-
* Telnyx signs `timestamp|payload` and provides:
|
|
1726
|
-
* - `telnyx-signature-ed25519` (Base64 signature)
|
|
1727
|
-
* - `telnyx-timestamp` (Unix seconds)
|
|
1728
|
-
*/
|
|
1729
|
-
function verifyTelnyxWebhook(ctx, publicKey, options) {
|
|
1730
|
-
if (options?.skipVerification) {
|
|
1731
|
-
const replayKey = createSkippedVerificationReplayKey("telnyx", ctx);
|
|
1732
|
-
return {
|
|
1733
|
-
ok: true,
|
|
1734
|
-
reason: "verification skipped (dev mode)",
|
|
1735
|
-
isReplay: markReplay(telnyxReplayCache, replayKey),
|
|
1736
|
-
verifiedRequestKey: replayKey
|
|
1737
|
-
};
|
|
1738
|
-
}
|
|
1739
|
-
if (!publicKey) return {
|
|
1740
|
-
ok: false,
|
|
1741
|
-
reason: "Missing telnyx.publicKey (configure to verify webhooks)"
|
|
1742
|
-
};
|
|
1743
|
-
const signature = getHeader(ctx.headers, "telnyx-signature-ed25519");
|
|
1744
|
-
const timestamp = getHeader(ctx.headers, "telnyx-timestamp");
|
|
1745
|
-
if (!signature || !timestamp) return {
|
|
1746
|
-
ok: false,
|
|
1747
|
-
reason: "Missing signature or timestamp header"
|
|
1748
|
-
};
|
|
1749
|
-
const eventTimeSec = parseInt(timestamp, 10);
|
|
1750
|
-
if (!Number.isFinite(eventTimeSec)) return {
|
|
1751
|
-
ok: false,
|
|
1752
|
-
reason: "Invalid timestamp header"
|
|
1753
|
-
};
|
|
1754
|
-
try {
|
|
1755
|
-
const signedPayload = `${timestamp}|${ctx.rawBody}`;
|
|
1756
|
-
const signatureBuffer = decodeBase64OrBase64Url(signature);
|
|
1757
|
-
const canonicalSignature = signatureBuffer.toString("base64");
|
|
1758
|
-
const key = importEd25519PublicKey(publicKey);
|
|
1759
|
-
if (!crypto.verify(null, Buffer.from(signedPayload), key, signatureBuffer)) return {
|
|
1760
|
-
ok: false,
|
|
1761
|
-
reason: "Invalid signature"
|
|
1762
|
-
};
|
|
1763
|
-
const maxSkewMs = options?.maxSkewMs ?? 300 * 1e3;
|
|
1764
|
-
const eventTimeMs = eventTimeSec * 1e3;
|
|
1765
|
-
if (Math.abs(Date.now() - eventTimeMs) > maxSkewMs) return {
|
|
1766
|
-
ok: false,
|
|
1767
|
-
reason: "Timestamp too old"
|
|
1768
|
-
};
|
|
1769
|
-
const replayKey = `telnyx:${sha256Hex(`${timestamp}\n${canonicalSignature}\n${ctx.rawBody}`)}`;
|
|
1770
|
-
return {
|
|
1771
|
-
ok: true,
|
|
1772
|
-
isReplay: markReplay(telnyxReplayCache, replayKey),
|
|
1773
|
-
verifiedRequestKey: replayKey
|
|
1774
|
-
};
|
|
1775
|
-
} catch (err) {
|
|
1776
|
-
return {
|
|
1777
|
-
ok: false,
|
|
1778
|
-
reason: `Verification error: ${err instanceof Error ? err.message : String(err)}`
|
|
1779
|
-
};
|
|
1780
|
-
}
|
|
1781
|
-
}
|
|
1782
|
-
/**
|
|
1783
|
-
* Verify Twilio webhook with full context and detailed result.
|
|
1784
|
-
*/
|
|
1785
|
-
function verifyTwilioWebhook(ctx, authToken, options) {
|
|
1786
|
-
if (options?.skipVerification) {
|
|
1787
|
-
const replayKey = createSkippedVerificationReplayKey("twilio", ctx);
|
|
1788
|
-
return {
|
|
1789
|
-
ok: true,
|
|
1790
|
-
reason: "verification skipped (dev mode)",
|
|
1791
|
-
isReplay: markReplay(twilioReplayCache, replayKey),
|
|
1792
|
-
verifiedRequestKey: replayKey
|
|
1793
|
-
};
|
|
1794
|
-
}
|
|
1795
|
-
const signature = getHeader(ctx.headers, "x-twilio-signature");
|
|
1796
|
-
if (!signature) return {
|
|
1797
|
-
ok: false,
|
|
1798
|
-
reason: "Missing X-Twilio-Signature header"
|
|
1799
|
-
};
|
|
1800
|
-
const isLoopback = isLoopbackAddress(options?.remoteIP ?? ctx.remoteAddress);
|
|
1801
|
-
const allowLoopbackForwarding = options?.allowNgrokFreeTierLoopbackBypass && isLoopback;
|
|
1802
|
-
const verificationUrl = buildTwilioVerificationUrl(ctx, options?.publicUrl, {
|
|
1803
|
-
allowedHosts: options?.allowedHosts,
|
|
1804
|
-
trustForwardingHeaders: options?.trustForwardingHeaders || allowLoopbackForwarding,
|
|
1805
|
-
trustedProxyIPs: options?.trustedProxyIPs,
|
|
1806
|
-
remoteIP: options?.remoteIP
|
|
1807
|
-
});
|
|
1808
|
-
const params = new URLSearchParams(ctx.rawBody);
|
|
1809
|
-
if (validateTwilioSignature(authToken, signature, verificationUrl, params)) {
|
|
1810
|
-
const replayKey = createTwilioReplayKey({
|
|
1811
|
-
verificationUrl,
|
|
1812
|
-
signature,
|
|
1813
|
-
requestParams: params
|
|
1814
|
-
});
|
|
1815
|
-
return {
|
|
1816
|
-
ok: true,
|
|
1817
|
-
verificationUrl,
|
|
1818
|
-
isReplay: markReplay(twilioReplayCache, replayKey),
|
|
1819
|
-
verifiedRequestKey: replayKey
|
|
1820
|
-
};
|
|
1821
|
-
}
|
|
1822
|
-
const variants = /* @__PURE__ */ new Set();
|
|
1823
|
-
variants.add(verificationUrl);
|
|
1824
|
-
variants.add(stripPortFromUrl(verificationUrl));
|
|
1825
|
-
if (options?.publicUrl) try {
|
|
1826
|
-
const publicPort = new URL(options.publicUrl).port;
|
|
1827
|
-
if (publicPort) variants.add(setPortOnUrl(verificationUrl, publicPort));
|
|
1828
|
-
} catch {}
|
|
1829
|
-
const hostHeaderPort = extractPortFromHostHeader(getHeader(ctx.headers, "host"));
|
|
1830
|
-
if (hostHeaderPort) variants.add(setPortOnUrl(verificationUrl, hostHeaderPort));
|
|
1831
|
-
for (const candidateUrl of variants) {
|
|
1832
|
-
if (candidateUrl === verificationUrl) continue;
|
|
1833
|
-
if (!validateTwilioSignature(authToken, signature, candidateUrl, params)) continue;
|
|
1834
|
-
const replayKey = createTwilioReplayKey({
|
|
1835
|
-
verificationUrl: candidateUrl,
|
|
1836
|
-
signature,
|
|
1837
|
-
requestParams: params
|
|
1838
|
-
});
|
|
1839
|
-
return {
|
|
1840
|
-
ok: true,
|
|
1841
|
-
verificationUrl: candidateUrl,
|
|
1842
|
-
isReplay: markReplay(twilioReplayCache, replayKey),
|
|
1843
|
-
verifiedRequestKey: replayKey
|
|
1844
|
-
};
|
|
1845
|
-
}
|
|
1846
|
-
const isNgrokFreeTier = verificationUrl.includes(".ngrok-free.app") || verificationUrl.includes(".ngrok.io");
|
|
1847
|
-
return {
|
|
1848
|
-
ok: false,
|
|
1849
|
-
reason: `Invalid signature for URL: ${verificationUrl}`,
|
|
1850
|
-
verificationUrl,
|
|
1851
|
-
isNgrokFreeTier
|
|
1852
|
-
};
|
|
1853
|
-
}
|
|
1854
|
-
function normalizeSignatureBase64(input) {
|
|
1855
|
-
return Buffer.from(input, "base64").toString("base64");
|
|
1856
|
-
}
|
|
1857
|
-
function getBaseUrlNoQuery(url) {
|
|
1858
|
-
const u = new URL(url);
|
|
1859
|
-
return `${u.protocol}//${u.host}${u.pathname}`;
|
|
1860
|
-
}
|
|
1861
|
-
function createPlivoV2ReplayKey(url, nonce) {
|
|
1862
|
-
return `plivo:v2:${sha256Hex(`${getBaseUrlNoQuery(url)}\n${nonce}`)}`;
|
|
1863
|
-
}
|
|
1864
|
-
function createPlivoV3ReplayKey(params) {
|
|
1865
|
-
return `plivo:v3:${sha256Hex(`${constructPlivoV3BaseUrl({
|
|
1866
|
-
method: params.method,
|
|
1867
|
-
url: params.url,
|
|
1868
|
-
postParams: params.postParams
|
|
1869
|
-
})}\n${params.nonce}`)}`;
|
|
1870
|
-
}
|
|
1871
|
-
function timingSafeEqualString(a, b) {
|
|
1872
|
-
return safeEqualSecret(a, b);
|
|
1873
|
-
}
|
|
1874
|
-
function validatePlivoV2Signature(params) {
|
|
1875
|
-
const baseUrl = getBaseUrlNoQuery(params.url);
|
|
1876
|
-
return timingSafeEqualString(normalizeSignatureBase64(crypto.createHmac("sha256", params.authToken).update(baseUrl + params.nonce).digest("base64")), normalizeSignatureBase64(params.signature));
|
|
1877
|
-
}
|
|
1878
|
-
function toParamMapFromSearchParams(sp) {
|
|
1879
|
-
const map = {};
|
|
1880
|
-
for (const [key, value] of sp.entries()) {
|
|
1881
|
-
if (!map[key]) map[key] = [];
|
|
1882
|
-
map[key].push(value);
|
|
1883
|
-
}
|
|
1884
|
-
return map;
|
|
1885
|
-
}
|
|
1886
|
-
function sortedQueryString(params) {
|
|
1887
|
-
const parts = [];
|
|
1888
|
-
for (const key of Object.keys(params).toSorted()) {
|
|
1889
|
-
const values = [...params[key]].toSorted();
|
|
1890
|
-
for (const value of values) parts.push(`${key}=${value}`);
|
|
1891
|
-
}
|
|
1892
|
-
return parts.join("&");
|
|
1893
|
-
}
|
|
1894
|
-
function sortedParamsString(params) {
|
|
1895
|
-
const parts = [];
|
|
1896
|
-
for (const key of Object.keys(params).toSorted()) {
|
|
1897
|
-
const values = [...params[key]].toSorted();
|
|
1898
|
-
for (const value of values) parts.push(`${key}${value}`);
|
|
1899
|
-
}
|
|
1900
|
-
return parts.join("");
|
|
1901
|
-
}
|
|
1902
|
-
function constructPlivoV3BaseUrl(params) {
|
|
1903
|
-
const hasPostParams = Object.keys(params.postParams).length > 0;
|
|
1904
|
-
const u = new URL(params.url);
|
|
1905
|
-
const baseNoQuery = `${u.protocol}//${u.host}${u.pathname}`;
|
|
1906
|
-
const queryString = sortedQueryString(toParamMapFromSearchParams(u.searchParams));
|
|
1907
|
-
let baseUrl = baseNoQuery;
|
|
1908
|
-
if (queryString.length > 0 || hasPostParams) baseUrl = `${baseNoQuery}?${queryString}`;
|
|
1909
|
-
if (queryString.length > 0 && hasPostParams) baseUrl = `${baseUrl}.`;
|
|
1910
|
-
if (params.method === "GET") return baseUrl;
|
|
1911
|
-
return baseUrl + sortedParamsString(params.postParams);
|
|
1912
|
-
}
|
|
1913
|
-
function validatePlivoV3Signature(params) {
|
|
1914
|
-
const hmacBase = `${constructPlivoV3BaseUrl({
|
|
1915
|
-
method: params.method,
|
|
1916
|
-
url: params.url,
|
|
1917
|
-
postParams: params.postParams
|
|
1918
|
-
})}.${params.nonce}`;
|
|
1919
|
-
const expected = normalizeSignatureBase64(crypto.createHmac("sha256", params.authToken).update(hmacBase).digest("base64"));
|
|
1920
|
-
const provided = params.signatureHeader.split(",").map((s) => s.trim()).filter(Boolean).map((s) => normalizeSignatureBase64(s));
|
|
1921
|
-
for (const sig of provided) if (timingSafeEqualString(expected, sig)) return true;
|
|
1922
|
-
return false;
|
|
1923
|
-
}
|
|
1924
|
-
/**
|
|
1925
|
-
* Verify Plivo webhooks using V3 signature if present; fall back to V2.
|
|
1926
|
-
*
|
|
1927
|
-
* Header names (case-insensitive; Node provides lower-case keys):
|
|
1928
|
-
* - V3: X-Plivo-Signature-V3 / X-Plivo-Signature-V3-Nonce
|
|
1929
|
-
* - V2: X-Plivo-Signature-V2 / X-Plivo-Signature-V2-Nonce
|
|
1930
|
-
*/
|
|
1931
|
-
function verifyPlivoWebhook(ctx, authToken, options) {
|
|
1932
|
-
if (options?.skipVerification) {
|
|
1933
|
-
const replayKey = createSkippedVerificationReplayKey("plivo", ctx);
|
|
1934
|
-
return {
|
|
1935
|
-
ok: true,
|
|
1936
|
-
reason: "verification skipped (dev mode)",
|
|
1937
|
-
isReplay: markReplay(plivoReplayCache, replayKey),
|
|
1938
|
-
verifiedRequestKey: replayKey
|
|
1939
|
-
};
|
|
1940
|
-
}
|
|
1941
|
-
const signatureV3 = getHeader(ctx.headers, "x-plivo-signature-v3");
|
|
1942
|
-
const nonceV3 = getHeader(ctx.headers, "x-plivo-signature-v3-nonce");
|
|
1943
|
-
const signatureV2 = getHeader(ctx.headers, "x-plivo-signature-v2");
|
|
1944
|
-
const nonceV2 = getHeader(ctx.headers, "x-plivo-signature-v2-nonce");
|
|
1945
|
-
const reconstructed = reconstructWebhookUrl(ctx, {
|
|
1946
|
-
allowedHosts: options?.allowedHosts,
|
|
1947
|
-
trustForwardingHeaders: options?.trustForwardingHeaders,
|
|
1948
|
-
trustedProxyIPs: options?.trustedProxyIPs,
|
|
1949
|
-
remoteIP: options?.remoteIP
|
|
1950
|
-
});
|
|
1951
|
-
let verificationUrl = reconstructed;
|
|
1952
|
-
if (options?.publicUrl) try {
|
|
1953
|
-
const req = new URL(reconstructed);
|
|
1954
|
-
const base = new URL(options.publicUrl);
|
|
1955
|
-
base.pathname = req.pathname;
|
|
1956
|
-
base.search = req.search;
|
|
1957
|
-
verificationUrl = base.toString();
|
|
1958
|
-
} catch {
|
|
1959
|
-
verificationUrl = reconstructed;
|
|
1960
|
-
}
|
|
1961
|
-
if (signatureV3 && nonceV3) {
|
|
1962
|
-
const method = ctx.method === "GET" || ctx.method === "POST" ? ctx.method : null;
|
|
1963
|
-
if (!method) return {
|
|
1964
|
-
ok: false,
|
|
1965
|
-
version: "v3",
|
|
1966
|
-
verificationUrl,
|
|
1967
|
-
reason: `Unsupported HTTP method for Plivo V3 signature: ${ctx.method}`
|
|
1968
|
-
};
|
|
1969
|
-
const postParams = toParamMapFromSearchParams(new URLSearchParams(ctx.rawBody));
|
|
1970
|
-
if (!validatePlivoV3Signature({
|
|
1971
|
-
authToken,
|
|
1972
|
-
signatureHeader: signatureV3,
|
|
1973
|
-
nonce: nonceV3,
|
|
1974
|
-
method,
|
|
1975
|
-
url: verificationUrl,
|
|
1976
|
-
postParams
|
|
1977
|
-
})) return {
|
|
1978
|
-
ok: false,
|
|
1979
|
-
version: "v3",
|
|
1980
|
-
verificationUrl,
|
|
1981
|
-
reason: "Invalid Plivo V3 signature"
|
|
1982
|
-
};
|
|
1983
|
-
const replayKey = createPlivoV3ReplayKey({
|
|
1984
|
-
method,
|
|
1985
|
-
url: verificationUrl,
|
|
1986
|
-
postParams,
|
|
1987
|
-
nonce: nonceV3
|
|
1988
|
-
});
|
|
1989
|
-
const isReplay = markReplay(plivoReplayCache, replayKey);
|
|
1990
|
-
return {
|
|
1991
|
-
ok: true,
|
|
1992
|
-
version: "v3",
|
|
1993
|
-
verificationUrl,
|
|
1994
|
-
isReplay,
|
|
1995
|
-
verifiedRequestKey: replayKey
|
|
1996
|
-
};
|
|
1997
|
-
}
|
|
1998
|
-
if (signatureV2 && nonceV2) {
|
|
1999
|
-
if (!validatePlivoV2Signature({
|
|
2000
|
-
authToken,
|
|
2001
|
-
signature: signatureV2,
|
|
2002
|
-
nonce: nonceV2,
|
|
2003
|
-
url: verificationUrl
|
|
2004
|
-
})) return {
|
|
2005
|
-
ok: false,
|
|
2006
|
-
version: "v2",
|
|
2007
|
-
verificationUrl,
|
|
2008
|
-
reason: "Invalid Plivo V2 signature"
|
|
2009
|
-
};
|
|
2010
|
-
const replayKey = createPlivoV2ReplayKey(verificationUrl, nonceV2);
|
|
2011
|
-
const isReplay = markReplay(plivoReplayCache, replayKey);
|
|
2012
|
-
return {
|
|
2013
|
-
ok: true,
|
|
2014
|
-
version: "v2",
|
|
2015
|
-
verificationUrl,
|
|
2016
|
-
isReplay,
|
|
2017
|
-
verifiedRequestKey: replayKey
|
|
2018
|
-
};
|
|
2019
|
-
}
|
|
2020
|
-
return {
|
|
2021
|
-
ok: false,
|
|
2022
|
-
reason: "Missing Plivo signature headers (V3 or V2)",
|
|
2023
|
-
verificationUrl
|
|
2024
|
-
};
|
|
2025
|
-
}
|
|
2026
|
-
//#endregion
|
|
2027
|
-
//#region extensions/voice-call/src/providers/shared/guarded-json-api.ts
|
|
2028
|
-
async function guardedJsonApiRequest(params) {
|
|
2029
|
-
const { response, release } = await fetchWithSsrFGuard({
|
|
2030
|
-
url: params.url,
|
|
2031
|
-
init: {
|
|
2032
|
-
method: params.method,
|
|
2033
|
-
headers: params.headers,
|
|
2034
|
-
body: params.body ? JSON.stringify(params.body) : void 0
|
|
2035
|
-
},
|
|
2036
|
-
policy: { allowedHostnames: params.allowedHostnames },
|
|
2037
|
-
auditContext: params.auditContext
|
|
2038
|
-
});
|
|
2039
|
-
try {
|
|
2040
|
-
if (!response.ok) {
|
|
2041
|
-
if (params.allowNotFound && response.status === 404) return;
|
|
2042
|
-
const errorText = await response.text();
|
|
2043
|
-
throw new Error(`${params.errorPrefix}: ${response.status} ${errorText}`);
|
|
2044
|
-
}
|
|
2045
|
-
const text = await response.text();
|
|
2046
|
-
return text ? JSON.parse(text) : void 0;
|
|
2047
|
-
} finally {
|
|
2048
|
-
await release();
|
|
2049
|
-
}
|
|
2050
|
-
}
|
|
2051
|
-
//#endregion
|
|
2052
|
-
//#region extensions/voice-call/src/providers/plivo.ts
|
|
2053
|
-
function createPlivoRequestDedupeKey(ctx) {
|
|
2054
|
-
const nonceV3 = getHeader(ctx.headers, "x-plivo-signature-v3-nonce");
|
|
2055
|
-
if (nonceV3) return `plivo:v3:${nonceV3}`;
|
|
2056
|
-
const nonceV2 = getHeader(ctx.headers, "x-plivo-signature-v2-nonce");
|
|
2057
|
-
if (nonceV2) return `plivo:v2:${nonceV2}`;
|
|
2058
|
-
return `plivo:fallback:${crypto.createHash("sha256").update(ctx.rawBody).digest("hex")}`;
|
|
2059
|
-
}
|
|
2060
|
-
var PlivoProvider = class PlivoProvider {
|
|
2061
|
-
constructor(config, options = {}) {
|
|
2062
|
-
this.name = "plivo";
|
|
2063
|
-
this.requestUuidToCallUuid = /* @__PURE__ */ new Map();
|
|
2064
|
-
this.callIdToWebhookUrl = /* @__PURE__ */ new Map();
|
|
2065
|
-
this.callUuidToWebhookUrl = /* @__PURE__ */ new Map();
|
|
2066
|
-
this.pendingSpeakByCallId = /* @__PURE__ */ new Map();
|
|
2067
|
-
this.pendingListenByCallId = /* @__PURE__ */ new Map();
|
|
2068
|
-
if (!config.authId) throw new Error("Plivo Auth ID is required");
|
|
2069
|
-
if (!config.authToken) throw new Error("Plivo Auth Token is required");
|
|
2070
|
-
this.authId = config.authId;
|
|
2071
|
-
this.authToken = config.authToken;
|
|
2072
|
-
this.baseUrl = `https://api.plivo.com/v1/Account/${this.authId}`;
|
|
2073
|
-
this.apiHost = new URL(this.baseUrl).hostname;
|
|
2074
|
-
this.options = options;
|
|
2075
|
-
}
|
|
2076
|
-
async apiRequest(params) {
|
|
2077
|
-
const { method, endpoint, body, allowNotFound } = params;
|
|
2078
|
-
return await guardedJsonApiRequest({
|
|
2079
|
-
url: `${this.baseUrl}${endpoint}`,
|
|
2080
|
-
method,
|
|
2081
|
-
headers: {
|
|
2082
|
-
Authorization: `Basic ${Buffer.from(`${this.authId}:${this.authToken}`).toString("base64")}`,
|
|
2083
|
-
"Content-Type": "application/json"
|
|
2084
|
-
},
|
|
2085
|
-
body,
|
|
2086
|
-
allowNotFound,
|
|
2087
|
-
allowedHostnames: [this.apiHost],
|
|
2088
|
-
auditContext: "voice-call.plivo.api",
|
|
2089
|
-
errorPrefix: "Plivo API error"
|
|
2090
|
-
});
|
|
2091
|
-
}
|
|
2092
|
-
verifyWebhook(ctx) {
|
|
2093
|
-
const result = verifyPlivoWebhook(ctx, this.authToken, {
|
|
2094
|
-
publicUrl: this.options.publicUrl,
|
|
2095
|
-
skipVerification: this.options.skipVerification,
|
|
2096
|
-
allowedHosts: this.options.webhookSecurity?.allowedHosts,
|
|
2097
|
-
trustForwardingHeaders: this.options.webhookSecurity?.trustForwardingHeaders,
|
|
2098
|
-
trustedProxyIPs: this.options.webhookSecurity?.trustedProxyIPs,
|
|
2099
|
-
remoteIP: ctx.remoteAddress
|
|
2100
|
-
});
|
|
2101
|
-
if (!result.ok) console.warn(`[plivo] Webhook verification failed: ${result.reason}`);
|
|
2102
|
-
return {
|
|
2103
|
-
ok: result.ok,
|
|
2104
|
-
reason: result.reason,
|
|
2105
|
-
isReplay: result.isReplay,
|
|
2106
|
-
verifiedRequestKey: result.verifiedRequestKey
|
|
2107
|
-
};
|
|
2108
|
-
}
|
|
2109
|
-
parseWebhookEvent(ctx, options) {
|
|
2110
|
-
const flow = typeof ctx.query?.flow === "string" ? ctx.query.flow.trim() : "";
|
|
2111
|
-
const parsed = this.parseBody(ctx.rawBody);
|
|
2112
|
-
if (!parsed) return {
|
|
2113
|
-
events: [],
|
|
2114
|
-
statusCode: 400
|
|
2115
|
-
};
|
|
2116
|
-
const callUuid = parsed.get("CallUUID") || void 0;
|
|
2117
|
-
if (callUuid) {
|
|
2118
|
-
const webhookBase = this.baseWebhookUrlFromCtx(ctx);
|
|
2119
|
-
if (webhookBase) this.callUuidToWebhookUrl.set(callUuid, webhookBase);
|
|
2120
|
-
}
|
|
2121
|
-
if (flow === "xml-speak") {
|
|
2122
|
-
const callId = this.getCallIdFromQuery(ctx);
|
|
2123
|
-
const pending = callId ? this.pendingSpeakByCallId.get(callId) : void 0;
|
|
2124
|
-
if (callId) this.pendingSpeakByCallId.delete(callId);
|
|
2125
|
-
return {
|
|
2126
|
-
events: [],
|
|
2127
|
-
providerResponseBody: pending ? PlivoProvider.xmlSpeak(pending.text, pending.locale) : PlivoProvider.xmlKeepAlive(),
|
|
2128
|
-
providerResponseHeaders: { "Content-Type": "text/xml" },
|
|
2129
|
-
statusCode: 200
|
|
2130
|
-
};
|
|
2131
|
-
}
|
|
2132
|
-
if (flow === "xml-listen") {
|
|
2133
|
-
const callId = this.getCallIdFromQuery(ctx);
|
|
2134
|
-
const pending = callId ? this.pendingListenByCallId.get(callId) : void 0;
|
|
2135
|
-
if (callId) this.pendingListenByCallId.delete(callId);
|
|
2136
|
-
const actionUrl = this.buildActionUrl(ctx, {
|
|
2137
|
-
flow: "getinput",
|
|
2138
|
-
callId
|
|
2139
|
-
});
|
|
2140
|
-
return {
|
|
2141
|
-
events: [],
|
|
2142
|
-
providerResponseBody: actionUrl && callId ? PlivoProvider.xmlGetInputSpeech({
|
|
2143
|
-
actionUrl,
|
|
2144
|
-
language: pending?.language
|
|
2145
|
-
}) : PlivoProvider.xmlKeepAlive(),
|
|
2146
|
-
providerResponseHeaders: { "Content-Type": "text/xml" },
|
|
2147
|
-
statusCode: 200
|
|
2148
|
-
};
|
|
2149
|
-
}
|
|
2150
|
-
const callIdFromQuery = this.getCallIdFromQuery(ctx);
|
|
2151
|
-
const dedupeKey = options?.verifiedRequestKey ?? createPlivoRequestDedupeKey(ctx);
|
|
2152
|
-
const event = this.normalizeEvent(parsed, callIdFromQuery, dedupeKey);
|
|
2153
|
-
return {
|
|
2154
|
-
events: event ? [event] : [],
|
|
2155
|
-
providerResponseBody: flow === "answer" || flow === "getinput" ? PlivoProvider.xmlKeepAlive() : PlivoProvider.xmlEmpty(),
|
|
2156
|
-
providerResponseHeaders: { "Content-Type": "text/xml" },
|
|
2157
|
-
statusCode: 200
|
|
2158
|
-
};
|
|
2159
|
-
}
|
|
2160
|
-
normalizeEvent(params, callIdOverride, dedupeKey) {
|
|
2161
|
-
const callUuid = params.get("CallUUID") || "";
|
|
2162
|
-
const requestUuid = params.get("RequestUUID") || "";
|
|
2163
|
-
if (requestUuid && callUuid) this.requestUuidToCallUuid.set(requestUuid, callUuid);
|
|
2164
|
-
const direction = params.get("Direction");
|
|
2165
|
-
const from = params.get("From") || void 0;
|
|
2166
|
-
const to = params.get("To") || void 0;
|
|
2167
|
-
const callStatus = params.get("CallStatus");
|
|
2168
|
-
const baseEvent = {
|
|
2169
|
-
id: crypto.randomUUID(),
|
|
2170
|
-
dedupeKey,
|
|
2171
|
-
callId: callIdOverride || callUuid || requestUuid,
|
|
2172
|
-
providerCallId: callUuid || requestUuid || void 0,
|
|
2173
|
-
timestamp: Date.now(),
|
|
2174
|
-
direction: direction === "inbound" ? "inbound" : direction === "outbound" ? "outbound" : void 0,
|
|
2175
|
-
from,
|
|
2176
|
-
to
|
|
2177
|
-
};
|
|
2178
|
-
const digits = params.get("Digits");
|
|
2179
|
-
if (digits) return {
|
|
2180
|
-
...baseEvent,
|
|
2181
|
-
type: "call.dtmf",
|
|
2182
|
-
digits
|
|
2183
|
-
};
|
|
2184
|
-
const transcript = PlivoProvider.extractTranscript(params);
|
|
2185
|
-
if (transcript) return {
|
|
2186
|
-
...baseEvent,
|
|
2187
|
-
type: "call.speech",
|
|
2188
|
-
transcript,
|
|
2189
|
-
isFinal: true
|
|
2190
|
-
};
|
|
2191
|
-
if (callStatus === "ringing") return {
|
|
2192
|
-
...baseEvent,
|
|
2193
|
-
type: "call.ringing"
|
|
2194
|
-
};
|
|
2195
|
-
if (callStatus === "in-progress") return {
|
|
2196
|
-
...baseEvent,
|
|
2197
|
-
type: "call.answered"
|
|
2198
|
-
};
|
|
2199
|
-
if (callStatus === "completed" || callStatus === "busy" || callStatus === "no-answer" || callStatus === "failed") return {
|
|
2200
|
-
...baseEvent,
|
|
2201
|
-
type: "call.ended",
|
|
2202
|
-
reason: callStatus === "completed" ? "completed" : callStatus === "busy" ? "busy" : callStatus === "no-answer" ? "no-answer" : "failed"
|
|
2203
|
-
};
|
|
2204
|
-
if (params.get("Event") === "StartApp" && callUuid) return {
|
|
2205
|
-
...baseEvent,
|
|
2206
|
-
type: "call.answered"
|
|
2207
|
-
};
|
|
2208
|
-
return null;
|
|
2209
|
-
}
|
|
2210
|
-
async initiateCall(input) {
|
|
2211
|
-
const webhookUrl = new URL(input.webhookUrl);
|
|
2212
|
-
webhookUrl.searchParams.set("provider", "plivo");
|
|
2213
|
-
webhookUrl.searchParams.set("callId", input.callId);
|
|
2214
|
-
const answerUrl = new URL(webhookUrl);
|
|
2215
|
-
answerUrl.searchParams.set("flow", "answer");
|
|
2216
|
-
const hangupUrl = new URL(webhookUrl);
|
|
2217
|
-
hangupUrl.searchParams.set("flow", "hangup");
|
|
2218
|
-
this.callIdToWebhookUrl.set(input.callId, input.webhookUrl);
|
|
2219
|
-
const ringTimeoutSec = this.options.ringTimeoutSec ?? 30;
|
|
2220
|
-
const result = await this.apiRequest({
|
|
2221
|
-
method: "POST",
|
|
2222
|
-
endpoint: "/Call/",
|
|
2223
|
-
body: {
|
|
2224
|
-
from: PlivoProvider.normalizeNumber(input.from),
|
|
2225
|
-
to: PlivoProvider.normalizeNumber(input.to),
|
|
2226
|
-
answer_url: answerUrl.toString(),
|
|
2227
|
-
answer_method: "POST",
|
|
2228
|
-
hangup_url: hangupUrl.toString(),
|
|
2229
|
-
hangup_method: "POST",
|
|
2230
|
-
hangup_on_ring: ringTimeoutSec
|
|
2231
|
-
}
|
|
2232
|
-
});
|
|
2233
|
-
const requestUuid = Array.isArray(result.request_uuid) ? result.request_uuid[0] : result.request_uuid;
|
|
2234
|
-
if (!requestUuid) throw new Error("Plivo call create returned no request_uuid");
|
|
2235
|
-
return {
|
|
2236
|
-
providerCallId: requestUuid,
|
|
2237
|
-
status: "initiated"
|
|
2238
|
-
};
|
|
2239
|
-
}
|
|
2240
|
-
async hangupCall(input) {
|
|
2241
|
-
const callUuid = this.requestUuidToCallUuid.get(input.providerCallId);
|
|
2242
|
-
if (callUuid) {
|
|
2243
|
-
await this.apiRequest({
|
|
2244
|
-
method: "DELETE",
|
|
2245
|
-
endpoint: `/Call/${callUuid}/`,
|
|
2246
|
-
allowNotFound: true
|
|
2247
|
-
});
|
|
2248
|
-
return;
|
|
2249
|
-
}
|
|
2250
|
-
await this.apiRequest({
|
|
2251
|
-
method: "DELETE",
|
|
2252
|
-
endpoint: `/Call/${input.providerCallId}/`,
|
|
2253
|
-
allowNotFound: true
|
|
2254
|
-
});
|
|
2255
|
-
await this.apiRequest({
|
|
2256
|
-
method: "DELETE",
|
|
2257
|
-
endpoint: `/Request/${input.providerCallId}/`,
|
|
2258
|
-
allowNotFound: true
|
|
2259
|
-
});
|
|
2260
|
-
}
|
|
2261
|
-
resolveCallContext(params) {
|
|
2262
|
-
const callUuid = this.requestUuidToCallUuid.get(params.providerCallId) ?? params.providerCallId;
|
|
2263
|
-
const webhookBase = this.callUuidToWebhookUrl.get(callUuid) || this.callIdToWebhookUrl.get(params.callId);
|
|
2264
|
-
if (!webhookBase) throw new Error("Missing webhook URL for this call (provider state missing)");
|
|
2265
|
-
if (!callUuid) throw new Error(`Missing Plivo CallUUID for ${params.operation}`);
|
|
2266
|
-
return {
|
|
2267
|
-
callUuid,
|
|
2268
|
-
webhookBase
|
|
2269
|
-
};
|
|
2270
|
-
}
|
|
2271
|
-
async transferCallLeg(params) {
|
|
2272
|
-
const transferUrl = new URL(params.webhookBase);
|
|
2273
|
-
transferUrl.searchParams.set("provider", "plivo");
|
|
2274
|
-
transferUrl.searchParams.set("flow", params.flow);
|
|
2275
|
-
transferUrl.searchParams.set("callId", params.callId);
|
|
2276
|
-
await this.apiRequest({
|
|
2277
|
-
method: "POST",
|
|
2278
|
-
endpoint: `/Call/${params.callUuid}/`,
|
|
2279
|
-
body: {
|
|
2280
|
-
legs: "aleg",
|
|
2281
|
-
aleg_url: transferUrl.toString(),
|
|
2282
|
-
aleg_method: "POST"
|
|
2283
|
-
}
|
|
2284
|
-
});
|
|
2285
|
-
}
|
|
2286
|
-
async playTts(input) {
|
|
2287
|
-
const { callUuid, webhookBase } = this.resolveCallContext({
|
|
2288
|
-
providerCallId: input.providerCallId,
|
|
2289
|
-
callId: input.callId,
|
|
2290
|
-
operation: "playTts"
|
|
2291
|
-
});
|
|
2292
|
-
this.pendingSpeakByCallId.set(input.callId, {
|
|
2293
|
-
text: input.text,
|
|
2294
|
-
locale: input.locale
|
|
2295
|
-
});
|
|
2296
|
-
await this.transferCallLeg({
|
|
2297
|
-
callUuid,
|
|
2298
|
-
webhookBase,
|
|
2299
|
-
callId: input.callId,
|
|
2300
|
-
flow: "xml-speak"
|
|
2301
|
-
});
|
|
2302
|
-
}
|
|
2303
|
-
async startListening(input) {
|
|
2304
|
-
const { callUuid, webhookBase } = this.resolveCallContext({
|
|
2305
|
-
providerCallId: input.providerCallId,
|
|
2306
|
-
callId: input.callId,
|
|
2307
|
-
operation: "startListening"
|
|
2308
|
-
});
|
|
2309
|
-
this.pendingListenByCallId.set(input.callId, { language: input.language });
|
|
2310
|
-
await this.transferCallLeg({
|
|
2311
|
-
callUuid,
|
|
2312
|
-
webhookBase,
|
|
2313
|
-
callId: input.callId,
|
|
2314
|
-
flow: "xml-listen"
|
|
2315
|
-
});
|
|
2316
|
-
}
|
|
2317
|
-
async stopListening(_input) {}
|
|
2318
|
-
async getCallStatus(input) {
|
|
2319
|
-
const terminalStatuses = new Set([
|
|
2320
|
-
"completed",
|
|
2321
|
-
"busy",
|
|
2322
|
-
"failed",
|
|
2323
|
-
"timeout",
|
|
2324
|
-
"no-answer",
|
|
2325
|
-
"cancel",
|
|
2326
|
-
"machine",
|
|
2327
|
-
"hangup"
|
|
2328
|
-
]);
|
|
2329
|
-
try {
|
|
2330
|
-
const data = await guardedJsonApiRequest({
|
|
2331
|
-
url: `${this.baseUrl}/Call/${input.providerCallId}/`,
|
|
2332
|
-
method: "GET",
|
|
2333
|
-
headers: { Authorization: `Basic ${Buffer.from(`${this.authId}:${this.authToken}`).toString("base64")}` },
|
|
2334
|
-
allowNotFound: true,
|
|
2335
|
-
allowedHostnames: [this.apiHost],
|
|
2336
|
-
auditContext: "plivo-get-call-status",
|
|
2337
|
-
errorPrefix: "Plivo get call status error"
|
|
2338
|
-
});
|
|
2339
|
-
if (!data) return {
|
|
2340
|
-
status: "not-found",
|
|
2341
|
-
isTerminal: true
|
|
2342
|
-
};
|
|
2343
|
-
const status = data.call_status ?? "unknown";
|
|
2344
|
-
return {
|
|
2345
|
-
status,
|
|
2346
|
-
isTerminal: terminalStatuses.has(status)
|
|
2347
|
-
};
|
|
2348
|
-
} catch {
|
|
2349
|
-
return {
|
|
2350
|
-
status: "error",
|
|
2351
|
-
isTerminal: false,
|
|
2352
|
-
isUnknown: true
|
|
2353
|
-
};
|
|
2354
|
-
}
|
|
2355
|
-
}
|
|
2356
|
-
static normalizeNumber(numberOrSip) {
|
|
2357
|
-
const trimmed = numberOrSip.trim();
|
|
2358
|
-
if (trimmed.toLowerCase().startsWith("sip:")) return trimmed;
|
|
2359
|
-
return trimmed.replace(/[^\d+]/g, "");
|
|
2360
|
-
}
|
|
2361
|
-
static xmlEmpty() {
|
|
2362
|
-
return `<?xml version="1.0" encoding="UTF-8"?><Response></Response>`;
|
|
2363
|
-
}
|
|
2364
|
-
static xmlKeepAlive() {
|
|
2365
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
2366
|
-
<Response>
|
|
2367
|
-
<Wait length="300" />
|
|
2368
|
-
</Response>`;
|
|
2369
|
-
}
|
|
2370
|
-
static xmlSpeak(text, locale) {
|
|
2371
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
2372
|
-
<Response>
|
|
2373
|
-
<Speak language="${escapeXml(locale || "en-US")}">${escapeXml(text)}</Speak>
|
|
2374
|
-
<Wait length="300" />
|
|
2375
|
-
</Response>`;
|
|
2376
|
-
}
|
|
2377
|
-
static xmlGetInputSpeech(params) {
|
|
2378
|
-
const language = params.language || "en-US";
|
|
2379
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
2380
|
-
<Response>
|
|
2381
|
-
<GetInput inputType="speech" method="POST" action="${escapeXml(params.actionUrl)}" language="${escapeXml(language)}" executionTimeout="30" speechEndTimeout="1" redirect="false">
|
|
2382
|
-
</GetInput>
|
|
2383
|
-
<Wait length="300" />
|
|
2384
|
-
</Response>`;
|
|
2385
|
-
}
|
|
2386
|
-
getCallIdFromQuery(ctx) {
|
|
2387
|
-
return (typeof ctx.query?.callId === "string" && ctx.query.callId.trim() ? ctx.query.callId.trim() : void 0) || void 0;
|
|
2388
|
-
}
|
|
2389
|
-
buildActionUrl(ctx, opts) {
|
|
2390
|
-
const base = this.baseWebhookUrlFromCtx(ctx);
|
|
2391
|
-
if (!base) return null;
|
|
2392
|
-
const u = new URL(base);
|
|
2393
|
-
u.searchParams.set("provider", "plivo");
|
|
2394
|
-
u.searchParams.set("flow", opts.flow);
|
|
2395
|
-
if (opts.callId) u.searchParams.set("callId", opts.callId);
|
|
2396
|
-
return u.toString();
|
|
2397
|
-
}
|
|
2398
|
-
baseWebhookUrlFromCtx(ctx) {
|
|
2399
|
-
try {
|
|
2400
|
-
if (this.options.publicUrl) {
|
|
2401
|
-
const base = new URL(this.options.publicUrl);
|
|
2402
|
-
base.pathname = new URL(ctx.url).pathname;
|
|
2403
|
-
return `${base.origin}${base.pathname}`;
|
|
2404
|
-
}
|
|
2405
|
-
const u = new URL(reconstructWebhookUrl(ctx, {
|
|
2406
|
-
allowedHosts: this.options.webhookSecurity?.allowedHosts,
|
|
2407
|
-
trustForwardingHeaders: this.options.webhookSecurity?.trustForwardingHeaders,
|
|
2408
|
-
trustedProxyIPs: this.options.webhookSecurity?.trustedProxyIPs,
|
|
2409
|
-
remoteIP: ctx.remoteAddress
|
|
2410
|
-
}));
|
|
2411
|
-
return `${u.origin}${u.pathname}`;
|
|
2412
|
-
} catch {
|
|
2413
|
-
return null;
|
|
2414
|
-
}
|
|
2415
|
-
}
|
|
2416
|
-
parseBody(rawBody) {
|
|
2417
|
-
try {
|
|
2418
|
-
return new URLSearchParams(rawBody);
|
|
2419
|
-
} catch {
|
|
2420
|
-
return null;
|
|
2421
|
-
}
|
|
2422
|
-
}
|
|
2423
|
-
static extractTranscript(params) {
|
|
2424
|
-
for (const key of [
|
|
2425
|
-
"Speech",
|
|
2426
|
-
"Transcription",
|
|
2427
|
-
"TranscriptionText",
|
|
2428
|
-
"SpeechResult",
|
|
2429
|
-
"RecognizedSpeech",
|
|
2430
|
-
"Text"
|
|
2431
|
-
]) {
|
|
2432
|
-
const value = params.get(key);
|
|
2433
|
-
if (value && value.trim()) return value.trim();
|
|
2434
|
-
}
|
|
2435
|
-
return null;
|
|
2436
|
-
}
|
|
2437
|
-
};
|
|
2438
|
-
//#endregion
|
|
2439
|
-
//#region extensions/voice-call/src/providers/telnyx.ts
|
|
2440
|
-
var TelnyxProvider = class {
|
|
2441
|
-
constructor(config, options = {}) {
|
|
2442
|
-
this.name = "telnyx";
|
|
2443
|
-
this.baseUrl = "https://api.telnyx.com/v2";
|
|
2444
|
-
this.apiHost = "api.telnyx.com";
|
|
2445
|
-
if (!config.apiKey) throw new Error("Telnyx API key is required");
|
|
2446
|
-
if (!config.connectionId) throw new Error("Telnyx connection ID is required");
|
|
2447
|
-
this.apiKey = config.apiKey;
|
|
2448
|
-
this.connectionId = config.connectionId;
|
|
2449
|
-
this.publicKey = config.publicKey;
|
|
2450
|
-
this.options = options;
|
|
2451
|
-
}
|
|
2452
|
-
/**
|
|
2453
|
-
* Make an authenticated request to the Telnyx API.
|
|
2454
|
-
*/
|
|
2455
|
-
async apiRequest(endpoint, body, options) {
|
|
2456
|
-
return await guardedJsonApiRequest({
|
|
2457
|
-
url: `${this.baseUrl}${endpoint}`,
|
|
2458
|
-
method: "POST",
|
|
2459
|
-
headers: {
|
|
2460
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
2461
|
-
"Content-Type": "application/json"
|
|
2462
|
-
},
|
|
2463
|
-
body,
|
|
2464
|
-
allowNotFound: options?.allowNotFound,
|
|
2465
|
-
allowedHostnames: [this.apiHost],
|
|
2466
|
-
auditContext: "voice-call.telnyx.api",
|
|
2467
|
-
errorPrefix: "Telnyx API error"
|
|
2468
|
-
});
|
|
2469
|
-
}
|
|
2470
|
-
/**
|
|
2471
|
-
* Verify Telnyx webhook signature using Ed25519.
|
|
2472
|
-
*/
|
|
2473
|
-
verifyWebhook(ctx) {
|
|
2474
|
-
const result = verifyTelnyxWebhook(ctx, this.publicKey, { skipVerification: this.options.skipVerification });
|
|
2475
|
-
return {
|
|
2476
|
-
ok: result.ok,
|
|
2477
|
-
reason: result.reason,
|
|
2478
|
-
isReplay: result.isReplay,
|
|
2479
|
-
verifiedRequestKey: result.verifiedRequestKey
|
|
2480
|
-
};
|
|
2481
|
-
}
|
|
2482
|
-
/**
|
|
2483
|
-
* Parse Telnyx webhook event into normalized format.
|
|
2484
|
-
*/
|
|
2485
|
-
parseWebhookEvent(ctx, options) {
|
|
2486
|
-
try {
|
|
2487
|
-
const data = JSON.parse(ctx.rawBody).data;
|
|
2488
|
-
if (!data || !data.event_type) return {
|
|
2489
|
-
events: [],
|
|
2490
|
-
statusCode: 200
|
|
2491
|
-
};
|
|
2492
|
-
const event = this.normalizeEvent(data, options?.verifiedRequestKey);
|
|
2493
|
-
return {
|
|
2494
|
-
events: event ? [event] : [],
|
|
2495
|
-
statusCode: 200
|
|
2496
|
-
};
|
|
2497
|
-
} catch {
|
|
2498
|
-
return {
|
|
2499
|
-
events: [],
|
|
2500
|
-
statusCode: 400
|
|
2501
|
-
};
|
|
2502
|
-
}
|
|
2503
|
-
}
|
|
2504
|
-
/**
|
|
2505
|
-
* Convert Telnyx event to normalized event format.
|
|
2506
|
-
*/
|
|
2507
|
-
normalizeEvent(data, dedupeKey) {
|
|
2508
|
-
let callId = "";
|
|
2509
|
-
if (data.payload?.client_state) try {
|
|
2510
|
-
callId = Buffer.from(data.payload.client_state, "base64").toString("utf8");
|
|
2511
|
-
} catch {
|
|
2512
|
-
callId = data.payload.client_state;
|
|
2513
|
-
}
|
|
2514
|
-
if (!callId) callId = data.payload?.call_control_id || "";
|
|
2515
|
-
const baseEvent = {
|
|
2516
|
-
id: data.id || crypto.randomUUID(),
|
|
2517
|
-
dedupeKey,
|
|
2518
|
-
callId,
|
|
2519
|
-
providerCallId: data.payload?.call_control_id,
|
|
2520
|
-
timestamp: Date.now()
|
|
2521
|
-
};
|
|
2522
|
-
switch (data.event_type) {
|
|
2523
|
-
case "call.initiated": return {
|
|
2524
|
-
...baseEvent,
|
|
2525
|
-
type: "call.initiated"
|
|
2526
|
-
};
|
|
2527
|
-
case "call.ringing": return {
|
|
2528
|
-
...baseEvent,
|
|
2529
|
-
type: "call.ringing"
|
|
2530
|
-
};
|
|
2531
|
-
case "call.answered": return {
|
|
2532
|
-
...baseEvent,
|
|
2533
|
-
type: "call.answered"
|
|
2534
|
-
};
|
|
2535
|
-
case "call.bridged": return {
|
|
2536
|
-
...baseEvent,
|
|
2537
|
-
type: "call.active"
|
|
2538
|
-
};
|
|
2539
|
-
case "call.speak.started": return {
|
|
2540
|
-
...baseEvent,
|
|
2541
|
-
type: "call.speaking",
|
|
2542
|
-
text: data.payload?.text || ""
|
|
2543
|
-
};
|
|
2544
|
-
case "call.transcription": return {
|
|
2545
|
-
...baseEvent,
|
|
2546
|
-
type: "call.speech",
|
|
2547
|
-
transcript: data.payload?.transcription || "",
|
|
2548
|
-
isFinal: data.payload?.is_final ?? true,
|
|
2549
|
-
confidence: data.payload?.confidence
|
|
2550
|
-
};
|
|
2551
|
-
case "call.hangup": return {
|
|
2552
|
-
...baseEvent,
|
|
2553
|
-
type: "call.ended",
|
|
2554
|
-
reason: this.mapHangupCause(data.payload?.hangup_cause)
|
|
2555
|
-
};
|
|
2556
|
-
case "call.dtmf.received": return {
|
|
2557
|
-
...baseEvent,
|
|
2558
|
-
type: "call.dtmf",
|
|
2559
|
-
digits: data.payload?.digit || ""
|
|
2560
|
-
};
|
|
2561
|
-
default: return null;
|
|
2562
|
-
}
|
|
2563
|
-
}
|
|
2564
|
-
/**
|
|
2565
|
-
* Map Telnyx hangup cause to normalized end reason.
|
|
2566
|
-
* @see https://developers.telnyx.com/docs/api/v2/call-control/Call-Commands#hangup-causes
|
|
2567
|
-
*/
|
|
2568
|
-
mapHangupCause(cause) {
|
|
2569
|
-
switch (cause) {
|
|
2570
|
-
case "normal_clearing":
|
|
2571
|
-
case "normal_unspecified": return "completed";
|
|
2572
|
-
case "originator_cancel": return "hangup-bot";
|
|
2573
|
-
case "call_rejected":
|
|
2574
|
-
case "user_busy": return "busy";
|
|
2575
|
-
case "no_answer":
|
|
2576
|
-
case "no_user_response": return "no-answer";
|
|
2577
|
-
case "destination_out_of_order":
|
|
2578
|
-
case "network_out_of_order":
|
|
2579
|
-
case "service_unavailable":
|
|
2580
|
-
case "recovery_on_timer_expire": return "failed";
|
|
2581
|
-
case "machine_detected":
|
|
2582
|
-
case "fax_detected": return "voicemail";
|
|
2583
|
-
case "user_hangup":
|
|
2584
|
-
case "subscriber_absent": return "hangup-user";
|
|
2585
|
-
default:
|
|
2586
|
-
if (cause) console.warn(`[telnyx] Unknown hangup cause: ${cause}`);
|
|
2587
|
-
return "completed";
|
|
2588
|
-
}
|
|
2589
|
-
}
|
|
2590
|
-
/**
|
|
2591
|
-
* Initiate an outbound call via Telnyx API.
|
|
2592
|
-
*/
|
|
2593
|
-
async initiateCall(input) {
|
|
2594
|
-
return {
|
|
2595
|
-
providerCallId: (await this.apiRequest("/calls", {
|
|
2596
|
-
connection_id: this.connectionId,
|
|
2597
|
-
to: input.to,
|
|
2598
|
-
from: input.from,
|
|
2599
|
-
webhook_url: input.webhookUrl,
|
|
2600
|
-
webhook_url_method: "POST",
|
|
2601
|
-
client_state: Buffer.from(input.callId).toString("base64"),
|
|
2602
|
-
timeout_secs: 30
|
|
2603
|
-
})).data.call_control_id,
|
|
2604
|
-
status: "initiated"
|
|
2605
|
-
};
|
|
2606
|
-
}
|
|
2607
|
-
/**
|
|
2608
|
-
* Hang up a call via Telnyx API.
|
|
2609
|
-
*/
|
|
2610
|
-
async hangupCall(input) {
|
|
2611
|
-
await this.apiRequest(`/calls/${input.providerCallId}/actions/hangup`, { command_id: crypto.randomUUID() }, { allowNotFound: true });
|
|
2612
|
-
}
|
|
2613
|
-
/**
|
|
2614
|
-
* Play TTS audio via Telnyx speak action.
|
|
2615
|
-
*/
|
|
2616
|
-
async playTts(input) {
|
|
2617
|
-
await this.apiRequest(`/calls/${input.providerCallId}/actions/speak`, {
|
|
2618
|
-
command_id: crypto.randomUUID(),
|
|
2619
|
-
payload: input.text,
|
|
2620
|
-
voice: input.voice || "female",
|
|
2621
|
-
language: input.locale || "en-US"
|
|
2622
|
-
});
|
|
2623
|
-
}
|
|
2624
|
-
/**
|
|
2625
|
-
* Start transcription (STT) via Telnyx.
|
|
2626
|
-
*/
|
|
2627
|
-
async startListening(input) {
|
|
2628
|
-
await this.apiRequest(`/calls/${input.providerCallId}/actions/transcription_start`, {
|
|
2629
|
-
command_id: crypto.randomUUID(),
|
|
2630
|
-
language: input.language || "en"
|
|
2631
|
-
});
|
|
2632
|
-
}
|
|
2633
|
-
/**
|
|
2634
|
-
* Stop transcription via Telnyx.
|
|
2635
|
-
*/
|
|
2636
|
-
async stopListening(input) {
|
|
2637
|
-
await this.apiRequest(`/calls/${input.providerCallId}/actions/transcription_stop`, { command_id: crypto.randomUUID() }, { allowNotFound: true });
|
|
2638
|
-
}
|
|
2639
|
-
async getCallStatus(input) {
|
|
2640
|
-
try {
|
|
2641
|
-
const data = await guardedJsonApiRequest({
|
|
2642
|
-
url: `${this.baseUrl}/calls/${input.providerCallId}`,
|
|
2643
|
-
method: "GET",
|
|
2644
|
-
headers: {
|
|
2645
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
2646
|
-
"Content-Type": "application/json"
|
|
2647
|
-
},
|
|
2648
|
-
allowNotFound: true,
|
|
2649
|
-
allowedHostnames: [this.apiHost],
|
|
2650
|
-
auditContext: "telnyx-get-call-status",
|
|
2651
|
-
errorPrefix: "Telnyx get call status error"
|
|
2652
|
-
});
|
|
2653
|
-
if (!data) return {
|
|
2654
|
-
status: "not-found",
|
|
2655
|
-
isTerminal: true
|
|
2656
|
-
};
|
|
2657
|
-
const state = data.data?.state ?? "unknown";
|
|
2658
|
-
const isAlive = data.data?.is_alive;
|
|
2659
|
-
if (isAlive === void 0) return {
|
|
2660
|
-
status: state,
|
|
2661
|
-
isTerminal: false,
|
|
2662
|
-
isUnknown: true
|
|
2663
|
-
};
|
|
2664
|
-
return {
|
|
2665
|
-
status: state,
|
|
2666
|
-
isTerminal: !isAlive
|
|
2667
|
-
};
|
|
2668
|
-
} catch {
|
|
2669
|
-
return {
|
|
2670
|
-
status: "error",
|
|
2671
|
-
isTerminal: false,
|
|
2672
|
-
isUnknown: true
|
|
2673
|
-
};
|
|
2674
|
-
}
|
|
2675
|
-
}
|
|
2676
|
-
};
|
|
2677
|
-
//#endregion
|
|
2678
|
-
//#region extensions/voice-call/src/telephony-audio.ts
|
|
2679
|
-
const TELEPHONY_SAMPLE_RATE = 8e3;
|
|
2680
|
-
const RESAMPLE_FILTER_TAPS = 31;
|
|
2681
|
-
const RESAMPLE_CUTOFF_GUARD = .94;
|
|
2682
|
-
function clamp16(value) {
|
|
2683
|
-
return Math.max(-32768, Math.min(32767, value));
|
|
2684
|
-
}
|
|
2685
|
-
function sinc(x) {
|
|
2686
|
-
if (x === 0) return 1;
|
|
2687
|
-
return Math.sin(Math.PI * x) / (Math.PI * x);
|
|
2688
|
-
}
|
|
2689
|
-
/**
|
|
2690
|
-
* Build a finite low-pass kernel centered on `srcPos`.
|
|
2691
|
-
* The kernel is windowed (Hann) to reduce ringing artifacts.
|
|
2692
|
-
*/
|
|
2693
|
-
function sampleBandlimited(input, inputSamples, srcPos, cutoffCyclesPerSample) {
|
|
2694
|
-
const half = Math.floor(RESAMPLE_FILTER_TAPS / 2);
|
|
2695
|
-
const center = Math.floor(srcPos);
|
|
2696
|
-
let weighted = 0;
|
|
2697
|
-
let weightSum = 0;
|
|
2698
|
-
for (let tap = -half; tap <= half; tap++) {
|
|
2699
|
-
const sampleIndex = center + tap;
|
|
2700
|
-
if (sampleIndex < 0 || sampleIndex >= inputSamples) continue;
|
|
2701
|
-
const distance = sampleIndex - srcPos;
|
|
2702
|
-
const lowPass = 2 * cutoffCyclesPerSample * sinc(2 * cutoffCyclesPerSample * distance);
|
|
2703
|
-
const tapIndex = tap + half;
|
|
2704
|
-
const coeff = lowPass * (.5 - .5 * Math.cos(2 * Math.PI * tapIndex / (RESAMPLE_FILTER_TAPS - 1)));
|
|
2705
|
-
weighted += input.readInt16LE(sampleIndex * 2) * coeff;
|
|
2706
|
-
weightSum += coeff;
|
|
2707
|
-
}
|
|
2708
|
-
if (weightSum === 0) {
|
|
2709
|
-
const nearest = Math.max(0, Math.min(inputSamples - 1, Math.round(srcPos)));
|
|
2710
|
-
return input.readInt16LE(nearest * 2);
|
|
2711
|
-
}
|
|
2712
|
-
return weighted / weightSum;
|
|
2713
|
-
}
|
|
2714
|
-
/**
|
|
2715
|
-
* Resample 16-bit PCM (little-endian mono) to 8kHz using a windowed low-pass kernel.
|
|
2716
|
-
*/
|
|
2717
|
-
function resamplePcmTo8k(input, inputSampleRate) {
|
|
2718
|
-
if (inputSampleRate === TELEPHONY_SAMPLE_RATE) return input;
|
|
2719
|
-
const inputSamples = Math.floor(input.length / 2);
|
|
2720
|
-
if (inputSamples === 0) return Buffer.alloc(0);
|
|
2721
|
-
const ratio = inputSampleRate / TELEPHONY_SAMPLE_RATE;
|
|
2722
|
-
const outputSamples = Math.floor(inputSamples / ratio);
|
|
2723
|
-
const output = Buffer.alloc(outputSamples * 2);
|
|
2724
|
-
const maxCutoff = .5;
|
|
2725
|
-
const downsampleCutoff = ratio > 1 ? maxCutoff / ratio : maxCutoff;
|
|
2726
|
-
const cutoffCyclesPerSample = Math.max(.01, downsampleCutoff * RESAMPLE_CUTOFF_GUARD);
|
|
2727
|
-
for (let i = 0; i < outputSamples; i++) {
|
|
2728
|
-
const srcPos = i * ratio;
|
|
2729
|
-
const sample = Math.round(sampleBandlimited(input, inputSamples, srcPos, cutoffCyclesPerSample));
|
|
2730
|
-
output.writeInt16LE(clamp16(sample), i * 2);
|
|
2731
|
-
}
|
|
2732
|
-
return output;
|
|
2733
|
-
}
|
|
2734
|
-
/**
|
|
2735
|
-
* Convert 16-bit PCM to 8-bit mu-law (G.711).
|
|
2736
|
-
*/
|
|
2737
|
-
function pcmToMulaw(pcm) {
|
|
2738
|
-
const samples = Math.floor(pcm.length / 2);
|
|
2739
|
-
const mulaw = Buffer.alloc(samples);
|
|
2740
|
-
for (let i = 0; i < samples; i++) mulaw[i] = linearToMulaw(pcm.readInt16LE(i * 2));
|
|
2741
|
-
return mulaw;
|
|
2742
|
-
}
|
|
2743
|
-
function convertPcmToMulaw8k(pcm, inputSampleRate) {
|
|
2744
|
-
return pcmToMulaw(resamplePcmTo8k(pcm, inputSampleRate));
|
|
2745
|
-
}
|
|
2746
|
-
/**
|
|
2747
|
-
* Chunk audio buffer into 20ms frames for streaming (8kHz mono mu-law).
|
|
2748
|
-
*/
|
|
2749
|
-
function chunkAudio(audio, chunkSize = 160) {
|
|
2750
|
-
return (function* () {
|
|
2751
|
-
for (let i = 0; i < audio.length; i += chunkSize) yield audio.subarray(i, Math.min(i + chunkSize, audio.length));
|
|
2752
|
-
})();
|
|
2753
|
-
}
|
|
2754
|
-
function linearToMulaw(sample) {
|
|
2755
|
-
const BIAS = 132;
|
|
2756
|
-
const CLIP = 32635;
|
|
2757
|
-
const sign = sample < 0 ? 128 : 0;
|
|
2758
|
-
if (sample < 0) sample = -sample;
|
|
2759
|
-
if (sample > CLIP) sample = CLIP;
|
|
2760
|
-
sample += BIAS;
|
|
2761
|
-
let exponent = 7;
|
|
2762
|
-
for (let expMask = 16384; (sample & expMask) === 0 && exponent > 0; exponent--) expMask >>= 1;
|
|
2763
|
-
const mantissa = sample >> exponent + 3 & 15;
|
|
2764
|
-
return ~(sign | exponent << 4 | mantissa) & 255;
|
|
2765
|
-
}
|
|
2766
|
-
//#endregion
|
|
2767
|
-
//#region extensions/voice-call/src/providers/shared/call-status.ts
|
|
2768
|
-
const TERMINAL_PROVIDER_STATUS_TO_END_REASON = {
|
|
2769
|
-
completed: "completed",
|
|
2770
|
-
failed: "failed",
|
|
2771
|
-
busy: "busy",
|
|
2772
|
-
"no-answer": "no-answer",
|
|
2773
|
-
canceled: "hangup-bot"
|
|
2774
|
-
};
|
|
2775
|
-
function normalizeProviderStatus(status) {
|
|
2776
|
-
const normalized = status?.trim().toLowerCase();
|
|
2777
|
-
return normalized && normalized.length > 0 ? normalized : "unknown";
|
|
2778
|
-
}
|
|
2779
|
-
function mapProviderStatusToEndReason(status) {
|
|
2780
|
-
return TERMINAL_PROVIDER_STATUS_TO_END_REASON[normalizeProviderStatus(status)] ?? null;
|
|
2781
|
-
}
|
|
2782
|
-
function isProviderStatusTerminal(status) {
|
|
2783
|
-
return mapProviderStatusToEndReason(status) !== null;
|
|
2784
|
-
}
|
|
2785
|
-
//#endregion
|
|
2786
|
-
//#region extensions/voice-call/src/providers/twilio/api.ts
|
|
2787
|
-
async function twilioApiRequest(params) {
|
|
2788
|
-
const bodyParams = params.body instanceof URLSearchParams ? params.body : Object.entries(params.body).reduce((acc, [key, value]) => {
|
|
2789
|
-
if (Array.isArray(value)) for (const entry of value) acc.append(key, entry);
|
|
2790
|
-
else if (typeof value === "string") acc.append(key, value);
|
|
2791
|
-
return acc;
|
|
2792
|
-
}, new URLSearchParams());
|
|
2793
|
-
const response = await fetch(`${params.baseUrl}${params.endpoint}`, {
|
|
2794
|
-
method: "POST",
|
|
2795
|
-
headers: {
|
|
2796
|
-
Authorization: `Basic ${Buffer.from(`${params.accountSid}:${params.authToken}`).toString("base64")}`,
|
|
2797
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
2798
|
-
},
|
|
2799
|
-
body: bodyParams
|
|
2800
|
-
});
|
|
2801
|
-
if (!response.ok) {
|
|
2802
|
-
if (params.allowNotFound && response.status === 404) return;
|
|
2803
|
-
const errorText = await response.text();
|
|
2804
|
-
throw new Error(`Twilio API error: ${response.status} ${errorText}`);
|
|
2805
|
-
}
|
|
2806
|
-
const text = await response.text();
|
|
2807
|
-
return text ? JSON.parse(text) : void 0;
|
|
2808
|
-
}
|
|
2809
|
-
//#endregion
|
|
2810
|
-
//#region extensions/voice-call/src/providers/twilio/twiml-policy.ts
|
|
2811
|
-
function isOutboundDirection(direction) {
|
|
2812
|
-
return direction?.startsWith("outbound") ?? false;
|
|
2813
|
-
}
|
|
2814
|
-
function readTwimlRequestView(ctx) {
|
|
2815
|
-
const params = new URLSearchParams(ctx.rawBody);
|
|
2816
|
-
const type = typeof ctx.query?.type === "string" ? ctx.query.type.trim() : void 0;
|
|
2817
|
-
const callIdFromQuery = typeof ctx.query?.callId === "string" && ctx.query.callId.trim() ? ctx.query.callId.trim() : void 0;
|
|
2818
|
-
return {
|
|
2819
|
-
callStatus: params.get("CallStatus"),
|
|
2820
|
-
direction: params.get("Direction"),
|
|
2821
|
-
isStatusCallback: type === "status",
|
|
2822
|
-
callSid: params.get("CallSid") || void 0,
|
|
2823
|
-
callIdFromQuery
|
|
2824
|
-
};
|
|
2825
|
-
}
|
|
2826
|
-
function decideTwimlResponse(input) {
|
|
2827
|
-
if (input.callIdFromQuery && !input.isStatusCallback) {
|
|
2828
|
-
if (input.hasStoredTwiml) return {
|
|
2829
|
-
kind: "stored",
|
|
2830
|
-
consumeStoredTwimlCallId: input.callIdFromQuery
|
|
2831
|
-
};
|
|
2832
|
-
if (input.isNotifyCall) return { kind: "empty" };
|
|
2833
|
-
if (isOutboundDirection(input.direction)) return input.canStream ? { kind: "stream" } : { kind: "pause" };
|
|
2834
|
-
}
|
|
2835
|
-
if (input.isStatusCallback) return { kind: "empty" };
|
|
2836
|
-
if (input.direction === "inbound") {
|
|
2837
|
-
if (input.hasActiveStreams) return { kind: "queue" };
|
|
2838
|
-
if (input.canStream && input.callSid) return {
|
|
2839
|
-
kind: "stream",
|
|
2840
|
-
activateStreamCallSid: input.callSid
|
|
2841
|
-
};
|
|
2842
|
-
return { kind: "pause" };
|
|
2843
|
-
}
|
|
2844
|
-
if (input.callStatus !== "in-progress") return { kind: "empty" };
|
|
2845
|
-
return input.canStream ? { kind: "stream" } : { kind: "pause" };
|
|
2846
|
-
}
|
|
2847
|
-
//#endregion
|
|
2848
|
-
//#region extensions/voice-call/src/providers/twilio/webhook.ts
|
|
2849
|
-
function verifyTwilioProviderWebhook(params) {
|
|
2850
|
-
const result = verifyTwilioWebhook(params.ctx, params.authToken, {
|
|
2851
|
-
publicUrl: params.currentPublicUrl || void 0,
|
|
2852
|
-
allowNgrokFreeTierLoopbackBypass: params.options.allowNgrokFreeTierLoopbackBypass ?? false,
|
|
2853
|
-
skipVerification: params.options.skipVerification,
|
|
2854
|
-
allowedHosts: params.options.webhookSecurity?.allowedHosts,
|
|
2855
|
-
trustForwardingHeaders: params.options.webhookSecurity?.trustForwardingHeaders,
|
|
2856
|
-
trustedProxyIPs: params.options.webhookSecurity?.trustedProxyIPs,
|
|
2857
|
-
remoteIP: params.ctx.remoteAddress
|
|
2858
|
-
});
|
|
2859
|
-
if (!result.ok) {
|
|
2860
|
-
console.warn(`[twilio] Webhook verification failed: ${result.reason}`);
|
|
2861
|
-
if (result.verificationUrl) console.warn(`[twilio] Verification URL: ${result.verificationUrl}`);
|
|
2862
|
-
}
|
|
2863
|
-
return {
|
|
2864
|
-
ok: result.ok,
|
|
2865
|
-
reason: result.reason,
|
|
2866
|
-
isReplay: result.isReplay,
|
|
2867
|
-
verifiedRequestKey: result.verifiedRequestKey
|
|
2868
|
-
};
|
|
2869
|
-
}
|
|
2870
|
-
//#endregion
|
|
2871
|
-
//#region extensions/voice-call/src/providers/twilio.ts
|
|
2872
|
-
function createTwilioRequestDedupeKey(ctx, verifiedRequestKey) {
|
|
2873
|
-
if (verifiedRequestKey) return verifiedRequestKey;
|
|
2874
|
-
const signature = getHeader(ctx.headers, "x-twilio-signature") ?? "";
|
|
2875
|
-
const params = new URLSearchParams(ctx.rawBody);
|
|
2876
|
-
const callSid = params.get("CallSid") ?? "";
|
|
2877
|
-
const callStatus = params.get("CallStatus") ?? "";
|
|
2878
|
-
const direction = params.get("Direction") ?? "";
|
|
2879
|
-
const callId = typeof ctx.query?.callId === "string" ? ctx.query.callId.trim() : "";
|
|
2880
|
-
const flow = typeof ctx.query?.flow === "string" ? ctx.query.flow.trim() : "";
|
|
2881
|
-
const turnToken = typeof ctx.query?.turnToken === "string" ? ctx.query.turnToken.trim() : "";
|
|
2882
|
-
return `twilio:fallback:${crypto.createHash("sha256").update(`${signature}\n${callSid}\n${callStatus}\n${direction}\n${callId}\n${flow}\n${turnToken}\n${ctx.rawBody}`).digest("hex")}`;
|
|
2883
|
-
}
|
|
2884
|
-
var TwilioProvider = class TwilioProvider {
|
|
2885
|
-
static {
|
|
2886
|
-
this.TTS_SYNTH_TIMEOUT_MS = 8e3;
|
|
2887
|
-
}
|
|
2888
|
-
/**
|
|
2889
|
-
* Delete stored TwiML for a given `callId`.
|
|
2890
|
-
*
|
|
2891
|
-
* We keep TwiML in-memory only long enough to satisfy the initial Twilio
|
|
2892
|
-
* webhook request (notify mode). Subsequent webhooks should not reuse it.
|
|
2893
|
-
*/
|
|
2894
|
-
deleteStoredTwiml(callId) {
|
|
2895
|
-
this.twimlStorage.delete(callId);
|
|
2896
|
-
this.notifyCalls.delete(callId);
|
|
2897
|
-
}
|
|
2898
|
-
/**
|
|
2899
|
-
* Delete stored TwiML for a call, addressed by Twilio's provider call SID.
|
|
2900
|
-
*
|
|
2901
|
-
* This is used when we only have `providerCallId` (e.g. hangup).
|
|
2902
|
-
*/
|
|
2903
|
-
deleteStoredTwimlForProviderCall(providerCallId) {
|
|
2904
|
-
const webhookUrl = this.callWebhookUrls.get(providerCallId);
|
|
2905
|
-
if (!webhookUrl) return;
|
|
2906
|
-
const callIdMatch = webhookUrl.match(/callId=([^&]+)/);
|
|
2907
|
-
if (!callIdMatch) return;
|
|
2908
|
-
this.deleteStoredTwiml(callIdMatch[1]);
|
|
2909
|
-
this.streamAuthTokens.delete(providerCallId);
|
|
2910
|
-
}
|
|
2911
|
-
constructor(config, options = {}) {
|
|
2912
|
-
this.name = "twilio";
|
|
2913
|
-
this.callWebhookUrls = /* @__PURE__ */ new Map();
|
|
2914
|
-
this.currentPublicUrl = null;
|
|
2915
|
-
this.ttsProvider = null;
|
|
2916
|
-
this.mediaStreamHandler = null;
|
|
2917
|
-
this.callStreamMap = /* @__PURE__ */ new Map();
|
|
2918
|
-
this.streamAuthTokens = /* @__PURE__ */ new Map();
|
|
2919
|
-
this.twimlStorage = /* @__PURE__ */ new Map();
|
|
2920
|
-
this.notifyCalls = /* @__PURE__ */ new Set();
|
|
2921
|
-
this.activeStreamCalls = /* @__PURE__ */ new Set();
|
|
2922
|
-
if (!config.accountSid) throw new Error("Twilio Account SID is required");
|
|
2923
|
-
if (!config.authToken) throw new Error("Twilio Auth Token is required");
|
|
2924
|
-
this.accountSid = config.accountSid;
|
|
2925
|
-
this.authToken = config.authToken;
|
|
2926
|
-
this.baseUrl = `https://api.twilio.com/2010-04-01/Accounts/${this.accountSid}`;
|
|
2927
|
-
this.options = options;
|
|
2928
|
-
if (options.publicUrl) this.currentPublicUrl = options.publicUrl;
|
|
2929
|
-
}
|
|
2930
|
-
setPublicUrl(url) {
|
|
2931
|
-
this.currentPublicUrl = url;
|
|
2932
|
-
}
|
|
2933
|
-
getPublicUrl() {
|
|
2934
|
-
return this.currentPublicUrl;
|
|
2935
|
-
}
|
|
2936
|
-
setTTSProvider(provider) {
|
|
2937
|
-
this.ttsProvider = provider;
|
|
2938
|
-
}
|
|
2939
|
-
setMediaStreamHandler(handler) {
|
|
2940
|
-
this.mediaStreamHandler = handler;
|
|
2941
|
-
}
|
|
2942
|
-
registerCallStream(callSid, streamSid) {
|
|
2943
|
-
this.callStreamMap.set(callSid, streamSid);
|
|
2944
|
-
}
|
|
2945
|
-
hasRegisteredStream(callSid) {
|
|
2946
|
-
return this.callStreamMap.has(callSid);
|
|
2947
|
-
}
|
|
2948
|
-
unregisterCallStream(callSid, streamSid) {
|
|
2949
|
-
const currentStreamSid = this.callStreamMap.get(callSid);
|
|
2950
|
-
if (!currentStreamSid) {
|
|
2951
|
-
if (!streamSid) this.activeStreamCalls.delete(callSid);
|
|
2952
|
-
return;
|
|
2953
|
-
}
|
|
2954
|
-
if (streamSid && currentStreamSid !== streamSid) return;
|
|
2955
|
-
this.callStreamMap.delete(callSid);
|
|
2956
|
-
this.activeStreamCalls.delete(callSid);
|
|
2957
|
-
}
|
|
2958
|
-
isConversationStreamConnectEnabled() {
|
|
2959
|
-
return Boolean(this.mediaStreamHandler && this.getStreamUrl());
|
|
2960
|
-
}
|
|
2961
|
-
isValidStreamToken(callSid, token) {
|
|
2962
|
-
const expected = this.streamAuthTokens.get(callSid);
|
|
2963
|
-
if (!expected || !token) return false;
|
|
2964
|
-
return safeEqualSecret(expected, token);
|
|
2965
|
-
}
|
|
2966
|
-
/**
|
|
2967
|
-
* Clear TTS queue for a call (barge-in).
|
|
2968
|
-
* Used when user starts speaking to interrupt current TTS playback.
|
|
2969
|
-
*/
|
|
2970
|
-
clearTtsQueue(callSid, reason = "unspecified") {
|
|
2971
|
-
const streamSid = this.callStreamMap.get(callSid);
|
|
2972
|
-
if (!streamSid || !this.mediaStreamHandler) return;
|
|
2973
|
-
this.mediaStreamHandler.clearTtsQueue(streamSid, reason);
|
|
2974
|
-
}
|
|
2975
|
-
/**
|
|
2976
|
-
* Make an authenticated request to the Twilio API.
|
|
2977
|
-
*/
|
|
2978
|
-
async apiRequest(endpoint, params, options) {
|
|
2979
|
-
return await twilioApiRequest({
|
|
2980
|
-
baseUrl: this.baseUrl,
|
|
2981
|
-
accountSid: this.accountSid,
|
|
2982
|
-
authToken: this.authToken,
|
|
2983
|
-
endpoint,
|
|
2984
|
-
body: params,
|
|
2985
|
-
allowNotFound: options?.allowNotFound
|
|
2986
|
-
});
|
|
2987
|
-
}
|
|
2988
|
-
/**
|
|
2989
|
-
* Verify Twilio webhook signature using HMAC-SHA1.
|
|
2990
|
-
*
|
|
2991
|
-
* Handles reverse proxy scenarios (Tailscale, nginx, ngrok) by reconstructing
|
|
2992
|
-
* the public URL from forwarding headers.
|
|
2993
|
-
*
|
|
2994
|
-
* @see https://www.twilio.com/docs/usage/webhooks/webhooks-security
|
|
2995
|
-
*/
|
|
2996
|
-
verifyWebhook(ctx) {
|
|
2997
|
-
return verifyTwilioProviderWebhook({
|
|
2998
|
-
ctx,
|
|
2999
|
-
authToken: this.authToken,
|
|
3000
|
-
currentPublicUrl: this.currentPublicUrl,
|
|
3001
|
-
options: this.options
|
|
3002
|
-
});
|
|
3003
|
-
}
|
|
3004
|
-
/**
|
|
3005
|
-
* Parse Twilio webhook event into normalized format.
|
|
3006
|
-
*/
|
|
3007
|
-
parseWebhookEvent(ctx, options) {
|
|
3008
|
-
try {
|
|
3009
|
-
const params = new URLSearchParams(ctx.rawBody);
|
|
3010
|
-
const callIdFromQuery = typeof ctx.query?.callId === "string" && ctx.query.callId.trim() ? ctx.query.callId.trim() : void 0;
|
|
3011
|
-
const turnTokenFromQuery = typeof ctx.query?.turnToken === "string" && ctx.query.turnToken.trim() ? ctx.query.turnToken.trim() : void 0;
|
|
3012
|
-
const dedupeKey = createTwilioRequestDedupeKey(ctx, options?.verifiedRequestKey);
|
|
3013
|
-
const event = this.normalizeEvent(params, {
|
|
3014
|
-
callIdOverride: callIdFromQuery,
|
|
3015
|
-
dedupeKey,
|
|
3016
|
-
turnToken: turnTokenFromQuery
|
|
3017
|
-
});
|
|
3018
|
-
const twiml = this.generateTwimlResponse(ctx);
|
|
3019
|
-
return {
|
|
3020
|
-
events: event ? [event] : [],
|
|
3021
|
-
providerResponseBody: twiml,
|
|
3022
|
-
providerResponseHeaders: { "Content-Type": "application/xml" },
|
|
3023
|
-
statusCode: 200
|
|
3024
|
-
};
|
|
3025
|
-
} catch {
|
|
3026
|
-
return {
|
|
3027
|
-
events: [],
|
|
3028
|
-
statusCode: 400
|
|
3029
|
-
};
|
|
3030
|
-
}
|
|
3031
|
-
}
|
|
3032
|
-
/**
|
|
3033
|
-
* Parse Twilio direction to normalized format.
|
|
3034
|
-
*/
|
|
3035
|
-
static parseDirection(direction) {
|
|
3036
|
-
if (direction === "inbound") return "inbound";
|
|
3037
|
-
if (direction === "outbound-api" || direction === "outbound-dial") return "outbound";
|
|
3038
|
-
}
|
|
3039
|
-
/**
|
|
3040
|
-
* Convert Twilio webhook params to normalized event format.
|
|
3041
|
-
*/
|
|
3042
|
-
normalizeEvent(params, options) {
|
|
3043
|
-
const callSid = params.get("CallSid") || "";
|
|
3044
|
-
const callIdOverride = options?.callIdOverride;
|
|
3045
|
-
const baseEvent = {
|
|
3046
|
-
id: crypto.randomUUID(),
|
|
3047
|
-
dedupeKey: options?.dedupeKey,
|
|
3048
|
-
callId: callIdOverride || callSid,
|
|
3049
|
-
providerCallId: callSid,
|
|
3050
|
-
timestamp: Date.now(),
|
|
3051
|
-
turnToken: options?.turnToken,
|
|
3052
|
-
direction: TwilioProvider.parseDirection(params.get("Direction")),
|
|
3053
|
-
from: params.get("From") || void 0,
|
|
3054
|
-
to: params.get("To") || void 0
|
|
3055
|
-
};
|
|
3056
|
-
const speechResult = params.get("SpeechResult");
|
|
3057
|
-
if (speechResult) return {
|
|
3058
|
-
...baseEvent,
|
|
3059
|
-
type: "call.speech",
|
|
3060
|
-
transcript: speechResult,
|
|
3061
|
-
isFinal: true,
|
|
3062
|
-
confidence: parseFloat(params.get("Confidence") || "0.9")
|
|
3063
|
-
};
|
|
3064
|
-
const digits = params.get("Digits");
|
|
3065
|
-
if (digits) return {
|
|
3066
|
-
...baseEvent,
|
|
3067
|
-
type: "call.dtmf",
|
|
3068
|
-
digits
|
|
3069
|
-
};
|
|
3070
|
-
const callStatus = normalizeProviderStatus(params.get("CallStatus"));
|
|
3071
|
-
if (callStatus === "initiated") return {
|
|
3072
|
-
...baseEvent,
|
|
3073
|
-
type: "call.initiated"
|
|
3074
|
-
};
|
|
3075
|
-
if (callStatus === "ringing") return {
|
|
3076
|
-
...baseEvent,
|
|
3077
|
-
type: "call.ringing"
|
|
3078
|
-
};
|
|
3079
|
-
if (callStatus === "in-progress") return {
|
|
3080
|
-
...baseEvent,
|
|
3081
|
-
type: "call.answered"
|
|
3082
|
-
};
|
|
3083
|
-
const endReason = mapProviderStatusToEndReason(callStatus);
|
|
3084
|
-
if (endReason) {
|
|
3085
|
-
this.streamAuthTokens.delete(callSid);
|
|
3086
|
-
this.activeStreamCalls.delete(callSid);
|
|
3087
|
-
if (callIdOverride) this.deleteStoredTwiml(callIdOverride);
|
|
3088
|
-
return {
|
|
3089
|
-
...baseEvent,
|
|
3090
|
-
type: "call.ended",
|
|
3091
|
-
reason: endReason
|
|
3092
|
-
};
|
|
3093
|
-
}
|
|
3094
|
-
return null;
|
|
3095
|
-
}
|
|
3096
|
-
static {
|
|
3097
|
-
this.EMPTY_TWIML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Response></Response>";
|
|
3098
|
-
}
|
|
3099
|
-
static {
|
|
3100
|
-
this.PAUSE_TWIML = `<?xml version="1.0" encoding="UTF-8"?>
|
|
3101
|
-
<Response>
|
|
3102
|
-
<Pause length="30"/>
|
|
3103
|
-
</Response>`;
|
|
3104
|
-
}
|
|
3105
|
-
static {
|
|
3106
|
-
this.QUEUE_TWIML = `<?xml version="1.0" encoding="UTF-8"?>
|
|
3107
|
-
<Response>
|
|
3108
|
-
<Say voice="alice">Please hold while we connect you.</Say>
|
|
3109
|
-
<Enqueue waitUrl="/voice/hold-music">hold-queue</Enqueue>
|
|
3110
|
-
</Response>`;
|
|
3111
|
-
}
|
|
3112
|
-
/**
|
|
3113
|
-
* Generate TwiML response for webhook.
|
|
3114
|
-
* When a call is answered, connects to media stream for bidirectional audio.
|
|
3115
|
-
*/
|
|
3116
|
-
generateTwimlResponse(ctx) {
|
|
3117
|
-
if (!ctx) return TwilioProvider.EMPTY_TWIML;
|
|
3118
|
-
const view = readTwimlRequestView(ctx);
|
|
3119
|
-
const storedTwiml = view.callIdFromQuery ? this.twimlStorage.get(view.callIdFromQuery) : void 0;
|
|
3120
|
-
const decision = decideTwimlResponse({
|
|
3121
|
-
...view,
|
|
3122
|
-
hasStoredTwiml: Boolean(storedTwiml),
|
|
3123
|
-
isNotifyCall: view.callIdFromQuery ? this.notifyCalls.has(view.callIdFromQuery) : false,
|
|
3124
|
-
hasActiveStreams: this.activeStreamCalls.size > 0,
|
|
3125
|
-
canStream: Boolean(view.callSid && this.getStreamUrl())
|
|
3126
|
-
});
|
|
3127
|
-
if (decision.consumeStoredTwimlCallId) this.deleteStoredTwiml(decision.consumeStoredTwimlCallId);
|
|
3128
|
-
if (decision.activateStreamCallSid) this.activeStreamCalls.add(decision.activateStreamCallSid);
|
|
3129
|
-
switch (decision.kind) {
|
|
3130
|
-
case "stored": return storedTwiml ?? TwilioProvider.EMPTY_TWIML;
|
|
3131
|
-
case "queue": return TwilioProvider.QUEUE_TWIML;
|
|
3132
|
-
case "pause": return TwilioProvider.PAUSE_TWIML;
|
|
3133
|
-
case "stream": {
|
|
3134
|
-
const streamUrl = view.callSid ? this.getStreamUrlForCall(view.callSid) : null;
|
|
3135
|
-
return streamUrl ? this.getStreamConnectXml(streamUrl) : TwilioProvider.PAUSE_TWIML;
|
|
3136
|
-
}
|
|
3137
|
-
default: return TwilioProvider.EMPTY_TWIML;
|
|
3138
|
-
}
|
|
3139
|
-
}
|
|
3140
|
-
/**
|
|
3141
|
-
* Get the WebSocket URL for media streaming.
|
|
3142
|
-
* Derives from the public URL origin + stream path.
|
|
3143
|
-
*/
|
|
3144
|
-
getStreamUrl() {
|
|
3145
|
-
if (!this.currentPublicUrl || !this.options.streamPath) return null;
|
|
3146
|
-
return `${new URL(this.currentPublicUrl).origin.replace(/^https:\/\//, "wss://").replace(/^http:\/\//, "ws://")}${this.options.streamPath.startsWith("/") ? this.options.streamPath : `/${this.options.streamPath}`}`;
|
|
3147
|
-
}
|
|
3148
|
-
getStreamAuthToken(callSid) {
|
|
3149
|
-
const existing = this.streamAuthTokens.get(callSid);
|
|
3150
|
-
if (existing) return existing;
|
|
3151
|
-
const token = crypto.randomBytes(16).toString("base64url");
|
|
3152
|
-
this.streamAuthTokens.set(callSid, token);
|
|
3153
|
-
return token;
|
|
3154
|
-
}
|
|
3155
|
-
getStreamUrlForCall(callSid) {
|
|
3156
|
-
const baseUrl = this.getStreamUrl();
|
|
3157
|
-
if (!baseUrl) return null;
|
|
3158
|
-
const token = this.getStreamAuthToken(callSid);
|
|
3159
|
-
const url = new URL(baseUrl);
|
|
3160
|
-
url.searchParams.set("token", token);
|
|
3161
|
-
return url.toString();
|
|
3162
|
-
}
|
|
3163
|
-
/**
|
|
3164
|
-
* Generate TwiML to connect a call to a WebSocket media stream.
|
|
3165
|
-
* This enables bidirectional audio streaming for real-time STT/TTS.
|
|
3166
|
-
*
|
|
3167
|
-
* @param streamUrl - WebSocket URL (wss://...) for the media stream
|
|
3168
|
-
*/
|
|
3169
|
-
getStreamConnectXml(streamUrl) {
|
|
3170
|
-
const parsed = new URL(streamUrl);
|
|
3171
|
-
const token = parsed.searchParams.get("token");
|
|
3172
|
-
parsed.searchParams.delete("token");
|
|
3173
|
-
const cleanUrl = parsed.toString();
|
|
3174
|
-
const paramXml = token ? `\n <Parameter name="token" value="${escapeXml(token)}" />` : "";
|
|
3175
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
3176
|
-
<Response>
|
|
3177
|
-
<Connect>
|
|
3178
|
-
<Stream url="${escapeXml(cleanUrl)}">${paramXml}
|
|
3179
|
-
</Stream>
|
|
3180
|
-
</Connect>
|
|
3181
|
-
</Response>`;
|
|
3182
|
-
}
|
|
3183
|
-
/**
|
|
3184
|
-
* Initiate an outbound call via Twilio API.
|
|
3185
|
-
* If inlineTwiml is provided, uses that directly (for notify mode).
|
|
3186
|
-
* Otherwise, uses webhook URL for dynamic TwiML.
|
|
3187
|
-
*/
|
|
3188
|
-
async initiateCall(input) {
|
|
3189
|
-
const url = new URL(input.webhookUrl);
|
|
3190
|
-
url.searchParams.set("callId", input.callId);
|
|
3191
|
-
const statusUrl = new URL(input.webhookUrl);
|
|
3192
|
-
statusUrl.searchParams.set("callId", input.callId);
|
|
3193
|
-
statusUrl.searchParams.set("type", "status");
|
|
3194
|
-
if (input.inlineTwiml) {
|
|
3195
|
-
this.twimlStorage.set(input.callId, input.inlineTwiml);
|
|
3196
|
-
this.notifyCalls.add(input.callId);
|
|
3197
|
-
}
|
|
3198
|
-
const params = {
|
|
3199
|
-
To: input.to,
|
|
3200
|
-
From: input.from,
|
|
3201
|
-
Url: url.toString(),
|
|
3202
|
-
StatusCallback: statusUrl.toString(),
|
|
3203
|
-
StatusCallbackEvent: [
|
|
3204
|
-
"initiated",
|
|
3205
|
-
"ringing",
|
|
3206
|
-
"answered",
|
|
3207
|
-
"completed"
|
|
3208
|
-
],
|
|
3209
|
-
Timeout: "30"
|
|
3210
|
-
};
|
|
3211
|
-
const result = await this.apiRequest("/Calls.json", params);
|
|
3212
|
-
this.callWebhookUrls.set(result.sid, url.toString());
|
|
3213
|
-
return {
|
|
3214
|
-
providerCallId: result.sid,
|
|
3215
|
-
status: result.status === "queued" ? "queued" : "initiated"
|
|
3216
|
-
};
|
|
3217
|
-
}
|
|
3218
|
-
/**
|
|
3219
|
-
* Hang up a call via Twilio API.
|
|
3220
|
-
*/
|
|
3221
|
-
async hangupCall(input) {
|
|
3222
|
-
this.deleteStoredTwimlForProviderCall(input.providerCallId);
|
|
3223
|
-
this.callWebhookUrls.delete(input.providerCallId);
|
|
3224
|
-
this.streamAuthTokens.delete(input.providerCallId);
|
|
3225
|
-
this.activeStreamCalls.delete(input.providerCallId);
|
|
3226
|
-
await this.apiRequest(`/Calls/${input.providerCallId}.json`, { Status: "completed" }, { allowNotFound: true });
|
|
3227
|
-
}
|
|
3228
|
-
/**
|
|
3229
|
-
* Play TTS audio via Twilio.
|
|
3230
|
-
*
|
|
3231
|
-
* Two modes:
|
|
3232
|
-
* 1. Core TTS + Media Streams: when an active stream exists, stream playback is required.
|
|
3233
|
-
* If telephony TTS is unavailable in that state, playback fails rather than mixing paths.
|
|
3234
|
-
* 2. TwiML <Say>: fallback only when there is no active stream for the call.
|
|
3235
|
-
*/
|
|
3236
|
-
async playTts(input) {
|
|
3237
|
-
const streamSid = this.callStreamMap.get(input.providerCallId);
|
|
3238
|
-
if (streamSid) {
|
|
3239
|
-
if (!this.ttsProvider || !this.mediaStreamHandler) throw new Error("Telephony TTS unavailable while media stream is active; refusing TwiML fallback");
|
|
3240
|
-
try {
|
|
3241
|
-
await this.playTtsViaStream(input.text, streamSid);
|
|
3242
|
-
return;
|
|
3243
|
-
} catch (err) {
|
|
3244
|
-
console.warn(`[voice-call] Telephony TTS failed:`, err instanceof Error ? err.message : err);
|
|
3245
|
-
throw err instanceof Error ? err : new Error(String(err));
|
|
3246
|
-
}
|
|
3247
|
-
}
|
|
3248
|
-
const webhookUrl = this.callWebhookUrls.get(input.providerCallId);
|
|
3249
|
-
if (!webhookUrl) throw new Error("Missing webhook URL for this call (provider state not initialized)");
|
|
3250
|
-
console.warn("[voice-call] Using TwiML <Say> fallback - telephony TTS not configured or media stream not active");
|
|
3251
|
-
const twiml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
3252
|
-
<Response>
|
|
3253
|
-
<Say voice="${mapVoiceToPolly(input.voice)}" language="${input.locale || "en-US"}">${escapeXml(input.text)}</Say>
|
|
3254
|
-
<Gather input="speech" speechTimeout="auto" action="${escapeXml(webhookUrl)}" method="POST">
|
|
3255
|
-
<Say>.</Say>
|
|
3256
|
-
</Gather>
|
|
3257
|
-
</Response>`;
|
|
3258
|
-
await this.apiRequest(`/Calls/${input.providerCallId}.json`, { Twiml: twiml });
|
|
3259
|
-
}
|
|
3260
|
-
/**
|
|
3261
|
-
* Play TTS via core TTS and Twilio Media Streams.
|
|
3262
|
-
* Generates audio with core TTS, converts to mu-law, and streams via WebSocket.
|
|
3263
|
-
* Uses a queue to serialize playback and prevent overlapping audio.
|
|
3264
|
-
*/
|
|
3265
|
-
async playTtsViaStream(text, streamSid) {
|
|
3266
|
-
if (!this.ttsProvider || !this.mediaStreamHandler) throw new Error("TTS provider and media stream handler required");
|
|
3267
|
-
const CHUNK_SIZE = 160;
|
|
3268
|
-
const CHUNK_DELAY_MS = 20;
|
|
3269
|
-
const SILENCE_CHUNK = Buffer.alloc(CHUNK_SIZE, 255);
|
|
3270
|
-
const handler = this.mediaStreamHandler;
|
|
3271
|
-
const ttsProvider = this.ttsProvider;
|
|
3272
|
-
const normalizeSendResult = (raw) => {
|
|
3273
|
-
if (!raw || typeof raw !== "object") return { sent: true };
|
|
3274
|
-
const typed = raw;
|
|
3275
|
-
return { sent: typed.sent === void 0 ? true : Boolean(typed.sent) };
|
|
3276
|
-
};
|
|
3277
|
-
const sendAudioChunk = (audio) => {
|
|
3278
|
-
return normalizeSendResult(handler.sendAudio(streamSid, audio));
|
|
3279
|
-
};
|
|
3280
|
-
const sendPlaybackMark = (name) => {
|
|
3281
|
-
return normalizeSendResult(handler.sendMark(streamSid, name));
|
|
3282
|
-
};
|
|
3283
|
-
await handler.queueTts(streamSid, async (signal) => {
|
|
3284
|
-
const sendKeepAlive = () => {
|
|
3285
|
-
sendAudioChunk(SILENCE_CHUNK);
|
|
3286
|
-
};
|
|
3287
|
-
sendKeepAlive();
|
|
3288
|
-
const keepAlive = setInterval(() => {
|
|
3289
|
-
if (!signal.aborted) sendKeepAlive();
|
|
3290
|
-
}, CHUNK_DELAY_MS);
|
|
3291
|
-
let muLawAudio;
|
|
3292
|
-
let synthTimeout = null;
|
|
3293
|
-
try {
|
|
3294
|
-
const synthPromise = ttsProvider.synthesizeForTelephony(text);
|
|
3295
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
3296
|
-
synthTimeout = setTimeout(() => {
|
|
3297
|
-
reject(/* @__PURE__ */ new Error(`Telephony TTS synthesis timed out after ${TwilioProvider.TTS_SYNTH_TIMEOUT_MS}ms`));
|
|
3298
|
-
}, TwilioProvider.TTS_SYNTH_TIMEOUT_MS);
|
|
3299
|
-
});
|
|
3300
|
-
muLawAudio = await Promise.race([synthPromise, timeoutPromise]);
|
|
3301
|
-
} finally {
|
|
3302
|
-
if (synthTimeout) clearTimeout(synthTimeout);
|
|
3303
|
-
clearInterval(keepAlive);
|
|
3304
|
-
}
|
|
3305
|
-
let chunkAttempts = 0;
|
|
3306
|
-
let chunkDelivered = 0;
|
|
3307
|
-
let nextChunkDueAt = Date.now() + CHUNK_DELAY_MS;
|
|
3308
|
-
for (const chunk of chunkAudio(muLawAudio, CHUNK_SIZE)) {
|
|
3309
|
-
if (signal.aborted) break;
|
|
3310
|
-
chunkAttempts += 1;
|
|
3311
|
-
if (sendAudioChunk(chunk).sent) chunkDelivered += 1;
|
|
3312
|
-
const waitMs = nextChunkDueAt - Date.now();
|
|
3313
|
-
if (waitMs > 0) await new Promise((resolve) => setTimeout(resolve, Math.ceil(waitMs)));
|
|
3314
|
-
nextChunkDueAt += CHUNK_DELAY_MS;
|
|
3315
|
-
if (signal.aborted) break;
|
|
3316
|
-
}
|
|
3317
|
-
let markSent = true;
|
|
3318
|
-
if (!signal.aborted) markSent = sendPlaybackMark(`tts-${Date.now()}`).sent;
|
|
3319
|
-
if (!signal.aborted && chunkAttempts > 0 && (chunkDelivered === 0 || !markSent)) {
|
|
3320
|
-
const failures = [];
|
|
3321
|
-
if (chunkDelivered === 0) failures.push("no audio chunks delivered");
|
|
3322
|
-
if (!markSent) failures.push("completion mark not delivered");
|
|
3323
|
-
throw new Error(`Telephony stream playback failed: ${failures.join("; ")}`);
|
|
3324
|
-
}
|
|
3325
|
-
});
|
|
3326
|
-
}
|
|
3327
|
-
/**
|
|
3328
|
-
* Start listening for speech via Twilio <Gather>.
|
|
3329
|
-
*/
|
|
3330
|
-
async startListening(input) {
|
|
3331
|
-
const webhookUrl = this.callWebhookUrls.get(input.providerCallId);
|
|
3332
|
-
if (!webhookUrl) throw new Error("Missing webhook URL for this call (provider state not initialized)");
|
|
3333
|
-
const actionUrl = new URL(webhookUrl);
|
|
3334
|
-
if (input.turnToken) actionUrl.searchParams.set("turnToken", input.turnToken);
|
|
3335
|
-
const twiml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
3336
|
-
<Response>
|
|
3337
|
-
<Gather input="speech" speechTimeout="auto" language="${input.language || "en-US"}" action="${escapeXml(actionUrl.toString())}" method="POST">
|
|
3338
|
-
</Gather>
|
|
3339
|
-
</Response>`;
|
|
3340
|
-
await this.apiRequest(`/Calls/${input.providerCallId}.json`, { Twiml: twiml });
|
|
3341
|
-
}
|
|
3342
|
-
/**
|
|
3343
|
-
* Stop listening - for Twilio this is a no-op as <Gather> auto-ends.
|
|
3344
|
-
*/
|
|
3345
|
-
async stopListening(_input) {}
|
|
3346
|
-
async getCallStatus(input) {
|
|
3347
|
-
try {
|
|
3348
|
-
const data = await guardedJsonApiRequest({
|
|
3349
|
-
url: `${this.baseUrl}/Calls/${input.providerCallId}.json`,
|
|
3350
|
-
method: "GET",
|
|
3351
|
-
headers: { Authorization: `Basic ${Buffer.from(`${this.accountSid}:${this.authToken}`).toString("base64")}` },
|
|
3352
|
-
allowNotFound: true,
|
|
3353
|
-
allowedHostnames: ["api.twilio.com"],
|
|
3354
|
-
auditContext: "twilio-get-call-status",
|
|
3355
|
-
errorPrefix: "Twilio get call status error"
|
|
3356
|
-
});
|
|
3357
|
-
if (!data) return {
|
|
3358
|
-
status: "not-found",
|
|
3359
|
-
isTerminal: true
|
|
3360
|
-
};
|
|
3361
|
-
const status = normalizeProviderStatus(data.status);
|
|
3362
|
-
return {
|
|
3363
|
-
status,
|
|
3364
|
-
isTerminal: isProviderStatusTerminal(status)
|
|
3365
|
-
};
|
|
3366
|
-
} catch {
|
|
3367
|
-
return {
|
|
3368
|
-
status: "error",
|
|
3369
|
-
isTerminal: false,
|
|
3370
|
-
isUnknown: true
|
|
3371
|
-
};
|
|
3372
|
-
}
|
|
3373
|
-
}
|
|
3374
|
-
};
|
|
3375
|
-
//#endregion
|
|
3376
|
-
//#region extensions/voice-call/src/telephony-tts.ts
|
|
3377
|
-
function createTelephonyTtsProvider(params) {
|
|
3378
|
-
const { coreConfig, ttsOverride, runtime, logger } = params;
|
|
3379
|
-
const mergedConfig = applyTtsOverride(coreConfig, ttsOverride);
|
|
3380
|
-
return { synthesizeForTelephony: async (text) => {
|
|
3381
|
-
const result = await runtime.textToSpeechTelephony({
|
|
3382
|
-
text,
|
|
3383
|
-
cfg: mergedConfig
|
|
3384
|
-
});
|
|
3385
|
-
if (!result.success || !result.audioBuffer || !result.sampleRate) throw new Error(result.error ?? "TTS conversion failed");
|
|
3386
|
-
if (result.fallbackFrom && result.provider && result.fallbackFrom !== result.provider) {
|
|
3387
|
-
const attemptedChain = result.attemptedProviders && result.attemptedProviders.length > 0 ? result.attemptedProviders.join(" -> ") : `${result.fallbackFrom} -> ${result.provider}`;
|
|
3388
|
-
logger?.warn?.(`[voice-call] Telephony TTS fallback used from=${result.fallbackFrom} to=${result.provider} attempts=${attemptedChain}`);
|
|
3389
|
-
}
|
|
3390
|
-
return convertPcmToMulaw8k(result.audioBuffer, result.sampleRate);
|
|
3391
|
-
} };
|
|
3392
|
-
}
|
|
3393
|
-
function applyTtsOverride(coreConfig, override) {
|
|
3394
|
-
if (!override) return coreConfig;
|
|
3395
|
-
const base = coreConfig.messages?.tts;
|
|
3396
|
-
const merged = mergeTtsConfig(base, override);
|
|
3397
|
-
if (!merged) return coreConfig;
|
|
3398
|
-
return {
|
|
3399
|
-
...coreConfig,
|
|
3400
|
-
messages: {
|
|
3401
|
-
...coreConfig.messages,
|
|
3402
|
-
tts: merged
|
|
3403
|
-
}
|
|
3404
|
-
};
|
|
3405
|
-
}
|
|
3406
|
-
function mergeTtsConfig(base, override) {
|
|
3407
|
-
if (!base && !override) return;
|
|
3408
|
-
if (!override) return base;
|
|
3409
|
-
if (!base) return override;
|
|
3410
|
-
return deepMergeDefined(base, override);
|
|
3411
|
-
}
|
|
3412
|
-
//#endregion
|
|
3413
|
-
//#region extensions/voice-call/src/webhook/tailscale.ts
|
|
3414
|
-
function runTailscaleCommand(args, timeoutMs = 2500) {
|
|
3415
|
-
return new Promise((resolve) => {
|
|
3416
|
-
const proc = spawn("tailscale", args, { stdio: [
|
|
3417
|
-
"ignore",
|
|
3418
|
-
"pipe",
|
|
3419
|
-
"pipe"
|
|
3420
|
-
] });
|
|
3421
|
-
let stdout = "";
|
|
3422
|
-
proc.stdout.on("data", (data) => {
|
|
3423
|
-
stdout += data;
|
|
3424
|
-
});
|
|
3425
|
-
const timer = setTimeout(() => {
|
|
3426
|
-
proc.kill("SIGKILL");
|
|
3427
|
-
resolve({
|
|
3428
|
-
code: -1,
|
|
3429
|
-
stdout: ""
|
|
3430
|
-
});
|
|
3431
|
-
}, timeoutMs);
|
|
3432
|
-
proc.on("close", (code) => {
|
|
3433
|
-
clearTimeout(timer);
|
|
3434
|
-
resolve({
|
|
3435
|
-
code: code ?? -1,
|
|
3436
|
-
stdout
|
|
3437
|
-
});
|
|
3438
|
-
});
|
|
3439
|
-
});
|
|
3440
|
-
}
|
|
3441
|
-
async function getTailscaleSelfInfo() {
|
|
3442
|
-
const { code, stdout } = await runTailscaleCommand(["status", "--json"]);
|
|
3443
|
-
if (code !== 0) return null;
|
|
3444
|
-
try {
|
|
3445
|
-
const status = JSON.parse(stdout);
|
|
3446
|
-
return {
|
|
3447
|
-
dnsName: status.Self?.DNSName?.replace(/\.$/, "") || null,
|
|
3448
|
-
nodeId: status.Self?.ID || null
|
|
3449
|
-
};
|
|
3450
|
-
} catch {
|
|
3451
|
-
return null;
|
|
3452
|
-
}
|
|
3453
|
-
}
|
|
3454
|
-
async function getTailscaleDnsName() {
|
|
3455
|
-
return (await getTailscaleSelfInfo())?.dnsName ?? null;
|
|
3456
|
-
}
|
|
3457
|
-
async function setupTailscaleExposureRoute(opts) {
|
|
3458
|
-
const dnsName = await getTailscaleDnsName();
|
|
3459
|
-
if (!dnsName) {
|
|
3460
|
-
console.warn("[voice-call] Could not get Tailscale DNS name");
|
|
3461
|
-
return null;
|
|
3462
|
-
}
|
|
3463
|
-
const { code } = await runTailscaleCommand([
|
|
3464
|
-
opts.mode,
|
|
3465
|
-
"--bg",
|
|
3466
|
-
"--yes",
|
|
3467
|
-
"--set-path",
|
|
3468
|
-
opts.path,
|
|
3469
|
-
opts.localUrl
|
|
3470
|
-
]);
|
|
3471
|
-
if (code === 0) {
|
|
3472
|
-
const publicUrl = `https://${dnsName}${opts.path}`;
|
|
3473
|
-
console.log(`[voice-call] Tailscale ${opts.mode} active: ${publicUrl}`);
|
|
3474
|
-
return publicUrl;
|
|
3475
|
-
}
|
|
3476
|
-
console.warn(`[voice-call] Tailscale ${opts.mode} failed`);
|
|
3477
|
-
return null;
|
|
3478
|
-
}
|
|
3479
|
-
async function cleanupTailscaleExposureRoute(opts) {
|
|
3480
|
-
await runTailscaleCommand([
|
|
3481
|
-
opts.mode,
|
|
3482
|
-
"off",
|
|
3483
|
-
opts.path
|
|
3484
|
-
]);
|
|
3485
|
-
}
|
|
3486
|
-
async function setupTailscaleExposure(config) {
|
|
3487
|
-
if (config.tailscale.mode === "off") return null;
|
|
3488
|
-
const mode = config.tailscale.mode === "funnel" ? "funnel" : "serve";
|
|
3489
|
-
const localUrl = `http://127.0.0.1:${config.serve.port}${config.serve.path}`;
|
|
3490
|
-
return setupTailscaleExposureRoute({
|
|
3491
|
-
mode,
|
|
3492
|
-
path: config.tailscale.path,
|
|
3493
|
-
localUrl
|
|
3494
|
-
});
|
|
3495
|
-
}
|
|
3496
|
-
async function cleanupTailscaleExposure(config) {
|
|
3497
|
-
if (config.tailscale.mode === "off") return;
|
|
3498
|
-
await cleanupTailscaleExposureRoute({
|
|
3499
|
-
mode: config.tailscale.mode === "funnel" ? "funnel" : "serve",
|
|
3500
|
-
path: config.tailscale.path
|
|
3501
|
-
});
|
|
3502
|
-
}
|
|
3503
|
-
//#endregion
|
|
3504
|
-
//#region extensions/voice-call/src/tunnel.ts
|
|
3505
|
-
/**
|
|
3506
|
-
* Start an ngrok tunnel to expose the local webhook server.
|
|
3507
|
-
*
|
|
3508
|
-
* Uses the ngrok CLI which must be installed: https://ngrok.com/download
|
|
3509
|
-
*
|
|
3510
|
-
* @example
|
|
3511
|
-
* const tunnel = await startNgrokTunnel({ port: 3334, path: '/voice/webhook' });
|
|
3512
|
-
* console.log('Public URL:', tunnel.publicUrl);
|
|
3513
|
-
* // Later: await tunnel.stop();
|
|
3514
|
-
*/
|
|
3515
|
-
async function startNgrokTunnel(config) {
|
|
3516
|
-
if (config.authToken) await runNgrokCommand([
|
|
3517
|
-
"config",
|
|
3518
|
-
"add-authtoken",
|
|
3519
|
-
config.authToken
|
|
3520
|
-
]);
|
|
3521
|
-
const args = [
|
|
3522
|
-
"http",
|
|
3523
|
-
String(config.port),
|
|
3524
|
-
"--log",
|
|
3525
|
-
"stdout",
|
|
3526
|
-
"--log-format",
|
|
3527
|
-
"json"
|
|
3528
|
-
];
|
|
3529
|
-
if (config.domain) args.push("--domain", config.domain);
|
|
3530
|
-
return new Promise((resolve, reject) => {
|
|
3531
|
-
const proc = spawn("ngrok", args, { stdio: [
|
|
3532
|
-
"ignore",
|
|
3533
|
-
"pipe",
|
|
3534
|
-
"pipe"
|
|
3535
|
-
] });
|
|
3536
|
-
let resolved = false;
|
|
3537
|
-
let publicUrl = null;
|
|
3538
|
-
let outputBuffer = "";
|
|
3539
|
-
const timeout = setTimeout(() => {
|
|
3540
|
-
if (!resolved) {
|
|
3541
|
-
resolved = true;
|
|
3542
|
-
proc.kill("SIGTERM");
|
|
3543
|
-
reject(/* @__PURE__ */ new Error("ngrok startup timed out (30s)"));
|
|
3544
|
-
}
|
|
3545
|
-
}, 3e4);
|
|
3546
|
-
const processLine = (line) => {
|
|
3547
|
-
try {
|
|
3548
|
-
const log = JSON.parse(line);
|
|
3549
|
-
if (log.msg === "started tunnel" && log.url) publicUrl = log.url;
|
|
3550
|
-
if (log.addr && log.url && !publicUrl) publicUrl = log.url;
|
|
3551
|
-
if (publicUrl && !resolved) {
|
|
3552
|
-
resolved = true;
|
|
3553
|
-
clearTimeout(timeout);
|
|
3554
|
-
const fullUrl = publicUrl + config.path;
|
|
3555
|
-
console.log(`[voice-call] ngrok tunnel active: ${fullUrl}`);
|
|
3556
|
-
resolve({
|
|
3557
|
-
publicUrl: fullUrl,
|
|
3558
|
-
provider: "ngrok",
|
|
3559
|
-
stop: async () => {
|
|
3560
|
-
proc.kill("SIGTERM");
|
|
3561
|
-
await new Promise((res) => {
|
|
3562
|
-
proc.on("close", () => res());
|
|
3563
|
-
setTimeout(res, 2e3);
|
|
3564
|
-
});
|
|
3565
|
-
}
|
|
3566
|
-
});
|
|
3567
|
-
}
|
|
3568
|
-
} catch {}
|
|
3569
|
-
};
|
|
3570
|
-
proc.stdout.on("data", (data) => {
|
|
3571
|
-
outputBuffer += data.toString();
|
|
3572
|
-
const lines = outputBuffer.split("\n");
|
|
3573
|
-
outputBuffer = lines.pop() || "";
|
|
3574
|
-
for (const line of lines) if (line.trim()) processLine(line);
|
|
3575
|
-
});
|
|
3576
|
-
proc.stderr.on("data", (data) => {
|
|
3577
|
-
const msg = data.toString();
|
|
3578
|
-
if (msg.includes("ERR_NGROK")) {
|
|
3579
|
-
if (!resolved) {
|
|
3580
|
-
resolved = true;
|
|
3581
|
-
clearTimeout(timeout);
|
|
3582
|
-
reject(/* @__PURE__ */ new Error(`ngrok error: ${msg}`));
|
|
3583
|
-
}
|
|
3584
|
-
}
|
|
3585
|
-
});
|
|
3586
|
-
proc.on("error", (err) => {
|
|
3587
|
-
if (!resolved) {
|
|
3588
|
-
resolved = true;
|
|
3589
|
-
clearTimeout(timeout);
|
|
3590
|
-
reject(/* @__PURE__ */ new Error(`Failed to start ngrok: ${err.message}`));
|
|
3591
|
-
}
|
|
3592
|
-
});
|
|
3593
|
-
proc.on("close", (code) => {
|
|
3594
|
-
if (!resolved) {
|
|
3595
|
-
resolved = true;
|
|
3596
|
-
clearTimeout(timeout);
|
|
3597
|
-
reject(/* @__PURE__ */ new Error(`ngrok exited unexpectedly with code ${code}`));
|
|
3598
|
-
}
|
|
3599
|
-
});
|
|
3600
|
-
});
|
|
3601
|
-
}
|
|
3602
|
-
/**
|
|
3603
|
-
* Run an ngrok command and wait for completion.
|
|
3604
|
-
*/
|
|
3605
|
-
async function runNgrokCommand(args) {
|
|
3606
|
-
return new Promise((resolve, reject) => {
|
|
3607
|
-
const proc = spawn("ngrok", args, { stdio: [
|
|
3608
|
-
"ignore",
|
|
3609
|
-
"pipe",
|
|
3610
|
-
"pipe"
|
|
3611
|
-
] });
|
|
3612
|
-
let stdout = "";
|
|
3613
|
-
let stderr = "";
|
|
3614
|
-
proc.stdout.on("data", (data) => {
|
|
3615
|
-
stdout += data.toString();
|
|
3616
|
-
});
|
|
3617
|
-
proc.stderr.on("data", (data) => {
|
|
3618
|
-
stderr += data.toString();
|
|
3619
|
-
});
|
|
3620
|
-
proc.on("close", (code) => {
|
|
3621
|
-
if (code === 0) resolve(stdout);
|
|
3622
|
-
else reject(/* @__PURE__ */ new Error(`ngrok command failed: ${stderr || stdout}`));
|
|
3623
|
-
});
|
|
3624
|
-
proc.on("error", reject);
|
|
3625
|
-
});
|
|
3626
|
-
}
|
|
3627
|
-
/**
|
|
3628
|
-
* Start a Tailscale serve/funnel tunnel.
|
|
3629
|
-
*/
|
|
3630
|
-
async function startTailscaleTunnel(config) {
|
|
3631
|
-
const dnsName = await getTailscaleDnsName();
|
|
3632
|
-
if (!dnsName) throw new Error("Could not get Tailscale DNS name. Is Tailscale running?");
|
|
3633
|
-
const path = config.path.startsWith("/") ? config.path : `/${config.path}`;
|
|
3634
|
-
const localUrl = `http://127.0.0.1:${config.port}${path}`;
|
|
3635
|
-
return new Promise((resolve, reject) => {
|
|
3636
|
-
const proc = spawn("tailscale", [
|
|
3637
|
-
config.mode,
|
|
3638
|
-
"--bg",
|
|
3639
|
-
"--yes",
|
|
3640
|
-
"--set-path",
|
|
3641
|
-
path,
|
|
3642
|
-
localUrl
|
|
3643
|
-
], { stdio: [
|
|
3644
|
-
"ignore",
|
|
3645
|
-
"pipe",
|
|
3646
|
-
"pipe"
|
|
3647
|
-
] });
|
|
3648
|
-
const timeout = setTimeout(() => {
|
|
3649
|
-
proc.kill("SIGKILL");
|
|
3650
|
-
reject(/* @__PURE__ */ new Error(`Tailscale ${config.mode} timed out`));
|
|
3651
|
-
}, 1e4);
|
|
3652
|
-
proc.on("close", (code) => {
|
|
3653
|
-
clearTimeout(timeout);
|
|
3654
|
-
if (code === 0) {
|
|
3655
|
-
const publicUrl = `https://${dnsName}${path}`;
|
|
3656
|
-
console.log(`[voice-call] Tailscale ${config.mode} active: ${publicUrl}`);
|
|
3657
|
-
resolve({
|
|
3658
|
-
publicUrl,
|
|
3659
|
-
provider: `tailscale-${config.mode}`,
|
|
3660
|
-
stop: async () => {
|
|
3661
|
-
await stopTailscaleTunnel(config.mode, path);
|
|
3662
|
-
}
|
|
3663
|
-
});
|
|
3664
|
-
} else reject(/* @__PURE__ */ new Error(`Tailscale ${config.mode} failed with code ${code}`));
|
|
3665
|
-
});
|
|
3666
|
-
proc.on("error", (err) => {
|
|
3667
|
-
clearTimeout(timeout);
|
|
3668
|
-
reject(err);
|
|
3669
|
-
});
|
|
3670
|
-
});
|
|
3671
|
-
}
|
|
3672
|
-
/**
|
|
3673
|
-
* Stop a Tailscale serve/funnel tunnel.
|
|
3674
|
-
*/
|
|
3675
|
-
async function stopTailscaleTunnel(mode, path) {
|
|
3676
|
-
return new Promise((resolve) => {
|
|
3677
|
-
const proc = spawn("tailscale", [
|
|
3678
|
-
mode,
|
|
3679
|
-
"off",
|
|
3680
|
-
path
|
|
3681
|
-
], { stdio: "ignore" });
|
|
3682
|
-
const timeout = setTimeout(() => {
|
|
3683
|
-
proc.kill("SIGKILL");
|
|
3684
|
-
resolve();
|
|
3685
|
-
}, 5e3);
|
|
3686
|
-
proc.on("close", () => {
|
|
3687
|
-
clearTimeout(timeout);
|
|
3688
|
-
resolve();
|
|
3689
|
-
});
|
|
3690
|
-
});
|
|
3691
|
-
}
|
|
3692
|
-
/**
|
|
3693
|
-
* Start a tunnel based on configuration.
|
|
3694
|
-
*/
|
|
3695
|
-
async function startTunnel(config) {
|
|
3696
|
-
switch (config.provider) {
|
|
3697
|
-
case "ngrok": return startNgrokTunnel({
|
|
3698
|
-
port: config.port,
|
|
3699
|
-
path: config.path,
|
|
3700
|
-
authToken: config.ngrokAuthToken,
|
|
3701
|
-
domain: config.ngrokDomain
|
|
3702
|
-
});
|
|
3703
|
-
case "tailscale-serve": return startTailscaleTunnel({
|
|
3704
|
-
mode: "serve",
|
|
3705
|
-
port: config.port,
|
|
3706
|
-
path: config.path
|
|
3707
|
-
});
|
|
3708
|
-
case "tailscale-funnel": return startTailscaleTunnel({
|
|
3709
|
-
mode: "funnel",
|
|
3710
|
-
port: config.port,
|
|
3711
|
-
path: config.path
|
|
3712
|
-
});
|
|
3713
|
-
default: return null;
|
|
3714
|
-
}
|
|
3715
|
-
}
|
|
3716
|
-
//#endregion
|
|
3717
|
-
//#region extensions/voice-call/src/media-stream.ts
|
|
3718
|
-
const DEFAULT_PRE_START_TIMEOUT_MS = 5e3;
|
|
3719
|
-
const DEFAULT_MAX_PENDING_CONNECTIONS = 32;
|
|
3720
|
-
const DEFAULT_MAX_PENDING_CONNECTIONS_PER_IP = 4;
|
|
3721
|
-
const DEFAULT_MAX_CONNECTIONS = 128;
|
|
3722
|
-
const MAX_INBOUND_MESSAGE_BYTES = 64 * 1024;
|
|
3723
|
-
const MAX_WS_BUFFERED_BYTES = 1024 * 1024;
|
|
3724
|
-
const CLOSE_REASON_LOG_MAX_CHARS = 120;
|
|
3725
|
-
function sanitizeLogText(value, maxChars) {
|
|
3726
|
-
const sanitized = value.replace(/[\u0000-\u001f\u007f]/g, " ").replace(/\s+/g, " ").trim();
|
|
3727
|
-
if (sanitized.length <= maxChars) return sanitized;
|
|
3728
|
-
return `${sanitized.slice(0, maxChars)}...`;
|
|
3729
|
-
}
|
|
3730
|
-
function normalizeWsMessageData(data) {
|
|
3731
|
-
if (Buffer.isBuffer(data)) return data;
|
|
3732
|
-
if (Array.isArray(data)) return Buffer.concat(data);
|
|
3733
|
-
return Buffer.from(data);
|
|
3734
|
-
}
|
|
3735
|
-
/**
|
|
3736
|
-
* Manages WebSocket connections for Twilio media streams.
|
|
3737
|
-
*/
|
|
3738
|
-
var MediaStreamHandler = class {
|
|
3739
|
-
constructor(config) {
|
|
3740
|
-
this.wss = null;
|
|
3741
|
-
this.sessions = /* @__PURE__ */ new Map();
|
|
3742
|
-
this.pendingConnections = /* @__PURE__ */ new Map();
|
|
3743
|
-
this.pendingByIp = /* @__PURE__ */ new Map();
|
|
3744
|
-
this.ttsQueues = /* @__PURE__ */ new Map();
|
|
3745
|
-
this.ttsPlaying = /* @__PURE__ */ new Map();
|
|
3746
|
-
this.ttsActiveControllers = /* @__PURE__ */ new Map();
|
|
3747
|
-
this.config = config;
|
|
3748
|
-
this.preStartTimeoutMs = config.preStartTimeoutMs ?? DEFAULT_PRE_START_TIMEOUT_MS;
|
|
3749
|
-
this.maxPendingConnections = config.maxPendingConnections ?? DEFAULT_MAX_PENDING_CONNECTIONS;
|
|
3750
|
-
this.maxPendingConnectionsPerIp = config.maxPendingConnectionsPerIp ?? DEFAULT_MAX_PENDING_CONNECTIONS_PER_IP;
|
|
3751
|
-
this.maxConnections = config.maxConnections ?? DEFAULT_MAX_CONNECTIONS;
|
|
3752
|
-
}
|
|
3753
|
-
/**
|
|
3754
|
-
* Handle WebSocket upgrade for media stream connections.
|
|
3755
|
-
*/
|
|
3756
|
-
handleUpgrade(request, socket, head) {
|
|
3757
|
-
if (!this.wss) {
|
|
3758
|
-
this.wss = new WebSocketServer({
|
|
3759
|
-
noServer: true,
|
|
3760
|
-
maxPayload: MAX_INBOUND_MESSAGE_BYTES
|
|
3761
|
-
});
|
|
3762
|
-
this.wss.on("connection", (ws, req) => this.handleConnection(ws, req));
|
|
3763
|
-
}
|
|
3764
|
-
if (this.wss.clients.size >= this.maxConnections) {
|
|
3765
|
-
this.rejectUpgrade(socket, 503, "Too many media stream connections");
|
|
3766
|
-
return;
|
|
3767
|
-
}
|
|
3768
|
-
this.wss.handleUpgrade(request, socket, head, (ws) => {
|
|
3769
|
-
this.wss?.emit("connection", ws, request);
|
|
3770
|
-
});
|
|
3771
|
-
}
|
|
3772
|
-
/**
|
|
3773
|
-
* Handle new WebSocket connection from Twilio.
|
|
3774
|
-
*/
|
|
3775
|
-
async handleConnection(ws, _request) {
|
|
3776
|
-
let session = null;
|
|
3777
|
-
const streamToken = this.getStreamToken(_request);
|
|
3778
|
-
const ip = this.getClientIp(_request);
|
|
3779
|
-
if (!this.registerPendingConnection(ws, ip)) {
|
|
3780
|
-
ws.close(1013, "Too many pending media stream connections");
|
|
3781
|
-
return;
|
|
3782
|
-
}
|
|
3783
|
-
ws.on("message", async (data) => {
|
|
3784
|
-
try {
|
|
3785
|
-
const raw = normalizeWsMessageData(data);
|
|
3786
|
-
const message = JSON.parse(raw.toString("utf8"));
|
|
3787
|
-
switch (message.event) {
|
|
3788
|
-
case "connected":
|
|
3789
|
-
console.log("[MediaStream] Twilio connected");
|
|
3790
|
-
break;
|
|
3791
|
-
case "start":
|
|
3792
|
-
session = await this.handleStart(ws, message, streamToken);
|
|
3793
|
-
if (session) this.clearPendingConnection(ws);
|
|
3794
|
-
break;
|
|
3795
|
-
case "media":
|
|
3796
|
-
if (session && message.media?.payload) {
|
|
3797
|
-
const audioBuffer = Buffer.from(message.media.payload, "base64");
|
|
3798
|
-
session.sttSession.sendAudio(audioBuffer);
|
|
3799
|
-
}
|
|
3800
|
-
break;
|
|
3801
|
-
case "stop":
|
|
3802
|
-
if (session) {
|
|
3803
|
-
this.handleStop(session);
|
|
3804
|
-
session = null;
|
|
3805
|
-
}
|
|
3806
|
-
break;
|
|
3807
|
-
}
|
|
3808
|
-
} catch (error) {
|
|
3809
|
-
console.error("[MediaStream] Error processing message:", error);
|
|
3810
|
-
}
|
|
3811
|
-
});
|
|
3812
|
-
ws.on("close", (code, reason) => {
|
|
3813
|
-
const reasonText = sanitizeLogText(Buffer.isBuffer(reason) ? reason.toString("utf8") : String(reason || ""), CLOSE_REASON_LOG_MAX_CHARS);
|
|
3814
|
-
console.log(`[MediaStream] WebSocket closed (code: ${code}, reason: ${reasonText || "none"})`);
|
|
3815
|
-
this.clearPendingConnection(ws);
|
|
3816
|
-
if (session) this.handleStop(session);
|
|
3817
|
-
});
|
|
3818
|
-
ws.on("error", (error) => {
|
|
3819
|
-
console.error("[MediaStream] WebSocket error:", error);
|
|
3820
|
-
});
|
|
3821
|
-
}
|
|
3822
|
-
/**
|
|
3823
|
-
* Handle stream start event.
|
|
3824
|
-
*/
|
|
3825
|
-
async handleStart(ws, message, streamToken) {
|
|
3826
|
-
const streamSid = message.streamSid || "";
|
|
3827
|
-
const callSid = message.start?.callSid || "";
|
|
3828
|
-
const effectiveToken = message.start?.customParameters?.token ?? streamToken;
|
|
3829
|
-
console.log(`[MediaStream] Stream started: ${streamSid} (call: ${callSid})`);
|
|
3830
|
-
if (!callSid) {
|
|
3831
|
-
console.warn("[MediaStream] Missing callSid; closing stream");
|
|
3832
|
-
ws.close(1008, "Missing callSid");
|
|
3833
|
-
return null;
|
|
3834
|
-
}
|
|
3835
|
-
if (this.config.shouldAcceptStream && !this.config.shouldAcceptStream({
|
|
3836
|
-
callId: callSid,
|
|
3837
|
-
streamSid,
|
|
3838
|
-
token: effectiveToken
|
|
3839
|
-
})) {
|
|
3840
|
-
console.warn(`[MediaStream] Rejecting stream for unknown call: ${callSid}`);
|
|
3841
|
-
ws.close(1008, "Unknown call");
|
|
3842
|
-
return null;
|
|
3843
|
-
}
|
|
3844
|
-
const sttSession = this.config.sttProvider.createSession();
|
|
3845
|
-
sttSession.onPartial((partial) => {
|
|
3846
|
-
this.config.onPartialTranscript?.(callSid, partial);
|
|
3847
|
-
});
|
|
3848
|
-
sttSession.onTranscript((transcript) => {
|
|
3849
|
-
this.config.onTranscript?.(callSid, transcript);
|
|
3850
|
-
});
|
|
3851
|
-
sttSession.onSpeechStart(() => {
|
|
3852
|
-
this.config.onSpeechStart?.(callSid);
|
|
3853
|
-
});
|
|
3854
|
-
const session = {
|
|
3855
|
-
callId: callSid,
|
|
3856
|
-
streamSid,
|
|
3857
|
-
ws,
|
|
3858
|
-
sttSession
|
|
3859
|
-
};
|
|
3860
|
-
this.sessions.set(streamSid, session);
|
|
3861
|
-
this.config.onConnect?.(callSid, streamSid);
|
|
3862
|
-
sttSession.connect().catch((err) => {
|
|
3863
|
-
console.warn(`[MediaStream] STT connection failed (TTS still works):`, err.message);
|
|
3864
|
-
});
|
|
3865
|
-
return session;
|
|
3866
|
-
}
|
|
3867
|
-
/**
|
|
3868
|
-
* Handle stream stop event.
|
|
3869
|
-
*/
|
|
3870
|
-
handleStop(session) {
|
|
3871
|
-
console.log(`[MediaStream] Stream stopped: ${session.streamSid}`);
|
|
3872
|
-
this.clearTtsState(session.streamSid);
|
|
3873
|
-
session.sttSession.close();
|
|
3874
|
-
this.sessions.delete(session.streamSid);
|
|
3875
|
-
this.config.onDisconnect?.(session.callId, session.streamSid);
|
|
3876
|
-
}
|
|
3877
|
-
getStreamToken(request) {
|
|
3878
|
-
if (!request.url || !request.headers.host) return;
|
|
3879
|
-
try {
|
|
3880
|
-
return new URL(request.url, `http://${request.headers.host}`).searchParams.get("token") ?? void 0;
|
|
3881
|
-
} catch {
|
|
3882
|
-
return;
|
|
3883
|
-
}
|
|
3884
|
-
}
|
|
3885
|
-
getClientIp(request) {
|
|
3886
|
-
return request.socket.remoteAddress || "unknown";
|
|
3887
|
-
}
|
|
3888
|
-
registerPendingConnection(ws, ip) {
|
|
3889
|
-
if (this.pendingConnections.size >= this.maxPendingConnections) {
|
|
3890
|
-
console.warn("[MediaStream] Rejecting connection: pending connection limit reached");
|
|
3891
|
-
return false;
|
|
3892
|
-
}
|
|
3893
|
-
const pendingForIp = this.pendingByIp.get(ip) ?? 0;
|
|
3894
|
-
if (pendingForIp >= this.maxPendingConnectionsPerIp) {
|
|
3895
|
-
console.warn(`[MediaStream] Rejecting connection: pending per-IP limit reached (${ip})`);
|
|
3896
|
-
return false;
|
|
3897
|
-
}
|
|
3898
|
-
const timeout = setTimeout(() => {
|
|
3899
|
-
if (!this.pendingConnections.has(ws)) return;
|
|
3900
|
-
console.warn(`[MediaStream] Closing pre-start idle connection after ${this.preStartTimeoutMs}ms (${ip})`);
|
|
3901
|
-
ws.close(1008, "Start timeout");
|
|
3902
|
-
}, this.preStartTimeoutMs);
|
|
3903
|
-
timeout.unref?.();
|
|
3904
|
-
this.pendingConnections.set(ws, {
|
|
3905
|
-
ip,
|
|
3906
|
-
timeout
|
|
3907
|
-
});
|
|
3908
|
-
this.pendingByIp.set(ip, pendingForIp + 1);
|
|
3909
|
-
return true;
|
|
3910
|
-
}
|
|
3911
|
-
clearPendingConnection(ws) {
|
|
3912
|
-
const pending = this.pendingConnections.get(ws);
|
|
3913
|
-
if (!pending) return;
|
|
3914
|
-
clearTimeout(pending.timeout);
|
|
3915
|
-
this.pendingConnections.delete(ws);
|
|
3916
|
-
const current = this.pendingByIp.get(pending.ip) ?? 0;
|
|
3917
|
-
if (current <= 1) {
|
|
3918
|
-
this.pendingByIp.delete(pending.ip);
|
|
3919
|
-
return;
|
|
3920
|
-
}
|
|
3921
|
-
this.pendingByIp.set(pending.ip, current - 1);
|
|
3922
|
-
}
|
|
3923
|
-
rejectUpgrade(socket, statusCode, message) {
|
|
3924
|
-
const statusText = statusCode === 429 ? "Too Many Requests" : "Service Unavailable";
|
|
3925
|
-
const body = `${message}\n`;
|
|
3926
|
-
socket.write(`HTTP/1.1 ${statusCode} ${statusText}\r\nConnection: close\r
|
|
3927
|
-
Content-Type: text/plain; charset=utf-8\r
|
|
3928
|
-
Content-Length: ${Buffer.byteLength(body)}\r\n\r
|
|
3929
|
-
` + body);
|
|
3930
|
-
socket.destroy();
|
|
3931
|
-
}
|
|
3932
|
-
/**
|
|
3933
|
-
* Get an active session with an open WebSocket, or undefined if unavailable.
|
|
3934
|
-
*/
|
|
3935
|
-
getOpenSession(streamSid) {
|
|
3936
|
-
const session = this.sessions.get(streamSid);
|
|
3937
|
-
return session?.ws.readyState === WebSocket.OPEN ? session : void 0;
|
|
3938
|
-
}
|
|
3939
|
-
/**
|
|
3940
|
-
* Send a message to a stream's WebSocket if available.
|
|
3941
|
-
*/
|
|
3942
|
-
sendToStream(streamSid, message) {
|
|
3943
|
-
const session = this.sessions.get(streamSid);
|
|
3944
|
-
if (!session) return {
|
|
3945
|
-
sent: false,
|
|
3946
|
-
bufferedBeforeBytes: 0,
|
|
3947
|
-
bufferedAfterBytes: 0
|
|
3948
|
-
};
|
|
3949
|
-
const readyState = session.ws.readyState;
|
|
3950
|
-
const bufferedBeforeBytes = session.ws.bufferedAmount;
|
|
3951
|
-
if (readyState !== WebSocket.OPEN) return {
|
|
3952
|
-
sent: false,
|
|
3953
|
-
readyState,
|
|
3954
|
-
bufferedBeforeBytes,
|
|
3955
|
-
bufferedAfterBytes: session.ws.bufferedAmount
|
|
3956
|
-
};
|
|
3957
|
-
if (bufferedBeforeBytes > MAX_WS_BUFFERED_BYTES) {
|
|
3958
|
-
try {
|
|
3959
|
-
session.ws.close(1013, "Backpressure: send buffer exceeded");
|
|
3960
|
-
} catch {}
|
|
3961
|
-
return {
|
|
3962
|
-
sent: false,
|
|
3963
|
-
readyState,
|
|
3964
|
-
bufferedBeforeBytes,
|
|
3965
|
-
bufferedAfterBytes: session.ws.bufferedAmount
|
|
3966
|
-
};
|
|
3967
|
-
}
|
|
3968
|
-
try {
|
|
3969
|
-
session.ws.send(JSON.stringify(message));
|
|
3970
|
-
const bufferedAfterBytes = session.ws.bufferedAmount;
|
|
3971
|
-
if (bufferedAfterBytes > MAX_WS_BUFFERED_BYTES) {
|
|
3972
|
-
try {
|
|
3973
|
-
session.ws.close(1013, "Backpressure: send buffer exceeded");
|
|
3974
|
-
} catch {}
|
|
3975
|
-
return {
|
|
3976
|
-
sent: false,
|
|
3977
|
-
readyState,
|
|
3978
|
-
bufferedBeforeBytes,
|
|
3979
|
-
bufferedAfterBytes
|
|
3980
|
-
};
|
|
3981
|
-
}
|
|
3982
|
-
return {
|
|
3983
|
-
sent: true,
|
|
3984
|
-
readyState,
|
|
3985
|
-
bufferedBeforeBytes,
|
|
3986
|
-
bufferedAfterBytes
|
|
3987
|
-
};
|
|
3988
|
-
} catch {
|
|
3989
|
-
return {
|
|
3990
|
-
sent: false,
|
|
3991
|
-
readyState,
|
|
3992
|
-
bufferedBeforeBytes,
|
|
3993
|
-
bufferedAfterBytes: session.ws.bufferedAmount
|
|
3994
|
-
};
|
|
3995
|
-
}
|
|
3996
|
-
}
|
|
3997
|
-
/**
|
|
3998
|
-
* Send audio to a specific stream (for TTS playback).
|
|
3999
|
-
* Audio should be mu-law encoded at 8kHz mono.
|
|
4000
|
-
*/
|
|
4001
|
-
sendAudio(streamSid, muLawAudio) {
|
|
4002
|
-
return this.sendToStream(streamSid, {
|
|
4003
|
-
event: "media",
|
|
4004
|
-
streamSid,
|
|
4005
|
-
media: { payload: muLawAudio.toString("base64") }
|
|
4006
|
-
});
|
|
4007
|
-
}
|
|
4008
|
-
/**
|
|
4009
|
-
* Send a mark event to track audio playback position.
|
|
4010
|
-
*/
|
|
4011
|
-
sendMark(streamSid, name) {
|
|
4012
|
-
return this.sendToStream(streamSid, {
|
|
4013
|
-
event: "mark",
|
|
4014
|
-
streamSid,
|
|
4015
|
-
mark: { name }
|
|
4016
|
-
});
|
|
4017
|
-
}
|
|
4018
|
-
/**
|
|
4019
|
-
* Clear audio buffer (interrupt playback).
|
|
4020
|
-
*/
|
|
4021
|
-
clearAudio(streamSid) {
|
|
4022
|
-
return this.sendToStream(streamSid, {
|
|
4023
|
-
event: "clear",
|
|
4024
|
-
streamSid
|
|
4025
|
-
});
|
|
4026
|
-
}
|
|
4027
|
-
/**
|
|
4028
|
-
* Queue a TTS operation for sequential playback.
|
|
4029
|
-
* Only one TTS operation plays at a time per stream to prevent overlap.
|
|
4030
|
-
*/
|
|
4031
|
-
async queueTts(streamSid, playFn) {
|
|
4032
|
-
const queue = this.getTtsQueue(streamSid);
|
|
4033
|
-
let resolveEntry;
|
|
4034
|
-
let rejectEntry;
|
|
4035
|
-
const promise = new Promise((resolve, reject) => {
|
|
4036
|
-
resolveEntry = resolve;
|
|
4037
|
-
rejectEntry = reject;
|
|
4038
|
-
});
|
|
4039
|
-
queue.push({
|
|
4040
|
-
playFn,
|
|
4041
|
-
controller: new AbortController(),
|
|
4042
|
-
resolve: resolveEntry,
|
|
4043
|
-
reject: rejectEntry
|
|
4044
|
-
});
|
|
4045
|
-
if (!this.ttsPlaying.get(streamSid)) this.processQueue(streamSid);
|
|
4046
|
-
return promise;
|
|
4047
|
-
}
|
|
4048
|
-
/**
|
|
4049
|
-
* Clear TTS queue and interrupt current playback (barge-in).
|
|
4050
|
-
*/
|
|
4051
|
-
clearTtsQueue(streamSid, _reason = "unspecified") {
|
|
4052
|
-
const queue = this.getTtsQueue(streamSid);
|
|
4053
|
-
queue.length = 0;
|
|
4054
|
-
this.ttsActiveControllers.get(streamSid)?.abort();
|
|
4055
|
-
this.clearAudio(streamSid);
|
|
4056
|
-
}
|
|
4057
|
-
/**
|
|
4058
|
-
* Get active session by call ID.
|
|
4059
|
-
*/
|
|
4060
|
-
getSessionByCallId(callId) {
|
|
4061
|
-
return [...this.sessions.values()].find((session) => session.callId === callId);
|
|
4062
|
-
}
|
|
4063
|
-
/**
|
|
4064
|
-
* Close all sessions.
|
|
4065
|
-
*/
|
|
4066
|
-
closeAll() {
|
|
4067
|
-
for (const session of this.sessions.values()) {
|
|
4068
|
-
this.clearTtsState(session.streamSid);
|
|
4069
|
-
session.sttSession.close();
|
|
4070
|
-
session.ws.close();
|
|
4071
|
-
}
|
|
4072
|
-
this.sessions.clear();
|
|
4073
|
-
}
|
|
4074
|
-
getTtsQueue(streamSid) {
|
|
4075
|
-
const existing = this.ttsQueues.get(streamSid);
|
|
4076
|
-
if (existing) return existing;
|
|
4077
|
-
const queue = [];
|
|
4078
|
-
this.ttsQueues.set(streamSid, queue);
|
|
4079
|
-
return queue;
|
|
4080
|
-
}
|
|
4081
|
-
/**
|
|
4082
|
-
* Process the TTS queue for a stream.
|
|
4083
|
-
* Uses iterative approach to avoid stack accumulation from recursion.
|
|
4084
|
-
*/
|
|
4085
|
-
async processQueue(streamSid) {
|
|
4086
|
-
this.ttsPlaying.set(streamSid, true);
|
|
4087
|
-
while (true) {
|
|
4088
|
-
const queue = this.ttsQueues.get(streamSid);
|
|
4089
|
-
if (!queue || queue.length === 0) {
|
|
4090
|
-
this.ttsPlaying.set(streamSid, false);
|
|
4091
|
-
this.ttsActiveControllers.delete(streamSid);
|
|
4092
|
-
return;
|
|
4093
|
-
}
|
|
4094
|
-
const entry = queue.shift();
|
|
4095
|
-
this.ttsActiveControllers.set(streamSid, entry.controller);
|
|
4096
|
-
try {
|
|
4097
|
-
await entry.playFn(entry.controller.signal);
|
|
4098
|
-
entry.resolve();
|
|
4099
|
-
} catch (error) {
|
|
4100
|
-
if (entry.controller.signal.aborted) entry.resolve();
|
|
4101
|
-
else {
|
|
4102
|
-
console.error("[MediaStream] TTS playback error:", error);
|
|
4103
|
-
entry.reject(error);
|
|
4104
|
-
}
|
|
4105
|
-
} finally {
|
|
4106
|
-
if (this.ttsActiveControllers.get(streamSid) === entry.controller) this.ttsActiveControllers.delete(streamSid);
|
|
4107
|
-
}
|
|
4108
|
-
}
|
|
4109
|
-
}
|
|
4110
|
-
clearTtsState(streamSid) {
|
|
4111
|
-
const queue = this.ttsQueues.get(streamSid);
|
|
4112
|
-
if (queue) queue.length = 0;
|
|
4113
|
-
this.ttsActiveControllers.get(streamSid)?.abort();
|
|
4114
|
-
this.ttsActiveControllers.delete(streamSid);
|
|
4115
|
-
this.ttsPlaying.delete(streamSid);
|
|
4116
|
-
this.ttsQueues.delete(streamSid);
|
|
4117
|
-
}
|
|
4118
|
-
};
|
|
4119
|
-
//#endregion
|
|
4120
|
-
//#region extensions/voice-call/src/providers/stt-openai-realtime.ts
|
|
4121
|
-
/**
|
|
4122
|
-
* OpenAI Realtime STT Provider
|
|
4123
|
-
*
|
|
4124
|
-
* Uses the OpenAI Realtime API for streaming transcription with:
|
|
4125
|
-
* - Direct mu-law audio support (no conversion needed)
|
|
4126
|
-
* - Built-in server-side VAD for turn detection
|
|
4127
|
-
* - Low-latency streaming transcription
|
|
4128
|
-
* - Partial transcript callbacks for real-time UI updates
|
|
4129
|
-
*/
|
|
4130
|
-
/**
|
|
4131
|
-
* Provider factory for OpenAI Realtime STT sessions.
|
|
4132
|
-
*/
|
|
4133
|
-
var OpenAIRealtimeSTTProvider = class {
|
|
4134
|
-
constructor(config) {
|
|
4135
|
-
this.name = "openai-realtime";
|
|
4136
|
-
if (!config.apiKey) throw new Error("OpenAI API key required for Realtime STT");
|
|
4137
|
-
this.apiKey = config.apiKey;
|
|
4138
|
-
this.model = config.model || "gpt-4o-transcribe";
|
|
4139
|
-
this.silenceDurationMs = config.silenceDurationMs ?? 800;
|
|
4140
|
-
this.vadThreshold = config.vadThreshold ?? .5;
|
|
4141
|
-
}
|
|
4142
|
-
/**
|
|
4143
|
-
* Create a new realtime transcription session.
|
|
4144
|
-
*/
|
|
4145
|
-
createSession() {
|
|
4146
|
-
return new OpenAIRealtimeSTTSession(this.apiKey, this.model, this.silenceDurationMs, this.vadThreshold);
|
|
4147
|
-
}
|
|
4148
|
-
};
|
|
4149
|
-
/**
|
|
4150
|
-
* WebSocket-based session for real-time speech-to-text.
|
|
4151
|
-
*/
|
|
4152
|
-
var OpenAIRealtimeSTTSession = class OpenAIRealtimeSTTSession {
|
|
4153
|
-
static {
|
|
4154
|
-
this.MAX_RECONNECT_ATTEMPTS = 5;
|
|
4155
|
-
}
|
|
4156
|
-
static {
|
|
4157
|
-
this.RECONNECT_DELAY_MS = 1e3;
|
|
4158
|
-
}
|
|
4159
|
-
constructor(apiKey, model, silenceDurationMs, vadThreshold) {
|
|
4160
|
-
this.apiKey = apiKey;
|
|
4161
|
-
this.model = model;
|
|
4162
|
-
this.silenceDurationMs = silenceDurationMs;
|
|
4163
|
-
this.vadThreshold = vadThreshold;
|
|
4164
|
-
this.ws = null;
|
|
4165
|
-
this.connected = false;
|
|
4166
|
-
this.closed = false;
|
|
4167
|
-
this.connectTimeout = null;
|
|
4168
|
-
this.reconnectAttempts = 0;
|
|
4169
|
-
this.pendingTranscript = "";
|
|
4170
|
-
this.onTranscriptCallback = null;
|
|
4171
|
-
this.onPartialCallback = null;
|
|
4172
|
-
this.onSpeechStartCallback = null;
|
|
4173
|
-
}
|
|
4174
|
-
async connect() {
|
|
4175
|
-
this.closed = false;
|
|
4176
|
-
this.reconnectAttempts = 0;
|
|
4177
|
-
return this.doConnect();
|
|
4178
|
-
}
|
|
4179
|
-
async doConnect() {
|
|
4180
|
-
return new Promise((resolve, reject) => {
|
|
4181
|
-
this.ws = new WebSocket$1("wss://api.openai.com/v1/realtime?intent=transcription", { headers: {
|
|
4182
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
4183
|
-
"OpenAI-Beta": "realtime=v1"
|
|
4184
|
-
} });
|
|
4185
|
-
this.ws.on("open", () => {
|
|
4186
|
-
console.log("[RealtimeSTT] WebSocket connected");
|
|
4187
|
-
this.connected = true;
|
|
4188
|
-
this.reconnectAttempts = 0;
|
|
4189
|
-
if (this.connectTimeout) {
|
|
4190
|
-
clearTimeout(this.connectTimeout);
|
|
4191
|
-
this.connectTimeout = null;
|
|
4192
|
-
}
|
|
4193
|
-
this.sendEvent({
|
|
4194
|
-
type: "transcription_session.update",
|
|
4195
|
-
session: {
|
|
4196
|
-
input_audio_format: "g711_ulaw",
|
|
4197
|
-
input_audio_transcription: { model: this.model },
|
|
4198
|
-
turn_detection: {
|
|
4199
|
-
type: "server_vad",
|
|
4200
|
-
threshold: this.vadThreshold,
|
|
4201
|
-
prefix_padding_ms: 300,
|
|
4202
|
-
silence_duration_ms: this.silenceDurationMs
|
|
4203
|
-
}
|
|
4204
|
-
}
|
|
4205
|
-
});
|
|
4206
|
-
resolve();
|
|
4207
|
-
});
|
|
4208
|
-
this.ws.on("message", (data) => {
|
|
4209
|
-
try {
|
|
4210
|
-
const event = JSON.parse(data.toString());
|
|
4211
|
-
this.handleEvent(event);
|
|
4212
|
-
} catch (e) {
|
|
4213
|
-
console.error("[RealtimeSTT] Failed to parse event:", e);
|
|
4214
|
-
}
|
|
4215
|
-
});
|
|
4216
|
-
this.ws.on("error", (error) => {
|
|
4217
|
-
console.error("[RealtimeSTT] WebSocket error:", error);
|
|
4218
|
-
if (!this.connected) reject(error);
|
|
4219
|
-
});
|
|
4220
|
-
this.ws.on("close", (code, reason) => {
|
|
4221
|
-
console.log(`[RealtimeSTT] WebSocket closed (code: ${code}, reason: ${reason?.toString() || "none"})`);
|
|
4222
|
-
this.connected = false;
|
|
4223
|
-
if (!this.closed) this.attemptReconnect();
|
|
4224
|
-
});
|
|
4225
|
-
this.connectTimeout = setTimeout(() => {
|
|
4226
|
-
this.connectTimeout = null;
|
|
4227
|
-
if (!this.connected) reject(/* @__PURE__ */ new Error("Realtime STT connection timeout"));
|
|
4228
|
-
}, 1e4);
|
|
4229
|
-
});
|
|
4230
|
-
}
|
|
4231
|
-
async attemptReconnect() {
|
|
4232
|
-
if (this.closed) return;
|
|
4233
|
-
if (this.reconnectAttempts >= OpenAIRealtimeSTTSession.MAX_RECONNECT_ATTEMPTS) {
|
|
4234
|
-
console.error(`[RealtimeSTT] Max reconnect attempts (${OpenAIRealtimeSTTSession.MAX_RECONNECT_ATTEMPTS}) reached`);
|
|
4235
|
-
return;
|
|
4236
|
-
}
|
|
4237
|
-
this.reconnectAttempts++;
|
|
4238
|
-
const delay = OpenAIRealtimeSTTSession.RECONNECT_DELAY_MS * 2 ** (this.reconnectAttempts - 1);
|
|
4239
|
-
console.log(`[RealtimeSTT] Reconnecting ${this.reconnectAttempts}/${OpenAIRealtimeSTTSession.MAX_RECONNECT_ATTEMPTS} in ${delay}ms...`);
|
|
4240
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
4241
|
-
if (this.closed) return;
|
|
4242
|
-
try {
|
|
4243
|
-
await this.doConnect();
|
|
4244
|
-
console.log("[RealtimeSTT] Reconnected successfully");
|
|
4245
|
-
} catch (error) {
|
|
4246
|
-
console.error("[RealtimeSTT] Reconnect failed:", error);
|
|
4247
|
-
}
|
|
4248
|
-
}
|
|
4249
|
-
handleEvent(event) {
|
|
4250
|
-
switch (event.type) {
|
|
4251
|
-
case "transcription_session.created":
|
|
4252
|
-
case "transcription_session.updated":
|
|
4253
|
-
case "input_audio_buffer.speech_stopped":
|
|
4254
|
-
case "input_audio_buffer.committed":
|
|
4255
|
-
console.log(`[RealtimeSTT] ${event.type}`);
|
|
4256
|
-
break;
|
|
4257
|
-
case "conversation.item.input_audio_transcription.delta":
|
|
4258
|
-
if (event.delta) {
|
|
4259
|
-
this.pendingTranscript += event.delta;
|
|
4260
|
-
this.onPartialCallback?.(this.pendingTranscript);
|
|
4261
|
-
}
|
|
4262
|
-
break;
|
|
4263
|
-
case "conversation.item.input_audio_transcription.completed":
|
|
4264
|
-
if (event.transcript) {
|
|
4265
|
-
console.log(`[RealtimeSTT] Transcript: ${event.transcript}`);
|
|
4266
|
-
this.onTranscriptCallback?.(event.transcript);
|
|
4267
|
-
}
|
|
4268
|
-
this.pendingTranscript = "";
|
|
4269
|
-
break;
|
|
4270
|
-
case "input_audio_buffer.speech_started":
|
|
4271
|
-
console.log("[RealtimeSTT] Speech started");
|
|
4272
|
-
this.pendingTranscript = "";
|
|
4273
|
-
this.onSpeechStartCallback?.();
|
|
4274
|
-
break;
|
|
4275
|
-
case "error":
|
|
4276
|
-
console.error("[RealtimeSTT] Error:", event.error);
|
|
4277
|
-
break;
|
|
4278
|
-
}
|
|
4279
|
-
}
|
|
4280
|
-
sendEvent(event) {
|
|
4281
|
-
if (this.ws?.readyState === WebSocket$1.OPEN) this.ws.send(JSON.stringify(event));
|
|
4282
|
-
}
|
|
4283
|
-
sendAudio(muLawData) {
|
|
4284
|
-
if (!this.connected) return;
|
|
4285
|
-
this.sendEvent({
|
|
4286
|
-
type: "input_audio_buffer.append",
|
|
4287
|
-
audio: muLawData.toString("base64")
|
|
4288
|
-
});
|
|
4289
|
-
}
|
|
4290
|
-
onPartial(callback) {
|
|
4291
|
-
this.onPartialCallback = callback;
|
|
4292
|
-
}
|
|
4293
|
-
onTranscript(callback) {
|
|
4294
|
-
this.onTranscriptCallback = callback;
|
|
4295
|
-
}
|
|
4296
|
-
onSpeechStart(callback) {
|
|
4297
|
-
this.onSpeechStartCallback = callback;
|
|
4298
|
-
}
|
|
4299
|
-
async waitForTranscript(timeoutMs = 3e4) {
|
|
4300
|
-
return new Promise((resolve, reject) => {
|
|
4301
|
-
const timeout = setTimeout(() => {
|
|
4302
|
-
this.onTranscriptCallback = null;
|
|
4303
|
-
reject(/* @__PURE__ */ new Error("Transcript timeout"));
|
|
4304
|
-
}, timeoutMs);
|
|
4305
|
-
this.onTranscriptCallback = (transcript) => {
|
|
4306
|
-
clearTimeout(timeout);
|
|
4307
|
-
this.onTranscriptCallback = null;
|
|
4308
|
-
resolve(transcript);
|
|
4309
|
-
};
|
|
4310
|
-
});
|
|
4311
|
-
}
|
|
4312
|
-
close() {
|
|
4313
|
-
this.closed = true;
|
|
4314
|
-
if (this.connectTimeout) {
|
|
4315
|
-
clearTimeout(this.connectTimeout);
|
|
4316
|
-
this.connectTimeout = null;
|
|
4317
|
-
}
|
|
4318
|
-
if (this.ws) {
|
|
4319
|
-
this.ws.close();
|
|
4320
|
-
this.ws = null;
|
|
4321
|
-
}
|
|
4322
|
-
this.connected = false;
|
|
4323
|
-
}
|
|
4324
|
-
isConnected() {
|
|
4325
|
-
return this.connected;
|
|
4326
|
-
}
|
|
4327
|
-
};
|
|
4328
|
-
//#endregion
|
|
4329
|
-
//#region extensions/voice-call/src/webhook/stale-call-reaper.ts
|
|
4330
|
-
const CHECK_INTERVAL_MS = 3e4;
|
|
4331
|
-
function startStaleCallReaper(params) {
|
|
4332
|
-
const maxAgeSeconds = params.staleCallReaperSeconds;
|
|
4333
|
-
if (!maxAgeSeconds || maxAgeSeconds <= 0) return null;
|
|
4334
|
-
const maxAgeMs = maxAgeSeconds * 1e3;
|
|
4335
|
-
const interval = setInterval(() => {
|
|
4336
|
-
const now = Date.now();
|
|
4337
|
-
for (const call of params.manager.getActiveCalls()) {
|
|
4338
|
-
const age = now - call.startedAt;
|
|
4339
|
-
if (age > maxAgeMs) {
|
|
4340
|
-
console.log(`[voice-call] Reaping stale call ${call.callId} (age: ${Math.round(age / 1e3)}s, state: ${call.state})`);
|
|
4341
|
-
params.manager.endCall(call.callId).catch((err) => {
|
|
4342
|
-
console.warn(`[voice-call] Reaper failed to end call ${call.callId}:`, err);
|
|
4343
|
-
});
|
|
4344
|
-
}
|
|
4345
|
-
}
|
|
4346
|
-
}, CHECK_INTERVAL_MS);
|
|
4347
|
-
return () => {
|
|
4348
|
-
clearInterval(interval);
|
|
4349
|
-
};
|
|
4350
|
-
}
|
|
4351
|
-
//#endregion
|
|
4352
|
-
//#region extensions/voice-call/src/webhook.ts
|
|
4353
|
-
const MAX_WEBHOOK_BODY_BYTES = WEBHOOK_BODY_READ_DEFAULTS.preAuth.maxBytes;
|
|
4354
|
-
const WEBHOOK_BODY_TIMEOUT_MS = WEBHOOK_BODY_READ_DEFAULTS.preAuth.timeoutMs;
|
|
4355
|
-
const STREAM_DISCONNECT_HANGUP_GRACE_MS = 2e3;
|
|
4356
|
-
const TRANSCRIPT_LOG_MAX_CHARS = 200;
|
|
4357
|
-
function sanitizeTranscriptForLog(value) {
|
|
4358
|
-
const sanitized = value.replace(/[\u0000-\u001f\u007f]/g, " ").replace(/\s+/g, " ").trim();
|
|
4359
|
-
if (sanitized.length <= TRANSCRIPT_LOG_MAX_CHARS) return sanitized;
|
|
4360
|
-
return `${sanitized.slice(0, TRANSCRIPT_LOG_MAX_CHARS)}...`;
|
|
4361
|
-
}
|
|
4362
|
-
function buildRequestUrl(requestUrl, requestHost, fallbackHost = "localhost") {
|
|
4363
|
-
return new URL$1(requestUrl ?? "/", `http://${requestHost ?? fallbackHost}`);
|
|
4364
|
-
}
|
|
4365
|
-
function normalizeWebhookResponse(parsed) {
|
|
4366
|
-
return {
|
|
4367
|
-
statusCode: parsed.statusCode ?? 200,
|
|
4368
|
-
headers: parsed.providerResponseHeaders,
|
|
4369
|
-
body: parsed.providerResponseBody ?? "OK"
|
|
4370
|
-
};
|
|
4371
|
-
}
|
|
4372
|
-
/**
|
|
4373
|
-
* HTTP server for receiving voice call webhooks from providers.
|
|
4374
|
-
* Supports WebSocket upgrades for media streams when streaming is enabled.
|
|
4375
|
-
*/
|
|
4376
|
-
var VoiceCallWebhookServer = class {
|
|
4377
|
-
constructor(config, manager, provider, coreConfig, agentRuntime) {
|
|
4378
|
-
this.server = null;
|
|
4379
|
-
this.listeningUrl = null;
|
|
4380
|
-
this.stopStaleCallReaper = null;
|
|
4381
|
-
this.webhookInFlightLimiter = createWebhookInFlightLimiter();
|
|
4382
|
-
this.mediaStreamHandler = null;
|
|
4383
|
-
this.pendingDisconnectHangups = /* @__PURE__ */ new Map();
|
|
4384
|
-
this.config = normalizeVoiceCallConfig(config);
|
|
4385
|
-
this.manager = manager;
|
|
4386
|
-
this.provider = provider;
|
|
4387
|
-
this.coreConfig = coreConfig ?? null;
|
|
4388
|
-
this.agentRuntime = agentRuntime ?? null;
|
|
4389
|
-
if (this.config.streaming.enabled) this.initializeMediaStreaming();
|
|
4390
|
-
}
|
|
4391
|
-
/**
|
|
4392
|
-
* Get the media stream handler (for wiring to provider).
|
|
4393
|
-
*/
|
|
4394
|
-
getMediaStreamHandler() {
|
|
4395
|
-
return this.mediaStreamHandler;
|
|
4396
|
-
}
|
|
4397
|
-
clearPendingDisconnectHangup(providerCallId) {
|
|
4398
|
-
const existing = this.pendingDisconnectHangups.get(providerCallId);
|
|
4399
|
-
if (!existing) return;
|
|
4400
|
-
clearTimeout(existing);
|
|
4401
|
-
this.pendingDisconnectHangups.delete(providerCallId);
|
|
4402
|
-
}
|
|
4403
|
-
shouldSuppressBargeInForInitialMessage(call) {
|
|
4404
|
-
if (!call || call.direction !== "outbound") return false;
|
|
4405
|
-
if (call.state !== "speaking") return false;
|
|
4406
|
-
if ((call.metadata?.mode ?? "conversation") !== "conversation") return false;
|
|
4407
|
-
return (typeof call.metadata?.initialMessage === "string" ? call.metadata.initialMessage.trim() : "").length > 0;
|
|
4408
|
-
}
|
|
4409
|
-
/**
|
|
4410
|
-
* Initialize media streaming with OpenAI Realtime STT.
|
|
4411
|
-
*/
|
|
4412
|
-
initializeMediaStreaming() {
|
|
4413
|
-
const streaming = this.config.streaming;
|
|
4414
|
-
const apiKey = streaming.openaiApiKey ?? process.env.OPENAI_API_KEY;
|
|
4415
|
-
if (!apiKey) {
|
|
4416
|
-
console.warn("[voice-call] Streaming enabled but no OpenAI API key found");
|
|
4417
|
-
return;
|
|
4418
|
-
}
|
|
4419
|
-
this.mediaStreamHandler = new MediaStreamHandler({
|
|
4420
|
-
sttProvider: new OpenAIRealtimeSTTProvider({
|
|
4421
|
-
apiKey,
|
|
4422
|
-
model: streaming.sttModel,
|
|
4423
|
-
silenceDurationMs: streaming.silenceDurationMs,
|
|
4424
|
-
vadThreshold: streaming.vadThreshold
|
|
4425
|
-
}),
|
|
4426
|
-
preStartTimeoutMs: streaming.preStartTimeoutMs,
|
|
4427
|
-
maxPendingConnections: streaming.maxPendingConnections,
|
|
4428
|
-
maxPendingConnectionsPerIp: streaming.maxPendingConnectionsPerIp,
|
|
4429
|
-
maxConnections: streaming.maxConnections,
|
|
4430
|
-
shouldAcceptStream: ({ callId, token }) => {
|
|
4431
|
-
if (!this.manager.getCallByProviderCallId(callId)) return false;
|
|
4432
|
-
if (this.provider.name === "twilio") {
|
|
4433
|
-
if (!this.provider.isValidStreamToken(callId, token)) {
|
|
4434
|
-
console.warn(`[voice-call] Rejecting media stream: invalid token for ${callId}`);
|
|
4435
|
-
return false;
|
|
4436
|
-
}
|
|
4437
|
-
}
|
|
4438
|
-
return true;
|
|
4439
|
-
},
|
|
4440
|
-
onTranscript: (providerCallId, transcript) => {
|
|
4441
|
-
const safeTranscript = sanitizeTranscriptForLog(transcript);
|
|
4442
|
-
console.log(`[voice-call] Transcript for ${providerCallId}: ${safeTranscript} (chars=${transcript.length})`);
|
|
4443
|
-
const call = this.manager.getCallByProviderCallId(providerCallId);
|
|
4444
|
-
if (!call) {
|
|
4445
|
-
console.warn(`[voice-call] No active call found for provider ID: ${providerCallId}`);
|
|
4446
|
-
return;
|
|
4447
|
-
}
|
|
4448
|
-
if (this.shouldSuppressBargeInForInitialMessage(call)) {
|
|
4449
|
-
console.log(`[voice-call] Ignoring barge transcript while initial message is still playing (${providerCallId})`);
|
|
4450
|
-
return;
|
|
4451
|
-
}
|
|
4452
|
-
if (this.provider.name === "twilio") this.provider.clearTtsQueue(providerCallId);
|
|
4453
|
-
const event = {
|
|
4454
|
-
id: `stream-transcript-${Date.now()}`,
|
|
4455
|
-
type: "call.speech",
|
|
4456
|
-
callId: call.callId,
|
|
4457
|
-
providerCallId,
|
|
4458
|
-
timestamp: Date.now(),
|
|
4459
|
-
transcript,
|
|
4460
|
-
isFinal: true
|
|
4461
|
-
};
|
|
4462
|
-
this.manager.processEvent(event);
|
|
4463
|
-
const callMode = call.metadata?.mode;
|
|
4464
|
-
if (call.direction === "inbound" || callMode === "conversation") this.handleInboundResponse(call.callId, transcript).catch((err) => {
|
|
4465
|
-
console.warn(`[voice-call] Failed to auto-respond:`, err);
|
|
4466
|
-
});
|
|
4467
|
-
},
|
|
4468
|
-
onSpeechStart: (providerCallId) => {
|
|
4469
|
-
if (this.provider.name !== "twilio") return;
|
|
4470
|
-
const call = this.manager.getCallByProviderCallId(providerCallId);
|
|
4471
|
-
if (this.shouldSuppressBargeInForInitialMessage(call)) return;
|
|
4472
|
-
this.provider.clearTtsQueue(providerCallId);
|
|
4473
|
-
},
|
|
4474
|
-
onPartialTranscript: (callId, partial) => {
|
|
4475
|
-
const safePartial = sanitizeTranscriptForLog(partial);
|
|
4476
|
-
console.log(`[voice-call] Partial for ${callId}: ${safePartial} (chars=${partial.length})`);
|
|
4477
|
-
},
|
|
4478
|
-
onConnect: (callId, streamSid) => {
|
|
4479
|
-
console.log(`[voice-call] Media stream connected: ${callId} -> ${streamSid}`);
|
|
4480
|
-
this.clearPendingDisconnectHangup(callId);
|
|
4481
|
-
if (this.provider.name === "twilio") this.provider.registerCallStream(callId, streamSid);
|
|
4482
|
-
this.manager.speakInitialMessage(callId).catch((err) => {
|
|
4483
|
-
console.warn(`[voice-call] Failed to speak initial message:`, err);
|
|
4484
|
-
});
|
|
4485
|
-
},
|
|
4486
|
-
onDisconnect: (callId, streamSid) => {
|
|
4487
|
-
console.log(`[voice-call] Media stream disconnected: ${callId} (${streamSid})`);
|
|
4488
|
-
if (this.provider.name === "twilio") this.provider.unregisterCallStream(callId, streamSid);
|
|
4489
|
-
this.clearPendingDisconnectHangup(callId);
|
|
4490
|
-
const timer = setTimeout(() => {
|
|
4491
|
-
this.pendingDisconnectHangups.delete(callId);
|
|
4492
|
-
const disconnectedCall = this.manager.getCallByProviderCallId(callId);
|
|
4493
|
-
if (!disconnectedCall) return;
|
|
4494
|
-
if (this.provider.name === "twilio") {
|
|
4495
|
-
if (this.provider.hasRegisteredStream(callId)) return;
|
|
4496
|
-
}
|
|
4497
|
-
console.log(`[voice-call] Auto-ending call ${disconnectedCall.callId} after stream disconnect grace`);
|
|
4498
|
-
this.manager.endCall(disconnectedCall.callId).catch((err) => {
|
|
4499
|
-
console.warn(`[voice-call] Failed to auto-end call ${disconnectedCall.callId}:`, err);
|
|
4500
|
-
});
|
|
4501
|
-
}, STREAM_DISCONNECT_HANGUP_GRACE_MS);
|
|
4502
|
-
timer.unref?.();
|
|
4503
|
-
this.pendingDisconnectHangups.set(callId, timer);
|
|
4504
|
-
}
|
|
4505
|
-
});
|
|
4506
|
-
console.log("[voice-call] Media streaming initialized");
|
|
4507
|
-
}
|
|
4508
|
-
/**
|
|
4509
|
-
* Start the webhook server.
|
|
4510
|
-
* Idempotent: returns immediately if the server is already listening.
|
|
4511
|
-
*/
|
|
4512
|
-
async start() {
|
|
4513
|
-
const { port, bind, path: webhookPath } = this.config.serve;
|
|
4514
|
-
const streamPath = this.config.streaming.streamPath;
|
|
4515
|
-
if (this.server?.listening) return this.listeningUrl ?? this.resolveListeningUrl(bind, webhookPath);
|
|
4516
|
-
return new Promise((resolve, reject) => {
|
|
4517
|
-
this.server = http.createServer((req, res) => {
|
|
4518
|
-
this.handleRequest(req, res, webhookPath).catch((err) => {
|
|
4519
|
-
console.error("[voice-call] Webhook error:", err);
|
|
4520
|
-
res.statusCode = 500;
|
|
4521
|
-
res.end("Internal Server Error");
|
|
4522
|
-
});
|
|
4523
|
-
});
|
|
4524
|
-
if (this.mediaStreamHandler) this.server.on("upgrade", (request, socket, head) => {
|
|
4525
|
-
if (this.getUpgradePathname(request) === streamPath) {
|
|
4526
|
-
console.log("[voice-call] WebSocket upgrade for media stream");
|
|
4527
|
-
this.mediaStreamHandler?.handleUpgrade(request, socket, head);
|
|
4528
|
-
} else socket.destroy();
|
|
4529
|
-
});
|
|
4530
|
-
this.server.on("error", reject);
|
|
4531
|
-
this.server.listen(port, bind, () => {
|
|
4532
|
-
const url = this.resolveListeningUrl(bind, webhookPath);
|
|
4533
|
-
this.listeningUrl = url;
|
|
4534
|
-
console.log(`[voice-call] Webhook server listening on ${url}`);
|
|
4535
|
-
if (this.mediaStreamHandler) {
|
|
4536
|
-
const address = this.server?.address();
|
|
4537
|
-
const actualPort = address && typeof address === "object" ? address.port : this.config.serve.port;
|
|
4538
|
-
console.log(`[voice-call] Media stream WebSocket on ws://${bind}:${actualPort}${streamPath}`);
|
|
4539
|
-
}
|
|
4540
|
-
resolve(url);
|
|
4541
|
-
this.stopStaleCallReaper = startStaleCallReaper({
|
|
4542
|
-
manager: this.manager,
|
|
4543
|
-
staleCallReaperSeconds: this.config.staleCallReaperSeconds
|
|
4544
|
-
});
|
|
4545
|
-
});
|
|
4546
|
-
});
|
|
4547
|
-
}
|
|
4548
|
-
/**
|
|
4549
|
-
* Stop the webhook server.
|
|
4550
|
-
*/
|
|
4551
|
-
async stop() {
|
|
4552
|
-
for (const timer of this.pendingDisconnectHangups.values()) clearTimeout(timer);
|
|
4553
|
-
this.pendingDisconnectHangups.clear();
|
|
4554
|
-
this.webhookInFlightLimiter.clear();
|
|
4555
|
-
if (this.stopStaleCallReaper) {
|
|
4556
|
-
this.stopStaleCallReaper();
|
|
4557
|
-
this.stopStaleCallReaper = null;
|
|
4558
|
-
}
|
|
4559
|
-
return new Promise((resolve) => {
|
|
4560
|
-
if (this.server) this.server.close(() => {
|
|
4561
|
-
this.server = null;
|
|
4562
|
-
this.listeningUrl = null;
|
|
4563
|
-
resolve();
|
|
4564
|
-
});
|
|
4565
|
-
else {
|
|
4566
|
-
this.listeningUrl = null;
|
|
4567
|
-
resolve();
|
|
4568
|
-
}
|
|
4569
|
-
});
|
|
4570
|
-
}
|
|
4571
|
-
resolveListeningUrl(bind, webhookPath) {
|
|
4572
|
-
const address = this.server?.address();
|
|
4573
|
-
if (address && typeof address === "object") {
|
|
4574
|
-
const host = address.address && address.address.length > 0 ? address.address : bind;
|
|
4575
|
-
return `http://${host.includes(":") && !host.startsWith("[") ? `[${host}]` : host}:${address.port}${webhookPath}`;
|
|
4576
|
-
}
|
|
4577
|
-
return `http://${bind}:${this.config.serve.port}${webhookPath}`;
|
|
4578
|
-
}
|
|
4579
|
-
getUpgradePathname(request) {
|
|
4580
|
-
try {
|
|
4581
|
-
return buildRequestUrl(request.url, request.headers.host).pathname;
|
|
4582
|
-
} catch {
|
|
4583
|
-
return null;
|
|
4584
|
-
}
|
|
4585
|
-
}
|
|
4586
|
-
normalizeWebhookPathForMatch(pathname) {
|
|
4587
|
-
const trimmed = pathname.trim();
|
|
4588
|
-
if (!trimmed) return "/";
|
|
4589
|
-
const prefixed = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
4590
|
-
if (prefixed === "/") return prefixed;
|
|
4591
|
-
return prefixed.endsWith("/") ? prefixed.slice(0, -1) : prefixed;
|
|
4592
|
-
}
|
|
4593
|
-
isWebhookPathMatch(requestPath, configuredPath) {
|
|
4594
|
-
return this.normalizeWebhookPathForMatch(requestPath) === this.normalizeWebhookPathForMatch(configuredPath);
|
|
4595
|
-
}
|
|
4596
|
-
/**
|
|
4597
|
-
* Handle incoming HTTP request.
|
|
4598
|
-
*/
|
|
4599
|
-
async handleRequest(req, res, webhookPath) {
|
|
4600
|
-
const payload = await this.runWebhookPipeline(req, webhookPath);
|
|
4601
|
-
this.writeWebhookResponse(res, payload);
|
|
4602
|
-
}
|
|
4603
|
-
async runWebhookPipeline(req, webhookPath) {
|
|
4604
|
-
const url = buildRequestUrl(req.url, req.headers.host);
|
|
4605
|
-
if (url.pathname === "/voice/hold-music") return {
|
|
4606
|
-
statusCode: 200,
|
|
4607
|
-
headers: { "Content-Type": "text/xml" },
|
|
4608
|
-
body: `<?xml version="1.0" encoding="UTF-8"?>
|
|
4609
|
-
<Response>
|
|
4610
|
-
<Say voice="alice">All agents are currently busy. Please hold.</Say>
|
|
4611
|
-
<Play loop="0">https://s3.amazonaws.com/com.twilio.music.classical/BusyStrings.mp3</Play>
|
|
4612
|
-
</Response>`
|
|
4613
|
-
};
|
|
4614
|
-
if (!this.isWebhookPathMatch(url.pathname, webhookPath)) return {
|
|
4615
|
-
statusCode: 404,
|
|
4616
|
-
body: "Not Found"
|
|
4617
|
-
};
|
|
4618
|
-
if (req.method !== "POST") return {
|
|
4619
|
-
statusCode: 405,
|
|
4620
|
-
body: "Method Not Allowed"
|
|
4621
|
-
};
|
|
4622
|
-
const headerGate = this.verifyPreAuthWebhookHeaders(req.headers);
|
|
4623
|
-
if (!headerGate.ok) {
|
|
4624
|
-
console.warn(`[voice-call] Webhook rejected before body read: ${headerGate.reason}`);
|
|
4625
|
-
return {
|
|
4626
|
-
statusCode: 401,
|
|
4627
|
-
body: "Unauthorized"
|
|
4628
|
-
};
|
|
4629
|
-
}
|
|
4630
|
-
const inFlightKey = req.socket.remoteAddress ?? "";
|
|
4631
|
-
if (!this.webhookInFlightLimiter.tryAcquire(inFlightKey)) {
|
|
4632
|
-
console.warn(`[voice-call] Webhook rejected before body read: too many in-flight requests`);
|
|
4633
|
-
return {
|
|
4634
|
-
statusCode: 429,
|
|
4635
|
-
body: "Too Many Requests"
|
|
4636
|
-
};
|
|
4637
|
-
}
|
|
4638
|
-
try {
|
|
4639
|
-
let body = "";
|
|
4640
|
-
try {
|
|
4641
|
-
body = await this.readBody(req, MAX_WEBHOOK_BODY_BYTES, WEBHOOK_BODY_TIMEOUT_MS);
|
|
4642
|
-
} catch (err) {
|
|
4643
|
-
if (isRequestBodyLimitError(err, "PAYLOAD_TOO_LARGE")) return {
|
|
4644
|
-
statusCode: 413,
|
|
4645
|
-
body: "Payload Too Large"
|
|
4646
|
-
};
|
|
4647
|
-
if (isRequestBodyLimitError(err, "REQUEST_BODY_TIMEOUT")) return {
|
|
4648
|
-
statusCode: 408,
|
|
4649
|
-
body: requestBodyErrorToText("REQUEST_BODY_TIMEOUT")
|
|
4650
|
-
};
|
|
4651
|
-
throw err;
|
|
4652
|
-
}
|
|
4653
|
-
const ctx = {
|
|
4654
|
-
headers: req.headers,
|
|
4655
|
-
rawBody: body,
|
|
4656
|
-
url: url.toString(),
|
|
4657
|
-
method: "POST",
|
|
4658
|
-
query: Object.fromEntries(url.searchParams),
|
|
4659
|
-
remoteAddress: req.socket.remoteAddress ?? void 0
|
|
4660
|
-
};
|
|
4661
|
-
const verification = this.provider.verifyWebhook(ctx);
|
|
4662
|
-
if (!verification.ok) {
|
|
4663
|
-
console.warn(`[voice-call] Webhook verification failed: ${verification.reason}`);
|
|
4664
|
-
return {
|
|
4665
|
-
statusCode: 401,
|
|
4666
|
-
body: "Unauthorized"
|
|
4667
|
-
};
|
|
4668
|
-
}
|
|
4669
|
-
if (!verification.verifiedRequestKey) {
|
|
4670
|
-
console.warn("[voice-call] Webhook verification succeeded without request identity key");
|
|
4671
|
-
return {
|
|
4672
|
-
statusCode: 401,
|
|
4673
|
-
body: "Unauthorized"
|
|
4674
|
-
};
|
|
4675
|
-
}
|
|
4676
|
-
const parsed = this.provider.parseWebhookEvent(ctx, { verifiedRequestKey: verification.verifiedRequestKey });
|
|
4677
|
-
if (verification.isReplay) console.warn("[voice-call] Replay detected; skipping event side effects");
|
|
4678
|
-
else this.processParsedEvents(parsed.events);
|
|
4679
|
-
return normalizeWebhookResponse(parsed);
|
|
4680
|
-
} finally {
|
|
4681
|
-
this.webhookInFlightLimiter.release(inFlightKey);
|
|
4682
|
-
}
|
|
4683
|
-
}
|
|
4684
|
-
verifyPreAuthWebhookHeaders(headers) {
|
|
4685
|
-
if (this.config.skipSignatureVerification) return { ok: true };
|
|
4686
|
-
switch (this.provider.name) {
|
|
4687
|
-
case "telnyx": {
|
|
4688
|
-
const signature = getHeader(headers, "telnyx-signature-ed25519");
|
|
4689
|
-
const timestamp = getHeader(headers, "telnyx-timestamp");
|
|
4690
|
-
if (signature && timestamp) return { ok: true };
|
|
4691
|
-
return {
|
|
4692
|
-
ok: false,
|
|
4693
|
-
reason: "missing Telnyx signature or timestamp header"
|
|
4694
|
-
};
|
|
4695
|
-
}
|
|
4696
|
-
case "twilio":
|
|
4697
|
-
if (getHeader(headers, "x-twilio-signature")) return { ok: true };
|
|
4698
|
-
return {
|
|
4699
|
-
ok: false,
|
|
4700
|
-
reason: "missing X-Twilio-Signature header"
|
|
4701
|
-
};
|
|
4702
|
-
case "plivo": {
|
|
4703
|
-
const hasV3 = Boolean(getHeader(headers, "x-plivo-signature-v3")) && Boolean(getHeader(headers, "x-plivo-signature-v3-nonce"));
|
|
4704
|
-
const hasV2 = Boolean(getHeader(headers, "x-plivo-signature-v2")) && Boolean(getHeader(headers, "x-plivo-signature-v2-nonce"));
|
|
4705
|
-
if (hasV3 || hasV2) return { ok: true };
|
|
4706
|
-
return {
|
|
4707
|
-
ok: false,
|
|
4708
|
-
reason: "missing Plivo signature headers"
|
|
4709
|
-
};
|
|
4710
|
-
}
|
|
4711
|
-
default: return { ok: true };
|
|
4712
|
-
}
|
|
4713
|
-
}
|
|
4714
|
-
processParsedEvents(events) {
|
|
4715
|
-
for (const event of events) try {
|
|
4716
|
-
this.manager.processEvent(event);
|
|
4717
|
-
} catch (err) {
|
|
4718
|
-
console.error(`[voice-call] Error processing event ${event.type}:`, err);
|
|
4719
|
-
}
|
|
4720
|
-
}
|
|
4721
|
-
writeWebhookResponse(res, payload) {
|
|
4722
|
-
res.statusCode = payload.statusCode;
|
|
4723
|
-
if (payload.headers) for (const [key, value] of Object.entries(payload.headers)) res.setHeader(key, value);
|
|
4724
|
-
res.end(payload.body);
|
|
4725
|
-
}
|
|
4726
|
-
/**
|
|
4727
|
-
* Read request body as string with timeout protection.
|
|
4728
|
-
*/
|
|
4729
|
-
readBody(req, maxBytes, timeoutMs = WEBHOOK_BODY_TIMEOUT_MS) {
|
|
4730
|
-
return readRequestBodyWithLimit(req, {
|
|
4731
|
-
maxBytes,
|
|
4732
|
-
timeoutMs
|
|
4733
|
-
});
|
|
4734
|
-
}
|
|
4735
|
-
/**
|
|
4736
|
-
* Handle auto-response for inbound calls using the agent system.
|
|
4737
|
-
* Supports tool calling for richer voice interactions.
|
|
4738
|
-
*/
|
|
4739
|
-
async handleInboundResponse(callId, userMessage) {
|
|
4740
|
-
console.log(`[voice-call] Auto-responding to inbound call ${callId}: "${userMessage}"`);
|
|
4741
|
-
const call = this.manager.getCall(callId);
|
|
4742
|
-
if (!call) {
|
|
4743
|
-
console.warn(`[voice-call] Call ${callId} not found for auto-response`);
|
|
4744
|
-
return;
|
|
4745
|
-
}
|
|
4746
|
-
if (!this.coreConfig) {
|
|
4747
|
-
console.warn("[voice-call] Core config missing; skipping auto-response");
|
|
4748
|
-
return;
|
|
4749
|
-
}
|
|
4750
|
-
if (!this.agentRuntime) {
|
|
4751
|
-
console.warn("[voice-call] Agent runtime missing; skipping auto-response");
|
|
4752
|
-
return;
|
|
4753
|
-
}
|
|
4754
|
-
try {
|
|
4755
|
-
const { generateVoiceResponse } = await import("./response-generator-ACP21F1j.js");
|
|
4756
|
-
const result = await generateVoiceResponse({
|
|
4757
|
-
voiceConfig: this.config,
|
|
4758
|
-
coreConfig: this.coreConfig,
|
|
4759
|
-
agentRuntime: this.agentRuntime,
|
|
4760
|
-
callId,
|
|
4761
|
-
from: call.from,
|
|
4762
|
-
transcript: call.transcript,
|
|
4763
|
-
userMessage
|
|
4764
|
-
});
|
|
4765
|
-
if (result.error) {
|
|
4766
|
-
console.error(`[voice-call] Response generation error: ${result.error}`);
|
|
4767
|
-
return;
|
|
4768
|
-
}
|
|
4769
|
-
if (result.text) {
|
|
4770
|
-
console.log(`[voice-call] AI response: "${result.text}"`);
|
|
4771
|
-
await this.manager.speak(callId, result.text);
|
|
4772
|
-
}
|
|
4773
|
-
} catch (err) {
|
|
4774
|
-
console.error(`[voice-call] Auto-response error:`, err);
|
|
4775
|
-
}
|
|
4776
|
-
}
|
|
4777
|
-
};
|
|
4778
|
-
//#endregion
|
|
4779
|
-
//#region extensions/voice-call/src/runtime.ts
|
|
4780
|
-
function createRuntimeResourceLifecycle(params) {
|
|
4781
|
-
let tunnelResult = null;
|
|
4782
|
-
let stopped = false;
|
|
4783
|
-
const runStep = async (step, suppressErrors) => {
|
|
4784
|
-
if (suppressErrors) {
|
|
4785
|
-
await step().catch(() => {});
|
|
4786
|
-
return;
|
|
4787
|
-
}
|
|
4788
|
-
await step();
|
|
4789
|
-
};
|
|
4790
|
-
return {
|
|
4791
|
-
setTunnelResult: (result) => {
|
|
4792
|
-
tunnelResult = result;
|
|
4793
|
-
},
|
|
4794
|
-
stop: async (opts) => {
|
|
4795
|
-
if (stopped) return;
|
|
4796
|
-
stopped = true;
|
|
4797
|
-
const suppressErrors = opts?.suppressErrors ?? false;
|
|
4798
|
-
await runStep(async () => {
|
|
4799
|
-
if (tunnelResult) await tunnelResult.stop();
|
|
4800
|
-
}, suppressErrors);
|
|
4801
|
-
await runStep(async () => {
|
|
4802
|
-
await cleanupTailscaleExposure(params.config);
|
|
4803
|
-
}, suppressErrors);
|
|
4804
|
-
await runStep(async () => {
|
|
4805
|
-
await params.webhookServer.stop();
|
|
4806
|
-
}, suppressErrors);
|
|
4807
|
-
}
|
|
4808
|
-
};
|
|
4809
|
-
}
|
|
4810
|
-
function isLoopbackBind(bind) {
|
|
4811
|
-
if (!bind) return false;
|
|
4812
|
-
return bind === "127.0.0.1" || bind === "::1" || bind === "localhost";
|
|
4813
|
-
}
|
|
4814
|
-
function resolveProvider(config) {
|
|
4815
|
-
const allowNgrokFreeTierLoopbackBypass = config.tunnel?.provider === "ngrok" && isLoopbackBind(config.serve?.bind) && (config.tunnel?.allowNgrokFreeTierLoopbackBypass ?? false);
|
|
4816
|
-
switch (config.provider) {
|
|
4817
|
-
case "telnyx": return new TelnyxProvider({
|
|
4818
|
-
apiKey: config.telnyx?.apiKey,
|
|
4819
|
-
connectionId: config.telnyx?.connectionId,
|
|
4820
|
-
publicKey: config.telnyx?.publicKey
|
|
4821
|
-
}, { skipVerification: config.skipSignatureVerification });
|
|
4822
|
-
case "twilio": return new TwilioProvider({
|
|
4823
|
-
accountSid: config.twilio?.accountSid,
|
|
4824
|
-
authToken: config.twilio?.authToken
|
|
4825
|
-
}, {
|
|
4826
|
-
allowNgrokFreeTierLoopbackBypass,
|
|
4827
|
-
publicUrl: config.publicUrl,
|
|
4828
|
-
skipVerification: config.skipSignatureVerification,
|
|
4829
|
-
streamPath: config.streaming?.enabled ? config.streaming.streamPath : void 0,
|
|
4830
|
-
webhookSecurity: config.webhookSecurity
|
|
4831
|
-
});
|
|
4832
|
-
case "plivo": return new PlivoProvider({
|
|
4833
|
-
authId: config.plivo?.authId,
|
|
4834
|
-
authToken: config.plivo?.authToken
|
|
4835
|
-
}, {
|
|
4836
|
-
publicUrl: config.publicUrl,
|
|
4837
|
-
skipVerification: config.skipSignatureVerification,
|
|
4838
|
-
ringTimeoutSec: Math.max(1, Math.floor(config.ringTimeoutMs / 1e3)),
|
|
4839
|
-
webhookSecurity: config.webhookSecurity
|
|
4840
|
-
});
|
|
4841
|
-
case "mock": return new MockProvider();
|
|
4842
|
-
default: throw new Error(`Unsupported voice-call provider: ${String(config.provider)}`);
|
|
4843
|
-
}
|
|
4844
|
-
}
|
|
4845
|
-
async function createVoiceCallRuntime(params) {
|
|
4846
|
-
const { config: rawConfig, coreConfig, agentRuntime, ttsRuntime, logger } = params;
|
|
4847
|
-
const log = logger ?? {
|
|
4848
|
-
info: console.log,
|
|
4849
|
-
warn: console.warn,
|
|
4850
|
-
error: console.error,
|
|
4851
|
-
debug: console.debug
|
|
4852
|
-
};
|
|
4853
|
-
const config = resolveVoiceCallConfig(rawConfig);
|
|
4854
|
-
if (!config.enabled) throw new Error("Voice call disabled. Enable the plugin entry in config.");
|
|
4855
|
-
if (config.skipSignatureVerification) log.warn("[voice-call] SECURITY WARNING: skipSignatureVerification=true disables webhook signature verification (development only). Do not use in production.");
|
|
4856
|
-
const validation = validateProviderConfig(config);
|
|
4857
|
-
if (!validation.valid) throw new Error(`Invalid voice-call config: ${validation.errors.join("; ")}`);
|
|
4858
|
-
const provider = resolveProvider(config);
|
|
4859
|
-
const manager = new CallManager(config);
|
|
4860
|
-
const webhookServer = new VoiceCallWebhookServer(config, manager, provider, coreConfig, agentRuntime);
|
|
4861
|
-
const lifecycle = createRuntimeResourceLifecycle({
|
|
4862
|
-
config,
|
|
4863
|
-
webhookServer
|
|
4864
|
-
});
|
|
4865
|
-
const localUrl = await webhookServer.start();
|
|
4866
|
-
try {
|
|
4867
|
-
let publicUrl = config.publicUrl ?? null;
|
|
4868
|
-
if (!publicUrl && config.tunnel?.provider && config.tunnel.provider !== "none") try {
|
|
4869
|
-
const nextTunnelResult = await startTunnel({
|
|
4870
|
-
provider: config.tunnel.provider,
|
|
4871
|
-
port: config.serve.port,
|
|
4872
|
-
path: config.serve.path,
|
|
4873
|
-
ngrokAuthToken: config.tunnel.ngrokAuthToken,
|
|
4874
|
-
ngrokDomain: config.tunnel.ngrokDomain
|
|
4875
|
-
});
|
|
4876
|
-
lifecycle.setTunnelResult(nextTunnelResult);
|
|
4877
|
-
publicUrl = nextTunnelResult?.publicUrl ?? null;
|
|
4878
|
-
} catch (err) {
|
|
4879
|
-
log.error(`[voice-call] Tunnel setup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
4880
|
-
}
|
|
4881
|
-
if (!publicUrl && config.tailscale?.mode !== "off") publicUrl = await setupTailscaleExposure(config);
|
|
4882
|
-
const webhookUrl = publicUrl ?? localUrl;
|
|
4883
|
-
if (publicUrl && provider.name === "twilio") provider.setPublicUrl(publicUrl);
|
|
4884
|
-
if (provider.name === "twilio" && config.streaming?.enabled) {
|
|
4885
|
-
const twilioProvider = provider;
|
|
4886
|
-
if (ttsRuntime?.textToSpeechTelephony) try {
|
|
4887
|
-
const ttsProvider = createTelephonyTtsProvider({
|
|
4888
|
-
coreConfig,
|
|
4889
|
-
ttsOverride: config.tts,
|
|
4890
|
-
runtime: ttsRuntime,
|
|
4891
|
-
logger: log
|
|
4892
|
-
});
|
|
4893
|
-
twilioProvider.setTTSProvider(ttsProvider);
|
|
4894
|
-
log.info("[voice-call] Telephony TTS provider configured");
|
|
4895
|
-
} catch (err) {
|
|
4896
|
-
log.warn(`[voice-call] Failed to initialize telephony TTS: ${err instanceof Error ? err.message : String(err)}`);
|
|
4897
|
-
}
|
|
4898
|
-
else log.warn("[voice-call] Telephony TTS unavailable; streaming TTS disabled");
|
|
4899
|
-
const mediaHandler = webhookServer.getMediaStreamHandler();
|
|
4900
|
-
if (mediaHandler) {
|
|
4901
|
-
twilioProvider.setMediaStreamHandler(mediaHandler);
|
|
4902
|
-
log.info("[voice-call] Media stream handler wired to provider");
|
|
4903
|
-
}
|
|
4904
|
-
}
|
|
4905
|
-
await manager.initialize(provider, webhookUrl);
|
|
4906
|
-
const stop = async () => await lifecycle.stop();
|
|
4907
|
-
log.info("[voice-call] Runtime initialized");
|
|
4908
|
-
log.info(`[voice-call] Webhook URL: ${webhookUrl}`);
|
|
4909
|
-
if (publicUrl) log.info(`[voice-call] Public URL: ${publicUrl}`);
|
|
4910
|
-
return {
|
|
4911
|
-
config,
|
|
4912
|
-
provider,
|
|
4913
|
-
manager,
|
|
4914
|
-
webhookServer,
|
|
4915
|
-
webhookUrl,
|
|
4916
|
-
publicUrl,
|
|
4917
|
-
stop
|
|
4918
|
-
};
|
|
4919
|
-
} catch (err) {
|
|
4920
|
-
await lifecycle.stop({ suppressErrors: true });
|
|
4921
|
-
throw err;
|
|
4922
|
-
}
|
|
4923
|
-
}
|
|
4924
|
-
//#endregion
|
|
4925
|
-
export { resolveUserPath as a, validateProviderConfig as c, setupTailscaleExposureRoute as i, cleanupTailscaleExposureRoute as n, VoiceCallConfigSchema as o, getTailscaleSelfInfo as r, resolveVoiceCallConfig as s, createVoiceCallRuntime as t };
|