feat(msteams): add MS Teams provider skeleton

- Add Microsoft 365 Agents SDK packages (@microsoft/agents-hosting,
  @microsoft/agents-hosting-express, @microsoft/agents-hosting-extensions-teams)
- Add MSTeamsConfig type and zod schema
- Create src/msteams/ provider with monitor, token, send, probe
- Wire provider into gateway (server-providers.ts, server.ts)
- Add msteams to all provider type unions (hooks, queue, cron, etc.)
- Update implementation guide with new SDK and progress
This commit is contained in:
Onur
2026-01-07 21:29:39 +03:00
committed by Peter Steinberger
parent 7274d6e757
commit d9cbecac7f
16 changed files with 708 additions and 41 deletions

View File

@@ -88,6 +88,14 @@ export type IMessageRuntimeStatus = {
dbPath?: string | null;
};
export type MSTeamsRuntimeStatus = {
running: boolean;
lastStartAt?: number | null;
lastStopAt?: number | null;
lastError?: string | null;
port?: number | null;
};
export type ProviderRuntimeSnapshot = {
whatsapp: WebProviderStatus;
whatsappAccounts?: Record<string, WebProviderStatus>;
@@ -101,6 +109,7 @@ export type ProviderRuntimeSnapshot = {
signalAccounts?: Record<string, SignalRuntimeStatus>;
imessage: IMessageRuntimeStatus;
imessageAccounts?: Record<string, IMessageRuntimeStatus>;
msteams: MSTeamsRuntimeStatus;
};
type SubsystemLogger = ReturnType<typeof createSubsystemLogger>;
@@ -113,12 +122,14 @@ type ProviderManagerOptions = {
logSlack: SubsystemLogger;
logSignal: SubsystemLogger;
logIMessage: SubsystemLogger;
logMSTeams: SubsystemLogger;
whatsappRuntimeEnv: RuntimeEnv;
telegramRuntimeEnv: RuntimeEnv;
discordRuntimeEnv: RuntimeEnv;
slackRuntimeEnv: RuntimeEnv;
signalRuntimeEnv: RuntimeEnv;
imessageRuntimeEnv: RuntimeEnv;
msteamsRuntimeEnv: RuntimeEnv;
};
export type ProviderManager = {
@@ -136,6 +147,8 @@ export type ProviderManager = {
stopSignalProvider: (accountId?: string) => Promise<void>;
startIMessageProvider: (accountId?: string) => Promise<void>;
stopIMessageProvider: (accountId?: string) => Promise<void>;
startMSTeamsProvider: () => Promise<void>;
stopMSTeamsProvider: () => Promise<void>;
markWhatsAppLoggedOut: (cleared: boolean, accountId?: string) => void;
};
@@ -150,12 +163,14 @@ export function createProviderManager(
logSlack,
logSignal,
logIMessage,
logMSTeams,
whatsappRuntimeEnv,
telegramRuntimeEnv,
discordRuntimeEnv,
slackRuntimeEnv,
signalRuntimeEnv,
imessageRuntimeEnv,
msteamsRuntimeEnv,
} = opts;
const whatsappAborts = new Map<string, AbortController>();
@@ -164,7 +179,9 @@ export function createProviderManager(
const slackAborts = new Map<string, AbortController>();
const signalAborts = new Map<string, AbortController>();
const imessageAborts = new Map<string, AbortController>();
let msteamsAbort: AbortController | null = null;
const whatsappTasks = new Map<string, Promise<unknown>>();
let msteamsTask: Promise<unknown> | null = null;
const telegramTasks = new Map<string, Promise<unknown>>();
const discordTasks = new Map<string, Promise<unknown>>();
const slackTasks = new Map<string, Promise<unknown>>();
@@ -224,6 +241,13 @@ export function createProviderManager(
cliPath: null,
dbPath: null,
});
let msteamsRuntime: MSTeamsRuntimeStatus = {
running: false,
lastStartAt: null,
lastStopAt: null,
lastError: null,
port: null,
};
const updateWhatsAppStatus = (accountId: string, next: WebProviderStatus) => {
whatsappRuntimes.set(accountId, next);
@@ -1026,6 +1050,83 @@ export function createProviderManager(
);
};
const startMSTeamsProvider = async () => {
if (msteamsTask) return;
const cfg = loadConfig();
if (!cfg.msteams) {
msteamsRuntime = {
...msteamsRuntime,
running: false,
lastError: "not configured",
};
if (shouldLogVerbose()) {
logMSTeams.debug("msteams provider not configured (no msteams config)");
}
return;
}
if (cfg.msteams?.enabled === false) {
msteamsRuntime = {
...msteamsRuntime,
running: false,
lastError: "disabled",
};
if (shouldLogVerbose()) {
logMSTeams.debug("msteams provider disabled (msteams.enabled=false)");
}
return;
}
const { monitorMSTeamsProvider } = await import("../msteams/index.js");
const port = cfg.msteams?.webhook?.port ?? 3978;
logMSTeams.info(`starting provider (port ${port})`);
msteamsAbort = new AbortController();
msteamsRuntime = {
...msteamsRuntime,
running: true,
lastStartAt: Date.now(),
lastError: null,
port,
};
const task = monitorMSTeamsProvider({
cfg,
runtime: msteamsRuntimeEnv,
abortSignal: msteamsAbort.signal,
})
.catch((err) => {
msteamsRuntime = {
...msteamsRuntime,
lastError: formatError(err),
};
logMSTeams.error(`provider exited: ${formatError(err)}`);
})
.finally(() => {
msteamsAbort = null;
msteamsTask = null;
msteamsRuntime = {
...msteamsRuntime,
running: false,
lastStopAt: Date.now(),
};
});
msteamsTask = task;
};
const stopMSTeamsProvider = async () => {
if (!msteamsAbort && !msteamsTask) return;
msteamsAbort?.abort();
try {
await msteamsTask;
} catch {
// ignore
}
msteamsAbort = null;
msteamsTask = null;
msteamsRuntime = {
...msteamsRuntime,
running: false,
lastStopAt: Date.now(),
};
};
const startProviders = async () => {
await startWhatsAppProvider();
await startDiscordProvider();
@@ -1033,6 +1134,7 @@ export function createProviderManager(
await startTelegramProvider();
await startSignalProvider();
await startIMessageProvider();
await startMSTeamsProvider();
};
const markWhatsAppLoggedOut = (cleared: boolean, accountId?: string) => {
@@ -1180,6 +1282,7 @@ export function createProviderManager(
signalAccounts,
imessage,
imessageAccounts,
msteams: { ...msteamsRuntime },
};
};
@@ -1198,6 +1301,8 @@ export function createProviderManager(
stopSignalProvider,
startIMessageProvider,
stopIMessageProvider,
startMSTeamsProvider,
stopMSTeamsProvider,
markWhatsAppLoggedOut,
};
}