UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

1,141 lines (1,140 loc) 46.7 kB
import { a as normalizeLowercaseStringOrEmpty, c as normalizeOptionalString, l as normalizeOptionalStringifiedId } from "./string-coerce-mnp54Vah.js"; import { u as parseThreadSessionSuffix } from "./session-key-utils-Bx3apsJ3.js"; import { n as normalizeAccountId, t as DEFAULT_ACCOUNT_ID } from "./account-id-Df9e41E6.js"; import { u as resolveStorePath } from "./paths-NEwU8m3X.js"; import { V as resolveSessionStoreEntry } from "./store-load-C4uq6Lrq.js"; import { i as createLazyRuntimeNamedExport } from "./lazy-runtime-D-7_JraP.js"; import { b as readStringParam, g as readPositiveIntegerParam, i as createActionGate, t as ToolAuthorizationError } from "./common-C4yy9V-D.js"; import { l as createScopedDmSecurityResolver, t as adaptScopedAccountAccessor } from "./channel-config-helpers-lj5quus3.js"; import "./string-coerce-runtime-CEGJWkQ_.js"; import { t as chunkTextForOutbound } from "./text-chunking-D45TeeEs.js"; import { n as describeAccountSnapshot } from "./account-helpers-pcH3lGFY.js"; import { t as createScopedAccountReplyToModeResolver } from "./threading-helpers-CMXJIj4M.js"; import { i as createChatChannelPlugin, n as buildThreadAwareOutboundSessionRoute, t as buildChannelOutboundSessionRoute } from "./core-DSxVv-v1.js"; import "./channel-core-IukkNCd-.js"; import "./routing-CQ63ICPX.js"; import { _ as createAllowlistProviderOpenWarningCollector, w as projectAccountConfigWarningCollector } from "./channel-policy-CIW58SA5.js"; import { t as createChannelDirectoryAdapter } from "./directory-runtime-om8jJAZt.js"; import { i as createResolvedDirectoryEntriesLister } from "./directory-config-helpers-DEDTHx2P.js"; import { n as createRuntimeOutboundDelegates, t as createRuntimeDirectoryLiveAdapter } from "./runtime-forwarders-Dxil5z45.js"; import { i as splitChannelApprovalCapability, n as createApproverRestrictedNativeApprovalCapability, r as createChannelApprovalCapability } from "./approval-delivery-helpers-IkWw0ui8.js"; import "./approval-delivery-runtime-CYw5jxIp.js"; import { n as createLazyChannelApprovalNativeRuntimeAdapter } from "./approval-handler-adapter-runtime-DLWtIqHq.js"; import { n as resolveApprovalRequestSessionConversation } from "./exec-approval-session-target-DfDihhuU.js"; import { r as createChannelNativeOriginTargetResolver } from "./approval-native-helpers-WitYuyrm.js"; import "./approval-native-runtime-BeD_vgZw.js"; import { w as createChannelMessageAdapterFromOutbound } from "./channel-outbound-B3_Zy-kG.js"; import "./conversation-runtime-VjM1-D7a.js"; import { t as PAIRING_APPROVED_MESSAGE } from "./pairing-message-DNhqI-OE.js"; import { c as collectStatusIssuesFromLastError, d as createDefaultChannelRuntimeState, i as buildProbeChannelStatusSummary, u as createComputedAccountStatusAdapter } from "./status-helpers-BevxX6Pu.js"; import "./channel-status-DxlXr0Xh.js"; import "./channel-actions-BOfvC-Gl.js"; import { i as createPairingPrefixStripper } from "./channel-pairing-Bk6_JhTm.js"; import { t as loadSessionStore } from "./session-store-runtime-rvUx2Fil.js"; import { t as extractToolSend } from "./tool-send-DzTXHBeR.js"; import { n as requiresExplicitMatrixDefaultAccount } from "./account-selection-CqvkLWBU.js"; import { a as resolveMatrixAccountConfig } from "./account-config-C2f9UtTn.js"; import { i as resolveMatrixAccount, r as resolveDefaultMatrixAccountId, t as listMatrixAccountIds } from "./accounts-ChjotiNL.js"; import { n as normalizeMatrixUserId } from "./allowlist-k_lkAH4x.js"; import { t as normalizeMatrixApproverId } from "./approval-ids-B2lyZmro.js"; import { a as isMatrixExecApprovalAuthorizedSender, c as shouldHandleMatrixApprovalRequest, d as matrixApprovalAuth, i as isMatrixApprovalClientEnabled, l as shouldSuppressLocalMatrixExecApprovalPrompt, n as getMatrixExecApprovalApprovers, o as isMatrixExecApprovalClientEnabled, r as isMatrixAnyApprovalClientEnabled, s as resolveMatrixExecApprovalTarget, t as getMatrixApprovalApprovers, u as getMatrixApprovalAuthApprovers } from "./exec-approvals-DAIt4CGn.js"; import { a as resolveMatrixTargetIdentity, i as resolveMatrixDirectUserId, n as normalizeMatrixMessagingTarget, r as normalizeMatrixResolvableTarget } from "./target-ids-BCKwsiMr.js"; import { t as formatMatrixErrorMessage } from "./errors-C4iaVh6O.js"; import { n as matrixConfigAdapter, t as MatrixChannelConfigSchema } from "./config-schema-BC0vqTXm.js"; import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-pj2qdle9.js"; import { n as resolveMatrixRoomConfig, t as resolveMatrixStoredSessionMeta } from "./session-store-metadata-DgTVHTWy.js"; import { d as setMatrixThreadBindingIdleTimeoutBySessionKey, p as setMatrixThreadBindingMaxAgeBySessionKey } from "./thread-bindings-shared-8xq3Fro3.js"; import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-DkDq4QVN.js"; import { a as namedAccountPromotionKeys, o as resolveSingleAccountPromotionTarget, s as singleAccountKeysToMove } from "./setup-dm-policy-CfftVKVu.js"; import { n as matrixSetupAdapter, t as createMatrixSetupWizardProxy } from "./setup-core-CJKT0lOC.js"; import { i as autoPrepareLegacyMatrixCrypto, o as autoMigrateLegacyMatrixState, r as resolveMatrixMigrationStatus } from "./matrix-migration.runtime.js"; import { t as maybeCreateMatrixMigrationSnapshot } from "./migration-snapshot-backup-B3kTguEe.js"; import { n as resolveMatrixInboundConversation } from "./thread-binding-api-D8Tu94PL.js"; import { Type } from "typebox"; //#region extensions/matrix/src/actions.ts const MATRIX_PLUGIN_HANDLED_ACTIONS = new Set([ "send", "poll-vote", "react", "reactions", "read", "edit", "delete", "pin", "unpin", "list-pins", "set-profile", "member-info", "channel-info", "permissions" ]); const MATRIX_PROFILE_MEDIA_PROPERTIES = { avatarUrl: Type.Optional(Type.String({ description: "Profile avatar URL for Matrix self-profile update actions. Matrix accepts mxc:// and http(s) URLs." })), avatar_url: Type.Optional(Type.String({ description: "snake_case alias of avatarUrl for Matrix self-profile update actions. Matrix accepts mxc:// and http(s) URLs." })), avatarPath: Type.Optional(Type.String({ description: "Local avatar file path for Matrix self-profile update actions. Matrix uploads this file and sets the resulting MXC URI." })), avatar_path: Type.Optional(Type.String({ description: "snake_case alias of avatarPath for Matrix self-profile update actions. Matrix uploads this file and sets the resulting MXC URI." })) }; const MATRIX_PROFILE_MEDIA_SOURCE_PARAMS = Object.freeze(["avatarUrl", "avatarPath"]); function createMatrixExposedActions(params) { const actions = new Set(["poll", "poll-vote"]); if (params.gate("messages")) { actions.add("send"); actions.add("read"); actions.add("edit"); actions.add("delete"); } if (params.gate("reactions")) { actions.add("react"); actions.add("reactions"); } if (params.gate("pins")) { actions.add("pin"); actions.add("unpin"); actions.add("list-pins"); } if (params.gate("profile") && params.senderIsOwner === true) actions.add("set-profile"); if (params.gate("memberInfo")) actions.add("member-info"); if (params.gate("channelInfo")) actions.add("channel-info"); if (params.encryptionEnabled && params.gate("verification")) actions.add("permissions"); return actions; } function buildMatrixProfileToolSchema() { return { actions: ["set-profile"], properties: { displayName: Type.Optional(Type.String({ description: "Profile display name for Matrix self-profile update actions." })), display_name: Type.Optional(Type.String({ description: "snake_case alias of displayName for Matrix self-profile update actions." })), ...MATRIX_PROFILE_MEDIA_PROPERTIES } }; } const matrixMessageActions = { describeMessageTool: ({ cfg, accountId, senderIsOwner }) => { const resolvedCfg = cfg; if (!accountId && requiresExplicitMatrixDefaultAccount(resolvedCfg)) return { actions: [], capabilities: [] }; const account = resolveMatrixAccount({ cfg: resolvedCfg, accountId: accountId ?? resolveDefaultMatrixAccountId(resolvedCfg) }); if (!account.enabled || !account.configured) return { actions: [], capabilities: [] }; const actions = createMatrixExposedActions({ gate: createActionGate(account.config.actions), encryptionEnabled: account.config.encryption === true, senderIsOwner }); const listedActions = Array.from(actions); return { actions: listedActions, capabilities: [], schema: listedActions.includes("set-profile") ? buildMatrixProfileToolSchema() : null, mediaSourceParams: listedActions.includes("set-profile") ? { "set-profile": MATRIX_PROFILE_MEDIA_SOURCE_PARAMS } : null }; }, supportsAction: ({ action }) => MATRIX_PLUGIN_HANDLED_ACTIONS.has(action), extractToolSend: ({ args }) => { return extractToolSend(args, "sendMessage"); }, handleAction: async (ctx) => { const { handleMatrixAction } = await import("./tool-actions.runtime.js"); const { action, params, cfg, accountId, mediaLocalRoots } = ctx; const dispatch = async (actionParams) => await handleMatrixAction({ ...actionParams, ...accountId ? { accountId } : {} }, cfg, { mediaLocalRoots }); const resolveRoomId = () => readStringParam(params, "roomId") ?? readStringParam(params, "channelId") ?? readStringParam(params, "to", { required: true }); if (action === "send") { const to = readStringParam(params, "to", { required: true }); const mediaUrl = readStringParam(params, "media", { trim: false }) ?? readStringParam(params, "mediaUrl", { trim: false }) ?? readStringParam(params, "filePath", { trim: false }) ?? readStringParam(params, "path", { trim: false }); const content = readStringParam(params, "message", { required: !mediaUrl, allowEmpty: true }); const replyTo = readStringParam(params, "replyTo"); const threadId = readStringParam(params, "threadId"); const audioAsVoice = typeof params.asVoice === "boolean" ? params.asVoice : typeof params.audioAsVoice === "boolean" ? params.audioAsVoice : void 0; return await dispatch({ action: "sendMessage", to, content, mediaUrl: mediaUrl ?? void 0, replyToId: replyTo ?? void 0, threadId: threadId ?? void 0, audioAsVoice }); } if (action === "poll-vote") return await dispatch({ ...params, action: "pollVote" }); if (action === "react") { const messageId = readStringParam(params, "messageId", { required: true }); const emoji = readStringParam(params, "emoji", { allowEmpty: true }); const remove = typeof params.remove === "boolean" ? params.remove : void 0; return await dispatch({ action: "react", roomId: resolveRoomId(), messageId, emoji, remove }); } if (action === "reactions") { const messageId = readStringParam(params, "messageId", { required: true }); const limit = readPositiveIntegerParam(params, "limit", { message: "limit must be a positive integer." }); return await dispatch({ action: "reactions", roomId: resolveRoomId(), messageId, limit }); } if (action === "read") { const limit = readPositiveIntegerParam(params, "limit", { message: "limit must be a positive integer." }); return await dispatch({ action: "readMessages", roomId: resolveRoomId(), limit, before: readStringParam(params, "before"), after: readStringParam(params, "after"), threadId: readStringParam(params, "threadId") }); } if (action === "edit") { const messageId = readStringParam(params, "messageId", { required: true }); const content = readStringParam(params, "message", { required: true }); return await dispatch({ action: "editMessage", roomId: resolveRoomId(), messageId, content }); } if (action === "delete") { const messageId = readStringParam(params, "messageId", { required: true }); return await dispatch({ action: "deleteMessage", roomId: resolveRoomId(), messageId }); } if (action === "pin" || action === "unpin" || action === "list-pins") { const messageId = action === "list-pins" ? void 0 : readStringParam(params, "messageId", { required: true }); return await dispatch({ action: action === "pin" ? "pinMessage" : action === "unpin" ? "unpinMessage" : "listPins", roomId: resolveRoomId(), messageId }); } if (action === "set-profile") { if (ctx.senderIsOwner !== true) throw new ToolAuthorizationError("Matrix profile updates require owner access."); const avatarPath = readStringParam(params, "avatarPath") ?? readStringParam(params, "path") ?? readStringParam(params, "filePath"); return await dispatch({ action: "setProfile", displayName: readStringParam(params, "displayName") ?? readStringParam(params, "name"), avatarUrl: readStringParam(params, "avatarUrl"), avatarPath }); } if (action === "member-info") return await dispatch({ action: "memberInfo", userId: readStringParam(params, "userId", { required: true }), roomId: readStringParam(params, "roomId") ?? readStringParam(params, "channelId") }); if (action === "channel-info") return await dispatch({ action: "channelInfo", roomId: resolveRoomId() }); if (action === "permissions") { const operation = normalizeLowercaseStringOrEmpty(readStringParam(params, "operation") ?? readStringParam(params, "mode") ?? "verification-list"); const operationToAction = { "encryption-status": "encryptionStatus", "verification-status": "verificationStatus", "verification-bootstrap": "verificationBootstrap", "verification-recovery-key": "verificationRecoveryKey", "verification-backup-status": "verificationBackupStatus", "verification-backup-restore": "verificationBackupRestore", "verification-list": "verificationList", "verification-request": "verificationRequest", "verification-accept": "verificationAccept", "verification-cancel": "verificationCancel", "verification-start": "verificationStart", "verification-generate-qr": "verificationGenerateQr", "verification-scan-qr": "verificationScanQr", "verification-sas": "verificationSas", "verification-confirm": "verificationConfirm", "verification-mismatch": "verificationMismatch", "verification-confirm-qr": "verificationConfirmQr" }; const resolvedAction = operationToAction[operation]; if (!resolvedAction) throw new Error(`Unsupported Matrix permissions operation: ${operation}. Supported values: ${Object.keys(operationToAction).join(", ")}`); return await dispatch({ ...params, action: resolvedAction }); } throw new Error(`Action ${action} is not supported for provider matrix.`); } }; //#endregion //#region extensions/matrix/src/approval-native.ts function normalizeComparableTarget(value) { const target = resolveMatrixTargetIdentity(value); if (!target) return normalizeLowercaseStringOrEmpty(value); if (target.kind === "user") return `user:${normalizeMatrixUserId(target.id)}`; return `${normalizeLowercaseStringOrEmpty(target.kind)}:${target.id}`; } function resolveMatrixNativeTarget(raw) { const target = resolveMatrixTargetIdentity(raw); if (!target) return null; return target.kind === "user" ? `user:${target.id}` : `room:${target.id}`; } function resolveTurnSourceMatrixOriginTarget(request) { const turnSourceChannel = normalizeLowercaseStringOrEmpty(request.request.turnSourceChannel); const target = resolveMatrixNativeTarget(request.request.turnSourceTo?.trim() || ""); if (turnSourceChannel !== "matrix" || !target) return null; return { to: target, threadId: normalizeOptionalStringifiedId(request.request.turnSourceThreadId) }; } function resolveSessionMatrixOriginTarget(sessionTarget) { const target = resolveMatrixNativeTarget(sessionTarget.to); if (!target) return null; return { to: target, threadId: normalizeOptionalStringifiedId(sessionTarget.threadId) }; } function normalizeMatrixOriginTarget(target) { return { ...target, to: normalizeComparableTarget(target.to) }; } function hasMatrixPluginApprovers(params) { return getMatrixApprovalAuthApprovers(params).length > 0; } function availabilityState(enabled) { return enabled ? { kind: "enabled" } : { kind: "disabled" }; } function hasMatrixApprovalApprovers(params) { return getMatrixApprovalApprovers({ cfg: params.cfg, accountId: params.accountId, approvalKind: params.approvalKind }).length > 0; } function hasAnyMatrixApprovalApprovers(params) { return getMatrixExecApprovalApprovers(params).length > 0 || getMatrixApprovalAuthApprovers(params).length > 0; } function isMatrixPluginAuthorizedSender(params) { const normalizedSenderId = params.senderId ? normalizeMatrixApproverId(params.senderId) : void 0; if (!normalizedSenderId) return false; return getMatrixApprovalAuthApprovers(params).includes(normalizedSenderId); } function resolveSuppressionAccountId(params) { return params.target.accountId?.trim() || params.request.request.turnSourceAccountId?.trim() || void 0; } const resolveMatrixOriginTarget = createChannelNativeOriginTargetResolver({ channel: "matrix", shouldHandleRequest: ({ cfg, accountId, request }) => shouldHandleMatrixApprovalRequest({ cfg, accountId, request }), resolveTurnSourceTarget: resolveTurnSourceMatrixOriginTarget, resolveSessionTarget: resolveSessionMatrixOriginTarget, normalizeTargetForMatch: normalizeMatrixOriginTarget, resolveFallbackTarget: (request) => { const sessionConversation = resolveApprovalRequestSessionConversation({ request, channel: "matrix" }); if (!sessionConversation) return null; const target = resolveMatrixNativeTarget(sessionConversation.id); if (!target) return null; return { to: target, threadId: normalizeOptionalStringifiedId(sessionConversation.threadId) }; } }); function resolveMatrixApproverDmTargets(params) { if (!shouldHandleMatrixApprovalRequest(params)) return []; return getMatrixApprovalApprovers(params).map((approver) => { const normalized = normalizeMatrixUserId(approver); return normalized ? { to: `user:${normalized}` } : null; }).filter((target) => target !== null); } const matrixNativeApprovalCapability = createApproverRestrictedNativeApprovalCapability({ channel: "matrix", channelLabel: "Matrix", describeExecApprovalSetup: ({ accountId }) => { const prefix = accountId && accountId !== "default" ? `channels.matrix.accounts.${accountId}` : "channels.matrix"; return `Approve it from the Web UI or terminal UI for now. Matrix supports native exec approvals for this account. Configure \`${prefix}.execApprovals.approvers\` or \`${prefix}.dm.allowFrom\`; leave \`${prefix}.execApprovals.enabled\` unset/\`auto\` or set it to \`true\`.`; }, listAccountIds: listMatrixAccountIds, hasApprovers: ({ cfg, accountId }) => hasAnyMatrixApprovalApprovers({ cfg, accountId }), isExecAuthorizedSender: ({ cfg, accountId, senderId }) => isMatrixExecApprovalAuthorizedSender({ cfg, accountId, senderId }), isPluginAuthorizedSender: ({ cfg, accountId, senderId }) => isMatrixPluginAuthorizedSender({ cfg, accountId, senderId }), isNativeDeliveryEnabled: ({ cfg, accountId }) => isMatrixExecApprovalClientEnabled({ cfg, accountId }), resolveNativeDeliveryMode: ({ cfg, accountId }) => resolveMatrixExecApprovalTarget({ cfg, accountId }), requireMatchingTurnSourceChannel: true, resolveSuppressionAccountId, resolveOriginTarget: resolveMatrixOriginTarget, resolveApproverDmTargets: resolveMatrixApproverDmTargets, notifyOriginWhenDmOnly: true, nativeRuntime: createLazyChannelApprovalNativeRuntimeAdapter({ eventKinds: ["exec", "plugin"], isConfigured: ({ cfg, accountId }) => isMatrixAnyApprovalClientEnabled({ cfg, accountId }), shouldHandle: ({ cfg, accountId, request }) => shouldHandleMatrixApprovalRequest({ cfg, accountId, request }), load: async () => (await import("./approval-handler.runtime-BvAj0bjN.js")).matrixApprovalNativeRuntime }) }); const splitMatrixApprovalCapability = splitChannelApprovalCapability(matrixNativeApprovalCapability); const matrixBaseNativeApprovalAdapter = splitMatrixApprovalCapability.native; const matrixBaseDeliveryAdapter = splitMatrixApprovalCapability.delivery; const matrixDeliveryAdapter = matrixBaseDeliveryAdapter && { ...matrixBaseDeliveryAdapter, shouldSuppressForwardingFallback: (params) => { const accountId = resolveSuppressionAccountId(params); if (!hasMatrixApprovalApprovers({ cfg: params.cfg, accountId, approvalKind: params.approvalKind })) return false; return matrixBaseDeliveryAdapter.shouldSuppressForwardingFallback?.(params) ?? false; } }; const matrixNativeAdapter = matrixBaseNativeApprovalAdapter && { describeDeliveryCapabilities: (params) => { const capabilities = matrixBaseNativeApprovalAdapter.describeDeliveryCapabilities(params); const hasApprovers = hasMatrixApprovalApprovers({ cfg: params.cfg, accountId: params.accountId, approvalKind: params.approvalKind }); const clientEnabled = isMatrixApprovalClientEnabled({ cfg: params.cfg, accountId: params.accountId, approvalKind: params.approvalKind }); return { ...capabilities, enabled: capabilities.enabled && hasApprovers && clientEnabled }; }, resolveOriginTarget: matrixBaseNativeApprovalAdapter.resolveOriginTarget, resolveApproverDmTargets: matrixBaseNativeApprovalAdapter.resolveApproverDmTargets }; const matrixApprovalCapability = createChannelApprovalCapability({ authorizeActorAction: (params) => { if (params.approvalKind !== "plugin") return matrixNativeApprovalCapability.authorizeActorAction?.(params) ?? { authorized: true }; if (!hasMatrixPluginApprovers({ cfg: params.cfg, accountId: params.accountId })) return { authorized: false, reason: "❌ Matrix plugin approvals are not enabled for this bot account." }; return matrixApprovalAuth.authorizeActorAction(params); }, getActionAvailabilityState: (params) => { if (params.approvalKind === "plugin") return availabilityState(hasMatrixPluginApprovers({ cfg: params.cfg, accountId: params.accountId })); return matrixNativeApprovalCapability.getActionAvailabilityState?.(params) ?? { kind: "disabled" }; }, getExecInitiatingSurfaceState: (params) => matrixNativeApprovalCapability.getExecInitiatingSurfaceState?.(params) ?? { kind: "disabled" }, describeExecApprovalSetup: matrixNativeApprovalCapability.describeExecApprovalSetup, delivery: matrixDeliveryAdapter, nativeRuntime: matrixNativeApprovalCapability.nativeRuntime, native: matrixNativeAdapter, render: matrixNativeApprovalCapability.render }); //#endregion //#region extensions/matrix/src/channel-account-paths.ts function createMatrixProbeAccount(params) { return async ({ account, timeoutMs, cfg }) => { try { const auth = await params.resolveMatrixAuth({ cfg, accountId: account.accountId }); return await params.probeMatrix({ homeserver: auth.homeserver, accessToken: auth.accessToken, userId: auth.userId, deviceId: auth.deviceId, timeoutMs: timeoutMs ?? 5e3, accountId: account.accountId, allowPrivateNetwork: auth.allowPrivateNetwork, ssrfPolicy: auth.ssrfPolicy, dispatcherPolicy: auth.dispatcherPolicy }); } catch (err) { return { ok: false, error: formatMatrixErrorMessage(err), elapsedMs: 0 }; } }; } function createMatrixPairingText(sendMessageMatrix) { return { idLabel: "matrixUserId", message: PAIRING_APPROVED_MESSAGE, normalizeAllowEntry: createPairingPrefixStripper(/^matrix:/i), notify: async ({ id, message, cfg, accountId }) => { await sendMessageMatrix(`user:${id}`, message, { cfg, ...accountId ? { accountId } : {} }); } }; } //#endregion //#region extensions/matrix/src/group-mentions.ts function resolveMatrixRoomConfigForGroup(params) { const roomId = normalizeMatrixResolvableTarget(params.groupId?.trim() ?? ""); const groupChannel = params.groupChannel?.trim() ?? ""; const aliases = groupChannel ? [normalizeMatrixResolvableTarget(groupChannel)] : []; const cfg = params.cfg; const matrixConfig = resolveMatrixAccountConfig({ cfg, accountId: params.accountId }); return resolveMatrixRoomConfig({ rooms: matrixConfig.groups ?? matrixConfig.rooms, roomId, aliases }).config; } function resolveMatrixGroupRequireMention(params) { const resolved = resolveMatrixRoomConfigForGroup(params); if (resolved) { if (resolved.autoReply === true) return false; if (resolved.autoReply === false) return true; if (typeof resolved.requireMention === "boolean") return resolved.requireMention; } return true; } function resolveMatrixGroupToolPolicy(params) { return resolveMatrixRoomConfigForGroup(params)?.tools; } //#endregion //#region extensions/matrix/src/resolver.ts const loadMatrixChannelRuntime$1 = createLazyRuntimeNamedExport(() => import("./resolver.runtime.js"), "matrixResolverRuntime"); const matrixResolverAdapter = { resolveTargets: async ({ cfg, accountId, inputs, kind, runtime }) => (await loadMatrixChannelRuntime$1()).resolveMatrixTargets({ cfg, accountId, inputs, kind, runtime }) }; //#endregion //#region extensions/matrix/src/session-route.ts function resolveEffectiveMatrixAccountId(params) { return normalizeAccountId(params.accountId ?? resolveDefaultMatrixAccountId(params.cfg)); } function resolveMatrixDmSessionScope(params) { return resolveMatrixAccountConfig({ cfg: params.cfg, accountId: params.accountId }).dm?.sessionScope ?? "per-user"; } function resolveMatrixCurrentDmRoomId(params) { const sessionKey = parseThreadSessionSuffix(params.currentSessionKey).baseSessionKey ?? params.currentSessionKey?.trim(); if (!sessionKey) return; try { const existing = resolveSessionStoreEntry({ store: loadSessionStore(resolveStorePath(params.cfg.session?.store, { agentId: params.agentId })), sessionKey }).existing; const currentSession = resolveMatrixStoredSessionMeta(existing); if (!currentSession) return; if (currentSession.accountId && currentSession.accountId !== params.accountId) return; if (!currentSession.directUserId || currentSession.directUserId !== params.targetUserId) return; return currentSession.roomId; } catch { return; } } function resolveMatrixOutboundSessionRoute(params) { const target = resolveMatrixTargetIdentity(params.resolvedTarget?.to ?? params.target) ?? resolveMatrixTargetIdentity(params.target); if (!target) return null; const effectiveAccountId = resolveEffectiveMatrixAccountId(params); const roomScopedDmId = target.kind === "user" && resolveMatrixDmSessionScope({ cfg: params.cfg, accountId: effectiveAccountId }) === "per-room" ? resolveMatrixCurrentDmRoomId({ cfg: params.cfg, agentId: params.agentId, accountId: effectiveAccountId, currentSessionKey: params.currentSessionKey, targetUserId: target.id }) : void 0; const peer = roomScopedDmId !== void 0 ? { kind: "channel", id: roomScopedDmId } : { kind: target.kind === "user" ? "direct" : "channel", id: target.id }; const chatType = target.kind === "user" ? "direct" : "channel"; const from = target.kind === "user" ? `matrix:${target.id}` : `matrix:channel:${target.id}`; const to = `room:${roomScopedDmId ?? target.id}`; return buildThreadAwareOutboundSessionRoute({ route: buildChannelOutboundSessionRoute({ cfg: params.cfg, agentId: params.agentId, channel: "matrix", accountId: effectiveAccountId, peer, chatType, from, to }), replyToId: params.replyToId, threadId: params.threadId, currentSessionKey: params.currentSessionKey, normalizeThreadId: (threadId) => threadId, canRecoverCurrentThread: ({ route }) => route.peer.kind !== "direct" || (params.cfg.session?.dmScope ?? "main") !== "main" }); } //#endregion //#region extensions/matrix/src/startup-maintenance.ts function logWarningOnlyMatrixMigrationReasons(params) { if (params.status.legacyState && "warning" in params.status.legacyState) params.log.warn?.(`matrix: ${params.status.legacyState.warning}`); if (params.status.legacyCrypto.warnings.length > 0) params.log.warn?.(`matrix: legacy encrypted-state warnings:\n${params.status.legacyCrypto.warnings.map((entry) => `- ${entry}`).join("\n")}`); } async function runBestEffortMatrixMigrationStep(params) { try { await params.run(); } catch (err) { params.log.warn?.(`${params.logPrefix?.trim() || "gateway"}: ${params.label} failed during Matrix migration; continuing startup: ${String(err)}`); } } async function runMatrixStartupMaintenance(params) { const env = params.env ?? process.env; const createSnapshot = params.deps?.maybeCreateMatrixMigrationSnapshot ?? maybeCreateMatrixMigrationSnapshot; const migrateLegacyState = params.deps?.autoMigrateLegacyMatrixState ?? autoMigrateLegacyMatrixState; const prepareLegacyCrypto = params.deps?.autoPrepareLegacyMatrixCrypto ?? autoPrepareLegacyMatrixCrypto; const trigger = params.trigger?.trim() || "gateway-startup"; const logPrefix = params.logPrefix?.trim() || "gateway"; const migrationStatus = resolveMatrixMigrationStatus({ cfg: params.cfg, env }); if (!migrationStatus.pending) return; if (!migrationStatus.actionable) { params.log.info?.("matrix: migration remains in a warning-only state; no pre-migration snapshot was needed yet"); logWarningOnlyMatrixMigrationReasons({ status: migrationStatus, log: params.log }); return; } try { await createSnapshot({ trigger, env, log: params.log }); } catch (err) { params.log.warn?.(`${logPrefix}: failed creating a Matrix migration snapshot; skipping Matrix migration for now: ${String(err)}`); return; } await runBestEffortMatrixMigrationStep({ label: "legacy Matrix state migration", log: params.log, logPrefix, run: () => migrateLegacyState({ cfg: params.cfg, env, log: params.log }) }); await runBestEffortMatrixMigrationStep({ label: "legacy Matrix encrypted-state preparation", log: params.log, logPrefix, run: () => prepareLegacyCrypto({ cfg: params.cfg, env, log: params.log }) }); } //#endregion //#region extensions/matrix/src/channel.ts let matrixStartupLock = Promise.resolve(); const loadMatrixSetupWizard = createLazyRuntimeNamedExport(() => import("./setup-surface-DmDK_Fq8.js"), "matrixSetupWizard"); const loadMatrixChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-qawLHhvu.js"), "matrixChannelRuntime"); let matrixDoctorModulePromise = null; const loadMatrixDoctorModule = async () => { matrixDoctorModulePromise ??= import("./doctor-NLgUbXs3.js"); return await matrixDoctorModulePromise; }; const meta = { id: "matrix", label: "Matrix", selectionLabel: "Matrix (plugin)", docsPath: "/channels/matrix", docsLabel: "matrix", blurb: "open protocol; configure a homeserver + access token.", order: 70, markdownCapable: true, quickstartAllowFrom: true }; function buildMatrixTrafficStatusSummary(snapshot) { return { lastInboundAt: snapshot?.lastInboundAt ?? null, lastOutboundAt: snapshot?.lastOutboundAt ?? null }; } const matrixDoctor = { dmAllowFromMode: "nestedOnly", groupModel: "sender", groupAllowFromFallbackToAllowFrom: false, warnOnEmptyGroupSenderAllowlist: true, legacyConfigRules, normalizeCompatibilityConfig, runConfigSequence: async ({ cfg, env, shouldRepair }) => await (await loadMatrixDoctorModule()).runMatrixDoctorSequence({ cfg, env, shouldRepair }), cleanStaleConfig: async ({ cfg }) => await (await loadMatrixDoctorModule()).cleanStaleMatrixPluginConfig(cfg) }; const listMatrixDirectoryPeersFromConfig = createResolvedDirectoryEntriesLister({ kind: "user", resolveAccount: adaptScopedAccountAccessor(resolveMatrixAccount), resolveSources: (account) => [ account.config.dm?.allowFrom ?? [], account.config.groupAllowFrom ?? [], ...Object.values(account.config.groups ?? account.config.rooms ?? {}).map((room) => room.users ?? []) ], normalizeId: (entry) => { const raw = entry.replace(/^matrix:/i, "").trim(); if (!raw || raw === "*") return null; const cleaned = normalizeLowercaseStringOrEmpty(raw).startsWith("user:") ? raw.slice(5).trim() : raw; return cleaned.startsWith("@") ? `user:${cleaned}` : cleaned; } }); const listMatrixDirectoryGroupsFromConfig = createResolvedDirectoryEntriesLister({ kind: "group", resolveAccount: adaptScopedAccountAccessor(resolveMatrixAccount), resolveSources: (account) => [Object.keys(account.config.groups ?? account.config.rooms ?? {})], normalizeId: (entry) => { const raw = entry.replace(/^matrix:/i, "").trim(); if (!raw || raw === "*") return null; const lowered = normalizeLowercaseStringOrEmpty(raw); if (lowered.startsWith("room:") || lowered.startsWith("channel:")) return raw; return raw.startsWith("!") ? `room:${raw}` : raw; } }); function projectMatrixConversationBinding(binding) { return { boundAt: binding.boundAt, lastActivityAt: typeof binding.metadata?.lastActivityAt === "number" ? binding.metadata.lastActivityAt : binding.boundAt, idleTimeoutMs: typeof binding.metadata?.idleTimeoutMs === "number" ? binding.metadata.idleTimeoutMs : void 0, maxAgeMs: typeof binding.metadata?.maxAgeMs === "number" ? binding.metadata.maxAgeMs : void 0 }; } const resolveMatrixDmPolicy = createScopedDmSecurityResolver({ channelKey: "matrix", resolvePolicy: (account) => account.config.dm?.policy, resolveAllowFrom: (account) => account.config.dm?.allowFrom, allowFromPathSuffix: "dm.", normalizeEntry: (raw) => normalizeMatrixUserId(raw) }); const collectMatrixSecurityWarnings = createAllowlistProviderOpenWarningCollector({ providerConfigPresent: (cfg) => cfg.channels?.matrix !== void 0, resolveGroupPolicy: (account) => account.config.groupPolicy, buildOpenWarning: { surface: "Matrix rooms", openBehavior: "allows any room to trigger (mention-gated)", remediation: "Set channels.matrix.groupPolicy=\"allowlist\" + channels.matrix.groups (and optionally channels.matrix.groupAllowFrom) to restrict rooms" } }); function resolveMatrixAccountConfigPath(accountId, field) { return accountId === "default" ? `channels.matrix.${field}` : `channels.matrix.accounts.${accountId}.${field}`; } function collectMatrixSecurityWarningsForAccount(params) { const warnings = collectMatrixSecurityWarnings(params); if (params.account.accountId !== "default") { const groupPolicyPath = resolveMatrixAccountConfigPath(params.account.accountId, "groupPolicy"); const groupsPath = resolveMatrixAccountConfigPath(params.account.accountId, "groups"); const groupAllowFromPath = resolveMatrixAccountConfigPath(params.account.accountId, "groupAllowFrom"); return warnings.map((warning) => warning.replace("channels.matrix.groupPolicy", groupPolicyPath).replace("channels.matrix.groups", groupsPath).replace("channels.matrix.groupAllowFrom", groupAllowFromPath)); } if (params.account.config.autoJoin !== "always") return warnings; const autoJoinPath = resolveMatrixAccountConfigPath(params.account.accountId, "autoJoin"); const autoJoinAllowlistPath = resolveMatrixAccountConfigPath(params.account.accountId, "autoJoinAllowlist"); return [...warnings, `- Matrix invites: autoJoin="always" joins any invited room before message policy applies. Set ${autoJoinPath}="allowlist" + ${autoJoinAllowlistPath} (or ${autoJoinPath}="off") to restrict joins.`]; } function normalizeMatrixAcpConversationId(conversationId) { const target = resolveMatrixTargetIdentity(conversationId); if (!target || target.kind !== "room") return null; return { conversationId: target.id }; } function matchMatrixAcpConversation(params) { const binding = normalizeMatrixAcpConversationId(params.bindingConversationId); if (!binding) return null; if (binding.conversationId === params.conversationId) return { conversationId: params.conversationId, matchPriority: 2 }; if (params.parentConversationId && params.parentConversationId !== params.conversationId && binding.conversationId === params.parentConversationId) return { conversationId: params.parentConversationId, matchPriority: 1 }; return null; } function resolveMatrixCommandConversation(params) { const parentConversationId = [ params.originatingTo, params.commandTo, params.fallbackTo ].map((candidate) => { const trimmed = candidate?.trim(); if (!trimmed) return; const target = resolveMatrixTargetIdentity(trimmed); return target?.kind === "room" ? target.id : void 0; }).find((candidate) => Boolean(candidate)); if (params.threadId) return { conversationId: params.threadId, ...parentConversationId ? { parentConversationId } : {} }; return parentConversationId ? { conversationId: parentConversationId } : null; } function resolveMatrixDeliveryTarget(params) { const parentConversationId = params.parentConversationId?.trim(); if (parentConversationId && parentConversationId !== params.conversationId.trim()) { const parentTarget = resolveMatrixTargetIdentity(parentConversationId); if (parentTarget?.kind === "room") return { to: `room:${parentTarget.id}`, threadId: params.conversationId.trim() }; } const conversationTarget = resolveMatrixTargetIdentity(params.conversationId); if (conversationTarget?.kind === "room") return { to: `room:${conversationTarget.id}` }; return null; } const matrixChannelOutbound = { deliveryMode: "direct", chunker: chunkTextForOutbound, chunkerMode: "markdown", textChunkLimit: 4e3, deliveryCapabilities: { durableFinal: { text: true, media: true, replyTo: true, thread: true, messageSendingHooks: true } }, presentationCapabilities: { supported: true, buttons: true, selects: true, context: true, divider: true, limits: { text: { markdownDialect: "markdown", supportsEdit: true } } }, shouldSuppressLocalPayloadPrompt: ({ cfg, accountId, payload }) => shouldSuppressLocalMatrixExecApprovalPrompt({ cfg, accountId, payload }), ...createRuntimeOutboundDelegates({ getRuntime: loadMatrixChannelRuntime, renderPresentation: { resolve: (runtime) => runtime.matrixOutbound.renderPresentation, unavailableMessage: "Matrix outbound presentation rendering is unavailable" }, sendPayload: { resolve: (runtime) => runtime.matrixOutbound.sendPayload, unavailableMessage: "Matrix outbound payload delivery is unavailable" }, sendText: { resolve: (runtime) => runtime.matrixOutbound.sendText, unavailableMessage: "Matrix outbound text delivery is unavailable" }, sendMedia: { resolve: (runtime) => runtime.matrixOutbound.sendMedia, unavailableMessage: "Matrix outbound media delivery is unavailable" }, sendPoll: { resolve: (runtime) => runtime.matrixOutbound.sendPoll, unavailableMessage: "Matrix outbound poll delivery is unavailable" } }) }; const matrixMessageAdapter = createChannelMessageAdapterFromOutbound({ id: "matrix", outbound: matrixChannelOutbound, live: { capabilities: { draftPreview: true, previewFinalization: true, progressUpdates: true, quietFinalization: true }, finalizer: { capabilities: { finalEdit: true, normalFallback: true, discardPending: true, previewReceipt: true } } } }); const matrixPlugin = createChatChannelPlugin({ base: { id: "matrix", meta, setupWizard: createMatrixSetupWizardProxy(async () => ({ matrixSetupWizard: await loadMatrixSetupWizard() })), capabilities: { chatTypes: [ "direct", "group", "thread" ], polls: true, reactions: true, threads: true, media: true, tts: { voice: { synthesisTarget: "voice-note" } } }, reload: { configPrefixes: ["channels.matrix"] }, configSchema: MatrixChannelConfigSchema, config: { ...matrixConfigAdapter, isConfigured: (account) => account.configured, describeAccount: (account) => describeAccountSnapshot({ account, configured: account.configured, extra: { baseUrl: account.homeserver } }) }, approvalCapability: matrixApprovalCapability, groups: { resolveRequireMention: resolveMatrixGroupRequireMention, resolveToolPolicy: resolveMatrixGroupToolPolicy }, conversationBindings: { supportsCurrentConversationBinding: true, defaultTopLevelPlacement: "child", setIdleTimeoutBySessionKey: ({ targetSessionKey, accountId, idleTimeoutMs }) => setMatrixThreadBindingIdleTimeoutBySessionKey({ targetSessionKey, accountId: accountId ?? "", idleTimeoutMs }).map(projectMatrixConversationBinding), setMaxAgeBySessionKey: ({ targetSessionKey, accountId, maxAgeMs }) => setMatrixThreadBindingMaxAgeBySessionKey({ targetSessionKey, accountId: accountId ?? "", maxAgeMs }).map(projectMatrixConversationBinding) }, messaging: { defaultMarkdownTableMode: "bullets", targetPrefixes: ["matrix"], normalizeTarget: normalizeMatrixMessagingTarget, resolveInboundConversation: ({ to, conversationId, threadId }) => resolveMatrixInboundConversation({ to, conversationId, threadId }), resolveDeliveryTarget: ({ conversationId, parentConversationId }) => resolveMatrixDeliveryTarget({ conversationId, parentConversationId }), resolveOutboundSessionRoute: (params) => resolveMatrixOutboundSessionRoute(params), targetResolver: { looksLikeId: (raw) => { const trimmed = raw.trim(); if (!trimmed) return false; if (/^(matrix:)?[!#@]/i.test(trimmed)) return true; return trimmed.includes(":"); }, hint: "<room|alias|user>" } }, directory: createChannelDirectoryAdapter({ listPeers: async (params) => { return (await listMatrixDirectoryPeersFromConfig(params)).map((entry) => { const raw = entry.id.startsWith("user:") ? entry.id.slice(5) : entry.id; return !raw.startsWith("@") || !raw.includes(":") ? Object.assign({}, entry, { name: `incomplete id; expected @user:server` }) : entry; }); }, listGroups: async (params) => await listMatrixDirectoryGroupsFromConfig(params), ...createRuntimeDirectoryLiveAdapter({ getRuntime: loadMatrixChannelRuntime, listPeersLive: (runtime) => runtime.listMatrixDirectoryPeersLive, listGroupsLive: (runtime) => runtime.listMatrixDirectoryGroupsLive }) }), resolver: matrixResolverAdapter, actions: matrixMessageActions, message: matrixMessageAdapter, secrets: { secretTargetRegistryEntries, collectRuntimeConfigAssignments }, setup: { ...matrixSetupAdapter, singleAccountKeysToMove, namedAccountPromotionKeys, resolveSingleAccountPromotionTarget }, bindings: { compileConfiguredBinding: ({ conversationId }) => normalizeMatrixAcpConversationId(conversationId), matchInboundConversation: ({ compiledBinding, conversationId, parentConversationId }) => matchMatrixAcpConversation({ bindingConversationId: compiledBinding.conversationId, conversationId, parentConversationId }), resolveCommandConversation: ({ threadId, originatingTo, commandTo, fallbackTo }) => resolveMatrixCommandConversation({ threadId, originatingTo, commandTo, fallbackTo }) }, status: createComputedAccountStatusAdapter({ defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID), collectStatusIssues: (accounts) => collectStatusIssuesFromLastError("matrix", accounts), buildChannelSummary: ({ snapshot }) => buildProbeChannelStatusSummary(snapshot, { baseUrl: snapshot.baseUrl ?? null }), probeAccount: async ({ account, timeoutMs, cfg }) => await createMatrixProbeAccount({ resolveMatrixAuth: async ({ cfg: cfgLocal, accountId }) => (await loadMatrixChannelRuntime()).resolveMatrixAuth({ cfg: cfgLocal, accountId }), probeMatrix: async (params) => await (await loadMatrixChannelRuntime()).probeMatrix(params) })({ account, timeoutMs, cfg }), resolveAccountSnapshot: ({ account, runtime }) => ({ accountId: account.accountId, name: account.name, enabled: account.enabled, configured: account.configured, extra: { baseUrl: account.homeserver, lastProbeAt: runtime?.lastProbeAt ?? null, ...buildMatrixTrafficStatusSummary(runtime) } }) }), gateway: { startAccount: async (ctx) => { const account = ctx.account; ctx.setStatus({ accountId: account.accountId, baseUrl: account.homeserver }); ctx.log?.info(`[${account.accountId}] starting provider (${account.homeserver ?? "matrix"})`); const previousLock = matrixStartupLock; let releaseLock = () => {}; matrixStartupLock = new Promise((resolve) => { releaseLock = resolve; }); await previousLock; let monitorMatrixProvider; try { monitorMatrixProvider = (await import("./monitor-D8In-Q7I.js")).monitorMatrixProvider; } finally { releaseLock(); } return monitorMatrixProvider({ runtime: ctx.runtime, channelRuntime: ctx.channelRuntime, abortSignal: ctx.abortSignal, mediaMaxMb: account.config.mediaMaxMb, initialSyncLimit: account.config.initialSyncLimit, replyToMode: account.config.replyToMode, accountId: account.accountId, setStatus: ctx.setStatus }); } }, doctor: matrixDoctor, lifecycle: { runStartupMaintenance: runMatrixStartupMaintenance }, heartbeat: { sendTyping: async ({ cfg, to, accountId }) => { await (await loadMatrixChannelRuntime()).sendTypingMatrix(to, true, { cfg, ...accountId ? { accountId } : {} }); }, clearTyping: async ({ cfg, to, accountId }) => { await (await loadMatrixChannelRuntime()).sendTypingMatrix(to, false, { cfg, ...accountId ? { accountId } : {} }); } } }, security: { resolveDmPolicy: resolveMatrixDmPolicy, collectWarnings: projectAccountConfigWarningCollector((cfg) => cfg, collectMatrixSecurityWarningsForAccount) }, pairing: { text: createMatrixPairingText(async (to, message, options) => await (await loadMatrixChannelRuntime()).sendMessageMatrix(to, message, options)) }, threading: { resolveReplyToMode: createScopedAccountReplyToModeResolver({ resolveAccount: adaptScopedAccountAccessor(resolveMatrixAccountConfig), resolveReplyToMode: (account) => account.replyToMode }), buildToolContext: ({ context, hasRepliedRef }) => { const currentTarget = context.To; return { currentChannelId: normalizeOptionalString(currentTarget), currentThreadTs: context.MessageThreadId != null ? String(context.MessageThreadId) : void 0, currentDirectUserId: resolveMatrixDirectUserId({ from: context.From, to: context.To, chatType: context.ChatType }), hasRepliedRef }; } }, outbound: matrixChannelOutbound }); //#endregion export { matrixPlugin as t };