From 408d0c5f468d8c3b98fd46abf69fee4d478265bd Mon Sep 17 00:00:00 2001 From: heyitsaamir Date: Thu, 21 May 2026 15:45:48 -0700 Subject: [PATCH 1/2] Merge root message entities in activities --- packages/api/src/activities/activity.spec.ts | 44 ++++++++++++++++++ packages/api/src/activities/activity.ts | 49 ++++++++++++++++---- 2 files changed, 84 insertions(+), 9 deletions(-) diff --git a/packages/api/src/activities/activity.spec.ts b/packages/api/src/activities/activity.spec.ts index 65bf9a9ec..228111722 100644 --- a/packages/api/src/activities/activity.spec.ts +++ b/packages/api/src/activities/activity.spec.ts @@ -139,6 +139,50 @@ describe('Activity', () => { }); }); + describe('addEntity/addEntities', () => { + it('should merge multiple root message entities into one', () => { + const activity = new Activity({ type: 'test' }) + .addEntity({ + type: 'https://schema.org/Message', + '@type': 'Message', + '@context': 'https://schema.org', + '@id': '', + additionalType: ['AIGeneratedContent'], + }) + .addEntities( + { + type: 'mention', + text: 'test-bot', + mentioned: bot, + }, + { + type: 'https://schema.org/Message', + '@type': 'Message', + '@context': 'https://schema.org', + '@id': '', + citation: [ + { + '@type': 'Claim', + position: 0, + appearance: { + '@type': 'DigitalDocument', + name: 'doc', + abstract: 'doc abstract', + }, + }, + ], + } + ); + + expect(activity.entities).toHaveLength(2); + const [messageEntity, mentionEntity] = activity.entities as any[]; + expect(messageEntity.type).toBe('https://schema.org/Message'); + expect(messageEntity.additionalType).toEqual(['AIGeneratedContent']); + expect(messageEntity.citation).toHaveLength(1); + expect(mentionEntity.type).toBe('mention'); + }); + }); + describe('withChannelData feedback normalization', () => { it('should upgrade legacy feedbackLoopEnabled:true to feedbackLoop default', () => { const activity = new Activity({ type: 'test' }).withChannelData({ feedbackLoopEnabled: true }); diff --git a/packages/api/src/activities/activity.ts b/packages/api/src/activities/activity.ts index 6b62db046..42092af99 100644 --- a/packages/api/src/activities/activity.ts +++ b/packages/api/src/activities/activity.ts @@ -335,6 +335,11 @@ export class Activity implements IActivity { * Add an entity. */ addEntity(value: Entity) { + if (this.isRootLevelMessageEntity(value)) { + this.mergeRootLevelMessageEntity(this.ensureSingleRootLevelMessageEntity(), value); + return this; + } + if (!this.entities) { this.entities = []; } @@ -347,11 +352,10 @@ export class Activity implements IActivity { * Add multiple entities */ addEntities(...value: Entity[]) { - if (!this.entities) { - this.entities = []; + for (const entity of value) { + this.addEntity(entity); } - this.entities.push(...value); return this; } @@ -467,20 +471,47 @@ export class Activity implements IActivity { * There should only be one root level message entity. */ private ensureSingleRootLevelMessageEntity(): MessageEntity { - let mesageEntity = this.entities?.find( - (e) => e.type === 'https://schema.org/Message' && e['@type'] === 'Message' + let messageEntity = this.entities?.find( + (e) => this.isRootLevelMessageEntity(e) ) as MessageEntity | undefined; - if (!mesageEntity) { - mesageEntity = { + if (!messageEntity) { + messageEntity = { type: 'https://schema.org/Message', '@type': 'Message', '@context': 'https://schema.org', '@id': '', }; - this.addEntity(mesageEntity); + if (!this.entities) { + this.entities = []; + } + this.entities.push(messageEntity); + } + + return messageEntity; + } + + private isRootLevelMessageEntity(entity: Entity): entity is MessageEntity { + return entity.type === 'https://schema.org/Message' && entity['@type'] === 'Message'; + } + + private mergeRootLevelMessageEntity(target: MessageEntity, source: MessageEntity): MessageEntity { + const merged: MessageEntity = { + ...target, + ...source, + }; + + if (target.additionalType || source.additionalType) { + merged.additionalType = [ + ...new Set([...(target.additionalType || []), ...(source.additionalType || [])]), + ]; + } + + if (target.citation || source.citation) { + merged.citation = [...(target.citation || []), ...(source.citation || [])]; } - return mesageEntity; + Object.assign(target, merged); + return target; } } From 7a672df336e236c47ebf7eebd770605a1823cf90 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 May 2026 00:44:24 +0000 Subject: [PATCH 2/2] Fix citation merge to dedupe by claim position --- package-lock.json | 20 ++++++-------------- packages/api/src/activities/activity.spec.ts | 12 ++++++++++++ packages/api/src/activities/activity.ts | 8 +++++++- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index a4c9fb83d..22377ee38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -554,16 +554,6 @@ "typescript": "^5.4.5" } }, - "examples/http-adapters/node_modules/@hono/node-server": { - "version": "1.19.13", - "license": "MIT", - "engines": { - "node": ">=18.14.1" - }, - "peerDependencies": { - "hono": "^4" - } - }, "examples/http-adapters/node_modules/accepts": { "version": "2.0.0", "license": "MIT", @@ -790,6 +780,7 @@ "examples/lights": { "name": "@examples/lights", "version": "0.0.6", + "extraneous": true, "license": "MIT", "dependencies": { "@microsoft/teams.ai": "*", @@ -1368,6 +1359,7 @@ "external/a2a": { "name": "@microsoft/teams.a2a", "version": "0.0.0", + "deprecated": "@microsoft/teams.a2a is deprecated. Use `@a2a-js/sdk` directly with a dedicated AI framework like the `openai` SDK. See examples/a2a for the new pattern.", "license": "MIT", "dependencies": { "@microsoft/teams.ai": "*", @@ -1392,6 +1384,7 @@ "external/mcp": { "name": "@microsoft/teams.mcp", "version": "0.0.0", + "deprecated": "@microsoft/teams.mcp is deprecated. Use `@modelcontextprotocol/sdk` directly. See examples/ai-mcp and examples/mcp-server for the new pattern.", "license": "MIT", "dependencies": { "@microsoft/teams.ai": "*", @@ -1416,6 +1409,7 @@ "external/mcpclient": { "name": "@microsoft/teams.mcpclient", "version": "0.0.0", + "deprecated": "@microsoft/teams.mcpclient is deprecated. Use `@modelcontextprotocol/sdk` directly with a dedicated AI framework like the `openai` SDK. See examples/ai-mcp for the new pattern.", "license": "MIT", "dependencies": { "@microsoft/teams.common": "*" @@ -2993,10 +2987,6 @@ "resolved": "examples/http-adapters", "link": true }, - "node_modules/@examples/lights": { - "resolved": "examples/lights", - "link": true - }, "node_modules/@examples/mcp-server": { "resolved": "examples/mcp-server", "link": true @@ -20185,6 +20175,7 @@ "packages/ai": { "name": "@microsoft/teams.ai", "version": "0.0.0", + "deprecated": "@microsoft/teams.ai is deprecated. Use a dedicated AI framework directly (e.g., the `openai` SDK with `chat.completions.runTools`). See examples/ai-mcp for the new pattern.", "license": "MIT", "dependencies": { "@microsoft/teams.common": "*" @@ -21090,6 +21081,7 @@ "packages/openai": { "name": "@microsoft/teams.openai", "version": "0.0.0", + "deprecated": "@microsoft/teams.openai is deprecated. Use the `openai` SDK directly (the `AzureOpenAI` client with `chat.completions.runTools`). See examples/ai-mcp for the new pattern.", "license": "MIT", "dependencies": { "@azure/openai": "^2.0.0", diff --git a/packages/api/src/activities/activity.spec.ts b/packages/api/src/activities/activity.spec.ts index 228111722..32d53ff7f 100644 --- a/packages/api/src/activities/activity.spec.ts +++ b/packages/api/src/activities/activity.spec.ts @@ -148,6 +148,17 @@ describe('Activity', () => { '@context': 'https://schema.org', '@id': '', additionalType: ['AIGeneratedContent'], + citation: [ + { + '@type': 'Claim', + position: 0, + appearance: { + '@type': 'DigitalDocument', + name: 'old doc', + abstract: 'old abstract', + }, + }, + ], }) .addEntities( { @@ -179,6 +190,7 @@ describe('Activity', () => { expect(messageEntity.type).toBe('https://schema.org/Message'); expect(messageEntity.additionalType).toEqual(['AIGeneratedContent']); expect(messageEntity.citation).toHaveLength(1); + expect(messageEntity.citation[0].appearance.abstract).toBe('doc abstract'); expect(mentionEntity.type).toBe('mention'); }); }); diff --git a/packages/api/src/activities/activity.ts b/packages/api/src/activities/activity.ts index 42092af99..b703a5f8b 100644 --- a/packages/api/src/activities/activity.ts +++ b/packages/api/src/activities/activity.ts @@ -508,7 +508,13 @@ export class Activity implements IActivity { } if (target.citation || source.citation) { - merged.citation = [...(target.citation || []), ...(source.citation || [])]; + const dedupedCitations = new Map[number]>(); + + for (const citation of [...(target.citation || []), ...(source.citation || [])]) { + dedupedCitations.set(citation.position, citation); + } + + merged.citation = Array.from(dedupedCitations.values()); } Object.assign(target, merged);