Skip to content

New Sample - Allow Inline Editing for Adaptive Card Responses in Microsoft 365 Copilot Declarative Agents#105

Merged
garrytrinder merged 3 commits intopnp:mainfrom
Harikrishnan-MSFT:main
Mar 12, 2026
Merged

New Sample - Allow Inline Editing for Adaptive Card Responses in Microsoft 365 Copilot Declarative Agents#105
garrytrinder merged 3 commits intopnp:mainfrom
Harikrishnan-MSFT:main

Conversation

@Harikrishnan-MSFT
Copy link
Copy Markdown
Contributor

@Harikrishnan-MSFT Harikrishnan-MSFT commented Jan 21, 2026

No description provided.

@Harikrishnan-MSFT Harikrishnan-MSFT changed the title New Sample - Allow Inline Editing for Adaptive Card Responses in Micr… New Sample - Allow Inline Editing for Adaptive Card Responses in Microsoft 365 Copilot Declarative Agents Jan 21, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new sample demonstrating inline editing of Adaptive Card responses in Microsoft 365 Copilot declarative agents. The sample showcases a car repair tracking system where users can view and edit repair records directly within Copilot using Action.Execute from Adaptive Cards v1.5.

Changes:

  • Implements an Azure Functions backend with GET and PATCH endpoints for car repair records
  • Provides API key authentication and OpenAPI specification for plugin integration
  • Includes Adaptive Card templates with inline editing capabilities using Action.Execute
  • Adds infrastructure configuration (Bicep templates) for Azure deployment

Reviewed changes

