import type { PlatformAdapter, SurfaceCapabilities, IngressSink, InteractionEvent, RunRenderer, ReplyTarget, ConversationStore, MessageRef, ProviderActor, UserQuery } from "@copilotkit/channels-core"; import type { ChannelNode, ThreadMessage } from "@copilotkit/channels-ui"; import type { AdaptiveCard } from "./render/adaptive-card.js"; import type { TeamsAdapterOptions } from "./types.js"; /** Native render output: a plain text activity or an Adaptive Card attachment. */ type TeamsActivityPayload = { text: string; } | { card: AdaptiveCard; }; /** * Microsoft Teams `PlatformAdapter`. * * Ingress: a `CloudAdapter` receives Teams activities at `POST /api/messages` * (M365 Agents SDK). The inbound HTTP turn is **acked immediately**; the agent * run is handed off to a detached `continueConversation` so it can outlive the * turn. This is required for HITL, where `awaitChoice` suspends the run until a user * clicks an Adaptive Card button (possibly minutes later). That detached * `continueConversation` provides a stable `TurnContext` the whole run streams * on, exactly as the Slack adapter runs in the background off a web client. * * Adaptive Card `Action.Submit` clicks arrive as Message activities carrying * the action `data` in `activity.value`; those are decoded and routed to * `sink.onInteraction` to resolve the waiter. This path needs no Microsoft * credentials in the local M365 Agents Playground. */ export declare class TeamsAdapter implements PlatformAdapter { private readonly opts; readonly platform = "teams"; readonly capabilities: SurfaceCapabilities; readonly ackDeadlineMs = 15000; private readonly store; private cloud; private server; private sink; constructor(opts?: TeamsAdapterOptions); start(sink: IngressSink): Promise; stop(): Promise; /** * Normalize an inbound activity, ack the HTTP turn immediately, and drive the * work into the engine on a **detached** `continueConversation` so it can * outlive this turn (HITL suspends the run until a later click). */ private handleActivity; /** * Resolve a message's uploaded files into AG-UI content parts. Personal chats * deliver the file inline (the Teams bot file API), which `buildFileContentParts` * handles; a channel doesn't include the file at all, so we fetch it through * Microsoft Graph when credentials are configured (group chats aren't wired * up yet). Failures are logged, never thrown. */ private collectInboundFileParts; /** App-only Graph credentials, when all three are configured. */ private graphCredentials; /** Pull the team/channel/message ids Graph needs out of a channel activity. */ private channelMessageRef; /** * Concise operational log for the inbound-file pipeline: how many parts we * built and any skip reasons. Deliberately content-free — it never logs file * bytes, decoded text, or SharePoint URLs. */ private logFileParts; /** Whether we can send proactively (out-of-turn). Requires a Microsoft app id. */ private canGoProactive; /** * Run `fn` against a proactive `TurnContext` opened by `continueConversation`, * detached from any inbound HTTP turn. Fire-and-forget: the caller acks the * inbound turn immediately and this runs (and may suspend at `awaitChoice`) * in the background. Errors are logged, never surfaced to the inbound turn. */ private runDetached; /** Open a proactive `TurnContext` for the conversation and await `fn`. */ private withProactive; /** * Render IR to a native payload: plain text when it collapses to text, * otherwise an Adaptive Card. (A bare `Echo: hi` is a text bubble; structured * or interactive UI becomes a card.) */ render(ir: ChannelNode[]): TeamsActivityPayload; post(target: ReplyTarget, ir: ChannelNode[]): Promise; update(ref: MessageRef, ir: ChannelNode[]): Promise; /** * Stream a text reply by message edit: post the first content, then * `updateActivity` the same message as the buffer grows (throttled). This is * Teams' baseline streaming model: no native token streaming. */ stream(target: ReplyTarget, chunks: AsyncIterable): Promise; delete(ref: MessageRef): Promise; /** * Post a file to the conversation. Teams renders an image inline when it's * sent as an attachment whose `contentUrl` is a `data:` URI, so we base64 the * bytes into one — exactly what `render_chart`/`render_diagram` need to drop a * PNG into the thread (the bot-slack `postFile` parallel). Non-image bytes are * still attached with their inferred MIME; whether Teams previews them is up * to the client. Sends on the live turn context, or proactively by reference. */ postFile(target: ReplyTarget, { bytes, filename, altText, }: { bytes: Uint8Array; filename: string; title?: string; altText?: string; }): Promise<{ ok: boolean; fileId?: string; error?: string; }>; createRunRenderer(target: ReplyTarget): RunRenderer; decodeInteraction(raw: unknown): InteractionEvent | undefined; lookupUser(_q: UserQuery): Promise; /** Classify the provider principal from the role Microsoft supplies. */ private actorFrom; /** Build bounded identity facts from one Teams activity. */ private identityContext; get conversationStore(): ConversationStore; /** Return the conversation transcript the adapter has accumulated. */ getMessages(target: ReplyTarget): Promise; /** Send plain Markdown text, preferring the live turn context. */ private sendText; /** Send an Adaptive Card as a message attachment on the live turn. */ private sendCard; /** Edit a previously-posted text activity in place (streamed-by-edit). */ private updateText; /** Fire a typing indicator (shown while the agent works). */ private sendTyping; /** * Send a typing indicator now and keep re-sending it every few seconds until * the returned stop fn is called. Teams' typing indicator lapses after a few * seconds, so a single ping can't cover slow work (a Graph download, a chart * render) where nothing posts in between. Returns a stop fn; call it in a * `finally` so the timer is always cleared. */ private startTypingHeartbeat; } /** Construct a Microsoft Teams `PlatformAdapter`. */ export declare function teams(opts?: TeamsAdapterOptions): TeamsAdapter; export {}; //# sourceMappingURL=adapter.d.ts.map