@mastra/teams 0.0.0 → 0.1.0-alpha.0
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/LICENSE.md +32 -0
- package/dist/index.cjs +113790 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +428 -0
- package/dist/index.d.ts +428 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +817 -0
- package/dist/index.js.map +1 -0
- package/package.json +12 -12
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["#fromRecord","#toRecord","#warnedPlaintext","#enc","#dec","#config","#mastra","#initPromise","#store","#adapters","#configured","#autoInitialize","#handleWebhook","#doInitialize","#getStore","#activateInstallation","#connectDelegated","#connectSelfManaged","#getBaseUrl","#resolveAgent","#getOrCreateAdapter","#createAgentChannels","createTeamsAdapter","#resolveStorage"],"sources":["../src/types.ts","../src/microsoft-clients.ts","../src/crypto.ts","../src/install-store.ts","../src/teams-provider.ts"],"sourcesContent":["import type {\n ChannelAdapterConfig,\n ChannelConfig,\n ChannelHandlers,\n StreamingConfig,\n WaitUntilFn,\n} from '@mastra/core/channels';\nimport type { ChannelsStorage } from '@mastra/core/storage';\nimport type { TeamsAdapterConfig } from '@chat-adapter/teams';\n\n/** Microsoft Graph origin used for Entra application provisioning. */\nexport const GRAPH_API_BASE_URL = 'https://graph.microsoft.com';\n\n/** Teams Developer Portal origin used for Bot Framework registrations. */\nexport const DEV_PORTAL_BASE_URL = 'https://dev.teams.microsoft.com';\n\n/** Microsoft login origin used to validate bot credentials (client-credentials mint). */\nexport const LOGIN_BASE_URL = 'https://login.microsoftonline.com';\n\n/**\n * OAuth scope for Microsoft Graph control-plane calls (create Entra apps,\n * mint client secrets). Passed to the delegated {@link TeamsTokenResolver}.\n */\nexport const TEAMS_GRAPH_SCOPE = 'https://graph.microsoft.com/.default';\n\n/**\n * OAuth scope for the Teams Developer Portal API (bot registrations).\n * Tokens for this audience are distinct from Graph tokens — a single access\n * token cannot serve both APIs. Passed to the delegated\n * {@link TeamsTokenResolver}.\n */\nexport const TEAMS_DEV_PORTAL_SCOPE = 'https://dev.teams.microsoft.com/AppDefinitions.ReadWrite';\n\n/**\n * Teams Developer Portal page where minted bot registrations are managed and\n * packaged into a Teams app for distribution.\n */\nexport const DEV_PORTAL_BOTS_URL = 'https://dev.teams.microsoft.com/bots';\n\n/**\n * Resolve an access token for a Microsoft OAuth scope on demand.\n *\n * Teams provisioning spans two token audiences — Microsoft Graph\n * ({@link TEAMS_GRAPH_SCOPE}) for Entra application management and the Teams\n * Developer Portal ({@link TEAMS_DEV_PORTAL_SCOPE}) for bot registrations — so\n * unlike the Slack/Telegram resolvers this one is scope-aware: the provider\n * passes the scope it needs and the resolver returns a token minted for that\n * audience.\n */\nexport type TeamsTokenResolver = (scope: string | string[], tenantId?: string) => Promise<string>;\n\n/**\n * Self-managed credentials: bring your own Azure Bot registration. The\n * Microsoft App ID / password are supplied directly (provider defaults and/or\n * per-`connect()`) and persisted in the install store. Mutually exclusive with\n * {@link TeamsDelegatedCredentialsConfig}.\n */\nexport interface TeamsSelfManagedCredentialsConfig {\n /** Microsoft App (client) ID of an existing bot registration. */\n appId?: string;\n /** Client secret for {@link appId}. */\n appPassword?: string;\n tokenResolver?: never;\n}\n\n/**\n * Delegated credentials: an external credential manager (e.g. the Mastra\n * platform) owns a *manager* credential that can provision bots. The provider\n * calls the resolver with the scope it needs ({@link TEAMS_GRAPH_SCOPE} or\n * {@link TEAMS_DEV_PORTAL_SCOPE}) during `connect()`/`disconnect()` to mint\n * per-agent Entra applications and Developer Portal bot registrations.\n *\n * The manager credential is used only at provisioning time. Each provisioned\n * bot gets its own client secret, stored (encrypted) in the install store, and\n * the runtime authenticates with Bot Framework using that per-agent secret —\n * no manager-credential round-trip on the message path.\n *\n * Mutually exclusive with direct `appId`/`appPassword` credentials.\n */\nexport interface TeamsDelegatedCredentialsConfig {\n /** Resolve a manager access token for the given Microsoft OAuth scope. */\n tokenResolver: TeamsTokenResolver;\n appId?: never;\n appPassword?: never;\n}\n\n/**\n * How the provider obtains bot credentials: either directly\n * (`appId`/`appPassword`, self-managed) or by provisioning per-agent bots via\n * a scope-aware `tokenResolver` (delegated) — never both.\n */\nexport type TeamsCredentialsConfig = TeamsSelfManagedCredentialsConfig | TeamsDelegatedCredentialsConfig;\n\n/**\n * Configuration for {@link TeamsProvider}.\n */\nexport interface TeamsProviderConfigBase {\n /**\n * Public HTTPS base URL used as the bot messaging endpoint\n * (`{baseUrl}/teams/events/{webhookId}`). May be omitted and auto-detected\n * from the Mastra server config.\n */\n baseUrl?: string;\n /**\n * Persistence for bot installations. Defaults to Mastra's channels storage\n * when available, falling back to an in-memory store (dev/test only).\n */\n storage?: ChannelsStorage;\n /**\n * Passphrase for encrypting the per-bot client secret at rest (AES-256-GCM).\n * Defaults to the `MASTRA_ENCRYPTION_KEY` env var.\n */\n encryptionKey?: string;\n /** Entra tenant ID for single-tenant bots. */\n appTenantId?: string;\n /**\n * Bot application audience. Provisioned (delegated) bots default to\n * `MultiTenant`.\n * @default 'MultiTenant'\n */\n appType?: 'MultiTenant' | 'SingleTenant';\n /** Override the Bot Framework service URL (e.g. sovereign clouds). Forwarded to the adapter. */\n apiUrl?: string;\n /** Override Microsoft Graph origin (tests / sovereign clouds). @default 'https://graph.microsoft.com' */\n graphBaseUrl?: string;\n /** Override the Teams Developer Portal origin (tests). @default 'https://dev.teams.microsoft.com' */\n devPortalBaseUrl?: string;\n /** Override the Microsoft login origin used for credential validation (tests). @default 'https://login.microsoftonline.com' */\n loginBaseUrl?: string;\n /** Icon URL recorded on provisioned bot registrations. */\n botIconUrl?: string;\n /**\n * Keep the serverless invocation alive while the agent stream runs after the\n * webhook returns 200. See `ChannelConfig.waitUntil`.\n */\n waitUntil?: WaitUntilFn;\n /**\n * Stream agent text to Teams as it generates (native Teams streaming in DMs,\n * post-and-edit elsewhere — handled by the adapter).\n * @default true\n */\n streaming?: StreamingConfig;\n /**\n * Keep a typing indicator alive during generation.\n * @default true\n */\n typingStatus?: boolean;\n\n // ---------------------------------------------------------------------------\n // AgentChannels passthrough — a curated subset of `ChannelConfig` /\n // `ChannelAdapterConfig` forwarded to every agent connected via this\n // provider, mirroring `@mastra/slack` / `@mastra/telegram`.\n // ---------------------------------------------------------------------------\n\n /** Override built-in event handlers. Forwarded to `AgentChannels`. */\n handlers?: ChannelHandlers;\n /** Which media types to send inline to the model. See `ChannelConfig.inlineMedia`. */\n inlineMedia?: ChannelConfig['inlineMedia'];\n /** Promote URLs in message text to file parts. See `ChannelConfig.inlineLinks`. */\n inlineLinks?: ChannelConfig['inlineLinks'];\n /** State adapter for deduplication, locking, and subscriptions. See `ChannelConfig.state`. */\n state?: ChannelConfig['state'];\n /** Fetch recent thread messages when the agent joins mid-conversation. See `ChannelConfig.threadContext`. */\n threadContext?: ChannelConfig['threadContext'];\n /** Additional options passed directly to the Chat SDK. See `ChannelConfig.chatOptions`. */\n chatOptions?: ChannelConfig['chatOptions'];\n /** Resolve the memory `resourceId` before a channel thread is created. See `ChannelConfig.resolveResourceId`. */\n resolveResourceId?: ChannelConfig['resolveResourceId'];\n /** Resolve `waitUntil` from the request's Hono `Context`. See `ChannelConfig.resolveWaitUntil`. */\n resolveWaitUntil?: ChannelConfig['resolveWaitUntil'];\n /** CORS configuration for the generated Teams webhook route. */\n cors?: ChannelAdapterConfig['cors'];\n /** Override how errors are rendered in Teams messages. See `ChannelAdapterConfig.formatError`. */\n formatError?: ChannelAdapterConfig['formatError'];\n /** How tool calls are rendered in the reply. See `ChannelAdapterConfig.toolDisplay`. */\n toolDisplay?: ChannelAdapterConfig['toolDisplay'];\n /** Whether to expose channel reaction tools to the agent. See `ChannelConfig.tools`. */\n tools?: ChannelConfig['tools'];\n /** Logger forwarded to the underlying `TeamsAdapter`. */\n logger?: TeamsAdapterConfig['logger'];\n /** Called after an agent successfully connects and the installation is persisted. */\n onInstall?: (installation: TeamsInstallation) => void | Promise<void>;\n}\n\n/** See {@link TeamsProviderConfigBase} and {@link TeamsCredentialsConfig}. */\nexport type TeamsProviderConfig = TeamsProviderConfigBase & TeamsCredentialsConfig;\n\n/** Options accepted by {@link TeamsProvider.connect}. */\nexport interface TeamsConnectOptions {\n /** Display name for the bot. Defaults to the agent id. */\n name?: string;\n /**\n * Microsoft App (client) ID of an existing bot registration (self-managed\n * mode). Falls back to the provider-level `appId`. Rejected in delegated\n * mode, where bots are provisioned per agent.\n */\n appId?: string;\n /** Client secret for {@link appId} (self-managed mode). Falls back to the provider-level `appPassword`. */\n appPassword?: string;\n /** Entra tenant ID for single-tenant bots. Falls back to the provider-level `appTenantId`. */\n appTenantId?: string;\n}\n\n/**\n * A Teams bot bound to a single agent (one bot = one agent).\n * Persisted through {@link TeamsInstallStore}.\n */\nexport interface TeamsInstallation {\n /** Stable installation id. */\n id: string;\n /** The agent this bot is bound to. */\n agentId: string;\n /**\n * Opaque id embedded in the messaging endpoint path\n * (`/teams/events/:webhookId`). Inbound requests are authenticated by\n * Microsoft's JWT signature (verified by the adapter), not by this id.\n */\n webhookId: string;\n /** Whether the bot is provisioned/validated and routable. */\n status: 'active' | 'pending';\n /** Microsoft App (client) ID of the bot. */\n appId?: string;\n /**\n * Entra application **object id** — only set for bots this provider\n * provisioned (delegated mode); used to delete the application on\n * disconnect.\n */\n entraObjectId?: string;\n /** Client secret the bot authenticates to Bot Framework with (encrypted at rest). */\n appPassword?: string;\n /** Entra tenant ID for single-tenant bots. */\n appTenantId?: string;\n /** Bot application audience. */\n appType?: 'MultiTenant' | 'SingleTenant';\n /** Display name of the bot. */\n botName?: string;\n /** The messaging endpoint registered for the bot. */\n messagingEndpoint?: string;\n /** When the installation was created. */\n installedAt: Date;\n}\n","import { DEV_PORTAL_BASE_URL, GRAPH_API_BASE_URL, LOGIN_BASE_URL } from './types';\n\n/**\n * Thin fetch wrappers for the two Microsoft control planes the provider\n * provisions through — Microsoft Graph (Entra applications) and the Teams\n * Developer Portal (Bot Framework registrations) — plus a credential\n * validation helper against the Microsoft login endpoint.\n *\n * All calls are plain `fetch` with a 15s timeout so tests can intercept them\n * with undici's `MockAgent` (same pattern as `@mastra/telegram`'s client).\n */\n\nconst REQUEST_TIMEOUT_MS = 15_000;\n\nasync function request(\n url: string,\n init: RequestInit & { headers: Record<string, string> },\n label: string,\n): Promise<Response> {\n let response: Response;\n try {\n response = await fetch(url, { ...init, signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS) });\n } catch (cause) {\n throw Object.assign(new Error(`${label} request failed`, { cause }), { isTransportError: true });\n }\n if (!response.ok) {\n const body = await response.text().catch(() => '');\n throw new Error(`${label} failed: HTTP ${response.status}${body ? ` — ${truncate(body)}` : ''}`);\n }\n return response;\n}\n\nfunction truncate(text: string, max = 300): string {\n return text.length > max ? `${text.slice(0, max)}…` : text;\n}\n\nfunction authJson(token: string): Record<string, string> {\n return { authorization: `Bearer ${token}`, 'content-type': 'application/json' };\n}\n\n// =============================================================================\n// Microsoft Graph — Entra application provisioning\n// =============================================================================\n\n/** The subset of a Graph `application` resource the provider uses. */\nexport interface EntraApplication {\n /** Directory **object id** — used for subsequent Graph calls (addPassword, delete). */\n id: string;\n /** The application (client) id — the bot's Microsoft App ID. */\n appId: string;\n}\n\n/**\n * Create an Entra application for a per-agent bot.\n * @see https://learn.microsoft.com/graph/api/application-post-applications\n */\nexport async function createApplication(\n token: string,\n options: { displayName: string; signInAudience: 'AzureADMultipleOrgs' | 'AzureADMyOrg' },\n graphBaseUrl: string = GRAPH_API_BASE_URL,\n): Promise<EntraApplication> {\n const response = await request(\n `${graphBaseUrl}/v1.0/applications`,\n {\n method: 'POST',\n headers: authJson(token),\n body: JSON.stringify({ displayName: options.displayName, signInAudience: options.signInAudience }),\n },\n 'Graph application create',\n );\n const body = (await response.json()) as Partial<EntraApplication>;\n if (!body.id || !body.appId) {\n throw new Error('Graph application create returned an unexpected payload (missing id/appId)');\n }\n return { id: body.id, appId: body.appId };\n}\n\n/**\n * Mint a client secret on an Entra application. The secret text is only\n * returned by this call — it cannot be read back later.\n * @see https://learn.microsoft.com/graph/api/application-addpassword\n */\nexport async function addApplicationPassword(\n token: string,\n applicationObjectId: string,\n displayName: string,\n graphBaseUrl: string = GRAPH_API_BASE_URL,\n): Promise<string> {\n const response = await request(\n `${graphBaseUrl}/v1.0/applications/${encodeURIComponent(applicationObjectId)}/addPassword`,\n {\n method: 'POST',\n headers: authJson(token),\n body: JSON.stringify({ passwordCredential: { displayName } }),\n },\n 'Graph addPassword',\n );\n const body = (await response.json()) as { secretText?: string };\n if (!body.secretText) {\n throw new Error('Graph addPassword returned an unexpected payload (missing secretText)');\n }\n return body.secretText;\n}\n\n/**\n * Delete an Entra application. Used to clean up provisioned bots on\n * disconnect and to roll back a partially provisioned connect.\n * @see https://learn.microsoft.com/graph/api/application-delete\n */\nexport async function deleteApplication(\n token: string,\n applicationObjectId: string,\n graphBaseUrl: string = GRAPH_API_BASE_URL,\n): Promise<void> {\n await request(\n `${graphBaseUrl}/v1.0/applications/${encodeURIComponent(applicationObjectId)}`,\n { method: 'DELETE', headers: { authorization: `Bearer ${token}` } },\n 'Graph application delete',\n );\n}\n\n// =============================================================================\n// Teams Developer Portal — Bot Framework registrations\n// =============================================================================\n\n/**\n * A Teams Developer Portal bot registration, as accepted by\n * `POST https://dev.teams.microsoft.com/api/botframework` (the same contract\n * Microsoft's Teams Toolkit CLI uses).\n */\nexport interface TeamsBotRegistration {\n /** The bot's Microsoft App (client) ID. */\n botId: string;\n name: string;\n description: string;\n iconUrl: string;\n messagingEndpoint: string;\n callingEndpoint: string;\n}\n\n/** Register a bot with the Teams Developer Portal. */\nexport async function createBotRegistration(\n token: string,\n registration: TeamsBotRegistration,\n devPortalBaseUrl: string = DEV_PORTAL_BASE_URL,\n): Promise<void> {\n await request(\n `${devPortalBaseUrl}/api/botframework`,\n { method: 'POST', headers: authJson(token), body: JSON.stringify(registration) },\n 'Dev Portal bot registration',\n );\n}\n\n/** Delete a Teams Developer Portal bot registration. */\nexport async function deleteBotRegistration(\n token: string,\n botId: string,\n devPortalBaseUrl: string = DEV_PORTAL_BASE_URL,\n): Promise<void> {\n await request(\n `${devPortalBaseUrl}/api/botframework/${encodeURIComponent(botId)}`,\n { method: 'DELETE', headers: { authorization: `Bearer ${token}` } },\n 'Dev Portal bot deletion',\n );\n}\n\n// =============================================================================\n// Credential validation — Bot Framework client-credentials mint\n// =============================================================================\n\n/**\n * Validate a bot's `appId`/`appPassword` by minting a Bot Framework token via\n * the OAuth2 client-credentials grant — the same exchange the adapter performs\n * at runtime, so a success here means outbound sends will authenticate.\n * Multi-tenant bots exchange against the `botframework.com` tenant.\n *\n * Throws when Microsoft rejects the credentials; transport failures are\n * surfaced as-is (tagged `isTransportError`) so callers can tell connectivity\n * problems apart from bad credentials.\n */\nexport async function validateBotCredentials(\n options: { appId: string; appPassword: string; appTenantId?: string },\n loginBaseUrl: string = LOGIN_BASE_URL,\n): Promise<void> {\n // Encoded: appTenantId can come from connect() options via the editor UI —\n // a value containing `/`, `?` or `#` must not redirect the secret elsewhere.\n const tenant = encodeURIComponent(options.appTenantId ?? 'botframework.com');\n const body = new URLSearchParams({\n grant_type: 'client_credentials',\n client_id: options.appId,\n client_secret: options.appPassword,\n scope: 'https://api.botframework.com/.default',\n });\n let response: Response;\n try {\n response = await fetch(`${loginBaseUrl}/${tenant}/oauth2/v2.0/token`, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n } catch (cause) {\n throw Object.assign(new Error('Bot credential validation request failed', { cause }), {\n isTransportError: true,\n });\n }\n if (!response.ok) {\n const detail = (await response.json().catch(() => null)) as { error_description?: string; error?: string } | null;\n throw new Error(\n `Microsoft rejected the bot credentials: ${detail?.error_description ?? detail?.error ?? `HTTP ${response.status}`}`,\n );\n }\n}\n","import { createCipheriv, createDecipheriv, hkdfSync, randomBytes } from 'node:crypto';\n\n/**\n * Opt-in AES-256-GCM encryption for installation secrets at rest, with\n * HKDF-SHA256 key derivation (mirrors `@mastra/slack`'s `crypto.ts`). Each value\n * gets a fresh random 16-byte salt + 12-byte IV; the salt travels in the\n * ciphertext, so the same passphrase never derives the same key twice. The\n * algorithm prefix lets plaintext and encrypted values coexist during migration,\n * so {@link decrypt} can no-op on plaintext.\n *\n * Format: `aes-256-gcm-hkdf:base64(salt):base64(iv):base64(authTag):base64(ciphertext)`\n */\nconst ALGO_PREFIX = 'aes-256-gcm-hkdf';\nconst HKDF_INFO = 'mastra-teams-encryption';\n\nfunction deriveKey(passphrase: string, salt: Buffer): Buffer {\n return Buffer.from(hkdfSync('sha256', passphrase, salt, HKDF_INFO, 32));\n}\n\n/** Whether a stored value was produced by {@link encrypt}. */\nexport function isEncrypted(value: string): boolean {\n return value.startsWith(`${ALGO_PREFIX}:`);\n}\n\n/** Encrypt a UTF-8 string with a per-value random salt + IV. */\nexport function encrypt(plaintext: string, passphrase: string): string {\n const salt = randomBytes(16);\n const iv = randomBytes(12);\n const cipher = createCipheriv('aes-256-gcm', deriveKey(passphrase, salt), iv);\n const enc = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);\n const tag = cipher.getAuthTag();\n return `${ALGO_PREFIX}:${salt.toString('base64')}:${iv.toString('base64')}:${tag.toString('base64')}:${enc.toString('base64')}`;\n}\n\n/** Decrypt a value from {@link encrypt}. Plaintext (unprefixed) is returned unchanged. */\nexport function decrypt(value: string, passphrase: string): string {\n if (!isEncrypted(value)) return value;\n const [, saltB64, ivB64, tagB64, ctB64] = value.split(':');\n if (!saltB64 || !ivB64 || !tagB64 || ctB64 === undefined) {\n throw new Error('Invalid ciphertext payload');\n }\n const decipher = createDecipheriv(\n 'aes-256-gcm',\n deriveKey(passphrase, Buffer.from(saltB64, 'base64')),\n Buffer.from(ivB64, 'base64'),\n );\n decipher.setAuthTag(Buffer.from(tagB64, 'base64'));\n return Buffer.concat([decipher.update(Buffer.from(ctB64, 'base64')), decipher.final()]).toString('utf8');\n}\n","import type { ChannelInstallationInfo } from '@mastra/core/channels';\nimport type { ChannelInstallation, ChannelsStorage } from '@mastra/core/storage';\nimport type { TeamsInstallation } from './types';\nimport { decrypt, encrypt, isEncrypted } from './crypto';\n\n/** Platform identifier used for every stored record and route. */\nexport const PLATFORM = 'teams';\n\n/** Per-bot fields serialized into a {@link ChannelInstallation.data} blob. */\ninterface TeamsInstallationData {\n appId?: string;\n entraObjectId?: string;\n appPassword?: string;\n appTenantId?: string;\n appType?: 'MultiTenant' | 'SingleTenant';\n botName?: string;\n messagingEndpoint?: string;\n}\n\n/**\n * Persistence for Teams bot installations, layered over the platform-agnostic\n * `ChannelsStorage` (the same store `@mastra/slack` / `@mastra/telegram` use).\n * Installations are keyed by agent — one bot = one agent — and the per-bot\n * fields live in the record's `data` blob. When an `encryptionKey` is\n * supplied, `appPassword` (the bot's client secret) is AES-256-GCM encrypted\n * at rest.\n */\nexport class TeamsInstallStore {\n #warnedPlaintext = false;\n\n constructor(\n private readonly storage: ChannelsStorage,\n private readonly encryptionKey?: string,\n ) {}\n\n /** Whether secrets written through this store are encrypted at rest. */\n get canEncrypt(): boolean {\n return Boolean(this.encryptionKey);\n }\n\n /** The active or pending installation for an agent, if any. */\n async getByAgent(agentId: string): Promise<TeamsInstallation | null> {\n const record = await this.storage.getInstallationByAgent(PLATFORM, agentId);\n return record ? this.#fromRecord(record) : null;\n }\n\n /** Look up an installation by the routing id in its messaging endpoint path. */\n async getByWebhookId(webhookId: string): Promise<TeamsInstallation | null> {\n const record = await this.storage.getInstallationByWebhookId(webhookId);\n return record && record.platform === PLATFORM ? this.#fromRecord(record) : null;\n }\n\n /** Insert or replace an installation. */\n async save(installation: TeamsInstallation): Promise<void> {\n await this.storage.saveInstallation(this.#toRecord(installation));\n }\n\n /** All Teams installations (active and pending). */\n async list(): Promise<TeamsInstallation[]> {\n const records = await this.storage.listInstallations(PLATFORM);\n return records.map(r => this.#fromRecord(r));\n }\n\n /** Remove an agent's installation, if present. */\n async deleteByAgent(agentId: string): Promise<void> {\n const record = await this.storage.getInstallationByAgent(PLATFORM, agentId);\n if (record) await this.storage.deleteInstallation(record.id);\n }\n\n #enc(value: string | undefined): string | undefined {\n if (value && !this.encryptionKey && !this.#warnedPlaintext) {\n this.#warnedPlaintext = true;\n console.warn(\n '[Teams] Storing the bot client secret WITHOUT encryption — no encryption key is configured. ' +\n 'Set `encryptionKey` on TeamsProvider or the MASTRA_ENCRYPTION_KEY environment variable to encrypt installation secrets at rest.',\n );\n }\n return value && this.encryptionKey ? encrypt(value, this.encryptionKey) : value;\n }\n\n #dec(value: string | undefined): string | undefined {\n if (!value) return value;\n if (!this.encryptionKey) {\n if (isEncrypted(value)) {\n throw new Error(\n 'Teams installation secrets are encrypted at rest, but no encryption key is configured. Set `encryptionKey` on TeamsProvider or MASTRA_ENCRYPTION_KEY.',\n );\n }\n return value;\n }\n return decrypt(value, this.encryptionKey);\n }\n\n #toRecord(install: TeamsInstallation): ChannelInstallation {\n const data: TeamsInstallationData = {\n appId: install.appId,\n entraObjectId: install.entraObjectId,\n appPassword: this.#enc(install.appPassword),\n appTenantId: install.appTenantId,\n appType: install.appType,\n botName: install.botName,\n messagingEndpoint: install.messagingEndpoint,\n };\n return {\n id: install.id,\n platform: PLATFORM,\n agentId: install.agentId,\n status: install.status,\n webhookId: install.webhookId,\n data: data as Record<string, unknown>,\n createdAt: install.installedAt,\n updatedAt: new Date(),\n };\n }\n\n #fromRecord(record: ChannelInstallation): TeamsInstallation {\n const data = (record.data ?? {}) as TeamsInstallationData;\n return {\n id: record.id,\n agentId: record.agentId,\n webhookId: record.webhookId ?? '',\n status: record.status === 'active' ? 'active' : 'pending',\n appId: data.appId,\n entraObjectId: data.entraObjectId,\n appPassword: this.#dec(data.appPassword),\n appTenantId: data.appTenantId,\n appType: data.appType,\n botName: data.botName,\n messagingEndpoint: data.messagingEndpoint,\n installedAt: record.createdAt,\n };\n }\n}\n\n/** Project an installation to its public, secret-free info for the editor UI. */\nexport function toInstallationInfo(install: TeamsInstallation): ChannelInstallationInfo {\n return {\n id: install.id,\n platform: PLATFORM,\n agentId: install.agentId,\n status: install.status,\n displayName: install.botName,\n installedAt: install.installedAt,\n };\n}\n","import { randomUUID } from 'node:crypto';\nimport { AgentChannels, resolveWaitUntil } from '@mastra/core/channels';\nimport type {\n ChannelAdapterConfig,\n ChannelConnectResult,\n ChannelInstallationInfo,\n ChannelPlatformInfo,\n ChannelProvider,\n StreamingConfig,\n} from '@mastra/core/channels';\nimport type { Agent } from '@mastra/core/agent';\nimport type { Mastra } from '@mastra/core/mastra';\nimport type { ApiRoute, ApiRouteHandler } from '@mastra/core/server';\nimport { InMemoryChannelsStorage } from '@mastra/core/storage';\nimport type { ChannelsStorage } from '@mastra/core/storage';\nimport { createTeamsAdapter } from '@chat-adapter/teams';\nimport type { TeamsAdapter } from '@chat-adapter/teams';\nimport {\n addApplicationPassword,\n createApplication,\n createBotRegistration,\n deleteApplication,\n deleteBotRegistration,\n validateBotCredentials,\n} from './microsoft-clients';\nimport { PLATFORM, TeamsInstallStore, toInstallationInfo } from './install-store';\nimport { DEV_PORTAL_BOTS_URL, TEAMS_DEV_PORTAL_SCOPE, TEAMS_GRAPH_SCOPE } from './types';\nimport type { TeamsConnectOptions, TeamsInstallation, TeamsProviderConfig } from './types';\n\n/**\n * Resolve the per-adapter streaming/typing config the provider applies to the\n * Teams entry in `AgentChannels.adapters` — both default on.\n */\nexport function resolveTeamsAdapterConfig(config: Pick<TeamsProviderConfig, 'streaming' | 'typingStatus'>): {\n streaming: StreamingConfig;\n typingStatus: boolean;\n} {\n return {\n streaming: config.streaming ?? true,\n typingStatus: config.typingStatus ?? true,\n };\n}\n\n/**\n * Microsoft Teams channel provider for Mastra — a {@link ChannelProvider} over\n * `@chat-adapter/teams`. The adapter handles the Bot Framework transport\n * (activity parse, JWT webhook verification, send/stream, typing, adaptive\n * cards); this provider adds the install/lifecycle layer.\n *\n * Credential modes (mutually exclusive, enforced at the type level):\n *\n * - **Self-managed** — bring your own Azure Bot registration: pass\n * `appId`/`appPassword` (provider-level or per-`connect()`). Point the bot's\n * messaging endpoint at `{baseUrl}/teams/events/{webhookId}`.\n * - **Delegated** — pass a scope-aware `tokenResolver` for a *manager*\n * credential; `connect(agentId)` then provisions a bot per agent: an Entra\n * application (Microsoft Graph), a fresh client secret, and a Teams\n * Developer Portal bot registration pointing at this server. The per-agent\n * secret is stored (encrypted) and used by the runtime — the manager\n * credential is never needed on the message path.\n *\n * @example\n * ```ts\n * const teams = new TeamsProvider({ baseUrl: 'https://my-app.example.com', tokenResolver });\n * const mastra = new Mastra({ agents: { myAgent }, channels: { teams } });\n * await teams.connect('my-agent'); // → { type: 'deep_link', url: 'https://dev.teams.microsoft.com/bots' }\n * ```\n */\nexport class TeamsProvider implements ChannelProvider {\n readonly id = PLATFORM;\n\n #config: TeamsProviderConfig;\n #mastra?: Mastra;\n #store?: TeamsInstallStore;\n /** Live adapters, keyed by installation id. */\n #adapters = new Map<string, TeamsAdapter>();\n /** Cached sync view of whether any active bot is registered (for {@link getInfo}). */\n #configured = false;\n #initPromise: Promise<void> | null = null;\n\n constructor(config: TeamsProviderConfig = {}) {\n this.#config = config;\n }\n\n /**\n * Called by Mastra when this channel is registered.\n * @internal\n */\n __attach(mastra: Mastra): void {\n if (this.#mastra && this.#mastra !== mastra) {\n this.#initPromise = null;\n this.#store = undefined;\n this.#adapters.clear();\n this.#configured = false;\n }\n this.#mastra = mastra;\n }\n\n /**\n * Per-bot messaging endpoint. A single POST route keyed by an opaque\n * `webhookId`; inbound requests are authenticated by Microsoft's JWT\n * signature (verified by the adapter against the bot's `appId`), not by the\n * path. Auto-initializes on first hit (mirrors `@mastra/slack`).\n */\n getRoutes(): ApiRoute[] {\n const self = this;\n const withInit = (handler: ApiRouteHandler) => {\n return async ({ mastra }: { mastra: Mastra }): Promise<ApiRouteHandler> => {\n self.#mastra = mastra;\n await self.#autoInitialize();\n return handler.bind(self);\n };\n };\n return [\n {\n path: `/${PLATFORM}/events/:webhookId`,\n method: 'POST',\n requiresAuth: false,\n createHandler: withInit(this.#handleWebhook),\n },\n ];\n }\n\n /** Discovery metadata for the editor UI. */\n getInfo(): ChannelPlatformInfo {\n const hasCredentials = Boolean(this.#config.tokenResolver || (this.#config.appId && this.#config.appPassword));\n return {\n id: this.id,\n name: 'Microsoft Teams',\n isConfigured: this.#configured || hasCredentials,\n connectOptionsSchema: {\n type: 'object',\n properties: {\n name: {\n type: 'string',\n description: 'Display name for the bot (defaults to the agent id).',\n },\n appId: {\n type: 'string',\n description:\n 'Microsoft App (client) ID of an existing bot registration (self-managed mode). Omit in delegated mode — a bot is provisioned per agent.',\n },\n appPassword: {\n type: 'string',\n description: 'Client secret for the bot (self-managed mode).',\n },\n appTenantId: {\n type: 'string',\n description: 'Entra tenant ID for single-tenant bots.',\n },\n },\n },\n };\n }\n\n /**\n * Restore installations from storage: rebuild an adapter per active bot and\n * inject `AgentChannels` so the agent can receive events immediately.\n * Idempotent. Does not re-register messaging endpoints (they persist in the\n * bot registration across restarts); reconnect an agent if its `baseUrl` changed.\n */\n async initialize(): Promise<void> {\n if (this.#initPromise) return this.#initPromise;\n this.#initPromise = this.#doInitialize();\n try {\n await this.#initPromise;\n } catch (err) {\n this.#initPromise = null;\n throw err;\n }\n }\n\n async #doInitialize(): Promise<void> {\n const store = await this.#getStore();\n const active = (await store.list()).filter(i => i.status === 'active');\n this.#configured = active.length > 0;\n for (const installation of active) {\n try {\n await this.#activateInstallation(installation);\n } catch (err) {\n console.error(`[Teams] Failed to restore installation \"${installation.id}\":`, err);\n }\n }\n }\n\n /**\n * Update runtime provider settings. Pass `appId`/`appPassword` to change the\n * default bot credentials new {@link connect} calls fall back to; pass\n * `baseUrl`/`apiUrl` to point the provider/adapter at a different host.\n * `null` clears the default credentials only — per-bot installs are managed\n * via {@link connect}/{@link disconnect}.\n */\n async configure(\n credentials: {\n appId?: string;\n appPassword?: string;\n appTenantId?: string;\n baseUrl?: string;\n apiUrl?: string;\n } | null,\n ): Promise<void> {\n if (this.#config.tokenResolver && (credentials?.appId !== undefined || credentials?.appPassword !== undefined)) {\n throw new Error(\n 'TeamsProvider was constructed with a tokenResolver — bot credentials are provisioned per agent. ' +\n 'Remove the tokenResolver to manage credentials manually via configure().',\n );\n }\n if (credentials === null) {\n this.#config = { ...this.#config, appId: undefined, appPassword: undefined } as TeamsProviderConfig;\n return;\n }\n const apiUrlChanged = credentials.apiUrl !== undefined && credentials.apiUrl !== this.#config.apiUrl;\n // The guard above rejects direct credentials when a tokenResolver is set,\n // so this merge cannot mix the two credential modes.\n this.#config = { ...this.#config, ...credentials } as TeamsProviderConfig;\n if (!apiUrlChanged) return;\n\n // Live adapters captured the previous apiUrl — drop them and, if\n // installations were already restored, rebuild against the new host.\n const wasInitialized = this.#initPromise !== null;\n this.#adapters.clear();\n this.#initPromise = null;\n if (wasInitialized) await this.initialize();\n }\n\n /**\n * Connect an agent to Microsoft Teams.\n *\n * - **Delegated** (`tokenResolver`): provisions a per-agent bot — Entra\n * application + client secret via Microsoft Graph, then a Teams Developer\n * Portal bot registration whose messaging endpoint points at this server —\n * and returns `{ type: 'deep_link' }` to the Developer Portal where the\n * bot is packaged into a Teams app for install.\n * - **Self-managed** with `appId`/`appPassword` (per-call or provider\n * default): validates the credentials via a Bot Framework token mint,\n * persists the installation, and returns `{ type: 'immediate' }`. Point\n * the bot registration's messaging endpoint at the installation's\n * `messagingEndpoint`.\n * - Self-managed without credentials: persists a pending installation and\n * returns `{ type: 'deep_link' }` pointing at the Teams Developer Portal.\n */\n async connect(agentId: string, options: TeamsConnectOptions = {}): Promise<ChannelConnectResult> {\n const store = await this.#getStore();\n const existing = await store.getByAgent(agentId);\n if (existing?.status === 'active') {\n throw new Error(`Agent \"${agentId}\" is already connected to Microsoft Teams. Disconnect first to reconnect.`);\n }\n\n if (this.#config.tokenResolver) {\n // Delegated mode provisions a dedicated bot per agent — supplying an\n // existing bot's credentials here would silently bypass provisioning.\n if (options.appId !== undefined || options.appPassword !== undefined) {\n throw new Error(\n 'TeamsProvider was constructed with a tokenResolver — bots are provisioned per agent ' +\n 'and credentials cannot be supplied per connect() call.',\n );\n }\n return this.#connectDelegated(agentId, options, existing);\n }\n return this.#connectSelfManaged(agentId, options, existing);\n }\n\n async #connectSelfManaged(\n agentId: string,\n options: TeamsConnectOptions,\n existing: TeamsInstallation | null,\n ): Promise<ChannelConnectResult> {\n const store = await this.#getStore();\n const appId = options.appId ?? this.#config.appId;\n const appPassword = options.appPassword ?? this.#config.appPassword;\n if (!appId || !appPassword) {\n const installationId = existing?.id ?? randomUUID();\n await store.save({\n id: installationId,\n agentId,\n webhookId: existing?.webhookId ?? randomUUID(),\n status: 'pending',\n installedAt: existing?.installedAt ?? new Date(),\n });\n return { type: 'deep_link', url: DEV_PORTAL_BOTS_URL, installationId };\n }\n\n // Two agents on one bot would receive each other's activities.\n const duplicate = (await store.list()).find(\n i => i.status === 'active' && i.agentId !== agentId && i.appId === appId,\n );\n if (duplicate) {\n throw new Error(\n `This Teams bot is already connected to agent \"${duplicate.agentId}\". Disconnect it before connecting another agent.`,\n );\n }\n\n const appTenantId = options.appTenantId ?? this.#config.appTenantId;\n await validateBotCredentials({ appId, appPassword, appTenantId }, this.#config.loginBaseUrl);\n\n const installationId = existing?.id ?? randomUUID();\n const webhookId = existing?.webhookId ?? randomUUID();\n const baseUrl = this.#getBaseUrl();\n const installation: TeamsInstallation = {\n id: installationId,\n agentId,\n webhookId,\n status: 'active',\n appId,\n appPassword,\n appTenantId,\n appType: this.#config.appType ?? (appTenantId ? 'SingleTenant' : 'MultiTenant'),\n botName: options.name ?? agentId,\n messagingEndpoint: baseUrl ? `${baseUrl}/${PLATFORM}/events/${webhookId}` : undefined,\n installedAt: existing?.installedAt ?? new Date(),\n };\n await store.save(installation);\n // The installation is saved — post-save failures are non-fatal (the\n // adapter is rebuilt lazily on the next webhook/initialize), and a\n // rejected connect() here would strand the agent in \"already connected\".\n try {\n await this.#activateInstallation(installation);\n } catch (err) {\n console.warn(`[Teams] Failed to activate installation for agent \"${agentId}\":`, err);\n }\n this.#configured = true;\n try {\n await this.#config.onInstall?.(installation);\n } catch (err) {\n console.warn(`[Teams] onInstall hook failed for agent \"${agentId}\":`, err);\n }\n return { type: 'immediate', installationId };\n }\n\n async #connectDelegated(\n agentId: string,\n options: TeamsConnectOptions,\n existing: TeamsInstallation | null,\n ): Promise<ChannelConnectResult> {\n const tokenResolver = this.#config.tokenResolver!;\n const store = await this.#getStore();\n if (!store.canEncrypt) {\n // Delegated provisioning creates a client secret the user never sees and\n // persists it, so refusing up front (before any Azure resources exist)\n // beats warning about a plaintext secret after the fact.\n throw new Error(\n 'Delegated Teams provisioning stores a Mastra-provisioned bot client secret and requires an encryption key. ' +\n 'Set `encryptionKey` on TeamsProvider or the MASTRA_ENCRYPTION_KEY environment variable.',\n );\n }\n const baseUrl = this.#getBaseUrl();\n if (!baseUrl) {\n throw new Error(\n 'TeamsProvider needs a baseUrl to register a bot messaging endpoint. Set `baseUrl` or configure the Mastra server.',\n );\n }\n\n const botName = options.name ?? agentId;\n const appType = this.#config.appType ?? 'MultiTenant';\n const appTenantId = options.appTenantId ?? this.#config.appTenantId;\n if (appType === 'SingleTenant' && !appTenantId) {\n throw new Error(\n 'TeamsProvider is configured for SingleTenant bots but no appTenantId is available. ' +\n 'Set `appTenantId` on the provider or pass it to connect().',\n );\n }\n const webhookId = existing?.webhookId ?? randomUUID();\n const messagingEndpoint = `${baseUrl}/${PLATFORM}/events/${webhookId}`;\n\n // 1. Entra application + client secret (Microsoft Graph audience).\n const graphToken = await tokenResolver(TEAMS_GRAPH_SCOPE, appTenantId);\n const application = await createApplication(\n graphToken,\n { displayName: botName, signInAudience: appType === 'SingleTenant' ? 'AzureADMyOrg' : 'AzureADMultipleOrgs' },\n this.#config.graphBaseUrl,\n );\n let appPassword: string;\n let devPortalToken: string | undefined;\n let botRegistered = false;\n let installation: TeamsInstallation;\n try {\n appPassword = await addApplicationPassword(graphToken, application.id, 'mastra-teams', this.#config.graphBaseUrl);\n\n // 2. Bot registration (Teams Developer Portal audience — a Graph token\n // is not accepted here, hence the second scope-specific resolve).\n devPortalToken = await tokenResolver(TEAMS_DEV_PORTAL_SCOPE, appTenantId);\n await createBotRegistration(\n devPortalToken,\n {\n botId: application.appId,\n name: botName,\n description: `Mastra agent \"${agentId}\"`,\n iconUrl: this.#config.botIconUrl ?? '',\n messagingEndpoint,\n callingEndpoint: '',\n },\n this.#config.devPortalBaseUrl,\n );\n botRegistered = true;\n\n // 3. Persist while still inside the rollback scope — a failed save must\n // not strand a live bot registration + Entra app with no local record\n // (disconnect() could never find them again, and the minted secret\n // would be lost).\n installation = {\n id: existing?.id ?? randomUUID(),\n agentId,\n webhookId,\n status: 'active',\n appId: application.appId,\n entraObjectId: application.id,\n appPassword,\n appTenantId,\n appType,\n botName,\n messagingEndpoint,\n installedAt: existing?.installedAt ?? new Date(),\n };\n await store.save(installation);\n } catch (err) {\n // Roll back the half-provisioned resources so retries don't accumulate\n // orphaned bot registrations / Entra apps in the tenant.\n if (botRegistered && devPortalToken) {\n try {\n await deleteBotRegistration(devPortalToken, application.appId, this.#config.devPortalBaseUrl);\n } catch (cleanupErr) {\n console.warn(`[Teams] Failed to roll back bot registration \"${application.appId}\":`, cleanupErr);\n }\n }\n try {\n await deleteApplication(graphToken, application.id, this.#config.graphBaseUrl);\n } catch (cleanupErr) {\n console.warn(`[Teams] Failed to roll back Entra application \"${application.appId}\":`, cleanupErr);\n }\n throw err;\n }\n\n // The installation is saved and the bot is live — post-save failures are\n // non-fatal (the adapter is rebuilt lazily on the next webhook/initialize).\n try {\n await this.#activateInstallation(installation);\n } catch (err) {\n console.warn(`[Teams] Failed to activate installation for agent \"${agentId}\":`, err);\n }\n this.#configured = true;\n try {\n await this.#config.onInstall?.(installation);\n } catch (err) {\n console.warn(`[Teams] onInstall hook failed for agent \"${agentId}\":`, err);\n }\n // The bot is live; the remaining human step is packaging it into a Teams\n // app (manifest + icons) in the Developer Portal for install/distribution.\n return { type: 'deep_link', url: DEV_PORTAL_BOTS_URL, installationId: installation.id };\n }\n\n /**\n * Disconnect an agent from Microsoft Teams. For bots this provider\n * provisioned (delegated mode), the Developer Portal registration and the\n * Entra application are deleted best-effort — a control-plane failure never\n * blocks removal of the local installation.\n */\n async disconnect(agentId: string): Promise<void> {\n const store = await this.#getStore();\n const existing = await store.getByAgent(agentId);\n if (!existing) {\n throw new Error(`No Teams installation found for agent \"${agentId}\"`);\n }\n if (existing.entraObjectId && this.#config.tokenResolver) {\n if (existing.appId) {\n try {\n const devPortalToken = await this.#config.tokenResolver(TEAMS_DEV_PORTAL_SCOPE, existing.appTenantId);\n await deleteBotRegistration(devPortalToken, existing.appId, this.#config.devPortalBaseUrl);\n } catch (err) {\n console.warn(`[Teams] Failed to delete bot registration for agent \"${agentId}\":`, err);\n }\n }\n try {\n const graphToken = await this.#config.tokenResolver(TEAMS_GRAPH_SCOPE, existing.appTenantId);\n await deleteApplication(graphToken, existing.entraObjectId, this.#config.graphBaseUrl);\n } catch (err) {\n console.warn(`[Teams] Failed to delete Entra application for agent \"${agentId}\":`, err);\n }\n }\n this.#adapters.delete(existing.id);\n await store.deleteByAgent(agentId);\n this.#configured = (await store.list()).some(i => i.status === 'active');\n }\n\n /** List installations (public info only — no secrets). */\n async listInstallations(): Promise<ChannelInstallationInfo[]> {\n const store = await this.#getStore();\n const installations = await store.list();\n return installations.map(toInstallationInfo);\n }\n\n /**\n * Get the full installation for an agent (includes the client secret).\n * Returns `null` if the agent has no Teams installation. Mirrors\n * `SlackProvider.getInstallation`.\n */\n async getInstallation(agentId: string): Promise<TeamsInstallation | null> {\n const store = await this.#getStore();\n return (await store.getByAgent(agentId)) ?? null;\n }\n\n /** Whether at least one bot is actively registered. */\n isConfigured(): boolean {\n return this.#configured;\n }\n\n /**\n * Get the live `TeamsAdapter` for an installation id, if one is active.\n * Used for message formatting/posting. Mirrors `SlackProvider.getAdapter`.\n */\n getAdapter(installationId: string): TeamsAdapter | undefined {\n return this.#adapters.get(installationId);\n }\n\n // ===========================================================================\n // Webhook handling\n // ===========================================================================\n\n async #handleWebhook(c: {\n req: { param: (k: string) => string | undefined; raw: Request };\n json: (body: unknown, status?: number) => Response;\n }): Promise<Response> {\n const webhookId = c.req.param('webhookId');\n if (!webhookId) return c.json({ ok: false, error: 'Missing webhookId' }, 400);\n\n const store = await this.#getStore();\n const installation = await store.getByWebhookId(webhookId);\n if (!installation || installation.status !== 'active') {\n return c.json({ ok: false, error: 'Unknown webhook' }, 404);\n }\n\n const agent = this.#resolveAgent(installation.agentId);\n if (!agent || !this.#mastra) {\n // Nothing to route to — ack so Bot Framework stops retrying. The\n // request has not been JWT-verified at this point, but it carries no\n // side effects either.\n return c.json({ ok: true });\n }\n\n const adapter = this.#getOrCreateAdapter(installation);\n let channels = agent.getChannels();\n if (!channels || channels.adapters[PLATFORM] !== adapter) {\n channels = this.#createAgentChannels(agent, adapter);\n await channels.initialize(this.#mastra);\n }\n\n // Authentication happens inside the adapter's handleWebhook: Microsoft\n // signs every activity with a JWT the adapter verifies against the bot's\n // appId audience.\n const waitUntil = this.#config.waitUntil ?? resolveWaitUntil(c as never);\n try {\n return await channels.handleWebhookEvent(PLATFORM, c.req.raw, waitUntil ? { waitUntil } : undefined);\n } catch (err) {\n console.error('[Teams] Error delegating to AgentChannels:', err);\n return c.json({ ok: true });\n }\n }\n\n // ===========================================================================\n // Internals\n // ===========================================================================\n\n #getOrCreateAdapter(installation: TeamsInstallation): TeamsAdapter {\n const existing = this.#adapters.get(installation.id);\n if (existing) return existing;\n const adapter = createTeamsAdapter({\n appId: installation.appId,\n appPassword: installation.appPassword,\n ...(installation.appTenantId !== undefined ? { appTenantId: installation.appTenantId } : {}),\n ...(installation.appType !== undefined ? { appType: installation.appType } : {}),\n ...(installation.botName !== undefined ? { userName: installation.botName } : {}),\n ...(this.#config.apiUrl !== undefined ? { apiUrl: this.#config.apiUrl } : {}),\n ...(this.#config.logger !== undefined ? { logger: this.#config.logger } : {}),\n });\n this.#adapters.set(installation.id, adapter);\n return adapter;\n }\n\n /** Rebuild the adapter and inject AgentChannels for an active installation. */\n async #activateInstallation(installation: TeamsInstallation): Promise<void> {\n const agent = this.#resolveAgent(installation.agentId);\n const adapter = this.#getOrCreateAdapter(installation);\n if (agent && this.#mastra) {\n const channels = this.#createAgentChannels(agent, adapter);\n await channels.initialize(this.#mastra);\n }\n }\n\n /**\n * Create AgentChannels for an agent with the Teams adapter, preserving any\n * adapters/config the agent author already configured (mirrors `@mastra/slack`).\n */\n #createAgentChannels(agent: Agent, adapter: TeamsAdapter): AgentChannels {\n const existing = agent.getChannels();\n const existingConfig = existing?.channelConfig;\n const cfg = this.#config;\n const entry = {\n adapter,\n ...resolveTeamsAdapterConfig(cfg),\n ...(cfg.toolDisplay !== undefined ? { toolDisplay: cfg.toolDisplay } : {}),\n ...(cfg.cors !== undefined ? { cors: cfg.cors } : {}),\n ...(cfg.formatError !== undefined ? { formatError: cfg.formatError } : {}),\n } as ChannelAdapterConfig;\n const channels = new AgentChannels({\n ...existingConfig,\n adapters: { ...existingConfig?.adapters, [PLATFORM]: entry },\n userName: agent.name,\n handlers: cfg.handlers ?? existingConfig?.handlers,\n inlineMedia: cfg.inlineMedia ?? existingConfig?.inlineMedia,\n inlineLinks: cfg.inlineLinks ?? existingConfig?.inlineLinks,\n state: cfg.state ?? existingConfig?.state,\n threadContext: cfg.threadContext ?? existingConfig?.threadContext,\n chatOptions: cfg.chatOptions ?? existingConfig?.chatOptions,\n tools: cfg.tools ?? existingConfig?.tools,\n resolveResourceId: cfg.resolveResourceId ?? existingConfig?.resolveResourceId,\n waitUntil: cfg.waitUntil ?? existingConfig?.waitUntil,\n resolveWaitUntil: cfg.resolveWaitUntil ?? existingConfig?.resolveWaitUntil,\n });\n agent.setChannels(channels);\n return channels;\n }\n\n async #autoInitialize(): Promise<void> {\n if (!this.#mastra) return;\n await this.initialize();\n }\n\n #resolveAgent(agentId: string): Agent | undefined {\n try {\n return this.#mastra?.getAgentById(agentId) as Agent | undefined;\n } catch {\n return undefined;\n }\n }\n\n async #getStore(): Promise<TeamsInstallStore> {\n if (this.#store) return this.#store;\n const encryptionKey = this.#config.encryptionKey ?? process.env.MASTRA_ENCRYPTION_KEY;\n this.#store = new TeamsInstallStore(await this.#resolveStorage(), encryptionKey);\n return this.#store;\n }\n\n async #resolveStorage(): Promise<ChannelsStorage> {\n if (this.#config.storage) return this.#config.storage;\n const mastraStore = this.#mastra?.getStorage();\n if (mastraStore) {\n // A configured store that fails to initialize must surface, not silently\n // degrade to in-memory — delegated installs hold real Entra apps and\n // secrets that would be orphaned when the non-persistent store is lost.\n await mastraStore.init();\n const channels = await mastraStore.getStore('channels');\n if (channels) return channels;\n }\n // No persistent storage available — fall back to in-memory. Installations\n // won't survive a restart; pass `storage` or configure Mastra storage in prod.\n console.warn(\n '[Teams] No persistent channels storage configured — installations are stored in memory and will be lost on restart.',\n );\n return new InMemoryChannelsStorage();\n }\n\n #getBaseUrl(): string | undefined {\n if (this.#config.baseUrl) return stripTrailingSlash(this.#config.baseUrl);\n const server = this.#mastra?.getServer();\n if (!server) return undefined;\n const protocol = server.studioProtocol ?? 'http';\n const host = server.studioHost ?? server.host ?? 'localhost';\n const port = server.studioPort ?? server.port ?? (Number(process.env.PORT) || 4111);\n const includePort = !((protocol === 'https' && port === 443) || (protocol === 'http' && port === 80));\n return includePort ? `${protocol}://${host}:${port}` : `${protocol}://${host}`;\n }\n}\n\nfunction stripTrailingSlash(url: string): string {\n return url.endsWith('/') ? url.slice(0, -1) : url;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/teams",
|
|
3
|
-
"version": "0.0.0",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
4
|
"description": "Microsoft Teams integration for Mastra agents — a ChannelProvider over @chat-adapter/teams with per-agent bot provisioning (Entra + Teams Developer Portal), webhooks, and streaming replies",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -21,23 +21,16 @@
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
|
-
"scripts": {
|
|
25
|
-
"build": "tsdown && node scripts/write-cts-shim.mjs",
|
|
26
|
-
"dev": "tsdown --watch",
|
|
27
|
-
"typecheck": "tsc --noEmit",
|
|
28
|
-
"test": "vitest run",
|
|
29
|
-
"test:watch": "vitest"
|
|
30
|
-
},
|
|
31
24
|
"dependencies": {
|
|
32
25
|
"@chat-adapter/teams": "~4.40.0"
|
|
33
26
|
},
|
|
34
27
|
"devDependencies": {
|
|
35
|
-
"@mastra/core": "workspace:*",
|
|
36
28
|
"@types/node": "^22.20.1",
|
|
37
29
|
"tsdown": "0.22.9",
|
|
38
|
-
"typescript": "
|
|
30
|
+
"typescript": "^7.0.2",
|
|
39
31
|
"undici": "^6.28.1",
|
|
40
|
-
"vitest": "^4.1.11"
|
|
32
|
+
"vitest": "^4.1.11",
|
|
33
|
+
"@mastra/core": "1.72.0-alpha.10"
|
|
41
34
|
},
|
|
42
35
|
"peerDependencies": {
|
|
43
36
|
"@mastra/core": ">=1.22.0 <2.0.0"
|
|
@@ -64,5 +57,12 @@
|
|
|
64
57
|
},
|
|
65
58
|
"engines": {
|
|
66
59
|
"node": ">=22.13.0"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "tsdown && node scripts/write-cts-shim.mjs",
|
|
63
|
+
"dev": "tsdown --watch",
|
|
64
|
+
"typecheck": "tsc --noEmit",
|
|
65
|
+
"test": "vitest run",
|
|
66
|
+
"test:watch": "vitest"
|
|
67
67
|
}
|
|
68
|
-
}
|
|
68
|
+
}
|