Copilot reviewed 27 out of 31 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
src/functions/repair.js Core API logic for listing and updating repair records with API key authentication
src/keyGen.js Utility script for generating API keys
src/repairsData.json Sample data file containing car repair records
package.json, package-lock.json Project dependencies including Azure Functions SDK
appPackage/apiSpecificationFile/repair.yml OpenAPI 3.0 specification defining the Repair Service API
appPackage/adaptiveCards/*.json Adaptive Card templates for displaying and editing repair records
appPackage/manifest.json Teams app manifest configuration
appPackage/repairDeclarativeAgent.json Declarative agent definition with instructions and conversation starters
infra/azure.bicep Azure infrastructure as code for Function App deployment
m365agents.yml, m365agents.local.yml Build and deployment configuration for local and production environments
README.md Comprehensive documentation with setup instructions and feature explanations
Files not reviewed (1)
  • samples/da-adaptive-card-inline-edit/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

description: The user who is responsible for the repair
date:
type: string
format: date-time
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OpenAPI specification defines the date field with format 'date-time', but the actual data in repairsData.json uses simple date format (e.g., "2023-05-23") without time component. This is a discrepancy between the API specification and the actual data format. Either update the format to 'date' or ensure the data includes time components in ISO 8601 format (e.g., "2023-05-23T00:00:00Z").

Suggested change
format: date-time
format: date

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

## Prerequisites

* [Microsoft 365 account with Copilot access](https://www.microsoft.com/microsoft-365/enterprise/copilot-for-microsoft-365)
* [Node.js](https://nodejs.org) version 18.x, 20.x, or 22.x
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README states that Node.js version 18.x, 20.x, or 22.x is supported, but this is inconsistent with the actual dependency requirements. The @azure/functions 4.10.0 package requires Node.js >=20.0, so Node.js 18.x will not work. Update this to state "Node.js version 20.x or 22.x" to accurately reflect the actual requirements.

Suggested change
* [Node.js](https://nodejs.org) version 18.x, 20.x, or 22.x
* [Node.js](https://nodejs.org) version 20.x or 22.x

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Comment thread samples/da-adaptive-card-inline-edit/package.json
Comment on lines +53 to +62
const filePath = path.join(__dirname, '../repairsData.json');
const fileData = fs.readFileSync(filePath, 'utf-8');
const repairs = JSON.parse(fileData);
const idx = repairs.findIndex(r => r.id === id);
if (idx < 0) {
return { status: 404 };
}
if (newTitle !== undefined) repairs[idx].title = newTitle;
if (newAssignee !== undefined) repairs[idx].assignedTo = newAssignee;
fs.writeFileSync(filePath, JSON.stringify(repairs, null, 2));
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updateRepair function has a critical race condition issue. Multiple concurrent requests can read the same file state, modify it, and write it back, resulting in lost updates. Consider using a database or implementing proper file locking mechanisms. For a production application, using synchronous file operations without locking is not safe for concurrent write operations.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is simple file-based approach, which is suitable for demonstration and single-user scenarios only. For production applications, we have to use database.

Comment on lines +50 to +61
const body = await req.json();
const newTitle = body.title;
const newAssignee = body.assignedTo;
const filePath = path.join(__dirname, '../repairsData.json');
const fileData = fs.readFileSync(filePath, 'utf-8');
const repairs = JSON.parse(fileData);
const idx = repairs.findIndex(r => r.id === id);
if (idx < 0) {
return { status: 404 };
}
if (newTitle !== undefined) repairs[idx].title = newTitle;
if (newAssignee !== undefined) repairs[idx].assignedTo = newAssignee;
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing input validation in the updateRepair function. The function should validate that newTitle and newAssignee are strings and not empty when provided. Without validation, malicious or malformed input could corrupt the data store or cause unexpected behavior.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to validate newTitle and newAssignee

}
{
name: 'WEBSITE_NODE_DEFAULT_VERSION'
value: '~18' // Set NodeJS version to 18.x
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Azure Bicep template specifies Node.js version ~18 for the Azure Function App, but this conflicts with the @azure/functions 4.10.0 dependency which requires Node.js >=20.0. This will cause the application to fail when deployed to Azure. Update this value to '~20' or higher to match the actual dependency requirements.

Suggested change
value: '~18' // Set NodeJS version to 18.x
value: '~20' // Set NodeJS version to 20.x to match @azure/functions requirement

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

type: object
properties:
updatedRepair:
$ref: '#/components/schemas/Repair'
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OpenAPI specification for the PATCH endpoint does not define error responses (401 Unauthorized, 404 Not Found, 500 Internal Server Error) even though the implementation returns these status codes. Add response definitions for '401' (missing/invalid API key), '404' (repair not found), and '500' (server error) to accurately document the API behavior.

Suggested change
$ref: '#/components/schemas/Repair'
$ref: '#/components/schemas/Repair'
'401':
description: Missing or invalid API key
content:
application/json:
schema:
type: object
properties:
code:
type: string
description: Error code
message:
type: string
description: Description of the authentication error
'404':
description: Repair not found
content:
application/json:
schema:
type: object
properties:
code:
type: string
description: Error code
message:
type: string
description: Details about the missing repair
'500':
description: Internal server error
content:
application/json:
schema:
type: object
properties:
code:
type: string
description: Error code
message:
type: string
description: Description of the server error

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to match with server responses.

Comment thread samples/da-adaptive-card-inline-edit/m365agents.local.yml
Comment on lines +54 to +66
const fileData = fs.readFileSync(filePath, 'utf-8');
const repairs = JSON.parse(fileData);
const idx = repairs.findIndex(r => r.id === id);
if (idx < 0) {
return { status: 404 };
}
if (newTitle !== undefined) repairs[idx].title = newTitle;
if (newAssignee !== undefined) repairs[idx].assignedTo = newAssignee;
fs.writeFileSync(filePath, JSON.stringify(repairs, null, 2));
return {
status: 200,
jsonBody: { updatedRepair: repairs[idx] }
};
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updateRepair function lacks error handling for file I/O operations and JSON parsing. If the file write fails or JSON parsing fails, the function will throw an unhandled exception. Consider wrapping these operations in try-catch blocks and returning appropriate error responses.

Suggested change
const fileData = fs.readFileSync(filePath, 'utf-8');
const repairs = JSON.parse(fileData);
const idx = repairs.findIndex(r => r.id === id);
if (idx < 0) {
return { status: 404 };
}
if (newTitle !== undefined) repairs[idx].title = newTitle;
if (newAssignee !== undefined) repairs[idx].assignedTo = newAssignee;
fs.writeFileSync(filePath, JSON.stringify(repairs, null, 2));
return {
status: 200,
jsonBody: { updatedRepair: repairs[idx] }
};
try {
const fileData = fs.readFileSync(filePath, 'utf-8');
const repairs = JSON.parse(fileData);
const idx = repairs.findIndex(r => r.id === id);
if (idx < 0) {
return { status: 404 };
}
if (newTitle !== undefined) repairs[idx].title = newTitle;
if (newAssignee !== undefined) repairs[idx].assignedTo = newAssignee;
fs.writeFileSync(filePath, JSON.stringify(repairs, null, 2));
return {
status: 200,
jsonBody: { updatedRepair: repairs[idx] }
};
} catch (err) {
if (context && typeof context.log === "function") {
context.log("Error updating repair record:", err);
}
return {
status: 500,
jsonBody: { error: "Failed to update repair record." }
};
}

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added try-catch blocks and returning appropriate error responses.

Comment on lines +36 to +38
const fullName = item.assignedTo.toLowerCase();
const [firstName, lastName] = fullName.split(" ");
return fullName === query || firstName === query || lastName === query;
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The filter logic assumes that all assignedTo values contain exactly two words (first and last name). If an assignedTo field contains more than two names or a single name, the filtering logic may produce unexpected results. Consider using a more robust string matching approach or documenting this limitation.

Suggested change
const fullName = item.assignedTo.toLowerCase();
const [firstName, lastName] = fullName.split(" ");
return fullName === query || firstName === query || lastName === query;
const fullName = String(item.assignedTo || "").trim().toLowerCase();
if (!fullName) {
return false;
}
const nameParts = fullName.split(/\s+/);
return fullName === query || nameParts.includes(query);

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@YugalPradhan31 YugalPradhan31 Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated filter logic to first check if full name matches query, if not split name into words and check if query matches any word

Copy link
Copy Markdown
Member

@garrytrinder garrytrinder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Schema version review - see inline comments

@@ -0,0 +1,18 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/copilot/declarative-agent/v1.5/schema.json",
"version": "v1.5",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The declarative agent schema should be updated to the latest version (v1.6). Current: v1.5. See: https://learn.microsoft.com/en-us/microsoft-365-copilot/extensibility/declarative-agent-manifest

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated version to v1.6

@@ -0,0 +1,62 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/copilot/plugin/v2.2/schema.json",
"schema_version": "v2.2",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API plugin schema should be updated to the latest version (v2.4). Current: v2.2. See: https://learn.microsoft.com/en-us/microsoft-365-copilot/extensibility/api-plugin-manifest

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated version to v2.2

Copy link
Copy Markdown
Member

@garrytrinder garrytrinder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the sample @Harikrishnan-MSFT! A few things need addressing before merge.

Comment thread samples/da-adaptive-card-inline-edit/m365agents.yml

Version|Date|Author|Comments
-------|----|----|--------
1.0|January 20, 2026|Microsoft|Initial release
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Author should list actual contributor name(s), not "Microsoft".

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added actual contributor's name.

Comment thread samples/da-adaptive-card-inline-edit/README.md
- [Adaptive Cards Schema Explorer](https://adaptivecards.io/explorer/)
- [API Plugins for Microsoft 365 Copilot](https://learn.microsoft.com/microsoft-365-copilot/extensibility/overview-api-plugins)

<img src="https://m365-visitor-stats.azurewebsites.net/copilot-pro-dev-samples/samples/da-adaptive-card-inline-edit" />
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracking image should use markdown format per template:

![](https://m365-visitor-stats.azurewebsites.net/copilot-pro-dev-samples/da-adaptive-card-inline-edit)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Comment thread samples/da-adaptive-card-inline-edit/assets/sample.json
Comment thread samples/da-adaptive-card-inline-edit/assets/sample.json
@garrytrinder garrytrinder marked this pull request as draft January 26, 2026 13:06
@garrytrinder
Copy link
Copy Markdown
Member

@Harikrishnan-MSFT are you able to provide an update? When you have resolved the comments, please mark this PR as ready for review, thank you!

@Harikrishnan-MSFT Harikrishnan-MSFT marked this pull request as ready for review March 10, 2026 15:29
@garrytrinder garrytrinder merged commit f6bc2fd into pnp:main Mar 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants