fix: msteams attachments + plugin prompt hints

Co-authored-by: Christof <10854026+Evizero@users.noreply.github.com>
This commit is contained in:
Peter Steinberger
2026-01-22 03:27:26 +00:00
parent 5fe8c4ab8c
commit 0f7f7bb95f
50 changed files with 2739 additions and 174 deletions

View File

@@ -17,7 +17,7 @@ import {
resolveMSTeamsChannelAllowlist,
resolveMSTeamsUserAllowlist,
} from "./resolve-allowlist.js";
import { sendMessageMSTeams } from "./send.js";
import { sendAdaptiveCardMSTeams, sendMessageMSTeams } from "./send.js";
import { resolveMSTeamsCredentials } from "./token.js";
import {
listMSTeamsDirectoryGroupsLive,
@@ -64,6 +64,19 @@ export const msteamsPlugin: ChannelPlugin<ResolvedMSTeamsAccount> = {
threads: true,
media: true,
},
agentPrompt: {
messageToolHints: () => [
"- Adaptive Cards supported. Use `action=send` with `card={type,version,body}` to send rich cards.",
"- MSTeams targeting: omit `target` to reply to the current conversation (auto-inferred). Explicit targets: `user:ID` or `user:Display Name` (requires Graph API) for DMs, `conversation:19:...@thread.tacv2` for groups/channels. Prefer IDs over display names for speed.",
],
},
threading: {
buildToolContext: ({ context, hasRepliedRef }) => ({
currentChannelId: context.To?.trim() || undefined,
currentThreadTs: context.ReplyToId,
hasRepliedRef,
}),
},
reload: { configPrefixes: ["channels.msteams"] },
configSchema: buildChannelConfigSchema(MSTeamsConfigSchema),
config: {
@@ -137,7 +150,12 @@ export const msteamsPlugin: ChannelPlugin<ResolvedMSTeamsAccount> = {
looksLikeId: (raw) => {
const trimmed = raw.trim();
if (!trimmed) return false;
if (/^(conversation:|user:)/i.test(trimmed)) return true;
if (/^conversation:/i.test(trimmed)) return true;
if (/^user:/i.test(trimmed)) {
// Only treat as ID if the value after user: looks like a UUID
const id = trimmed.slice("user:".length).trim();
return /^[0-9a-fA-F-]{16,}$/.test(id);
}
return trimmed.includes("@thread");
},
hint: "<conversationId|user:ID|conversation:ID>",
@@ -320,6 +338,50 @@ export const msteamsPlugin: ChannelPlugin<ResolvedMSTeamsAccount> = {
if (!enabled) return [];
return ["poll"] satisfies ChannelMessageActionName[];
},
supportsCards: ({ cfg }) => {
return (
cfg.channels?.msteams?.enabled !== false &&
Boolean(resolveMSTeamsCredentials(cfg.channels?.msteams))
);
},
handleAction: async (ctx) => {
// Handle send action with card parameter
if (ctx.action === "send" && ctx.params.card) {
const card = ctx.params.card as Record<string, unknown>;
const to =
typeof ctx.params.to === "string"
? ctx.params.to.trim()
: typeof ctx.params.target === "string"
? ctx.params.target.trim()
: "";
if (!to) {
return {
isError: true,
content: [{ type: "text", text: "Card send requires a target (to)." }],
};
}
const result = await sendAdaptiveCardMSTeams({
cfg: ctx.cfg,
to,
card,
});
return {
content: [
{
type: "text",
text: JSON.stringify({
ok: true,
channel: "msteams",
messageId: result.messageId,
conversationId: result.conversationId,
}),
},
],
};
}
// Return null to fall through to default handler
return null as never;
},
},
outbound: msteamsOutbound,
status: {