diff --git a/changelog.d/fixes/8971-fix.plan.md b/changelog.d/fixes/8971-fix.plan.md new file mode 100644 index 0000000000..b4f0183830 --- /dev/null +++ b/changelog.d/fixes/8971-fix.plan.md @@ -0,0 +1 @@ +- fix(providers): copilot-m365-web enterprise turns send disconnectBehavior=continue (#8971) diff --git a/open-sse/executors/copilot-m365-frames.ts b/open-sse/executors/copilot-m365-frames.ts index c15782e756..add2716ee0 100644 --- a/open-sse/executors/copilot-m365-frames.ts +++ b/open-sse/executors/copilot-m365-frames.ts @@ -166,6 +166,13 @@ export interface ChatInvocationOptions { tone?: string; /** Tier-specific allowed message types; defaults to {@link ALLOWED_MESSAGE_TYPES}. */ allowedMessageTypes?: readonly string[]; + /** + * Tier-specific disconnect behavior sent in every type:4 chat invocation. The work + * Surface rejects any value other than exactly "continue" (#8971). Defaults to "" + * for individual/consumer/EDU tiers; {@link resolveChatInvocationOverrides} returns + * "continue" for the enterprise tier. + */ + disconnectBehavior?: string; } /** @@ -178,18 +185,21 @@ export function resolveChatInvocationOverrides(tier: string | undefined): { optionsSets: string[]; tone: string; allowedMessageTypes: readonly string[]; + disconnectBehavior: string; } { if (tier === "enterprise") { return { optionsSets: [...M365_ENTERPRISE_OPTION_SETS], tone: "Magic", allowedMessageTypes: [...ALLOWED_MESSAGE_TYPES, ...M365_ENTERPRISE_EXTRA_MESSAGE_TYPES], + disconnectBehavior: "continue", }; } return { optionsSets: [...M365_DEFAULT_OPTION_SETS], tone: "", allowedMessageTypes: ALLOWED_MESSAGE_TYPES, + disconnectBehavior: "", }; } @@ -253,7 +263,7 @@ export function buildChatInvocation(opts: ChatInvocationOptions): Record { + const invocationArgs = await sendChatInvocation("enterprise"); + assert.equal( + invocationArgs.disconnectBehavior, + "continue", + `enterprise-tier invocation must carry disconnectBehavior="continue"; got ${JSON.stringify(invocationArgs.disconnectBehavior)}` + ); +}); + +test("#8971: individual (no tier) chat invocation disconnectBehavior remains empty (byte-identical to #4042)", async () => { + const invocationArgs = await sendChatInvocation(undefined); + assert.equal( + invocationArgs.disconnectBehavior, + "", + `individual-tier invocation must carry disconnectBehavior=""; got ${JSON.stringify(invocationArgs.disconnectBehavior)}` + ); +});