diff --git a/samples/da-HRHelpdesk/.vscode/extensions.json b/samples/da-HRHelpdesk/.vscode/extensions.json new file mode 100644 index 000000000..aac0a6e34 --- /dev/null +++ b/samples/da-HRHelpdesk/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "TeamsDevApp.ms-teams-vscode-extension" + ] +} diff --git a/samples/da-HRHelpdesk/.vscode/launch.json b/samples/da-HRHelpdesk/.vscode/launch.json new file mode 100644 index 000000000..a4d4fb37d --- /dev/null +++ b/samples/da-HRHelpdesk/.vscode/launch.json @@ -0,0 +1,35 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Preview in Copilot (Edge)", + "type": "msedge", + "request": "launch", + "url": "https://m365.cloud.microsoft/chat/entity1-d870f6cd-4aa5-4d42-9626-ab690c041429/${agent-hint}?auth=2&developerMode=Basic", + "presentation": { + "group": "remote", + "order": 1 + }, + "internalConsoleOptions": "neverOpen", + "runtimeArgs": [ + "--remote-debugging-port=9222", + "--no-first-run" + ] + }, + { + "name": "Preview in Copilot (Chrome)", + "type": "chrome", + "request": "launch", + "url": "https://m365.cloud.microsoft/chat/entity1-d870f6cd-4aa5-4d42-9626-ab690c041429/${agent-hint}?auth=2&developerMode=Basic", + "presentation": { + "group": "remote", + "order": 2 + }, + "internalConsoleOptions": "neverOpen", + "runtimeArgs": [ + "--remote-debugging-port=9223", + "--no-first-run" + ] + } + ] +} diff --git a/samples/da-HRHelpdesk/.vscode/settings.json b/samples/da-HRHelpdesk/.vscode/settings.json new file mode 100644 index 000000000..429962025 --- /dev/null +++ b/samples/da-HRHelpdesk/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "debug.onTaskErrors": "abort", + "json.schemas": [ + { + "fileMatch": [ + "/aad.*.json" + ], + "schema": {} + } + ] +} diff --git a/samples/da-HRHelpdesk/README.md b/samples/da-HRHelpdesk/README.md new file mode 100644 index 000000000..e70d52e44 --- /dev/null +++ b/samples/da-HRHelpdesk/README.md @@ -0,0 +1,110 @@ +## Summary + +HR Helpdesk is a template Declarative Agent designed to help employees troubleshoot their issues using insights from ServiceNow KB, and if not successful create a case in the right Centre of Excellence/table without much burden on the end user. + Goals: +1. Help employees resolve their HR issues via self serve +2. If necessary, help them create a ServiceNow case with minimal effort. + +## Features + +This sample illustrates the following concepts: + +- 🧭 Guide employees through common HR issues with conversational, easy-to-follow resolutions that reduce downtime +- 💡 Help users self-resolve problems in Accounts, Payroll, Benefits, Tax, Leave, Onboarding, or any other general HR support needed +- 📝 Create service /incident tickets in the correct Centre of Excellence or ServiceNow table (auto classification) with a clear summary of the issue, and relevant first-level debug information — reducing back-and-forth and enabling faster, more accurate resolution by Support teams. + + +## Contributors + +* [Sébastien Levert](https://github.com/sebastienlevert) +* [Akhil Sai Valluri](https://github.com/akhilsaivalluri) +* [Suryamanohar Mallela](https://github.com/SuryaMSFT) + +## Version history + +Version|Date|Comments +-------|----|-------- +1.0|July 25, 2025|Initial release + +## Prerequisites + +* Microsoft 365 tenant with Microsoft 365 Copilot +* [Visual Studio Code](https://code.visualstudio.com/) with the [Microsoft 365 Agents Toolkit](https://marketplace.visualstudio.com/items?itemName=TeamsDevApp.ms-teams-vscode-extension) extension +* [Node.js v20](https://nodejs.org/en/download/package-manager) +* A ServiceNow account with API access + +## Example Prompts + +1. I didn’t get my salary credit for last month + image + image + image + +2. I’m travelling for work. However, the manager information in the Business Travel letter tool is incorrect. + image + image + image + image + +## Minimal path to awesome + +* Clone this repository (or [download this solution as a .ZIP file](https://pnp.github.io/download-partial/?url=https://github.com/pnp/copilot-pro-dev-samples/tree/main/samples/da-ITHelpdesk) then unzip it) +* Open the Agents Toolkit extension and sign in to your Microsoft 365 tenant with Microsoft 365 Copilot +* Select **Preview in Copilot (Edge)** from the launch configuration dropdown + +## Configuration + +### ServiceNow Plugin Configuration + +In your OpenAPI spec [`appPackage/apiSpecificationFile/openapi.yaml`](appPackage/apiSpecificationFile/openapi.yaml): + +* Update the server URL to point to your ServiceNow instance +image + +* Point authorization, token and refresh urls to your ServiceNow instance +image + +* In your [`ai-plugin.json`](ai-plugin.json) (plugin manifest), go to the `response_semantics` section for each function. You can customize the layout and data bindings to create richer, domain-specific views. Ensure all links point to your ServiceNow instance. +image + + +### ServiceNow Authentication Setup + +1. Log into your ServiceNow account and go to Application Registry under System OAuth + image + image + +2. Create a new registry. Select option “Create an OAuth API endpoint for external clients” + image + +3. Add callback URL as https://teams.microsoft.com/api/platform/v1.0/oAuthRedirect. You can leave other settings as defaults or customize it as per your needs. Hit Submit. + image + +4. After Submit, your app will show in the list. Click on it to get access to Client secret + image + +5. Copy Client ID and Client Secret. You will require it in step 6. + image + +6. Setup Microsoft Graph Connector to add ServiceNow knowledge – This will allow agent to provide troubleshooting guidance based on your ServiceNow KB articles. Follow the steps in https://learn.microsoft.com/en-us/microsoftsearch/servicenow-knowledge-connector . After you setup Microsoft Graph Connector to add ServiceNow knowledge, add the connection IDs in the Agent file. + image + +7. Provision Key & Secret in Copilot: When provisioning the Declarative Agent via the Teams Toolkit, you’ll be prompted to enter the Client ID (Key) and Client Secret. Use the values from the ServiceNow OAuth app you created. + image + + +## Help + +We do not support samples, but this community is always willing to help, and we want to improve these samples. We use GitHub to track issues, which makes it easy for community members to volunteer their time and help resolve issues. + +You can try looking at [issues related to this sample](https://github.com/pnp/copilot-pro-dev-samples/issues?q=label%3A%22sample%3A%20da-HRHelpdesk%22) to see if anybody else is having the same issues. + +If you encounter any issues using this sample, [create a new issue](https://github.com/pnp/copilot-pro-dev-samples/issues/new). + +Finally, if you have an idea for improvement, [make a suggestion](https://github.com/pnp/copilot-pro-dev-samples/issues/new). + +## Disclaimer + +**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.** + +![](https://m365-visitor-stats.azurewebsites.net/SamplesGallery/da-HRHelpdesk) diff --git a/samples/da-HRHelpdesk/appPackage/ai-plugin.json b/samples/da-HRHelpdesk/appPackage/ai-plugin.json new file mode 100644 index 000000000..077d04186 --- /dev/null +++ b/samples/da-HRHelpdesk/appPackage/ai-plugin.json @@ -0,0 +1,76 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/copilot/plugin/v2.1/schema.json", + "schema_version": "v2.1", + "name_for_human": "Table API - Subset", + "description_for_human": "Allows you to perform create, read, update and delete (CRUD) operations on existing tables", + "description_for_model": "Allows you to perform create, read, update and delete (CRUD) operations on existing tables", + "contact_email": "publisher-email@example.com", + "namespace": "hrhelpdeskdatemplate", + "capabilities": { + "conversation_starters": [ + { + "text": "Retrieve centres of excellence and HR Service deta" + }, + { + "text": "Create a record in the appropriate table. Get the " + }, + { + "text": "Retrieve a record" + }, + { + "text": "Modify a record" + }, + { + "text": "Delete a record" + }, + { + "text": "Update a record" + } + ] + }, + "functions": [ + { + "name": "api_now_table_item_get", + "description": "Retrieve centres of excellence and HR Service details for identifying the right path/table to create a new case" + }, + { + "name": "api_now_table_item_item_delete", + "description": "Delete a record" + }, + { + "name": "api_now_table_item_item_get", + "description": "Retrieve a record" + }, + { + "name": "api_now_table_item_item_patch", + "description": "Update a record" + }, + { + "name": "api_now_table_item_item_put", + "description": "Modify a record" + }, + { + "name": "api_now_table_item_post", + "description": "Create a record in the appropriate table. Get the tableName where new record is created from service_table field of api_now_table_item_get operation" + } + ], + "runtimes": [ + { + "type": "OpenApi", + "auth": { + "type": "None" + }, + "spec": { + "url": "apiSpecificationFile/openapi.yaml" + }, + "run_for_functions": [ + "api_now_table_item_get", + "api_now_table_item_post", + "api_now_table_item_item_get", + "api_now_table_item_item_put", + "api_now_table_item_item_delete", + "api_now_table_item_item_patch" + ] + } + ] +} diff --git a/samples/da-HRHelpdesk/appPackage/apiSpecificationFile/openapi.yaml b/samples/da-HRHelpdesk/appPackage/apiSpecificationFile/openapi.yaml new file mode 100644 index 000000000..e4aef06b3 --- /dev/null +++ b/samples/da-HRHelpdesk/appPackage/apiSpecificationFile/openapi.yaml @@ -0,0 +1,336 @@ +openapi: 3.0.4 +info: + title: Table API - Subset - Subset + description: 'Allows you to perform create, read, update and delete (CRUD) operations on existing tables' + version: latest +servers: + - url: https://dev187160.service-now.com/ +paths: + '/api/now/table/{tableName}': + get: + description: Retrieve centres of excellence and HR Service details for identifying the right path/table to create a new case + operationId: api_now_table_item_get + parameters: + - name: tableName + in: path + description: 'Table name from which we retrieve data on centres of excellence and HR service IDs. (default: sn_hr_core_service)' + required: true + schema: { } + - name: sysparm_query + in: query + description: An encoded query string used to filter the results + explode: false + schema: { } + - name: sysparm_display_value + in: query + description: 'Return field display values (true), actual values (false), or both (all) (default: false)' + explode: false + schema: { } + - name: sysparm_exclude_reference_link + in: query + description: 'True to exclude Table API links for reference fields (default: false)' + explode: false + schema: { } + - name: sysparm_suppress_pagination_header + in: query + description: 'True to supress pagination header (default: false)' + explode: false + schema: { } + - name: sysparm_fields + in: query + description: 'A comma-separated list of fields to return in the response (default: sys_id,service_table,name,active,sys_name)' + required: true + explode: false + schema: { } + - name: sysparm_limit + in: query + description: 'The maximum number of results returned per page (default: 10,000)' + explode: false + schema: { } + - name: sysparm_view + in: query + description: Render the response according to the specified UI view (overridden by sysparm_fields) + explode: false + schema: { } + - name: sysparm_query_category + in: query + description: Name of the query category (read replica category) to use for queries + explode: false + schema: { } + - name: sysparm_query_no_domain + in: query + description: 'True to access data across domains if authorized (default: false)' + explode: false + schema: { } + - name: sysparm_no_count + in: query + description: 'Do not execute a select count(*) on table (default: false)' + explode: false + schema: { } + responses: + '200': + description: ok + content: + application/json: { } + application/xml: { } + text/xml: { } + post: + description: Create a record in the appropriate table. Get the tableName where new record is created from service_table field of api_now_table_item_get operation + operationId: api_now_table_item_post + parameters: + - name: tableName + in: path + description: 'Use the service_table of the appropriate from api_now_table_item_get based on the name the user selects. For example tableName- sn_hr_core_case_total_rewards for Vision Benefits Enrollment/Modification, and similarly tableName- sn_hr_core_case_payroll for Request Corporate Credit Card' + required: true + schema: { } + - name: sysparm_display_value + in: query + description: 'Return field display values (true), actual values (false), or both (all) (default: false)' + explode: false + schema: { } + - name: sysparm_exclude_reference_link + in: query + description: 'True to exclude Table API links for reference fields (default: false)' + explode: false + schema: { } + - name: sysparm_fields + in: query + description: A comma-separated list of fields to return in the response + explode: false + schema: { } + - name: sysparm_input_display_value + in: query + description: 'Set field values using their display value (true) or actual value (false) (default: false)' + explode: false + schema: { } + - name: sysparm_suppress_auto_sys_field + in: query + description: 'True to suppress auto generation of system fields (default: false)' + explode: false + schema: { } + - name: sysparm_view + in: query + description: Render the response according to the specified UI view (overridden by sysparm_fields) + explode: false + schema: { } + requestBody: + content: + application/json: + schema: + type: object + properties: + hr_service: + type: string + description: corresponding to the which the user provides. It is a guid. Look up in the response of api_now_table_item_get operation + opened_for: + type: string + description: email address of the person creating the case + work_notes: + type: string + description: Description of case shared by user + required: true + responses: + '200': + description: ok + content: + application/json: { } + application/xml: { } + text/xml: { } + '/api/now/table/{tableName}/{sys_id}': + get: + description: Retrieve a record + operationId: api_now_table_item_item_get + parameters: + - name: tableName + in: path + required: true + schema: { } + - name: sys_id + in: path + required: true + schema: { } + - name: sysparm_display_value + in: query + description: 'Return field display values (true), actual values (false), or both (all) (default: false)' + explode: false + schema: { } + - name: sysparm_exclude_reference_link + in: query + description: 'True to exclude Table API links for reference fields (default: false)' + explode: false + schema: { } + - name: sysparm_fields + in: query + description: A comma-separated list of fields to return in the response + explode: false + schema: { } + - name: sysparm_view + in: query + description: Render the response according to the specified UI view (overridden by sysparm_fields) + explode: false + schema: { } + - name: sysparm_query_no_domain + in: query + description: 'True to access data across domains if authorized (default: false) ' + explode: false + schema: { } + responses: + '200': + description: ok + content: + application/json: { } + application/xml: { } + text/xml: { } + put: + description: Modify a record + operationId: api_now_table_item_item_put + parameters: + - name: tableName + in: path + required: true + schema: { } + - name: sys_id + in: path + required: true + schema: { } + - name: sysparm_display_value + in: query + description: 'Return field display values (true), actual values (false), or both (all) (default: false)' + explode: false + schema: { } + - name: sysparm_exclude_reference_link + in: query + description: 'True to exclude Table API links for reference fields (default: false)' + explode: false + schema: { } + - name: sysparm_fields + in: query + description: A comma-separated list of fields to return in the response + explode: false + schema: { } + - name: sysparm_input_display_value + in: query + description: 'Set field values using their display value (true) or actual value (false) (default: false)' + explode: false + schema: { } + - name: sysparm_suppress_auto_sys_field + in: query + description: 'True to suppress auto generation of system fields (default: false)' + explode: false + schema: { } + - name: sysparm_view + in: query + description: Render the response according to the specified UI view (overridden by sysparm_fields) + explode: false + schema: { } + - name: sysparm_query_no_domain + in: query + description: 'True to access data across domains if authorized (default: false)' + explode: false + schema: { } + requestBody: + content: + application/json: { } + application/xml: { } + text/xml: { } + responses: + '200': + description: ok + content: + application/json: { } + application/xml: { } + text/xml: { } + delete: + description: Delete a record + operationId: api_now_table_item_item_delete + parameters: + - name: tableName + in: path + required: true + schema: { } + - name: sys_id + in: path + required: true + schema: { } + - name: sysparm_query_no_domain + in: query + description: 'True to access data across domains if authorized (default: false)' + explode: false + schema: { } + responses: + '200': + description: ok + content: + application/json: { } + application/xml: { } + text/xml: { } + patch: + description: Update a record + operationId: api_now_table_item_item_patch + parameters: + - name: tableName + in: path + required: true + schema: { } + - name: sys_id + in: path + required: true + schema: { } + - name: sysparm_display_value + in: query + description: 'Return field display values (true), actual values (false), or both (all) (default: false)' + explode: false + schema: { } + - name: sysparm_exclude_reference_link + in: query + description: 'True to exclude Table API links for reference fields (default: false)' + explode: false + schema: { } + - name: sysparm_fields + in: query + description: A comma-separated list of fields to return in the response + explode: false + schema: { } + - name: sysparm_input_display_value + in: query + description: 'Set field values using their display value (true) or actual value (false) (default: false)' + explode: false + schema: { } + - name: sysparm_suppress_auto_sys_field + in: query + description: 'True to suppress auto generation of system fields (default: false)' + explode: false + schema: { } + - name: sysparm_view + in: query + description: Render the response according to the specified UI view (overridden by sysparm_fields) + explode: false + schema: { } + - name: sysparm_query_no_domain + in: query + description: 'True to access data across domains if authorized (default: false)' + explode: false + schema: { } + requestBody: + content: + application/json: { } + application/xml: { } + text/xml: { } + responses: + '200': + description: ok + content: + application/json: { } + application/xml: { } + text/xml: { } +components: + securitySchemes: + oAuth2AuthCode: + type: oauth2 + flows: + authorizationCode: + authorizationUrl: https://dev187160.service-now.com/oauth_auth.do + tokenUrl: https://dev187160.service-now.com/oauth_token.do + refreshUrl: https://dev187160.service-now.com/oauth_token.do + scopes: { } + x-ai-auth-reference-id: '{oAuth2AuthCode_REGISTRATION_ID}' \ No newline at end of file diff --git a/samples/da-HRHelpdesk/appPackage/apiSpecificationFile/openapi.yaml.original b/samples/da-HRHelpdesk/appPackage/apiSpecificationFile/openapi.yaml.original new file mode 100644 index 000000000..79414b8fe --- /dev/null +++ b/samples/da-HRHelpdesk/appPackage/apiSpecificationFile/openapi.yaml.original @@ -0,0 +1,339 @@ +openapi: 3.0.4 +info: + title: Table API - Subset + description: 'Allows you to perform create, read, update and delete (CRUD) operations on existing tables' + version: latest +servers: + - url: https://dev187160.service-now.com/ +paths: + '/api/now/table/{tableName}': + get: + description: Retrieve centres of excellence and HR Service details for identifying the right path/table to create a new case + operationId: api_now_table_item_get + parameters: + - name: tableName + in: path + description: 'Table name from which we retrieve data on centres of excellence and HR service IDs. (default: sn_hr_core_service)' + required: true + schema: { } + - name: sysparm_query + in: query + description: An encoded query string used to filter the results + explode: false + schema: { } + - name: sysparm_display_value + in: query + description: 'Return field display values (true), actual values (false), or both (all) (default: false)' + explode: false + schema: { } + - name: sysparm_exclude_reference_link + in: query + description: 'True to exclude Table API links for reference fields (default: false)' + explode: false + schema: { } + - name: sysparm_suppress_pagination_header + in: query + description: 'True to supress pagination header (default: false)' + explode: false + schema: { } + - name: sysparm_fields + in: query + description: 'A comma-separated list of fields to return in the response (default: sys_id,service_table,name,active,sys_name)' + required: true + explode: false + schema: { } + - name: sysparm_limit + in: query + description: 'The maximum number of results returned per page (default: 10,000)' + explode: false + schema: { } + - name: sysparm_view + in: query + description: Render the response according to the specified UI view (overridden by sysparm_fields) + explode: false + schema: { } + - name: sysparm_query_category + in: query + description: Name of the query category (read replica category) to use for queries + explode: false + schema: { } + - name: sysparm_query_no_domain + in: query + description: 'True to access data across domains if authorized (default: false)' + explode: false + schema: { } + - name: sysparm_no_count + in: query + description: 'Do not execute a select count(*) on table (default: false)' + explode: false + schema: { } + responses: + '200': + description: ok + content: + application/json: { } + application/xml: { } + text/xml: { } + post: + description: Create a record in the appropriate table. Get the tableName where new record is created from service_table field of api_now_table_item_get operation + operationId: api_now_table_item_post + parameters: + - name: tableName + in: path + required: true + schema: { } + description: Use the service_table of the appropriate from api_now_table_item_get based on the name the user selects. For example tableName- sn_hr_core_case_total_rewards for Vision Benefits Enrollment/Modification, and similarly tableName- sn_hr_core_case_payroll for Request Corporate Credit Card + - name: sysparm_display_value + in: query + description: 'Return field display values (true), actual values (false), or both (all) (default: false)' + explode: false + schema: { } + - name: sysparm_exclude_reference_link + in: query + description: 'True to exclude Table API links for reference fields (default: false)' + explode: false + schema: { } + - name: sysparm_fields + in: query + description: A comma-separated list of fields to return in the response + explode: false + schema: { } + - name: sysparm_input_display_value + in: query + description: 'Set field values using their display value (true) or actual value (false) (default: false)' + explode: false + schema: { } + - name: sysparm_suppress_auto_sys_field + in: query + description: 'True to suppress auto generation of system fields (default: false)' + explode: false + schema: { } + - name: sysparm_view + in: query + description: Render the response according to the specified UI view (overridden by sysparm_fields) + explode: false + schema: { } + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateCase' + responses: + '200': + description: ok + content: + application/json: { } + application/xml: { } + text/xml: { } + '/api/now/table/{tableName}/{sys_id}': + get: + description: Retrieve a record + operationId: api_now_table_item_item_get + parameters: + - name: tableName + in: path + required: true + schema: { } + - name: sys_id + in: path + required: true + schema: { } + - name: sysparm_display_value + in: query + description: 'Return field display values (true), actual values (false), or both (all) (default: false)' + explode: false + schema: { } + - name: sysparm_exclude_reference_link + in: query + description: 'True to exclude Table API links for reference fields (default: false)' + explode: false + schema: { } + - name: sysparm_fields + in: query + description: A comma-separated list of fields to return in the response + explode: false + schema: { } + - name: sysparm_view + in: query + description: Render the response according to the specified UI view (overridden by sysparm_fields) + explode: false + schema: { } + - name: sysparm_query_no_domain + in: query + description: 'True to access data across domains if authorized (default: false) ' + explode: false + schema: { } + responses: + '200': + description: ok + content: + application/json: { } + application/xml: { } + text/xml: { } + put: + description: Modify a record + operationId: api_now_table_item_item_put + parameters: + - name: tableName + in: path + required: true + schema: { } + - name: sys_id + in: path + required: true + schema: { } + - name: sysparm_display_value + in: query + description: 'Return field display values (true), actual values (false), or both (all) (default: false)' + explode: false + schema: { } + - name: sysparm_exclude_reference_link + in: query + description: 'True to exclude Table API links for reference fields (default: false)' + explode: false + schema: { } + - name: sysparm_fields + in: query + description: A comma-separated list of fields to return in the response + explode: false + schema: { } + - name: sysparm_input_display_value + in: query + description: 'Set field values using their display value (true) or actual value (false) (default: false)' + explode: false + schema: { } + - name: sysparm_suppress_auto_sys_field + in: query + description: 'True to suppress auto generation of system fields (default: false)' + explode: false + schema: { } + - name: sysparm_view + in: query + description: Render the response according to the specified UI view (overridden by sysparm_fields) + explode: false + schema: { } + - name: sysparm_query_no_domain + in: query + description: 'True to access data across domains if authorized (default: false)' + explode: false + schema: { } + requestBody: + content: + application/json: { } + application/xml: { } + text/xml: { } + responses: + '200': + description: ok + content: + application/json: { } + application/xml: { } + text/xml: { } + delete: + description: Delete a record + operationId: api_now_table_item_item_delete + parameters: + - name: tableName + in: path + required: true + schema: { } + - name: sys_id + in: path + required: true + schema: { } + - name: sysparm_query_no_domain + in: query + description: 'True to access data across domains if authorized (default: false)' + explode: false + schema: { } + responses: + '200': + description: ok + content: + application/json: { } + application/xml: { } + text/xml: { } + patch: + description: Update a record + operationId: api_now_table_item_item_patch + parameters: + - name: tableName + in: path + required: true + schema: { } + - name: sys_id + in: path + required: true + schema: { } + - name: sysparm_display_value + in: query + description: 'Return field display values (true), actual values (false), or both (all) (default: false)' + explode: false + schema: { } + - name: sysparm_exclude_reference_link + in: query + description: 'True to exclude Table API links for reference fields (default: false)' + explode: false + schema: { } + - name: sysparm_fields + in: query + description: A comma-separated list of fields to return in the response + explode: false + schema: { } + - name: sysparm_input_display_value + in: query + description: 'Set field values using their display value (true) or actual value (false) (default: false)' + explode: false + schema: { } + - name: sysparm_suppress_auto_sys_field + in: query + description: 'True to suppress auto generation of system fields (default: false)' + explode: false + schema: { } + - name: sysparm_view + in: query + description: Render the response according to the specified UI view (overridden by sysparm_fields) + explode: false + schema: { } + - name: sysparm_query_no_domain + in: query + description: 'True to access data across domains if authorized (default: false)' + explode: false + schema: { } + requestBody: + content: + application/json: { } + application/xml: { } + text/xml: { } + responses: + '200': + description: ok + content: + application/json: { } + application/xml: { } + text/xml: { } + +components: + securitySchemes: + oAuth2AuthCode: + type: oauth2 + flows: + authorizationCode: + authorizationUrl: https://dev187160.service-now.com/oauth_auth.do + tokenUrl: https://dev187160.service-now.com/oauth_token.do + refreshUrl: https://dev187160.service-now.com/oauth_token.do + scopes: {} + schemas: + CreateCase: + type: object + properties: + hr_service: + type: string + description: corresponding to the which the user provides. It is a guid. Look up in the response of api_now_table_item_get operation + opened_for: + type: string + description: email address of the person creating the case + work_notes: + type: string + description: Description of case shared by user \ No newline at end of file diff --git a/samples/da-HRHelpdesk/appPackage/color.png b/samples/da-HRHelpdesk/appPackage/color.png new file mode 100644 index 000000000..7eca3d853 Binary files /dev/null and b/samples/da-HRHelpdesk/appPackage/color.png differ diff --git a/samples/da-HRHelpdesk/appPackage/declarativeAgent.json b/samples/da-HRHelpdesk/appPackage/declarativeAgent.json new file mode 100644 index 000000000..ed940960b --- /dev/null +++ b/samples/da-HRHelpdesk/appPackage/declarativeAgent.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/copilot/declarative-agent/v1.4/schema.json", + "version": "v1.4", + "name": "HRHelpdeskDATemplate", + "description": "Declarative agent created with Microsoft 365 Agents Toolkit can assist user in calling APIs and retrieving responses", + "instructions": "$[file('instruction.txt')]", + "actions": [ + { + "id": "action_1", + "file": "ai-plugin.json" + } + ], + "conversation_starters": [ + { + "text": "Retrieve centres of excellence and HR Service deta" + }, + { + "text": "Create a record in the appropriate table. Get the " + }, + { + "text": "Retrieve a record" + }, + { + "text": "Modify a record" + }, + { + "text": "Delete a record" + }, + { + "text": "Update a record" + } + ] +} \ No newline at end of file diff --git a/samples/da-HRHelpdesk/appPackage/instruction.txt b/samples/da-HRHelpdesk/appPackage/instruction.txt new file mode 100644 index 000000000..a61755958 --- /dev/null +++ b/samples/da-HRHelpdesk/appPackage/instruction.txt @@ -0,0 +1,43 @@ +You are an agent who can assist users in resolving their issues in an easy conversational and interactive fashion. + +# Workflows +## Workflow 1 (troubleshooting an HR case) +This workflow is triggered when a user is trying to resolve a problem. Eg- "How do I apply for leave?", "I cannot find my payslips" etc. + +If need to troubleshoot, follow these steps: +### Step 1: Gather Basic Details of the Issue +- Check if the user has provided a basic description of the issue. +- If the description is unclear, ask minimal follow-up questions, preferably just one, to gather essential information needed to understand the issue. + +### Step 2: Identify Targeted Resolutions from ServiceNow KB +- Search ServiceNow KB articles related to the user's issue description. +- Analyze the articles. + - Thoroughly read each article. + - Identify all issues, symptoms, and resolution paths mentioned within each article. Note: A single article may describe multiple problems and corresponding solutions. +- Do not immediately suggest all resolution options. +- Instead, progressively narrow down to the right resolution by asking targeted clarifying questions. + - First Round: + - Compare all the retrieved articles. + - Ask targeted clarifying questions based strictly on differences between the symptoms and resolutions described in the ServiceNow KB articles. + - Use the user's answers to eliminate unrelated articles or solutions. (e.g., Narrow 10 → 5 candidates.) + - Second Round: + - Compare the remaining articles/resolutions. + - Again, ask targeted clarifying questions based on ServiceNow KB content to further eliminate irrelevant options.(e.g., Narrow 5 → 3 candidates.) + - Repeat the process: + - Continue comparing the remaining options and asking focused clarifying questions, round by round, until only most relevant article and resolution path remains. +- Once a correct resolution is determined: provide clear, concise, step-by-step resolution to the user. Avoid overwhelming the user with unnecessary details — focus only on the relevant resolution steps. +- Confirm user satisfaction. Ask: "Did this help? I can dig in further if you give me more information, or I can go ahead and create a support ticket - just let me know.". +- If user says it helped or doesn't respond, end the conversation politely. +- If user provides more information, repeat Step 3 to refine the search and resolution based on the updated details. +- If user asks to create a ticket, proceed to Workflow 2 (Creating an HR case). + +## Workflow 2: Creating an HR case +This workflow is triggered when user needs to create a new HR case. The following scenarios require creating a new case: +1. When self serve / troubleshooting did not work and user is still not able to resolve the issue. Eg- "I am not able to solve the issue..." +2. When user specifically asks for case to be created. Eg- ""Please create a case asking for parental leave of absence...", "Open a new HR case...". + +If asked to create HR case, follow the following steps: +### Step 1: Retrieve the types of HR cases (sys_name) and HR Service ID (sys_id) +### Step 2: Show **all** types of HR cases to user (neatly table with the following columns- Category, name, ID) and ask them to choose. +### Step 3: **Always ask user** for more details of the case she selected earlier +### Step 4: Identify the right table to create case in from the type of HR case which the user shared. Create a case here with the details the user shared.n \ No newline at end of file diff --git a/samples/da-HRHelpdesk/appPackage/manifest.json b/samples/da-HRHelpdesk/appPackage/manifest.json new file mode 100644 index 000000000..7a41c7b5d --- /dev/null +++ b/samples/da-HRHelpdesk/appPackage/manifest.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/teams/v1.21/MicrosoftTeams.schema.json", + "manifestVersion": "1.21", + "version": "1.0.0", + "id": "${{TEAMS_APP_ID}}", + "developer": { + "name": "My App, Inc.", + "websiteUrl": "https://www.example.com", + "privacyUrl": "https://www.example.com/privacy", + "termsOfUseUrl": "https://www.example.com/termofuse" + }, + "icons": { + "color": "color.png", + "outline": "outline.png" + }, + "name": { + "short": "HRHelpdeskDemo${{APP_NAME_SUFFIX}}", + "full": "HR Helpdesk Demo" + }, + "description": { + "short": "HR Helpdesk Demo agent", + "full": "HR Helpdesk agent which can help users troubleshoot their issues, and if required raise cases in the right path" + }, + "accentColor": "#FFFFFF", + "copilotAgents": { + "declarativeAgents": [ + { + "id": "declarativeAgent", + "file": "declarativeAgent.json" + } + ] + }, + "permissions": [ + "identity", + "messageTeamMembers" + ], + "validDomains": [] +} \ No newline at end of file diff --git a/samples/da-HRHelpdesk/appPackage/outline.png b/samples/da-HRHelpdesk/appPackage/outline.png new file mode 100644 index 000000000..f7a4c8644 Binary files /dev/null and b/samples/da-HRHelpdesk/appPackage/outline.png differ diff --git a/samples/da-HRHelpdesk/assets/hrhelpdesk.png b/samples/da-HRHelpdesk/assets/hrhelpdesk.png new file mode 100644 index 000000000..f7584b700 Binary files /dev/null and b/samples/da-HRHelpdesk/assets/hrhelpdesk.png differ diff --git a/samples/da-HRHelpdesk/assets/sample.json b/samples/da-HRHelpdesk/assets/sample.json new file mode 100644 index 000000000..80a856507 --- /dev/null +++ b/samples/da-HRHelpdesk/assets/sample.json @@ -0,0 +1,74 @@ +[ + { + "name": "pnp-copilot-pro-dev-da-HRHelpdesk", + "source": "pnp", + "title": "HR Heldpdesk Declarative Agent", + "shortDescription": "HR Helpdesk is a template Declarative Agent designed to help employees troubleshoot their issues using insights from ServiceNow KB, and if not successful create a case in the right Centre of Excellence/table without much burden on the end user.", + "url": "https://github.com/pnp/copilot-pro-dev-samples/tree/main/samples/da-HRHelpdesk", + "downloadUrl": "https://pnp.github.io/download-partial/?url=https://github.com/pnp/copilot-pro-dev-samples/tree/main/samples/da-HRHelpdesk", + "longDescription": [ + "HR Helpdesk is a template Declarative Agent designed to help employees troubleshoot their issues using insights from ServiceNow KB, and if not successful create a case in the right Centre of Excellence/table without much burden on the end user." + ], + "creationDateTime": "2025-10-14", + "updateDateTime": "2025-10-14", + "products": [ + "Microsoft 365 Copilot" + ], + "metadata": [ + { + "key": "PLATFORM", + "value": "Node.js" + }, + { + "key": "API-PLUGIN", + "value": "Yes" + }, + { + "key": "GRAPH-CONNECTOR", + "value": "No" + } + ], + "thumbnails": [ + { + "type": "image", + "order": 100, + "url": "https://github.com/pnp/copilot-pro-dev-samples/raw/main/samples/da-HRHelpdesk/assets/hrhelpdesk.png", + "alt": "HR Helpdesk" + } + ], + "authors": [ + { + "gitHubAccount": "sebastienlevert", + "pictureUrl": "https://github.com/sebastienlevert.png", + "name": "Sébastien Levert" + }, + { + "gitHubAccount": "akhilsaivalluri", + "pictureUrl": "https://github.com/akhilsaivalluri.png", + "name": "Akhil Sai Valluri" + }, + { + "gitHubAccount": "garrytrinder", + "pictureUrl": "https://github.com/garrytrinder.png", + "name": "Garry Trinder" + } + ], + "references": [ + { + "name": "Microsoft 365 Copilot extensibility", + "description": "Learn more about what Microsoft 365 Copilot and how you can extend it.", + "url": "https://learn.microsoft.com/microsoft-365-copilot/extensibility/" + }, + { + "name": "Declarative agents for Microsoft 365 Copilot overview", + "description": "Learn more about what declarative agents for Microsoft 365 Copilot are.", + "url": "https://learn.microsoft.com/microsoft-365-copilot/extensibility/overview-declarative-agent" + }, + { + "name": "Build a declarative agent for Microsoft 365 Copilot using TypeSpec for Microsoft 365 Copilot", + "description": "Learn how to build a declarative agent for Microsoft 365 Copilot using TypeSpec for Microsoft 365 Copilot.", + "url": "https://learn.microsoft.com/microsoft-365-copilot/extensibility/build-declarative-agents-typespec" + } + ] + } +] \ No newline at end of file diff --git a/samples/da-HRHelpdesk/env/.env.dev b/samples/da-HRHelpdesk/env/.env.dev new file mode 100644 index 000000000..bfdd4c66a --- /dev/null +++ b/samples/da-HRHelpdesk/env/.env.dev @@ -0,0 +1,8 @@ +# This file includes environment variables that will be committed to git by default. + +# Built-in environment variables +TEAMSFX_ENV=dev +APP_NAME_SUFFIX=dev +ENDPOINT_URL=https://bubble-dev-ed.develop.my.salesforce.com + +# Generated during provision, you can also add your own variables. diff --git a/samples/da-HRHelpdesk/m365agents.yml b/samples/da-HRHelpdesk/m365agents.yml new file mode 100644 index 000000000..e067d9bce --- /dev/null +++ b/samples/da-HRHelpdesk/m365agents.yml @@ -0,0 +1,82 @@ +# yaml-language-server: $schema=https://aka.ms/teams-toolkit/v1.8/yaml.schema.json +# Visit https://aka.ms/teamsfx-v5.0-guide for details on this file +# Visit https://aka.ms/teamsfx-actions for details on actions +version: v1.8 +additionalMetadata: + sampleTag: pnp-copilot-pro-dev:da-HRHelpdesk +environmentFolderPath: ./env + +# Triggered when 'teamsapp provision' is executed +provision: + # Creates an app + - uses: teamsApp/create + with: + # app name + name: HRHelpdeskDATemplate${{APP_NAME_SUFFIX}} + # Write the information of created resources into environment file for + # the specified environment variable(s). + writeToEnvironmentFile: + teamsAppId: TEAMS_APP_ID + + # Build app package with latest env value + - uses: teamsApp/zipAppPackage + with: + # Path to manifest template + manifestPath: ./appPackage/manifest.json + outputZipPath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + outputFolder: ./appPackage/build + # Validate app package using validation rules + - uses: teamsApp/validateAppPackage + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + # Apply the app manifest to an existing app in + # Developer Portal. + # Will use the app id in manifest file to determine which app to update. + - uses: teamsApp/update + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + # Extend your app to Outlook and the Microsoft 365 app + - uses: teamsApp/extendToM365 + with: + # Relative path to the build app package. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + # Write the information of created resources into environment file for + # the specified environment variable(s). + writeToEnvironmentFile: + titleId: M365_TITLE_ID + appId: M365_APP_ID + +# Triggered when 'teamsapp publish' is executed +publish: + # Build app package with latest env value + - uses: teamsApp/zipAppPackage + with: + # Path to manifest template + manifestPath: ./appPackage/manifest.json + outputZipPath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + outputFolder: ./appPackage/build + # Validate app package using validation rules + - uses: teamsApp/validateAppPackage + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + # Apply the app manifest to an existing app in + # Developer Portal. + # Will use the app id in manifest file to determine which app to update. + - uses: teamsApp/update + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + # Publish the app to + # Teams Admin Center (https://admin.teams.microsoft.com/policies/manage-apps) + # for review and approval + - uses: teamsApp/publishAppPackage + with: + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + # Write the information of created resources into environment file for + # the specified environment variable(s). + writeToEnvironmentFile: + publishedAppId: TEAMS_APP_PUBLISHED_APP_ID + diff --git a/samples/da-ITHelpdesk/.vscode/extensions.json b/samples/da-ITHelpdesk/.vscode/extensions.json new file mode 100644 index 000000000..aac0a6e34 --- /dev/null +++ b/samples/da-ITHelpdesk/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "TeamsDevApp.ms-teams-vscode-extension" + ] +} diff --git a/samples/da-ITHelpdesk/.vscode/launch.json b/samples/da-ITHelpdesk/.vscode/launch.json new file mode 100644 index 000000000..a4d4fb37d --- /dev/null +++ b/samples/da-ITHelpdesk/.vscode/launch.json @@ -0,0 +1,35 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Preview in Copilot (Edge)", + "type": "msedge", + "request": "launch", + "url": "https://m365.cloud.microsoft/chat/entity1-d870f6cd-4aa5-4d42-9626-ab690c041429/${agent-hint}?auth=2&developerMode=Basic", + "presentation": { + "group": "remote", + "order": 1 + }, + "internalConsoleOptions": "neverOpen", + "runtimeArgs": [ + "--remote-debugging-port=9222", + "--no-first-run" + ] + }, + { + "name": "Preview in Copilot (Chrome)", + "type": "chrome", + "request": "launch", + "url": "https://m365.cloud.microsoft/chat/entity1-d870f6cd-4aa5-4d42-9626-ab690c041429/${agent-hint}?auth=2&developerMode=Basic", + "presentation": { + "group": "remote", + "order": 2 + }, + "internalConsoleOptions": "neverOpen", + "runtimeArgs": [ + "--remote-debugging-port=9223", + "--no-first-run" + ] + } + ] +} diff --git a/samples/da-ITHelpdesk/.vscode/settings.json b/samples/da-ITHelpdesk/.vscode/settings.json new file mode 100644 index 000000000..429962025 --- /dev/null +++ b/samples/da-ITHelpdesk/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "debug.onTaskErrors": "abort", + "json.schemas": [ + { + "fileMatch": [ + "/aad.*.json" + ], + "schema": {} + } + ] +} diff --git a/samples/da-ITHelpdesk/README.md b/samples/da-ITHelpdesk/README.md new file mode 100644 index 000000000..1e48a5c14 --- /dev/null +++ b/samples/da-ITHelpdesk/README.md @@ -0,0 +1,119 @@ +## Summary + +IT Helpdesk Agent is a Declarative Agent designed to assist employees with IT issues through (a) guided, conversational troubleshooting, (b) outage detection, and ticket creation for escalation – powered through ServiceNow offerings (Knowledge, Catalogue and Tickets). + +Goals: IT helpdesk Agent has been created with two key objectives, +1. Reduce overall number of tickets raised by **offering easy-to-follow, self-serve troubleshooting guidance** to employees +2. Improve SLA to ticket resolution by **capturing first-level debug information** up front, reducing back and forth + +## Features + +This sample illustrates the following concepts: + +- 🧭 Guide employees through common IT issues with conversational, easy-to-follow resolutions that reduce downtime +- ⚠️ Proactively inform users about service outages, minimizing repeated tickets and unnecessary troubleshooting +- 💡 Help users self-resolve problems like VPN errors, login failures, password resets, and software installation using relevant resolutions from Service Now +- 📝 Create service /incident tickets with the correct category and subcategory (auto-classification), a clear summary of the issue, and relevant first-level debug information — reducing back-and-forth and enabling faster, more accurate resolution by IT teams. +- 🎫 Help users track the status of their support tickets, view recent updates, and stay informed — improving transparency and reducing the need to follow up manually + +## Contributors + +* [Sébastien Levert](https://github.com/sebastienlevert) +* [Akhil Sai Valluri](https://github.com/akhilsaivalluri) +* [Suryamanohar Mallela](https://github.com/SuryaMSFT) + +## Version history + +Version|Date|Comments +-------|----|-------- +1.0|July 25, 2025|Initial release + +## Prerequisites + +* Microsoft 365 tenant with Microsoft 365 Copilot +* [Visual Studio Code](https://code.visualstudio.com/) with the [Microsoft 365 Agents Toolkit](https://marketplace.visualstudio.com/items?itemName=TeamsDevApp.ms-teams-vscode-extension) extension +* [Node.js v20](https://nodejs.org/en/download/package-manager) +* A ServiceNow account with API access + +## Example Prompts + +1. Help me connect to VPN +image +image + +2. I can’t access internal portal +image + +## Minimal path to awesome + +* Clone this repository (or [download this solution as a .ZIP file](https://pnp.github.io/download-partial/?url=https://github.com/pnp/copilot-pro-dev-samples/tree/main/samples/da-ITHelpdesk) then unzip it) +* Open the Agents Toolkit extension and sign in to your Microsoft 365 tenant with Microsoft 365 Copilot +* Select **Preview in Copilot (Edge)** from the launch configuration dropdown + +## Configuration + +### ServiceNow Plugin Configuration + +In your OpenAPI spec [`appPackage/apiSpecificationFile/openapi.yaml`](appPackage/apiSpecificationFile/openapi.yaml): + +* Update the server URL to point to your ServiceNow instance +image + +* Point authorization, token and refresh urls to your ServiceNow instance +image + +* In your [`ai-plugin.json`](ai-plugin.json) (plugin manifest), go to the `response_semantics` section for each function. This sample comes with basic Adaptive Card views for rich representation. You can further customize the layout and data bindings to create richer, domain-specific views. Ensure all links point to your ServiceNow instance. +image + + +### ServiceNow Authentication Setup + +1. Log into your ServiceNow account and go to Application Registry under System OAuth + image + image + +2. Create a new registry. Select option “Create an OAuth API endpoint for external clients” + image + +3. Add callback URL as https://teams.microsoft.com/api/platform/v1.0/oAuthRedirect. You can leave other settings as defaults or customize it as per your needs. Hit Submit. + image + +4. After Submit, your app will show in the list. Click on it to get access to Client secret + image + +5. Copy Client ID and Client Secret. You will require it in step 6. + image + +6. Setup Microsoft Graph Connector to add ServiceNow knowledge – This will allow agent to provide troubleshooting guidance based on your ServiceNow KB articles. Follow the steps in https://learn.microsoft.com/en-us/microsoftsearch/servicenow-knowledge-connector . After you setup Microsoft Graph Connector to add ServiceNow knowledge, add the connection IDs in the Agent file. + image + +7. Add knowledge for incident autoclassification: This step is required if you want Copilot to perform autoclassification of an incident (Category and Subcategory fields) while creating the incident itself by matching issue description with the available choices. + - Go to Choice Lists under System definition + image + - Click on the filter icon and select Table as the field, and “is” “ incident” as other query parameters to filter out just the choicelists for your incident table. Run the query. + image + - Right click on the table header row and select export option. Select csv format. + image + - Now add this file to your SharePoint folder and copy url of the location. Add the url in your Agent file as knowledge under Capabilities. + image + - Note: All users who you want this Agent to be available should also have access to this file for Copilot to use it during autoclassification. + +8. Provision Key & Secret in Copilot: When provisioning the Declarative Agent via the Teams Toolkit, you’ll be prompted to enter the Client ID (Key) and Client Secret. Use the values from the ServiceNow OAuth app you created. + image + + +## Help + +We do not support samples, but this community is always willing to help, and we want to improve these samples. We use GitHub to track issues, which makes it easy for community members to volunteer their time and help resolve issues. + +You can try looking at [issues related to this sample](https://github.com/pnp/copilot-pro-dev-samples/issues?q=label%3A%22sample%3A%20da-ITHelpdesk%22) to see if anybody else is having the same issues. + +If you encounter any issues using this sample, [create a new issue](https://github.com/pnp/copilot-pro-dev-samples/issues/new). + +Finally, if you have an idea for improvement, [make a suggestion](https://github.com/pnp/copilot-pro-dev-samples/issues/new). + +## Disclaimer + +**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.** + +![](https://m365-visitor-stats.azurewebsites.net/SamplesGallery/da-ITHelpdesk) diff --git a/samples/da-ITHelpdesk/appPackage/adaptiveCards/getOngoingOutages.json b/samples/da-ITHelpdesk/appPackage/adaptiveCards/getOngoingOutages.json new file mode 100644 index 000000000..95a3dc626 --- /dev/null +++ b/samples/da-ITHelpdesk/appPackage/adaptiveCards/getOngoingOutages.json @@ -0,0 +1,87 @@ +{ + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "type": "AdaptiveCard", + "version": "1.0", + "body": [ + { + "type": "Container", + "items": [ + { + "type": "TextBlock", + "text": "This is your adaptive card template", + "weight": "bolder", + "size": "medium" + }, + { + "type": "ColumnSet", + "columns": [ + { + "type": "Column", + "width": "auto", + "items": [ + { + "type": "Image", + "url": "https://github.com/microsoft/kiota/blob/main/vscode/microsoft-kiota/images/logo.png?raw=true", + "altText": "Kiota logo", + "size": "medium", + "style": "person" + } + ] + }, + { + "type": "Column", + "width": "auto", + "items": [ + { + "type": "TextBlock", + "text": "Adaptive Card", + "weight": "bolder", + "wrap": true + } + ] + } + ] + } + ] + }, + { + "type": "Container", + "items": [ + { + "type": "TextBlock", + "text": "Now that we have defined the adaptive card template, one can go to the plugin manifest file and edit it to create a card that displays the relevant information for their users.", + "wrap": true + } + ] + }, + { + "type": "ColumnSet", + "columns": [ + { + "type": "Column", + "width": "auto", + "items": [ + { + "type": "TextBlock", + "horizontalAlignment": "center", + "text": "Learn about [Adaptive Cards](https://adaptivecards.io/)", + "wrap": true + } + ] + }, + { + "type": "Column", + "width": "auto", + "items": [ + { + "type": "TextBlock", + "horizontalAlignment": "center", + "text": "Learn about [API Plugin for Microsoft 365 Copilot](https://learn.microsoft.com/en-us/microsoft-365-copilot/extensibility/overview-api-plugins)", + "wrap": true + } + ] + } + ] + } + ] +} diff --git a/samples/da-ITHelpdesk/appPackage/adaptiveCards/getUserDetails.json b/samples/da-ITHelpdesk/appPackage/adaptiveCards/getUserDetails.json new file mode 100644 index 000000000..95a3dc626 --- /dev/null +++ b/samples/da-ITHelpdesk/appPackage/adaptiveCards/getUserDetails.json @@ -0,0 +1,87 @@ +{ + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "type": "AdaptiveCard", + "version": "1.0", + "body": [ + { + "type": "Container", + "items": [ + { + "type": "TextBlock", + "text": "This is your adaptive card template", + "weight": "bolder", + "size": "medium" + }, + { + "type": "ColumnSet", + "columns": [ + { + "type": "Column", + "width": "auto", + "items": [ + { + "type": "Image", + "url": "https://github.com/microsoft/kiota/blob/main/vscode/microsoft-kiota/images/logo.png?raw=true", + "altText": "Kiota logo", + "size": "medium", + "style": "person" + } + ] + }, + { + "type": "Column", + "width": "auto", + "items": [ + { + "type": "TextBlock", + "text": "Adaptive Card", + "weight": "bolder", + "wrap": true + } + ] + } + ] + } + ] + }, + { + "type": "Container", + "items": [ + { + "type": "TextBlock", + "text": "Now that we have defined the adaptive card template, one can go to the plugin manifest file and edit it to create a card that displays the relevant information for their users.", + "wrap": true + } + ] + }, + { + "type": "ColumnSet", + "columns": [ + { + "type": "Column", + "width": "auto", + "items": [ + { + "type": "TextBlock", + "horizontalAlignment": "center", + "text": "Learn about [Adaptive Cards](https://adaptivecards.io/)", + "wrap": true + } + ] + }, + { + "type": "Column", + "width": "auto", + "items": [ + { + "type": "TextBlock", + "horizontalAlignment": "center", + "text": "Learn about [API Plugin for Microsoft 365 Copilot](https://learn.microsoft.com/en-us/microsoft-365-copilot/extensibility/overview-api-plugins)", + "wrap": true + } + ] + } + ] + } + ] +} diff --git a/samples/da-ITHelpdesk/appPackage/adaptiveCards/listIncidents.json b/samples/da-ITHelpdesk/appPackage/adaptiveCards/listIncidents.json new file mode 100644 index 000000000..95a3dc626 --- /dev/null +++ b/samples/da-ITHelpdesk/appPackage/adaptiveCards/listIncidents.json @@ -0,0 +1,87 @@ +{ + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "type": "AdaptiveCard", + "version": "1.0", + "body": [ + { + "type": "Container", + "items": [ + { + "type": "TextBlock", + "text": "This is your adaptive card template", + "weight": "bolder", + "size": "medium" + }, + { + "type": "ColumnSet", + "columns": [ + { + "type": "Column", + "width": "auto", + "items": [ + { + "type": "Image", + "url": "https://github.com/microsoft/kiota/blob/main/vscode/microsoft-kiota/images/logo.png?raw=true", + "altText": "Kiota logo", + "size": "medium", + "style": "person" + } + ] + }, + { + "type": "Column", + "width": "auto", + "items": [ + { + "type": "TextBlock", + "text": "Adaptive Card", + "weight": "bolder", + "wrap": true + } + ] + } + ] + } + ] + }, + { + "type": "Container", + "items": [ + { + "type": "TextBlock", + "text": "Now that we have defined the adaptive card template, one can go to the plugin manifest file and edit it to create a card that displays the relevant information for their users.", + "wrap": true + } + ] + }, + { + "type": "ColumnSet", + "columns": [ + { + "type": "Column", + "width": "auto", + "items": [ + { + "type": "TextBlock", + "horizontalAlignment": "center", + "text": "Learn about [Adaptive Cards](https://adaptivecards.io/)", + "wrap": true + } + ] + }, + { + "type": "Column", + "width": "auto", + "items": [ + { + "type": "TextBlock", + "horizontalAlignment": "center", + "text": "Learn about [API Plugin for Microsoft 365 Copilot](https://learn.microsoft.com/en-us/microsoft-365-copilot/extensibility/overview-api-plugins)", + "wrap": true + } + ] + } + ] + } + ] +} diff --git a/samples/da-ITHelpdesk/appPackage/adaptiveCards/updateIncident.json b/samples/da-ITHelpdesk/appPackage/adaptiveCards/updateIncident.json new file mode 100644 index 000000000..95a3dc626 --- /dev/null +++ b/samples/da-ITHelpdesk/appPackage/adaptiveCards/updateIncident.json @@ -0,0 +1,87 @@ +{ + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "type": "AdaptiveCard", + "version": "1.0", + "body": [ + { + "type": "Container", + "items": [ + { + "type": "TextBlock", + "text": "This is your adaptive card template", + "weight": "bolder", + "size": "medium" + }, + { + "type": "ColumnSet", + "columns": [ + { + "type": "Column", + "width": "auto", + "items": [ + { + "type": "Image", + "url": "https://github.com/microsoft/kiota/blob/main/vscode/microsoft-kiota/images/logo.png?raw=true", + "altText": "Kiota logo", + "size": "medium", + "style": "person" + } + ] + }, + { + "type": "Column", + "width": "auto", + "items": [ + { + "type": "TextBlock", + "text": "Adaptive Card", + "weight": "bolder", + "wrap": true + } + ] + } + ] + } + ] + }, + { + "type": "Container", + "items": [ + { + "type": "TextBlock", + "text": "Now that we have defined the adaptive card template, one can go to the plugin manifest file and edit it to create a card that displays the relevant information for their users.", + "wrap": true + } + ] + }, + { + "type": "ColumnSet", + "columns": [ + { + "type": "Column", + "width": "auto", + "items": [ + { + "type": "TextBlock", + "horizontalAlignment": "center", + "text": "Learn about [Adaptive Cards](https://adaptivecards.io/)", + "wrap": true + } + ] + }, + { + "type": "Column", + "width": "auto", + "items": [ + { + "type": "TextBlock", + "horizontalAlignment": "center", + "text": "Learn about [API Plugin for Microsoft 365 Copilot](https://learn.microsoft.com/en-us/microsoft-365-copilot/extensibility/overview-api-plugins)", + "wrap": true + } + ] + } + ] + } + ] +} diff --git a/samples/da-ITHelpdesk/appPackage/ai-plugin.json b/samples/da-ITHelpdesk/appPackage/ai-plugin.json new file mode 100644 index 000000000..e930c2c41 --- /dev/null +++ b/samples/da-ITHelpdesk/appPackage/ai-plugin.json @@ -0,0 +1,120 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/copilot/plugin/v2.1/schema.json", + "schema_version": "v2.1", + "name_for_human": "ServiceNow Knowledge Management REST API", + "description_for_human": "API for searching, retrieving, creation, updating and deletion of ticket incidents from ServiceNow.", + "description_for_model": "API for searching, retrieving, creation, updating and deletion of ticket incidents from ServiceNow.", + "contact_email": "publisher-email@example.com", + "namespace": "ithelpdesk", + "capabilities": { + "conversation_starters": [ + { + "text": "Retrieve user details from ServiceNow sys_user tab" + }, + { + "text": "Retrieve a list of incidents" + }, + { + "text": "Create a new incident" + }, + { + "text": "Update an existing incident" + }, + { + "text": "Delete an existing incident" + }, + { + "text": "Get ongoing outages" + } + ] + }, + "functions": [ + { + "name": "createIncident", + "description": "Create a new incident record in the ServiceNow instance." + }, + { + "name": "deleteIncident", + "description": "Delete an incident record from the ServiceNow instance." + }, + { + "name": "getOngoingOutages", + "description": "Get ongoing outages", + "capabilities": { + "response_semantics": { + "data_path": "$", + "static_template": { + "file": "./adaptiveCards/getOngoingOutages.json" + } + } + } + }, + { + "name": "getUserDetails", + "description": "Retrieve user details from ServiceNow sys_user table", + "capabilities": { + "response_semantics": { + "data_path": "$", + "static_template": { + "file": "./adaptiveCards/getUserDetails.json" + } + } + } + }, + { + "name": "listIncidents", + "description": "Retrieve a list of incidents with optional filtering and pagination.", + "capabilities": { + "response_semantics": { + "data_path": "$", + "static_template": { + "file": "./adaptiveCards/listIncidents.json" + } + } + } + }, + { + "name": "updateIncident", + "description": "Update fields of an existing incident record.", + "capabilities": { + "response_semantics": { + "data_path": "$", + "static_template": { + "file": "./adaptiveCards/updateIncident.json" + } + } + } + } + ], + "runtimes": [ + { + "type": "OpenApi", + "auth": { + "type": "None" + }, + "spec": { + "url": "apiSpecificationFile/openapi.yaml" + }, + "run_for_functions": [ + "getOngoingOutages" + ] + }, + { + "type": "OpenApi", + "auth": { + "type": "OAuthPluginVault", + "reference_id": "${{OAUTH2AUTHCODE_REGISTRATION_ID}}" + }, + "spec": { + "url": "apiSpecificationFile/openapi.yaml" + }, + "run_for_functions": [ + "getUserDetails", + "listIncidents", + "createIncident", + "updateIncident", + "deleteIncident" + ] + } + ] +} diff --git a/samples/da-ITHelpdesk/appPackage/apiSpecificationFile/openapi.yaml b/samples/da-ITHelpdesk/appPackage/apiSpecificationFile/openapi.yaml new file mode 100644 index 000000000..196195c0d --- /dev/null +++ b/samples/da-ITHelpdesk/appPackage/apiSpecificationFile/openapi.yaml @@ -0,0 +1,437 @@ +openapi: 3.0.4 +info: + title: ServiceNow Knowledge Management REST API - Subset + description: 'API for searching, retrieving, creation, updating and deletion of ticket incidents from ServiceNow.' + version: 1.0.0 +servers: + - url: https://dev328797.service-now.com/ + description: ServiceNow instance URL + variables: + instance: + default: your_instance_name +paths: + /api/now/table/sys_user: + get: + description: Retrieve user details from ServiceNow sys_user table + operationId: getUserDetails + parameters: + - name: sysparm_query + in: query + description: Query to filter users by caller_id + explode: false + schema: + type: string + - name: sysparm_fields + in: query + description: Comma-separated list of fields to return (default is sys_id) + explode: false + schema: + type: string + default: sys_id + responses: + '200': + description: User details retrieved successfully. + content: + application/json: + schema: + type: object + properties: + result: + type: array + items: + type: object + properties: + sys_id: + type: string + description: GUID to identify the user sys_id + name: + type: string + description: Name of the user. + email: + type: string + description: Email address of the user. + user_name: + type: string + description: Username of the user. + phone: + type: string + description: Phone number of the user. + department: + type: string + description: Department of the user. + security: + - oAuth2AuthCode: [ ] + /api/now/table/incident: + get: + summary: Retrieve a list of incidents + description: Retrieve a list of incidents with optional filtering and pagination. + operationId: listIncidents + parameters: + - name: sysparm_query + in: query + description: "Query to filter incidents using ServiceNow query language. Example: `caller_id.name=` or `sys_id=` or any other valid query.\n" + explode: false + schema: + pattern: ^sys_id=.*$ + type: string + - name: sysparm_limit + in: query + description: Maximum number of results to return. + explode: false + schema: + type: integer + default: 10 + - name: sysparm_offset + in: query + description: Number of records to skip for pagination. + explode: false + schema: + type: integer + default: 0 + responses: + '200': + description: A list of incidents. + content: + application/json: + schema: + type: object + properties: + result: + type: array + items: + type: object + properties: + sys_id: + type: string + description: GUID to identify the incident. + number: + type: string + description: Incident number. + short_description: + type: string + description: Brief description of the incident. + description: + type: string + description: Detailed description of the incident. + priority: + type: integer + description: Priority level of the incident. + caller_id: + type: string + description: name of the user reporting the incident. + state: + type: integer + description: Current state of the incident. + category: + type: string + description: Category of the incident. + subcategory: + type: string + description: Subcategory of the incident. + impact: + type: integer + description: Impact level of the incident. + urgency: + type: integer + description: Urgency level of the incident. + security: + - oAuth2AuthCode: [ ] + post: + summary: Create a new incident + description: Create a new incident record in the ServiceNow instance. + operationId: createIncident + requestBody: + content: + application/json: + schema: + required: + - short_description + - caller_id + type: object + properties: + short_description: + type: string + description: Brief description of the incident. + description: + type: string + description: Detailed description of the incident. + caller_id: + type: string + description: Email or UPN of the user reporting the incident. + category: + type: string + description: Category of the incident. + subcategory: + type: string + description: Subcategory of the incident. + impact: + type: integer + description: Impact level of the incident. Default value 2. + urgency: + type: integer + description: Urgency level of the incident. Default value 2. + required: true + responses: + '201': + description: Incident created successfully. + content: + application/json: + schema: + type: object + properties: + result: + type: object + properties: + sys_id: + type: string + description: GUID to identify the incident. + number: + type: string + description: Incident number. + short_description: + type: string + description: Brief description of the incident. + description: + type: string + description: Detailed description of the incident. + priority: + type: integer + description: Priority level of the incident. + caller_id: + type: string + description: name of the user reporting the incident. + state: + type: integer + description: Current state of the incident. + category: + type: string + description: Category of the incident. + subcategory: + type: string + description: Subcategory of the incident. + impact: + type: integer + description: Impact level of the incident. + urgency: + type: integer + description: Urgency level of the incident. + security: + - oAuth2AuthCode: [ ] + '/api/now/table/incident/{sys_id}': + patch: + summary: Update an existing incident + description: Update fields of an existing incident record. + operationId: updateIncident + parameters: + - name: sys_id + in: path + description: Sys_id of the incident to update. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + required: + - short_description + - caller_id + type: object + properties: + short_description: + type: string + description: Brief description of the incident. + description: + type: string + description: Detailed description of the incident. + priority: + type: integer + description: Priority level of the incident. + caller_id: + type: string + description: name of the user reporting the incident. + state: + type: integer + description: 'The state of the incident, 1 (New), 2 (In Progress), 3 (On Hold), 6 (Resolved), 7 (Closed), 8 (Canceled). It is enum, possible values are 1, 2, 3, 6, 7, 8' + example: 1 + close_code: + type: string + description: 'Required when state is set to 6 (Resolved) or 7 (Closed). Provides the resolution code. It is enum, possible values are No resolution provided, Resolved by request, Resolved by caller, Solution provided, Duplicate, Resolved by change, Workaround provided, Known error, Resolved by problem, User error' + example: No resolution provided + close_notes: + type: string + description: 'Additional notes about the resolution, required when state is set to 6 (Resolved), 7 (Closed).' + example: Issue resolved by restarting the server. + category: + type: string + description: Category of the incident. + subcategory: + type: string + description: Subcategory of the incident. + impact: + type: integer + description: Impact level of the incident. + urgency: + type: integer + description: Urgency level of the incident. + required: true + responses: + '200': + description: Incident updated successfully. + content: + application/json: + schema: + type: object + properties: + result: + type: object + properties: + sys_id: + type: string + description: GUID to identify the incident. + number: + type: string + description: Incident number. + short_description: + type: string + description: Brief description of the incident. + description: + type: string + description: Detailed description of the incident. + priority: + type: integer + description: Priority level of the incident. + caller_id: + type: string + description: name of the user reporting the incident. + state: + type: integer + description: Current state of the incident. + category: + type: string + description: Category of the incident. + subcategory: + type: string + description: Subcategory of the incident. + impact: + type: integer + description: Impact level of the incident. + urgency: + type: integer + description: Urgency level of the incident. + security: + - oAuth2AuthCode: [ ] + delete: + summary: Delete an existing incident + description: Delete an incident record from the ServiceNow instance. + operationId: deleteIncident + parameters: + - name: sys_id + in: path + description: Sys_id of the incident to delete. + required: true + schema: + type: string + responses: + '204': + description: Incident deleted successfully. + security: + - oAuth2AuthCode: [ ] + /api/now/table/cmdb_ci_outage: + get: + summary: Get ongoing outages + operationId: getOngoingOutages + parameters: + - name: sysparm_query + in: query + description: Always use value:start<=javascript:gs.now()^end>javascript:gs.now()^OR(end=NULL^active=true) + explode: false + schema: + type: string + example: start<=javascript:gs.now()^end>javascript:gs.now()^OR(end=NULL^active=true) + - name: sysparm_limit + in: query + description: Maximum number of results to return + explode: false + schema: + type: integer + default: 100 + responses: + '200': + description: List of ongoing outages + content: + application/json: + schema: + type: object + properties: + result: + type: array + items: + type: object + properties: + short_description: + type: string + cmdb_ci: + type: object + properties: + link: + type: string + format: uri + value: + type: string + sys_mod_count: + type: string + sys_updated_on: + type: string + format: date-time + message: + type: string + sys_domain_path: + type: string + sys_tags: + type: string + type: + type: string + duration: + type: string + format: date-time + number: + type: string + sys_id: + type: string + sys_updated_by: + type: string + sys_created_on: + type: string + format: date-time + sys_domain: + type: object + properties: + link: + type: string + format: uri + value: + type: string + details: + type: string + end: + type: string + format: date-time + task_number: + type: string + begin: + type: string + format: date-time + sys_created_by: + type: string +components: + securitySchemes: + oAuth2AuthCode: + type: oauth2 + flows: + authorizationCode: + authorizationUrl: https://dev328797.service-now.com/oauth_auth.do + tokenUrl: https://dev328797.service-now.com/oauth_token.do + refreshUrl: https://dev328797.service-now.com/oauth_token.do + scopes: { } + x-ai-auth-reference-id: '{oAuth2AuthCode_REGISTRATION_ID}' \ No newline at end of file diff --git a/samples/da-ITHelpdesk/appPackage/apiSpecificationFile/openapi.yaml.original b/samples/da-ITHelpdesk/appPackage/apiSpecificationFile/openapi.yaml.original new file mode 100644 index 000000000..0f1e5ec17 --- /dev/null +++ b/samples/da-ITHelpdesk/appPackage/apiSpecificationFile/openapi.yaml.original @@ -0,0 +1,418 @@ +openapi: 3.0.0 +info: + title: ServiceNow Knowledge Management REST API + description: API for searching, retrieving, creation, updating and deletion of ticket incidents from ServiceNow. + version: 1.0.0 +servers: + - url: https://dev328797.service-now.com/ + description: ServiceNow instance URL + variables: + instance: + default: your_instance_name +paths: + /api/now/table/sys_user: + get: + security: + - oAuth2AuthCode: [] + operationId: getUserDetails + description: Retrieve user details from ServiceNow sys_user table + parameters: + - name: sysparm_query + in: query + required: false + description: Query to filter users by caller_id + schema: + type: string + - name: sysparm_fields + in: query + required: false + description: Comma-separated list of fields to return (default is sys_id) + schema: + type: string + default: sys_id + responses: + '200': + description: User details retrieved successfully. + content: + application/json: + schema: + type: object + properties: + result: + type: array + items: + $ref: '#/components/schemas/SysUser' + '400': + description: Bad request due to invalid parameters. + '401': + description: Unauthorized access due to invalid credentials. + '500': + description: Internal server error. + /api/now/table/incident: + get: + security: + - oAuth2AuthCode: [] + operationId: listIncidents + summary: Retrieve a list of incidents + description: Retrieve a list of incidents with optional filtering and pagination. + parameters: + - name: sysparm_query + in: query + required: false + description: > + Query to filter incidents using ServiceNow query language. + Example: `caller_id.name=` or `sys_id=` or any other valid query. + schema: + type: string + pattern: "^sys_id=.*$" + - name: sysparm_limit + in: query + required: false + description: Maximum number of results to return. + schema: + type: integer + default: 10 + - name: sysparm_offset + in: query + required: false + description: Number of records to skip for pagination. + schema: + type: integer + default: 0 + responses: + '200': + description: A list of incidents. + content: + application/json: + schema: + type: object + properties: + result: + type: array + items: + $ref: '#/components/schemas/Incident' + '400': + description: Bad request due to invalid parameters. + '401': + description: Unauthorized access due to invalid credentials. + '500': + description: Internal server error. + post: + security: + - oAuth2AuthCode: [] + operationId: createIncident + summary: Create a new incident + description: Create a new incident record in the ServiceNow instance. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IncidentCreate' + responses: + '201': + description: Incident created successfully. + content: + application/json: + schema: + type: object + properties: + result: + $ref: '#/components/schemas/Incident' + '400': + description: Bad request due to invalid input. + '401': + description: Unauthorized access due to invalid credentials. + '500': + description: Internal server error. + /api/now/table/incident/{sys_id}: + patch: + security: + - oAuth2AuthCode: [] + operationId: updateIncident + summary: Update an existing incident + description: Update fields of an existing incident record. + parameters: + - name: sys_id + in: path + required: true + description: Sys_id of the incident to update. + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IncidentUpdate' + responses: + '200': + description: Incident updated successfully. + content: + application/json: + schema: + type: object + properties: + result: + $ref: '#/components/schemas/Incident' + '400': + description: Bad request due to invalid input. + '401': + description: Unauthorized access due to invalid credentials. + '404': + description: Incident not found. + '500': + description: Internal server error. + delete: + security: + - oAuth2AuthCode: [] + operationId: deleteIncident + summary: Delete an existing incident + description: Delete an incident record from the ServiceNow instance. + parameters: + - name: sys_id + in: path + required: true + description: Sys_id of the incident to delete. + schema: + type: string + responses: + '204': + description: Incident deleted successfully. + '401': + description: Unauthorized access due to invalid credentials. + '404': + description: Incident not found. + '500': + description: Internal server error. + /api/now/table/cmdb_ci_outage: + get: + summary: Get ongoing outages + operationId: getOngoingOutages + parameters: + - name: sysparm_query + in: query + description: Always use value:start<=javascript:gs.now()^end>javascript:gs.now()^OR(end=NULL^active=true) + required: false + schema: + type: string + example: start<=javascript:gs.now()^end>javascript:gs.now()^OR(end=NULL^active=true) + - name: sysparm_limit + in: query + description: Maximum number of results to return + required: false + schema: + type: integer + default: 100 + responses: + '200': + description: List of ongoing outages + content: + application/json: + schema: + $ref: '#/components/schemas/Outage' + '401': + description: Unauthorized – Invalid credentials + '500': + description: Server error + +components: + securitySchemes: + oAuth2AuthCode: + type: oauth2 + flows: + authorizationCode: + authorizationUrl: https://dev328797.service-now.com/oauth_auth.do + tokenUrl: https://dev328797.service-now.com/oauth_token.do + refreshUrl: https://dev328797.service-now.com/oauth_token.do + scopes: {} + schemas: + SysUser: + type: object + properties: + sys_id: + type: string + description: GUID to identify the user sys_id + name: + type: string + description: Name of the user. + email: + type: string + description: Email address of the user. + user_name: + type: string + description: Username of the user. + phone: + type: string + description: Phone number of the user. + department: + type: string + description: Department of the user. + Incident: + type: object + properties: + sys_id: + type: string + description: GUID to identify the incident. + number: + type: string + description: Incident number. + short_description: + type: string + description: Brief description of the incident. + description: + type: string + description: Detailed description of the incident. + priority: + type: integer + description: Priority level of the incident. + caller_id: + type: string + description: name of the user reporting the incident. + state: + type: integer + description: Current state of the incident. + category: + type: string + description: Category of the incident. + subcategory: + type: string + description: Subcategory of the incident. + impact: + type: integer + description: Impact level of the incident. + urgency: + type: integer + description: Urgency level of the incident. + IncidentCreate: + type: object + properties: + short_description: + type: string + description: Brief description of the incident. + description: + type: string + description: Detailed description of the incident. + caller_id: + type: string + description: Email or UPN of the user reporting the incident. + category: + type: string + description: Category of the incident. + subcategory: + type: string + description: Subcategory of the incident. + impact: + type: integer + description: Impact level of the incident. Default value 2. + urgency: + type: integer + description: Urgency level of the incident. Default value 2. + required: + - short_description + - caller_id + IncidentUpdate: + type: object + properties: + short_description: + type: string + description: Brief description of the incident. + description: + type: string + description: Detailed description of the incident. + priority: + type: integer + description: Priority level of the incident. + caller_id: + type: string + description: name of the user reporting the incident. + state: + type: integer + description: The state of the incident, 1 (New), 2 (In Progress), 3 (On Hold), 6 (Resolved), 7 (Closed), 8 (Canceled). It is enum, possible values are 1, 2, 3, 6, 7, 8 + example: 1 + close_code: + type: string + description: Required when state is set to 6 (Resolved) or 7 (Closed). Provides the resolution code. It is enum, possible values are No resolution provided, Resolved by request, Resolved by caller, Solution provided, Duplicate, Resolved by change, Workaround provided, Known error, Resolved by problem, User error + example: No resolution provided + close_notes: + type: string + description: Additional notes about the resolution, required when state is set to 6 (Resolved), 7 (Closed). + example: "Issue resolved by restarting the server." + category: + type: string + description: Category of the incident. + subcategory: + type: string + description: Subcategory of the incident. + impact: + type: integer + description: Impact level of the incident. + urgency: + type: integer + description: Urgency level of the incident. + required: + - short_description + - caller_id + Outage: + type: object + properties: + result: + type: array + items: + type: object + properties: + short_description: + type: string + cmdb_ci: + type: object + properties: + link: + type: string + format: uri + value: + type: string + sys_mod_count: + type: string + sys_updated_on: + type: string + format: date-time + message: + type: string + sys_domain_path: + type: string + sys_tags: + type: string + type: + type: string + duration: + type: string + format: date-time + number: + type: string + sys_id: + type: string + sys_updated_by: + type: string + sys_created_on: + type: string + format: date-time + sys_domain: + type: object + properties: + link: + type: string + format: uri + value: + type: string + details: + type: string + end: + type: string + format: date-time + task_number: + type: string + begin: + type: string + format: date-time + sys_created_by: + type: string \ No newline at end of file diff --git a/samples/da-ITHelpdesk/appPackage/color.png b/samples/da-ITHelpdesk/appPackage/color.png new file mode 100644 index 000000000..a8e8f07d6 Binary files /dev/null and b/samples/da-ITHelpdesk/appPackage/color.png differ diff --git a/samples/da-ITHelpdesk/appPackage/declarativeAgent.json b/samples/da-ITHelpdesk/appPackage/declarativeAgent.json new file mode 100644 index 000000000..200b0eaa0 --- /dev/null +++ b/samples/da-ITHelpdesk/appPackage/declarativeAgent.json @@ -0,0 +1,68 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/copilot/declarative-agent/v1.4/schema.json", + "version": "v1.4", + "name": "ITHelpdesk", + "description": "IT Support Agent designed to assist users with common IT issues using a **conversational and interactive troubleshooting process**", + "instructions": "$[file('instruction.txt')]", + "actions": [ + { + "id": "action_1", + "file": "ai-plugin.json" + } + ], + "conversation_starters": [ + { + "text": "Facing issue connecting to vpn", + "title": "Help" + }, + { + "text": "Facing issues with AV Link", + "title": "Help" + }, + { + "text": "Facing issues accessing internal portal", + "title": "Help" + }, + { + "text": "My OneNote is not syncing", + "title": "Help" + }, + { + "text": "Facing issue with Teams", + "title": "Help" + }, + { + "text": "How do I initiate a workflow for hardware request?", + "title": "Workflow Assistance" + } + ], + "capabilities": [ + { + "name": "GraphConnectors", + "connections": [ + { + "connection_id": "ServiceNowKB2211" + }, + { + "connection_id": "ServiceNowCatalog2212" + } + ] + }, + { + "name": "OneDriveAndSharePoint", + "items_by_sharepoint_ids": [], + "items_by_url": [ + { + "url": "https://microsoft.sharepoint.com/teams/ActionPlatform/Shared%20Documents/BizChat%20Extensibility/Multi-step%20workflows/DA%20Manifests%20(Latest)/KnowledgeBase/sys_choice.xlsx" + } + ] + }, + { + "name":"People" + }, + { + "name": "CodeInterpreter" + } + ] + +} \ No newline at end of file diff --git a/samples/da-ITHelpdesk/appPackage/instruction.txt b/samples/da-ITHelpdesk/appPackage/instruction.txt new file mode 100644 index 000000000..b027ae2b8 --- /dev/null +++ b/samples/da-ITHelpdesk/appPackage/instruction.txt @@ -0,0 +1,65 @@ +Assist users with common IT issues using a **conversational and interactive troubleshooting workflow**. + +# Troubleshooting workflow steps + +## Step 1: Gather Basic Details of the Issue +- Check if the user has provided a basic description of the issue. +- If the description is unclear, ask minimal follow-up questions, preferably just one, to gather essential information needed to understand the issue. + - Do not overwhelm the user by asking for too many details upfront. + - Focus on simple clarifying questions directly related to the user's original statement. + Example 1: + User says: “I’m facing an issue accessing a portal.” + Response: “Which portal are you having trouble accessing?” + Example 2: + User says: “I’m facing an issue connecting to vpn” + Response: “Are you seeing any specific error?” +- Once basic clarity is achieved, proceed immediately to Step 2. + +## Step 2: Check for Ongoing Outages +- Query ServiceNow Outages. +- If the user's issue is related to any of the outages: + - Provide details about the outage. + - Include the estimated resolution time. + - Ask: “Is your issue unrelated to this outage? If yes, I can help you troubleshoot further.” If user says yes, proceed to Step 3. If user says it is related to the outage or doesn't provide any input, end the conversation politely. +- If no relevant outages to the user's issue are found, tell the user that no relevant ongoing outages are found and proceed to Step 3. + +## Step 3: Identify Targeted Resolutions from ServiceNow KB +- Search ServiceNow KB articles related to the user's issue description. +- Analyze the articles. + - Thoroughly read each article. + - Identify all issues, symptoms, and resolution paths mentioned within each article. Note: A single article may describe multiple problems and corresponding solutions. +- Do not immediately suggest all resolution options. +- Instead, progressively narrow down to the right resolution by asking targeted clarifying questions. + - First Round: + - Compare all the retrieved articles. + - Ask targeted clarifying questions based strictly on differences between the symptoms and resolutions described in the ServiceNow KB articles. + - Use the user's answers to eliminate unrelated articles or solutions. (e.g., Narrow 10 → 5 candidates.) + - Second Round: + - Compare the remaining articles/resolutions. + - Again, ask targeted clarifying questions based on ServiceNow KB content to further eliminate irrelevant options.(e.g., Narrow 5 → 3 candidates.) + - Repeat the process: + - Continue comparing the remaining options and asking focused clarifying questions, round by round, until only most relevant article and resolution path remains. +- Once a correct resolution is determined: provide clear, concise, step-by-step resolution to the user. Avoid overwhelming the user with unnecessary details — focus only on the relevant resolution steps. +- Confirm user satisfaction. Ask: "Did this help? I can dig in further if you give me more information, or I can go ahead and create a support ticket - just let me know.". +- If user says it helped or doesn't respond, end the conversation politely. +- If user provides more information, repeat Step 3 to refine the search and resolution based on the updated details. +- If user asks to create a ticket, proceed to Step 4. + +## Step 4: Create a Support Ticket +- Use the `sys_choice` SharePoint file to determine the correct **category** and **subcategory** for the issue. +- Alanyze the file to: + 1. Search for the **closest matching category** from rows where Element is category. + - Use similarity based on the user’s issue description. An exact string match is not required - choose the most relevant category. + 2. Once a category is identified, find valid **subcategory** values where: + - Element is subcategory and Dependent value equals the identified category +- Always use the exact values from the `sys_choice` SharePoint file for both **category** and **subcategory** when creating the ticket. +- **If a suitable category or subcategory cannot be confidently mapped, leave those fields empty.** +- Fetch user's UPN email address using people capability. +- Fill in the ticket with: + - **Caller ID** (User's UPN) + - **Category** (if available) + - **Subcategory** (if available) + - **User issue description** + - **Troubleshooting steps attempted** + - **Any error codes or messages** + - **Relevant metadata** \ No newline at end of file diff --git a/samples/da-ITHelpdesk/appPackage/manifest.json b/samples/da-ITHelpdesk/appPackage/manifest.json new file mode 100644 index 000000000..2cfc282bf --- /dev/null +++ b/samples/da-ITHelpdesk/appPackage/manifest.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/teams/v1.21/MicrosoftTeams.schema.json", + "manifestVersion": "1.21", + "version": "1.0.0", + "id": "${{TEAMS_APP_ID}}", + "developer": { + "name": "My App, Inc.", + "websiteUrl": "https://www.example.com", + "privacyUrl": "https://www.example.com/privacy", + "termsOfUseUrl": "https://www.example.com/termofuse" + }, + "icons": { + "color": "color.png", + "outline": "outline.png" + }, + "name": { + "short": "ITHelpdesk${{APP_NAME_SUFFIX}}", + "full": "Full name for ITHelpdesk" + }, + "description": { + "short": "Short description for ITHelpdesk", + "full": "Full description for ITHelpdesk" + }, + "accentColor": "#FFFFFF", + "copilotAgents": { + "declarativeAgents": [ + { + "id": "declarativeAgent", + "file": "declarativeAgent.json" + } + ] + }, + "permissions": [ + "identity", + "messageTeamMembers" + ], + "validDomains": [] +} \ No newline at end of file diff --git a/samples/da-ITHelpdesk/appPackage/outline.png b/samples/da-ITHelpdesk/appPackage/outline.png new file mode 100644 index 000000000..04e5b2331 Binary files /dev/null and b/samples/da-ITHelpdesk/appPackage/outline.png differ diff --git a/samples/da-ITHelpdesk/assets/ithelpdesk.png b/samples/da-ITHelpdesk/assets/ithelpdesk.png new file mode 100644 index 000000000..4e511ff6b Binary files /dev/null and b/samples/da-ITHelpdesk/assets/ithelpdesk.png differ diff --git a/samples/da-ITHelpdesk/assets/sample.json b/samples/da-ITHelpdesk/assets/sample.json new file mode 100644 index 000000000..3cbb6ec29 --- /dev/null +++ b/samples/da-ITHelpdesk/assets/sample.json @@ -0,0 +1,74 @@ +[ + { + "name": "pnp-copilot-pro-dev-da-ITHelpdesk", + "source": "pnp", + "title": "IT Helpdesk Declarative Agent", + "shortDescription": "IT Helpdesk Agent is a Declarative Agent designed to assist employees with IT issues through (a) guided, conversational troubleshooting, (b) outage detection, and ticket creation for escalation – powered through ServiceNow offerings (Knowledge, Catalogue and Tickets). ", + "url": "https://github.com/pnp/copilot-pro-dev-samples/tree/main/samples/da-ITHelpdesk", + "downloadUrl": "https://pnp.github.io/download-partial/?url=https://github.com/pnp/copilot-pro-dev-samples/tree/main/samples/da-ITHelpdesk", + "longDescription": [ + "IT Helpdesk Agent is a Declarative Agent designed to assist employees with IT issues through (a) guided, conversational troubleshooting, (b) outage detection, and ticket creation for escalation – powered through ServiceNow offerings (Knowledge, Catalogue and Tickets)." + ], + "creationDateTime": "2025-10-14", + "updateDateTime": "2025-10-14", + "products": [ + "Microsoft 365 Copilot" + ], + "metadata": [ + { + "key": "PLATFORM", + "value": "Node.js" + }, + { + "key": "API-PLUGIN", + "value": "Yes" + }, + { + "key": "GRAPH-CONNECTOR", + "value": "No" + } + ], + "thumbnails": [ + { + "type": "image", + "order": 100, + "url": "https://github.com/pnp/copilot-pro-dev-samples/raw/main/samples/da-ITHelpdesk/assets/ithelpdesk.png", + "alt": "IT Helpdesk" + } + ], + "authors": [ + { + "gitHubAccount": "sebastienlevert", + "pictureUrl": "https://github.com/sebastienlevert.png", + "name": "Sébastien Levert" + }, + { + "gitHubAccount": "akhilsaivalluri", + "pictureUrl": "https://github.com/akhilsaivalluri.png", + "name": "Akhil Sai Valluri" + }, + { + "gitHubAccount": "garrytrinder", + "pictureUrl": "https://github.com/garrytrinder.png", + "name": "Garry Trinder" + } + ], + "references": [ + { + "name": "Microsoft 365 Copilot extensibility", + "description": "Learn more about what Microsoft 365 Copilot and how you can extend it.", + "url": "https://learn.microsoft.com/microsoft-365-copilot/extensibility/" + }, + { + "name": "Declarative agents for Microsoft 365 Copilot overview", + "description": "Learn more about what declarative agents for Microsoft 365 Copilot are.", + "url": "https://learn.microsoft.com/microsoft-365-copilot/extensibility/overview-declarative-agent" + }, + { + "name": "Build a declarative agent for Microsoft 365 Copilot using TypeSpec for Microsoft 365 Copilot", + "description": "Learn how to build a declarative agent for Microsoft 365 Copilot using TypeSpec for Microsoft 365 Copilot.", + "url": "https://learn.microsoft.com/microsoft-365-copilot/extensibility/build-declarative-agents-typespec" + } + ] + } +] \ No newline at end of file diff --git a/samples/da-ITHelpdesk/env/.env.dev b/samples/da-ITHelpdesk/env/.env.dev new file mode 100644 index 000000000..bfdd4c66a --- /dev/null +++ b/samples/da-ITHelpdesk/env/.env.dev @@ -0,0 +1,8 @@ +# This file includes environment variables that will be committed to git by default. + +# Built-in environment variables +TEAMSFX_ENV=dev +APP_NAME_SUFFIX=dev +ENDPOINT_URL=https://bubble-dev-ed.develop.my.salesforce.com + +# Generated during provision, you can also add your own variables. diff --git a/samples/da-ITHelpdesk/m365agents.yml b/samples/da-ITHelpdesk/m365agents.yml new file mode 100644 index 000000000..5797cd546 --- /dev/null +++ b/samples/da-ITHelpdesk/m365agents.yml @@ -0,0 +1,97 @@ +# yaml-language-server: $schema=https://aka.ms/teams-toolkit/v1.8/yaml.schema.json +# Visit https://aka.ms/teamsfx-v5.0-guide for details on this file +# Visit https://aka.ms/teamsfx-actions for details on actions +version: v1.8 +additionalMetadata: + sampleTag: pnp-copilot-pro-dev:da-ITHelpdesk +environmentFolderPath: ./env + +# Triggered when 'teamsapp provision' is executed +provision: + # Creates an app + - uses: teamsApp/create + with: + # app name + name: ITHelpdesk${{APP_NAME_SUFFIX}} + # Write the information of created resources into environment file for + # the specified environment variable(s). + writeToEnvironmentFile: + teamsAppId: TEAMS_APP_ID + + - uses: oauth/register + with: + name: oAuth2AuthCode + flow: authorizationCode + # app ID + appId: ${{TEAMS_APP_ID}} + # Path to OpenAPI description document + apiSpecPath: ./appPackage/apiSpecificationFile/openapi.yaml + # Use below property to change token exchange behaviour, BasicAuthorizationHeader: token exchange is done via HTTP headers. PostRequestBody: token exchange is done via request body + # tokenExchangeMethodType: BasicAuthorizationHeader + # Uncomment below property to use proof key for code exchange (PKCE) + # isPKCEEnabled: true + writeToEnvironmentFile: + configurationId: OAUTH2AUTHCODE_REGISTRATION_ID + + # Build app package with latest env value + - uses: teamsApp/zipAppPackage + with: + # Path to manifest template + manifestPath: ./appPackage/manifest.json + outputZipPath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + outputFolder: ./appPackage/build + # Validate app package using validation rules + - uses: teamsApp/validateAppPackage + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + # Apply the app manifest to an existing app in + # Developer Portal. + # Will use the app id in manifest file to determine which app to update. + - uses: teamsApp/update + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + # Extend your app to Outlook and the Microsoft 365 app + - uses: teamsApp/extendToM365 + with: + # Relative path to the build app package. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + # Write the information of created resources into environment file for + # the specified environment variable(s). + writeToEnvironmentFile: + titleId: M365_TITLE_ID + appId: M365_APP_ID + +# Triggered when 'teamsapp publish' is executed +publish: + # Build app package with latest env value + - uses: teamsApp/zipAppPackage + with: + # Path to manifest template + manifestPath: ./appPackage/manifest.json + outputZipPath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + outputFolder: ./appPackage/build + # Validate app package using validation rules + - uses: teamsApp/validateAppPackage + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + # Apply the app manifest to an existing app in + # Developer Portal. + # Will use the app id in manifest file to determine which app to update. + - uses: teamsApp/update + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + # Publish the app to + # Teams Admin Center (https://admin.teams.microsoft.com/policies/manage-apps) + # for review and approval + - uses: teamsApp/publishAppPackage + with: + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + # Write the information of created resources into environment file for + # the specified environment variable(s). + writeToEnvironmentFile: + publishedAppId: TEAMS_APP_PUBLISHED_APP_ID +