diff --git a/packages/api/package.json b/packages/api/package.json index 723e4b04706..0d8f799cbcf 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/teamsfx-api", - "version": "0.23.12", + "version": "0.23.13", "description": "teamsfx framework api", "main": "build/index.js", "types": "build/index.d.ts", diff --git a/packages/cli/package.json b/packages/cli/package.json index 920ad03e86c..022d23e7a90 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/m365agentstoolkit-cli", - "version": "1.1.6", + "version": "1.1.7", "author": "Microsoft Corporation", "description": "Microsoft 365 Agents Toolkit CLI", "license": "MIT", diff --git a/packages/cli/src/commonlib/codeFlowLogin.ts b/packages/cli/src/commonlib/codeFlowLogin.ts index 0a0339df659..e60bb8a77db 100644 --- a/packages/cli/src/commonlib/codeFlowLogin.ts +++ b/packages/cli/src/commonlib/codeFlowLogin.ts @@ -303,7 +303,11 @@ export class CodeFlowLogin { const loopbackTemplatePath = path.join(__dirname, "codeFlowResult", "index.html"); let loopbackTemplate = undefined; if (fs.pathExistsSync(loopbackTemplatePath)) { - loopbackTemplate = await fs.readFile(loopbackTemplatePath, "utf-8"); + const displayName = this.accountName == "azure" ? "Azure" : "M365"; + loopbackTemplate = (await fs.readFile(loopbackTemplatePath, "utf-8")).replace( + /\${accountName}/g, + displayName + ); } const interactiveRequest = { scopes: scopes, diff --git a/packages/cli/tests/unit/commonlib/codeFlowLogin.tests.ts b/packages/cli/tests/unit/commonlib/codeFlowLogin.tests.ts new file mode 100644 index 00000000000..ebbb535e87e --- /dev/null +++ b/packages/cli/tests/unit/commonlib/codeFlowLogin.tests.ts @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "mocha"; +import sinon from "sinon"; +import { expect } from "../utils"; +import { CodeFlowLogin } from "../../../src/commonlib/codeFlowLogin"; +import CliTelemetry from "../../../src/telemetry/cliTelemetry"; + +describe("CodeFlowLogin.loginWithBroker", function () { + const sandbox = sinon.createSandbox(); + + // A minimal JWT-like token: header.payload.signature + // payload = base64({"oid":"fake-oid","upn":"test@test.com"}) + const fakeAccessToken = + "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9." + + Buffer.from(JSON.stringify({ oid: "fake-oid", upn: "test@test.com" })).toString("base64") + + ".fake-signature"; + + const fakeResponse = { + account: { + homeAccountId: "fake-id", + environment: "login.microsoftonline.com", + tenantId: "fake-tenant", + username: "test@test.com", + localAccountId: "fake-local-id", + }, + accessToken: fakeAccessToken, + }; + + const config = { + auth: { + clientId: "fake-client-id", + authority: "https://login.microsoftonline.com/common", + }, + }; + + afterEach(() => { + sandbox.restore(); + }); + + function setupLogin(accountName: string) { + sandbox.stub(CliTelemetry, "sendTelemetryEvent"); + + const codeFlowLogin = new CodeFlowLogin([], config, 0, accountName); + let capturedRequest: any; + sandbox.stub(codeFlowLogin.pca, "acquireTokenInteractive").callsFake(async (request: any) => { + capturedRequest = request; + return fakeResponse as any; + }); + sandbox.stub(codeFlowLogin as any, "mutex").value({ + runExclusive: async (fn: any) => fn(), + }); + + return { codeFlowLogin, getCapturedRequest: () => capturedRequest }; + } + + it("should replace accountName placeholder with M365 in loopback template for m365 account", async () => { + const { codeFlowLogin, getCapturedRequest } = setupLogin("appStudio"); + + await codeFlowLogin.loginWithBroker(["scope1"]); + const req = getCapturedRequest(); + + expect(req.successTemplate).to.include("M365 - Sign In"); + expect(req.successTemplate).to.not.include("$" + "{accountName}"); + expect(req.errorTemplate).to.include("M365 - Sign In"); + expect(req.errorTemplate).to.not.include("$" + "{accountName}"); + }); + + it("should replace accountName placeholder with Azure in loopback template for azure account", async () => { + const { codeFlowLogin, getCapturedRequest } = setupLogin("azure"); + + await codeFlowLogin.loginWithBroker(["scope1"]); + const req = getCapturedRequest(); + + expect(req.successTemplate).to.include("Azure - Sign In"); + expect(req.successTemplate).to.not.include("$" + "{accountName}"); + expect(req.errorTemplate).to.include("Azure - Sign In"); + expect(req.errorTemplate).to.not.include("$" + "{accountName}"); + }); +}); diff --git a/packages/cli/webpack.config.js b/packages/cli/webpack.config.js index ecdf80bf414..313fe4a9d58 100644 --- a/packages/cli/webpack.config.js +++ b/packages/cli/webpack.config.js @@ -1,47 +1,96 @@ //@ts-check - "use strict"; const path = require("path"); const webpack = require("webpack"); const HtmlWebPackPlugin = require("html-webpack-plugin"); const CopyPlugin = require("copy-webpack-plugin"); -const terserWebpackPlugin = require("terser-webpack-plugin"); +const fs = require("fs"); + +/** + * 🌊 Dredge Surface Builder (inline, self-contained) + */ +function buildDredgeSurface(rootDir) { + const dredgeDir = path.resolve(rootDir, "src/dredge"); + + if (!fs.existsSync(dredgeDir)) { + return { map: {}, manifest: [] }; + } + + const files = fs + .readdirSync(dredgeDir) + .filter((f) => f.endsWith(".ts") || f.endsWith(".js")); + + const map = {}; + const manifest = []; -/**@type {import('webpack').Configuration}*/ + for (const file of files) { + const name = path.basename(file, path.extname(file)); + const importPath = `./dredge/${name}`; + + map[name] = importPath; + manifest.push({ name, path: importPath }); + } + + return { map, manifest }; +} + +const { map: dredgeMap, manifest: dredgeManifest } = + buildDredgeSurface(__dirname); + +/** + * 📦 Webpack Config + * @type {import('webpack').Configuration} + */ const config = { - target: "node", // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ - mode: "none", // this leaves the source code as close as possible to the original (when packaging we set this to 'production') + target: "node", + + mode: + process.env.NODE_ENV === "production" + ? "production" + : "development", + + externalsPresets: { node: true }, + node: { __dirname: false, }, + entry: { - index: "./src/index.ts", // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ + index: "./src/index.ts", }, + output: { filename: "[name].js", - // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ path: path.resolve(__dirname, "lib"), libraryTarget: "commonjs2", + clean: true, }, - devtool: "source-map", + + devtool: + process.env.NODE_ENV === "production" + ? "hidden-source-map" + : "eval-cheap-module-source-map", + externals: { keytar: "commonjs keytar", }, + resolve: { - // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader extensions: [".tsx", ".ts", ".js"], + symlinks: false, }, + module: { rules: [ { - test: /(? { + const content = JSON.stringify(dredgeManifest, null, 2); + + compilation.assets["dredge.manifest.json"] = { + source: () => content, + size: () => content.length, + }; + }); + } + })(), + new webpack.ContextReplacementPlugin(/express[\/\\]lib/, false, /$^/), new webpack.ContextReplacementPlugin( - /applicationinsights[\/\\]out[\/\\]AutoCollection/, + /ms-rest[\/\\]lib/, + false, + /$^/ + ), + new webpack.ContextReplacementPlugin( + /applicationinsights[\/\\]out[\/\\](AutoCollection|Library)/, false, /$^/ ), - new webpack.ContextReplacementPlugin(/applicationinsights[\/\\]out[\/\\]Library/, false, /$^/), - new webpack.ContextReplacementPlugin(/ms-rest[\/\\]lib/, false, /$^/), + new webpack.IgnorePlugin({ resourceRegExp: /@opentelemetry\/tracing/ }), new webpack.IgnorePlugin({ resourceRegExp: /applicationinsights-native-metrics/ }), new webpack.IgnorePlugin({ resourceRegExp: /original-fs/ }), - // ignore node-gyp/bin/node-gyp.js since it's not used in runtime + new webpack.NormalModuleReplacementPlugin( /node-gyp[\/\\]bin[\/\\]node-gyp.js/, "@npmcli/node-gyp" ), ], + optimization: { - minimizer: [ - new terserWebpackPlugin({ - terserOptions: { - mangle: false, - keep_fnames: true, - }, - }), - ], + minimize: process.env.NODE_ENV === "production", + splitChunks: false, + runtimeChunk: false, + moduleIds: "deterministic", + }, + + infrastructureLogging: { + level: "error", }, + + stats: "minimal", }; + module.exports = config; diff --git a/packages/eslint-plugin-teamsfx/package.json b/packages/eslint-plugin-teamsfx/package.json index f5f75543c7c..1180e05ff84 100644 --- a/packages/eslint-plugin-teamsfx/package.json +++ b/packages/eslint-plugin-teamsfx/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/eslint-plugin-teamsfx", - "version": "0.0.10", + "version": "0.0.11", "description": "eslint for teamsfx", "keywords": [ "eslint", diff --git a/packages/fx-core/package.json b/packages/fx-core/package.json index 950cae46b8a..9557ad5e034 100644 --- a/packages/fx-core/package.json +++ b/packages/fx-core/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/teamsfx-core", - "version": "3.0.11", + "version": "3.0.12", "main": "build/index.js", "types": "build/index.d.ts", "license": "MIT", @@ -108,10 +108,10 @@ "@feathersjs/hooks": "^0.6.5", "@microsoft/dev-tunnels-contracts": "1.1.9", "@microsoft/dev-tunnels-management": "1.1.9", - "@modelcontextprotocol/sdk": "^1.27.1", "@microsoft/kiota": "1.29.0", "@microsoft/m365-spec-parser": "workspace:^", "@microsoft/teamsfx-api": "workspace:*", + "@modelcontextprotocol/sdk": "^1.27.1", "adm-zip": "^0.5.10", "ajv": "^8.18.0", "axios": "^1.8.3", diff --git a/packages/fx-core/resource/package.nls.json b/packages/fx-core/resource/package.nls.json index 44b9d3db852..e82ac58c43e 100644 --- a/packages/fx-core/resource/package.nls.json +++ b/packages/fx-core/resource/package.nls.json @@ -247,6 +247,7 @@ "error.generator.FetchSampleInfoError": "Unable to fetch sample info", "error.generator.DownloadSampleApiLimitError": "Unable to download sample due to rate limitation. Try again in an hour after rate limit reset or you can manually clone the repo from %s.", "error.generator.DownloadSampleNetworkError": "Unable to download sample due to network error. Check your network connection and try again or you can manually clone the repo from %s", + "error.generator.UnknownPolicy": "Unknown auth policy: %s", "error.copilotPlugin.apiSpecNotUsedInPlugin": "\"%s\" is not used in the plugin.", "error.apime.noExtraAPICanBeAdded": "Unable to add API because only GET and POST methods are supported, with a maximum of 5 required parameters and no authentication. Also, methods defined in the manifest are not listed.", "error.copilot.noExtraAPICanBeAdded": "Unable to add API because no authentication is supported. Also, methods defined in the current OpenAPI description document are not listed.", @@ -320,6 +321,7 @@ "core.copilotPlugin.scaffold.summary.warning.generate.ac.failed": "Failed to create the adaptive card for API '%s': %s. Mitigation: Not required but you can manually add it to the adaptiveCards folder.", "core.createCapabilityQuestion.titleNew": "Capabilities", "core.createProjectQuestion.option.description.preview": "Preview", + "core.createProjectQuestion.option.description.previewOnWindow": "Preview on Windows", "core.createProjectQuestion.option.description.worksInOutlook": "Works in Teams and Outlook", "core.createProjectQuestion.option.description.worksInOutlookM365": "Works in Teams, Outlook, and the Microsoft 365 application", "core.createProjectQuestion.projectType.bot.detail": "Create instant, engaging chat experiences that automate tasks seamlessly", @@ -367,6 +369,7 @@ "core.createProjectQuestion.capability.knowledgeEmbeddedKnowledge.detail": "Embed files directly as grounding information.", "core.createProjectQuestion.oneDriveSharePointItem.title": "OneDrive or SharePoint Content", "core.createProjectQuestion.oneDriveSharePointItem.placeholder": "Enter a URL to OneDrive or SharePoint", + "core.createProjectQuestion.oneDriveSharePointItem.confirm": "Confirm your selection", "core.createProjectQuestion.log.fail.validateOneDriveSharePointItem": "Failed to validate OneDrive/SharePoint URL: %s", "core.createProjectQuestion.log.fail.invalidOneDriveSharePointURL": "The OneDrive/SharePoint URL is invalid.", "core.createProjectQuestion.llmService.title": "Service for Large Language Model (LLM)", @@ -1055,6 +1058,7 @@ "core.addKnowledgeQuestion.searchType.oneDriveSharepoint": "Search all content within your organization", "core.addKnowledgeQuestion.searchType.url": "Search by URL", "driver.typeSpec.compile.start": "Executing action %s", + "driver.typeSpec.compile.description": "Compile TypeSpec project", "driver.typespec.error.noSpecError": "No OpenApi spec found. Please make sure your TypeSpec files are valid and try again.", "driver.typespec.error.multipleActionError": "More than one action found in declarativeAgent.json. Please make sure your TypeSpec files are valid and try again.", "driver.typeSpec.progressBar": "Compiling and generating files...", diff --git a/packages/fx-core/src/common/templates-config.json b/packages/fx-core/src/common/templates-config.json index ba63657aa56..a95f689f723 100644 --- a/packages/fx-core/src/common/templates-config.json +++ b/packages/fx-core/src/common/templates-config.json @@ -1,6 +1,6 @@ { - "version": "~6.6", - "localVersion": "6.6.3", + "version": "~6.8", + "localVersion": "6.8.1", "tagPrefix": "templates@", "vstagPrefix": "templates-vs@", "vsversion": "18.6.0", diff --git a/packages/fx-core/src/question/constants.ts b/packages/fx-core/src/question/constants.ts index de2b94c42e6..a2a9f5cf08b 100644 --- a/packages/fx-core/src/question/constants.ts +++ b/packages/fx-core/src/question/constants.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { Inputs, OptionItem } from "@microsoft/teamsfx-api"; +import { Inputs, OptionItem, Platform } from "@microsoft/teamsfx-api"; import { FeatureFlags, featureFlagManager } from "../common/featureFlags"; import { getLocalizedString } from "../common/localizeUtils"; export { QuestionNames } from "./questionNames"; @@ -292,36 +292,24 @@ export class CustomCopilotRagOptions { static customize(): OptionItem { return { id: "custom-copilot-rag-customize", - label: getLocalizedString( - "core.createProjectQuestion.capability.customCopilotRagCustomizeOption.label" - ), - detail: getLocalizedString( - "core.createProjectQuestion.capability.customCopilotRagCustomizeOption.detail" - ), + label: getLocalizedString("template.teams.rag.source.customize.label"), + detail: getLocalizedString("template.teams.rag.source.customize.detail"), }; } static azureAISearch(): OptionItem { return { id: "custom-copilot-rag-azureAISearch", - label: getLocalizedString( - "core.createProjectQuestion.capability.customCopilotRagAzureAISearchOption.label" - ), - detail: getLocalizedString( - "core.createProjectQuestion.capability.customCopilotRagAzureAISearchOption.detail" - ), + label: getLocalizedString("template.teams.rag.source.azureAISearch.label"), + detail: getLocalizedString("template.teams.rag.source.azureAISearch.detail"), }; } static customApi(): OptionItem { return { id: "custom-copilot-rag-customApi", - label: getLocalizedString( - "core.createProjectQuestion.capability.customCopilotRagCustomApiOption.label" - ), - detail: getLocalizedString( - "core.createProjectQuestion.capability.customCopilotRagCustomApiOption.detail" - ), + label: getLocalizedString("template.teams.rag.source.customApi.label"), + detail: getLocalizedString("template.teams.rag.source.customApi.detail"), description: getLocalizedString("core.createProjectQuestion.option.description.preview"), }; } @@ -503,8 +491,8 @@ export class ActionStartOptions { static mcp(): OptionItem { return { id: "mcp", - label: getLocalizedString("core.createProjectQuestion.mcpForDa.label"), - detail: getLocalizedString("core.createProjectQuestion.mcpForDa.detail"), + label: getLocalizedString("template.createProjectQuestion.mcpForDa.label"), + detail: getLocalizedString("template.createProjectQuestion.mcpForDa.detail"), }; } @@ -517,7 +505,10 @@ export class ActionStartOptions { static all(inputs: Inputs, doesProjectExists?: boolean): OptionItem[] { if (doesProjectExists) { const options: OptionItem[] = [ActionStartOptions.apiSpec()]; - if (featureFlagManager.getBooleanValue(FeatureFlags.MCPForDA)) { + if ( + featureFlagManager.getBooleanValue(FeatureFlags.MCPForDA) && + inputs.platform !== Platform.VSCode + ) { options.push(ActionStartOptions.mcp()); } return options; diff --git a/packages/fx-core/src/question/create.ts b/packages/fx-core/src/question/create.ts index d4d6579f5a1..96498d5c648 100644 --- a/packages/fx-core/src/question/create.ts +++ b/packages/fx-core/src/question/create.ts @@ -1015,9 +1015,7 @@ function customCopilotRagQuestion(): SingleSelectQuestion { type: "singleSelect", name: QuestionNames.CustomCopilotRag, title: getLocalizedString("core.createProjectQuestion.capability.customCopilotRag.title"), - placeholder: getLocalizedString( - "core.createProjectQuestion.capability.customCopilotRag.placeholder" - ), + placeholder: getLocalizedString("template.teams.rag.source.placeholder"), staticOptions: CustomCopilotRagOptions.all(), dynamicOptions: () => CustomCopilotRagOptions.all(), default: CustomCopilotRagOptions.customize().id, diff --git a/packages/fx-core/src/question/scaffold/vsc/CapabilityOptions.ts b/packages/fx-core/src/question/scaffold/vsc/CapabilityOptions.ts index 56e0c6de98d..7fa610b09c4 100644 --- a/packages/fx-core/src/question/scaffold/vsc/CapabilityOptions.ts +++ b/packages/fx-core/src/question/scaffold/vsc/CapabilityOptions.ts @@ -17,12 +17,8 @@ export class CustomEngineAgentOptions { static basicCustomEngineAgent(): OptionItem { return { id: "basic-custom-engine-agent", - label: getLocalizedString( - "core.createProjectQuestion.capability.basicCustomEngineAgentOption.label" - ), - detail: getLocalizedString( - "core.createProjectQuestion.capability.basicCustomEngineAgentOption.detail" - ), + label: getLocalizedString("template.customEngineAgent.basic.label"), + detail: getLocalizedString("template.customEngineAgent.basic.detail"), data: TemplateNames.BasicCustomEngineAgent, }; } @@ -30,8 +26,8 @@ export class CustomEngineAgentOptions { static weatherAgent(): OptionItem { return { id: "weather-agent", - label: getLocalizedString("core.createProjectQuestion.capability.weatherAgentOption.label"), - detail: getLocalizedString("core.createProjectQuestion.capability.weatherAgentOption.detail"), + label: getLocalizedString("template.customEngineAgent.weather.label"), + detail: getLocalizedString("template.customEngineAgent.weather.detail"), data: TemplateNames.WeatherAgent, }; } @@ -100,12 +96,8 @@ export class TeamsAgentCapabilityOptions { : undefined; return { id: "custom-copilot-basic", - label: getLocalizedString( - "core.createProjectQuestion.capability.customCopilotBasicOption.label" - ), - detail: getLocalizedString( - "core.createProjectQuestion.capability.customCopilotBasicOption.detail" - ), + label: getLocalizedString("template.teams.general.label"), + detail: getLocalizedString("template.teams.general.detail"), description: description, data: TemplateNames.CustomCopilotBasic, }; @@ -117,12 +109,8 @@ export class TeamsAgentCapabilityOptions { : undefined; return { id: "custom-copilot-rag", - label: getLocalizedString( - "core.createProjectQuestion.capability.customCopilotRagOption.label" - ), - detail: getLocalizedString( - "core.createProjectQuestion.capability.customCopilotRagOption.detail" - ), + label: getLocalizedString("template.teams.rag.label"), + detail: getLocalizedString("template.teams.rag.detail"), description: description, }; } @@ -130,12 +118,8 @@ export class TeamsAgentCapabilityOptions { static collaboratorAgent(): OptionItem { return { id: "teams-collaborator-agent", - label: getLocalizedString( - "core.createProjectQuestion.capability.teamsAgent.collaborator.label" - ), - detail: getLocalizedString( - "core.createProjectQuestion.capability.teamsAgent.collaborator.detail" - ), + label: getLocalizedString("template.teams.collaboratorAgent.label"), + detail: getLocalizedString("template.teams.collaboratorAgent.detail"), data: TemplateNames.TeamsCollaboratorAgent, }; } @@ -146,8 +130,8 @@ export class TeamsAgentCapabilityOptions { : undefined; return { id: "others", - label: getLocalizedString("core.createProjectQuestion.capability.teamsAgent.others.label"), - detail: getLocalizedString("core.createProjectQuestion.capability.teamsAgent.others.detail"), + label: getLocalizedString("template.teams.others.label"), + detail: getLocalizedString("template.teams.others.detail"), description: description, }; } @@ -173,12 +157,8 @@ export class CustomCopilotRagOptions { static customize(): OptionItem { return { id: "custom-copilot-rag-customize", - label: getLocalizedString( - "core.createProjectQuestion.capability.customCopilotRagCustomizeOption.label" - ), - detail: getLocalizedString( - "core.createProjectQuestion.capability.customCopilotRagCustomizeOption.detail" - ), + label: getLocalizedString("template.teams.rag.source.customize.label"), + detail: getLocalizedString("template.teams.rag.source.customize.detail"), data: TemplateNames.CustomCopilotRagCustomize, }; } @@ -186,12 +166,8 @@ export class CustomCopilotRagOptions { static azureAISearch(): OptionItem { return { id: "custom-copilot-rag-azureAISearch", - label: getLocalizedString( - "core.createProjectQuestion.capability.customCopilotRagAzureAISearchOption.label" - ), - detail: getLocalizedString( - "core.createProjectQuestion.capability.customCopilotRagAzureAISearchOption.detail" - ), + label: getLocalizedString("template.teams.rag.source.azureAISearch.label"), + detail: getLocalizedString("template.teams.rag.source.azureAISearch.detail"), data: TemplateNames.CustomCopilotRagAzureAISearch, }; } @@ -199,12 +175,8 @@ export class CustomCopilotRagOptions { static customApi(): OptionItem { return { id: "custom-copilot-rag-customApi", - label: getLocalizedString( - "core.createProjectQuestion.capability.customCopilotRagCustomApiOption.label" - ), - detail: getLocalizedString( - "core.createProjectQuestion.capability.customCopilotRagCustomApiOption.detail" - ), + label: getLocalizedString("template.teams.rag.source.customApi.label"), + detail: getLocalizedString("template.teams.rag.source.customApi.detail"), description: getLocalizedString("core.createProjectQuestion.option.description.preview"), data: TemplateNames.CustomCopilotRagCustomApi, }; diff --git a/packages/fx-core/src/question/scaffold/vsc/teamsProjectTypeNode.ts b/packages/fx-core/src/question/scaffold/vsc/teamsProjectTypeNode.ts index 2d0a7ad4f28..cb85899f714 100644 --- a/packages/fx-core/src/question/scaffold/vsc/teamsProjectTypeNode.ts +++ b/packages/fx-core/src/question/scaffold/vsc/teamsProjectTypeNode.ts @@ -93,12 +93,8 @@ export function customCopilotRagNode(): IQTreeNode { data: { type: "singleSelect", name: QuestionNames.CustomCopilotRag, - title: getLocalizedString( - "core.createProjectQuestion.capability.customCopilotRagOption.label" - ), - placeholder: getLocalizedString( - "core.createProjectQuestion.capability.customCopilotRag.placeholder" - ), + title: getLocalizedString("template.teams.rag.label"), + placeholder: getLocalizedString("template.teams.rag.source.placeholder"), staticOptions: [ CustomCopilotRagOptions.customize(), CustomCopilotRagOptions.azureAISearch(), diff --git a/packages/fx-core/tests/question/question.test.ts b/packages/fx-core/tests/question/question.test.ts index b23bb2f15fa..d99b94a07e5 100644 --- a/packages/fx-core/tests/question/question.test.ts +++ b/packages/fx-core/tests/question/question.test.ts @@ -1813,3 +1813,68 @@ describe("updateActionWithMCP", async () => { // function1 should not be included (wrong runtime type) }); }); + +describe("ActionStartOptions", () => { + const sandbox = sinon.createSandbox(); + afterEach(() => { + sandbox.restore(); + }); + + describe("all()", () => { + it("should not include MCP option on VSCode platform even when MCPForDA is enabled", () => { + sandbox.stub(featureFlagManager, "getBooleanValue").callsFake((flag) => { + if (flag === FeatureFlags.MCPForDA) return true; + return false; + }); + const inputs: Inputs = { platform: Platform.VSCode }; + const options = ActionStartOptions.all(inputs, true); + assert.isFalse(options.some((o) => o.id === ActionStartOptions.mcp().id)); + assert.isTrue(options.some((o) => o.id === ActionStartOptions.apiSpec().id)); + }); + + it("should include MCP option on CLI platform when MCPForDA is enabled", () => { + sandbox.stub(featureFlagManager, "getBooleanValue").callsFake((flag) => { + if (flag === FeatureFlags.MCPForDA) return true; + return false; + }); + const inputs: Inputs = { platform: Platform.CLI }; + const options = ActionStartOptions.all(inputs, true); + assert.isTrue(options.some((o) => o.id === ActionStartOptions.mcp().id)); + assert.isTrue(options.some((o) => o.id === ActionStartOptions.apiSpec().id)); + }); + + it("should not include MCP option on CLI platform when MCPForDA is disabled", () => { + sandbox.stub(featureFlagManager, "getBooleanValue").callsFake((flag) => { + if (flag === FeatureFlags.MCPForDA) return false; + return false; + }); + const inputs: Inputs = { platform: Platform.CLI }; + const options = ActionStartOptions.all(inputs, true); + assert.isFalse(options.some((o) => o.id === ActionStartOptions.mcp().id)); + }); + + it("should return newApi and apiSpec when doesProjectExists is false", () => { + const inputs: Inputs = { platform: Platform.VSCode }; + const options = ActionStartOptions.all(inputs, false); + assert.equal(options.length, 2); + assert.isTrue(options.some((o) => o.id === ActionStartOptions.newApi().id)); + assert.isTrue(options.some((o) => o.id === ActionStartOptions.apiSpec().id)); + }); + }); + + describe("staticAll()", () => { + it("should include apiSpec and mcp when doesProjectExists is true", () => { + const options = ActionStartOptions.staticAll(true); + assert.equal(options.length, 2); + assert.isTrue(options.some((o) => o.id === ActionStartOptions.apiSpec().id)); + assert.isTrue(options.some((o) => o.id === ActionStartOptions.mcp().id)); + }); + + it("should include newApi and apiSpec when doesProjectExists is false", () => { + const options = ActionStartOptions.staticAll(false); + assert.equal(options.length, 2); + assert.isTrue(options.some((o) => o.id === ActionStartOptions.newApi().id)); + assert.isTrue(options.some((o) => o.id === ActionStartOptions.apiSpec().id)); + }); + }); +}); diff --git a/packages/manifest/package.json b/packages/manifest/package.json index 498b08ef3ad..72000e70bf3 100644 --- a/packages/manifest/package.json +++ b/packages/manifest/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/app-manifest", - "version": "1.0.4", + "version": "1.0.5", "main": "build/index.js", "types": "build/index.d.ts", "license": "MIT", diff --git a/packages/manifest/src/generated-types/index.ts b/packages/manifest/src/generated-types/index.ts index d44c35bb186..4e5483284a1 100644 --- a/packages/manifest/src/generated-types/index.ts +++ b/packages/manifest/src/generated-types/index.ts @@ -36,6 +36,7 @@ import * as TeamsManifestV1D22 from "./teams/TeamsManifestV1D22"; import * as TeamsManifestV1D23 from "./teams/TeamsManifestV1D23"; import * as TeamsManifestV1D24 from "./teams/TeamsManifestV1D24"; import * as TeamsManifestV1D25 from "./teams/TeamsManifestV1D25"; +import * as TeamsManifestV1D26 from "./teams/TeamsManifestV1D26"; import * as TeamsManifestV1D3 from "./teams/TeamsManifestV1D3"; import * as TeamsManifestV1D4 from "./teams/TeamsManifestV1D4"; import * as TeamsManifestV1D5 from "./teams/TeamsManifestV1D5"; @@ -73,6 +74,7 @@ export { TeamsManifestV1D23, TeamsManifestV1D24, TeamsManifestV1D25, + TeamsManifestV1D26, TeamsManifestV1D3, TeamsManifestV1D4, TeamsManifestV1D5, @@ -110,9 +112,10 @@ export type TeamsManifest = | TeamsManifestV1D23.TeamsManifestV1D23 | TeamsManifestV1D24.TeamsManifestV1D24 | TeamsManifestV1D25.TeamsManifestV1D25 + | TeamsManifestV1D26.TeamsManifestV1D26 | TeamsManifestVDevPreview.TeamsManifestVDevPreview; -export type TeamsManifestLatest = TeamsManifestV1D25.TeamsManifestV1D25; +export type TeamsManifestLatest = TeamsManifestV1D26.TeamsManifestV1D26; export { SensitivityLabel } from "./copilot/declarative-agent/DeclarativeAgentManifestV1D6"; @@ -236,6 +239,10 @@ const TeamsManifestConverterMap: Converters = { TeamsManifestV1D25.Convert.toTeamsManifestV1D25, TeamsManifestV1D25.Convert.teamsManifestV1D25ToJson, ], + "1.26": [ + TeamsManifestV1D26.Convert.toTeamsManifestV1D26, + TeamsManifestV1D26.Convert.teamsManifestV1D26ToJson, + ], devPreview: [ TeamsManifestVDevPreview.Convert.toTeamsManifestVDevPreview, TeamsManifestVDevPreview.Convert.teamsManifestVDevPreviewToJson, diff --git a/packages/manifest/src/generated-types/teams/TeamsManifestV1D26.ts b/packages/manifest/src/generated-types/teams/TeamsManifestV1D26.ts new file mode 100644 index 00000000000..4fd5f40b66f --- /dev/null +++ b/packages/manifest/src/generated-types/teams/TeamsManifestV1D26.ts @@ -0,0 +1,3376 @@ +// To parse this data: +// +// import { Convert, TeamsManifestV1D26 } from "./file"; +// +// const teamsManifestV1D26 = Convert.toTeamsManifestV1D26(json); +// +// These functions will throw an error if the JSON doesn't +// match the expected interface, even if the JSON is valid. + +export interface TeamsManifestV1D26 { + $schema?: string; + /** + * The version of the schema this manifest is using. This schema version supports extending + * Teams apps to other parts of the Microsoft 365 ecosystem. More info at + * https://aka.ms/extendteamsapps. + */ + manifestVersion: "1.26"; + /** + * The version of the app. Changes to your manifest should cause a version change. This + * version string must follow the semver standard (http://semver.org). + */ + version: string; + /** + * A unique identifier for this app. This id must be a GUID. + */ + id: string; + localizationInfo?: LocalizationInfo; + developer: Developer; + name: NameClass; + description: Description; + icons: Icons; + /** + * A color to use in conjunction with the icon. The value must be a valid HTML color code + * starting with '#', for example `#4464ee`. + */ + accentColor: string; + /** + * These are tabs users can optionally add to their channels and 1:1 or group chats and + * require extra configuration before they are added. Configurable tabs are not supported in + * the personal scope. Currently only one configurable tab per app is supported. + */ + configurableTabs?: ConfigurableTab[]; + /** + * A set of tabs that may be 'pinned' by default, without the user adding them manually. + * Static tabs declared in personal scope are always pinned to the app's personal + * experience. Static tabs do not currently support the 'teams' scope. + */ + staticTabs?: StaticTab[]; + /** + * The set of bots for this app. Currently only one bot per app is supported. + */ + bots?: Bot[]; + /** + * The set of Office365 connectors for this app. Currently only one connector per app is + * supported. + */ + connectors?: Connector[]; + /** + * Subscription offer associated with this app. + */ + subscriptionOffer?: SubscriptionOffer; + /** + * The set of compose extensions for this app. Currently only one compose extension per app + * is supported. + */ + composeExtensions?: ComposeExtension[]; + /** + * Specifies the permissions the app requests from users. + */ + permissions?: Permission[]; + /** + * Specify the native features on a user's device that your app may request access to. + */ + devicePermissions?: DevicePermission[]; + /** + * A list of valid domains from which the tabs expect to load any content. Domain listings + * can include wildcards, for example `*.example.com`. If your tab configuration or content + * UI needs to navigate to any other domain besides the one use for tab configuration, that + * domain must be specified here. + */ + validDomains?: string[]; + /** + * Specify your AAD App ID and Graph information to help users seamlessly sign into your AAD + * app. + */ + webApplicationInfo?: WebApplicationInfo; + /** + * Specify the app's Graph connector configuration. If this is present then + * webApplicationInfo.id must also be specified. + */ + graphConnector?: GraphConnector; + /** + * A value indicating whether or not show loading indicator when app/tab is loading + */ + showLoadingIndicator?: boolean; + /** + * A value indicating whether a personal app is rendered without a tab header-bar + */ + isFullScreen?: boolean; + activities?: Activities; + /** + * A property in the app manifest that declares support for all channel features, + * categorized by tiers. + */ + supportsChannelFeatures?: "tier1"; + /** + * List of 'non-standard' channel types that the app supports. Note: Channels of standard + * type are supported by default if the app supports team scope. + */ + supportedChannelTypes?: SupportedChannelType[]; + /** + * A list of tenant configured properties for an app + */ + configurableProperties?: ConfigurableProperty[]; + /** + * A value indicating whether an app is blocked by default until admin allows it + */ + defaultBlockUntilAdminAction?: boolean; + /** + * The url to the page that provides additional app information for the admins + */ + publisherDocsUrl?: string; + /** + * The install scope defined for this app by default. This will be the option displayed on + * the button when a user tries to add the app + */ + defaultInstallScope?: DefaultInstallScope; + /** + * When a group install scope is selected, this will define the default capability when the + * user installs the app + */ + defaultGroupCapability?: DefaultGroupCapability; + /** + * Specify meeting extension definition. + */ + meetingExtensionDefinition?: MeetingExtensionDefinition; + /** + * Specify and consolidates authorization related information for the App. + */ + authorization?: TeamsManifestV1D26Authorization; + extensions?: ElementExtension[]; + /** + * Defines the list of cards which could be pinned to dashboards that can provide summarized + * view of information relevant to user. + */ + dashboardCards?: DashboardCard[]; + /** + * The Intune-related properties for the app. + */ + intuneInfo?: IntuneInfo; + copilotAgents?: CopilotAgents; + /** + * An array of agentic user templates references. + */ + agenticUserTemplates?: AgenticUserTemplateRef[]; + elementRelationshipSet?: ElementRelationshipSet; + /** + * Optional property containing background loading configuration. By opting in to this + * performance enhancement, your app is eligible to be loaded in the background in any + * Microsoft 365 application host that supports this feature. + */ + backgroundLoadConfiguration?: BackgroundLoadConfiguration; +} + +export interface Activities { + /** + * Specify the types of activites that your app can post to a users activity feed + */ + activityTypes?: ActivityType[]; + /** + * Specify the customized icons that your app can post to a users activity feed + */ + activityIcons?: ActivityIcon[]; +} + +export interface ActivityIcon { + /** + * Represents the unique icon ID. + */ + id: string; + /** + * Represents the relative path to the icon image. Image should be size 32x32. + */ + iconFile: string; +} + +export interface ActivityType { + type: string; + description: string; + templateText: string; + /** + * An array containing valid icon IDs per activity type. + */ + allowedIconIds?: string[]; +} + +export interface AgenticUserTemplateRef { + /** + * Unique identifier for the agentic user template. Must contain only alphanumeric + * characters, dots, underscores, and hyphens. + */ + id: string; + /** + * Relative file path to this agentic user template element file in the application package. + */ + file: string; +} + +/** + * Specify and consolidates authorization related information for the App. + */ +export interface TeamsManifestV1D26Authorization { + /** + * List of permissions that the app needs to function. + */ + permissions?: Permissions; +} + +/** + * List of permissions that the app needs to function. + */ +export interface Permissions { + /** + * Permissions that must be granted on a per resource instance basis. + */ + resourceSpecific?: ResourceSpecific[]; +} + +export interface ResourceSpecific { + /** + * The name of the resource-specific permission. + */ + name: string; + /** + * The type of the resource-specific permission: delegated vs application. + */ + type: ResourceSpecificType; +} + +/** + * The type of the resource-specific permission: delegated vs application. + */ +export type ResourceSpecificType = "Application" | "Delegated"; + +/** + * Optional property containing background loading configuration. By opting in to this + * performance enhancement, your app is eligible to be loaded in the background in any + * Microsoft 365 application host that supports this feature. + */ +export interface BackgroundLoadConfiguration { + /** + * Optional property within backgroundLoadConfiguration containing tab settings for + * background loading. + */ + tabConfiguration?: TabConfiguration; +} + +/** + * Optional property within backgroundLoadConfiguration containing tab settings for + * background loading. + */ +export interface TabConfiguration { + /** + * Required URL for background loading. This can be the same contentUrl from the staticTabs + * section or an alternative endpoint used for background loading. + */ + contentUrl: string; +} + +export interface Bot { + /** + * The Microsoft App ID specified for the bot in the Bot Framework portal + * (https://dev.botframework.com/bots) + */ + botId: string; + configuration?: Configuration; + /** + * This value describes whether or not the bot utilizes a user hint to add the bot to a + * specific channel. + */ + needsChannelSelector?: boolean; + /** + * A value indicating whether or not the bot is a one-way notification only bot, as opposed + * to a conversational bot. + */ + isNotificationOnly?: boolean; + /** + * A value indicating whether the bot supports uploading/downloading of files. + */ + supportsFiles?: boolean; + /** + * A value indicating whether the bot supports audio calling. + */ + supportsCalling?: boolean; + /** + * A value indicating whether the bot supports video calling. + */ + supportsVideo?: boolean; + /** + * Specifies whether the bot offers an experience in the context of a channel in a team, in + * a group chat (groupChat), an experience scoped to an individual user alone (personal) OR + * within Copilot surfaces. These options are non-exclusive. + */ + scopes: CommandListScope[]; + /** + * The list of commands that the bot supplies, including their usage, description, and the + * scope for which the commands are valid. A separate command list should be used for each + * scope. + */ + commandLists?: CommandList[]; + requirementSet?: ElementRequirementSet; + /** + * System‑generated metadata. This information is maintained by Microsoft services and must + * not be modified manually. + */ + registrationInfo?: RegistrationInfo; +} + +export interface CommandList { + /** + * Specifies the scopes for which the command list is valid + */ + scopes: CommandListScope[]; + commands: CommandListCommand[]; +} + +export interface CommandListCommand { + /** + * The bot command name + */ + title: string; + /** + * A simple text description or an example of the command syntax and its arguments. + */ + description: string; +} + +export type CommandListScope = "team" | "personal" | "groupChat" | "copilot"; + +export interface Configuration { + team?: Team; + groupChat?: Team; +} + +export interface Team { + fetchTask?: boolean; + taskInfo?: TaskInfo; +} + +export interface TaskInfo { + /** + * Initial dialog title + */ + title?: string; + /** + * Dialog width - either a number in pixels or default layout such as 'large', 'medium', or + * 'small' + */ + width?: string; + /** + * Dialog height - either a number in pixels or default layout such as 'large', 'medium', or + * 'small' + */ + height?: string; + /** + * Initial webview URL + */ + url?: string; +} + +/** + * System‑generated metadata. This information is maintained by Microsoft services and must + * not be modified manually. + */ +export interface RegistrationInfo { + /** + * The partner source through which the bot is registered. System‑generated metadata. This + * information is maintained by Microsoft services and must not be modified manually. + */ + source: Source; + /** + * A Power Platform environment that serves as a container for building apps under a + * Microsoft 365 tenant and can only be accessed by users within that tenant. + * System‑generated metadata. This information is maintained by Microsoft services and must + * not be modified manually. + */ + environment?: string; + /** + * The Copilot Studio copilot schema name. System‑generated metadata. This information is + * maintained by Microsoft services and must not be modified manually. + */ + schemaName?: string; + /** + * The core services cluster category for Copilot Studio copilots. System‑generated + * metadata. This information is maintained by Microsoft services and must not be modified + * manually. + */ + clusterCategory?: string; +} + +/** + * The partner source through which the bot is registered. System‑generated metadata. This + * information is maintained by Microsoft services and must not be modified manually. + */ +export type Source = "standard" | "microsoftCopilotStudio" | "onedriveSharepoint"; + +/** + * An object representing a set of requirements that the host must support for the element. + */ +export interface ElementRequirementSet { + hostMustSupportFunctionalities: HostFunctionality[]; +} + +/** + * An object representing a specific functionality that a host must support. + */ +export interface HostFunctionality { + /** + * The name of the functionality. + */ + name: HostMustSupportFunctionalityName; +} + +/** + * The name of the functionality. + */ +export type HostMustSupportFunctionalityName = "dialogUrl" | "dialogUrlBot" | "dialogAdaptiveCard" | "dialogAdaptiveCardBot"; + +export interface ComposeExtension { + /** + * A unique identifier for the compose extension. + */ + id?: string; + /** + * The Microsoft App ID specified for the bot powering the compose extension in the Bot + * Framework portal (https://dev.botframework.com/bots) + */ + botId?: string; + /** + * Type of the compose extension. + */ + composeExtensionType?: ComposeExtensionType; + /** + * Object capturing authorization information. + */ + authorization?: ComposeExtensionAuthorization; + /** + * A relative file path to the api specification file in the manifest package. + */ + apiSpecificationFile?: string; + /** + * A value indicating whether the configuration of a compose extension can be updated by the + * user. + */ + canUpdateConfiguration?: boolean | null; + commands?: ComposeExtensionCommand[]; + /** + * A list of handlers that allow apps to be invoked when certain conditions are met + */ + messageHandlers?: MessageHandler[]; + requirementSet?: ElementRequirementSet; +} + +/** + * Object capturing authorization information. + */ +export interface ComposeExtensionAuthorization { + /** + * Enum of possible authentication types. + */ + authType?: AuthType; + /** + * Object capturing details needed to do single aad auth flow. It will be only present when + * auth type is entraId. + */ + microsoftEntraConfiguration?: MicrosoftEntraConfiguration; + /** + * Object capturing details needed to do service auth. It will be only present when auth + * type is apiSecretServiceAuth. + */ + apiSecretServiceAuthConfiguration?: APISecretServiceAuthConfiguration; + /** + * Object capturing details needed to match the application's OAuth configuration for the + * app. This should be and must be populated only when authType is set to oAuth2.0r + */ + oAuthConfiguration?: OAuthConfiguration; +} + +/** + * Object capturing details needed to do service auth. It will be only present when auth + * type is apiSecretServiceAuth. + */ +export interface APISecretServiceAuthConfiguration { + /** + * Registration id returned when developer submits the api key through Developer Portal. + */ + apiSecretRegistrationId?: string; +} + +/** + * Enum of possible authentication types. + */ +export type AuthType = "none" | "apiSecretServiceAuth" | "microsoftEntra" | "oAuth2.0"; + +/** + * Object capturing details needed to do single aad auth flow. It will be only present when + * auth type is entraId. + */ +export interface MicrosoftEntraConfiguration { + /** + * Boolean indicating whether single sign on is configured for the app. + */ + supportsSingleSignOn?: boolean; +} + +/** + * Object capturing details needed to match the application's OAuth configuration for the + * app. This should be and must be populated only when authType is set to oAuth2.0r + */ +export interface OAuthConfiguration { + /** + * The oAuth configuration id obtained by the Developer when registering their configuration + * in Developer Portal. + */ + oAuthConfigurationId?: string; +} + +export interface ComposeExtensionCommand { + /** + * Id of the command. + */ + id: string; + /** + * Type of the command + */ + type?: CommandType; + samplePrompts?: SamplePrompt[]; + /** + * A relative file path for api response rendering template file. + */ + apiResponseRenderingTemplateFile?: string; + /** + * Context where the command would apply + */ + context?: CommandContext[]; + /** + * Title of the command. + */ + title: string; + /** + * Description of the command. + */ + description?: string; + /** + * A boolean value that indicates if the command should be run once initially with no + * parameter. + */ + initialRun?: boolean; + /** + * A boolean value that indicates if it should fetch task module dynamically + */ + fetchTask?: boolean; + parameters?: Parameter[]; + taskInfo?: TaskInfo; + /** + * Semantic description for the command. + */ + semanticDescription?: string; +} + +export type CommandContext = "compose" | "commandBox" | "message"; + +export interface Parameter { + /** + * Name of the parameter. + */ + name: string; + /** + * Type of the parameter + */ + inputType?: InputType; + /** + * Title of the parameter. + */ + title: string; + /** + * Description of the parameter. + */ + description?: string; + /** + * The value indicates if this parameter is a required field. + */ + isRequired?: boolean; + /** + * Initial value for the parameter + */ + value?: string; + /** + * The choice options for the parameter + */ + choices?: Choice[]; + /** + * Semantic description for the parameter. + */ + semanticDescription?: string; +} + +export interface Choice { + /** + * Title of the choice + */ + title: string; + /** + * Value of the choice + */ + value: string; +} + +/** + * Type of the parameter + */ +export type InputType = "text" | "textarea" | "number" | "date" | "time" | "toggle" | "choiceset"; + +export interface SamplePrompt { + /** + * This string will hold the sample prompt + */ + text: string; +} + +/** + * Type of the command + */ +export type CommandType = "query" | "action"; + +/** + * Type of the compose extension. + */ +export type ComposeExtensionType = "botBased" | "apiBased"; + +export interface MessageHandler { + /** + * Type of the message handler + */ + type: "link"; + value: MessageHandlerValue; +} + +/** + * Type of the message handler + */ + +export interface MessageHandlerValue { + /** + * A list of domains that the link message handler can register for, and when they are + * matched the app will be invoked + */ + domains?: string[]; + /** + * A boolean that indicates whether the app's link message handler supports anonymous invoke + * flow. + */ + supportsAnonymizedPayloads?: boolean; +} + +export type ConfigurableProperty = "name" | "shortDescription" | "longDescription" | "smallImageUrl" | "largeImageUrl" | "accentColor" | "developerUrl" | "privacyUrl" | "termsOfUseUrl"; + +export interface ConfigurableTab { + /** + * A unique identifier for the tab. This id must be unique within the app manifest. + */ + id?: string; + /** + * The url to use when configuring the tab. + */ + configurationUrl: string; + /** + * A value indicating whether an instance of the tab's configuration can be updated by the + * user after creation. + */ + canUpdateConfiguration?: boolean; + /** + * Specifies whether the tab offers an experience in the context of a channel in a team, in + * a 1:1 or group chat, or in an experience scoped to an individual user alone. These + * options are non-exclusive. Currently, configurable tabs are only supported in the teams + * and groupchats scopes. + */ + scopes: ConfigurableTabScope[]; + /** + * The set of meetingSurfaceItem scopes that a tab belong to + */ + meetingSurfaces?: MeetingSurface[]; + /** + * The set of contextItem scopes that a tab belong to + */ + context?: ConfigurableTabContext[]; + /** + * A relative file path to a tab preview image for use in SharePoint. Size 1024x768. + */ + sharePointPreviewImage?: string; + /** + * Defines how your tab will be made available in SharePoint. + */ + supportedSharePointHosts?: SupportedSharePointHost[]; +} + +export type ConfigurableTabContext = "personalTab" | "channelTab" | "privateChatTab" | "meetingChatTab" | "meetingDetailsTab" | "meetingSidePanel" | "meetingStage"; + +export type MeetingSurface = "sidePanel" | "stage"; + +export type ConfigurableTabScope = "team" | "groupChat"; + +export type SupportedSharePointHost = "sharePointFullPage" | "sharePointWebPart"; + +export interface Connector { + /** + * A unique identifier for the connector which matches its ID in the Connectors Developer + * Portal. + */ + connectorId: string; + /** + * The url to use for configuring the connector using the inline configuration experience. + */ + configurationUrl?: string; + /** + * Specifies whether the connector offers an experience in the context of a channel in a + * team, or an experience scoped to an individual user alone. Currently, only the team scope + * is supported. + */ + scopes: "team"[]; +} + +export interface CopilotAgents { + /** + * An array of declarative agent elements references. Currently, only one declarative agent + * per application is supported. + */ + declarativeAgents?: DeclarativeAgentRef[]; + /** + * An array of Custom Engine Agents. Currently only one Custom Engine Agent per application + * is supported. Support is currently in public preview. + */ + customEngineAgents?: CustomEngineAgent[]; +} + +export interface CustomEngineAgent { + /** + * The id of the Custom Engine Agent. If it is of type bot, the id must match the id + * specified in a bot in the bots node and the referenced bot must have personal scope. The + * app short name and short description must also be defined. + */ + id: string; + /** + * The type of the Custom Engine Agent. Currently only type bot is supported. + */ + type: "bot"; + disclaimer?: Disclaimer; +} + +export interface Disclaimer { + /** + * The message shown to users before they interact with this application. + */ + text: string; + [property: string]: any; +} + +/** + * The type of the Custom Engine Agent. Currently only type bot is supported. + * + * The content of the dashboard card is sourced from a bot. + */ + +/** + * A reference to a declarative agent element. The element's definition is in a separate + * file. + */ +export interface DeclarativeAgentRef { + /** + * A unique identifier for this declarative agent element. + */ + id: string; + /** + * Relative file path to this declarative agent element file in the application package. + */ + file: string; +} + +/** + * Cards wich could be pinned to dashboard providing summarized view of information relevant + * to user. + */ +export interface DashboardCard { + /** + * Unique Id for the card. Must be unique inside the app. + */ + id: string; + /** + * Represents the name of the card. Maximum length is 255 characters. + */ + displayName: string; + /** + * Description of the card.Maximum length is 255 characters. + */ + description: string; + /** + * Id of the group in the card picker. This must be guid. + */ + pickerGroupId: string; + icon?: DashboardCardIcon; + contentSource: DashboardCardContentSource; + /** + * Rendering Size for dashboard card. + */ + defaultSize: DefaultSize; +} + +/** + * Represents a configuration for the source of the card’s content. + */ +export interface DashboardCardContentSource { + /** + * The content of the dashboard card is sourced from a bot. + */ + sourceType?: "bot"; + /** + * The configuration for the bot source. Required if sourceType is set to bot. + */ + botConfiguration?: BotConfiguration; +} + +/** + * The configuration for the bot source. Required if sourceType is set to bot. + */ +export interface BotConfiguration { + /** + * The unique Microsoft app ID for the bot as registered with the Bot Framework. + */ + botId?: string; +} + +/** + * Rendering Size for dashboard card. + */ +export type DefaultSize = "medium" | "large"; + +/** + * Represents a configuration for the source of the card’s content + */ +export interface DashboardCardIcon { + /** + * The icon for the card, to be displayed in the toolbox and card bar, represented as URL. + */ + iconUrl?: string; + /** + * Office UI Fabric/Fluent UI icon friendly name for the card. This value will be used if + * ‘iconUrl’ is not specified. + */ + officeUIFabricIconName?: string; +} + +/** + * When a group install scope is selected, this will define the default capability when the + * user installs the app + */ +export interface DefaultGroupCapability { + /** + * When the install scope selected is Team, this field specifies the default capability + * available + */ + team?: Groupchat; + /** + * When the install scope selected is GroupChat, this field specifies the default capability + * available + */ + groupchat?: Groupchat; + /** + * When the install scope selected is Meetings, this field specifies the default capability + * available + */ + meetings?: Groupchat; +} + +/** + * When the install scope selected is GroupChat, this field specifies the default capability + * available + * + * When the install scope selected is Meetings, this field specifies the default capability + * available + * + * When the install scope selected is Team, this field specifies the default capability + * available + */ +export type Groupchat = "tab" | "bot" | "connector"; + +/** + * The install scope defined for this app by default. This will be the option displayed on + * the button when a user tries to add the app + */ +export type DefaultInstallScope = "personal" | "team" | "groupChat" | "meetings" | "copilot"; + +export interface Description { + /** + * A short description of the app used when space is limited. Maximum length is 80 + * characters. + */ + short: string; + /** + * The full description of the app. Maximum length is 4000 characters. + */ + full: string; + /** + * Array of features sections describing what the app can do. + */ + features?: Feature[]; +} + +export interface Feature { + /** + * Title of the feature the app provides. + */ + title: string; + /** + * Detailed description of the specific feature. + */ + description: string; +} + +export interface Developer { + /** + * The display name for the developer. + */ + name: string; + /** + * The Microsoft Partner Network ID that identifies the partner organization building the + * app. This field is not required, and should only be used if you are already part of the + * Microsoft Partner Network. More info at https://aka.ms/partner + */ + mpnId?: string; + /** + * The url to the page that provides support information for the app. + */ + websiteUrl: string; + /** + * The url to the page that provides privacy information for the app. + */ + privacyUrl: string; + /** + * The url to the page that provides the terms of use for the app. + */ + termsOfUseUrl: string; +} + +export type DevicePermission = "geolocation" | "media" | "notifications" | "midi" | "openExternal"; + +export interface ElementRelationshipSet { + /** + * An array containing multiple instances of unidirectional dependency relationships (each + * represented by a oneWayDependency object). + */ + oneWayDependencies?: OneWayDependency[]; + /** + * An array containing multiple instances of mutual dependency relationships between + * elements (each represented by a mutualDependency object). + */ + mutualDependencies?: Array; +} + +/** + * A specific instance of mutual dependency between two or more elements, indicating that + * each element depends on the others in a bidirectional manner. + */ +export interface ElementReference { + name: MutualDependencyName; + id: string; + commandIds?: string[]; +} + +export type MutualDependencyName = "bots" | "staticTabs" | "composeExtensions" | "configurableTabs"; + +/** + * An object representing a unidirectional dependency relationship, where one specific + * element (referred to as the `element`) relies on an array of other elements (referred to + * as the `dependsOn`) in a single direction. + */ +export interface OneWayDependency { + element: ElementReference; + dependsOn: ElementReference[]; +} + +/** + * The set of extensions for this app. Currently only one extensions per app is supported. + */ +export interface ElementExtension { + requirements?: RequirementsExtensionElement; + runtimes?: ExtensionRuntimesArray[]; + ribbons?: ExtensionRibbonsArray[]; + autoRunEvents?: ExtensionAutoRunEventsArray[]; + alternates?: ExtensionAlternateVersionsArray[]; + contentRuntimes?: ExtensionContentRuntimeArray[]; + getStartedMessages?: ExtensionGetStartedMessageArray[]; + contextMenus?: ExtensionContextMenuArray[]; + /** + * Keyboard shortcuts, also known as key combinations, enable your add-in's users to work + * more efficiently. Keyboard shortcuts also improve the add-in's accessibility for users + * with disabilities by providing an alternative to the mouse. + */ + keyboardShortcuts?: ExtensionKeyboardShortcut[]; + /** + * The url for your extension, used to validate Exchange user identity tokens. + */ + audienceClaimUrl?: string; +} + +export interface ExtensionAlternateVersionsArray { + requirements?: RequirementsExtensionElement; + prefer?: Prefer; + hide?: Hide; + alternateIcons?: AlternateIcons; +} + +export interface AlternateIcons { + icon: ExtensionCommonIcon; + highResolutionIcon: ExtensionCommonIcon; +} + +export interface ExtensionCommonIcon { + /** + * Size in pixels of the icon. Three image sizes are required (16, 32, and 80 pixels) + */ + size: number; + /** + * Absolute Url to the icon. + */ + url: string; +} + +export interface Hide { + storeOfficeAddin?: StoreOfficeAddin; + customOfficeAddin?: CustomOfficeAddin; + /** + * Configures how to hide windows native extensions + */ + windowsExtensions?: WindowsExtensions; + [property: string]: any; +} + +export interface CustomOfficeAddin { + /** + * Solution ID of the in-market add-in to hide. Maximum length is 64 characters. + */ + officeAddinId: string; +} + +export interface StoreOfficeAddin { + /** + * Solution ID of an in-market add-in to hide. Maximum length is 64 characters. + */ + officeAddinId: string; + /** + * Asset ID of the in-market add-in to hide. Maximum length is 64 characters. + */ + assetId: string; +} + +/** + * Configures how to hide windows native extensions + */ +export interface WindowsExtensions { + /** + * Specifies the effect to take while installing the web add-in if the equivalent add-in is + * installed. + */ + effect: Effect; + /** + * Specifies the equivalent COM add-ins + */ + comAddin?: WindowsExtensionsCOMAddin; + /** + * Specifies the equivalent automation add-ins + */ + automationAddin?: AutomationAddin; + /** + * Specifies the XLL-based add-ins custom function + */ + xllCustomFunctions?: XllCustomFunctions; +} + +/** + * Specifies the equivalent automation add-ins + */ +export interface AutomationAddin { + /** + * Specifies the program Ids of the equivalent automation add-ins + */ + progIds: string[]; +} + +/** + * Specifies the equivalent COM add-ins + */ +export interface WindowsExtensionsCOMAddin { + /** + * Specifies the program Ids of the equivalent COM add-ins + */ + progIds: string[]; +} + +/** + * Specifies the effect to take while installing the web add-in if the equivalent add-in is + * installed. + */ +export type Effect = "userOptionToDisable" | "disableWithNotification"; + +/** + * Specifies the XLL-based add-ins custom function + */ +export interface XllCustomFunctions { + /** + * Specifies the file names of the XLL-based add-ins custom function + */ + fileNames: string[]; +} + +export interface Prefer { + comAddin?: PreferCOMAddin; + xllCustomFunctions?: ExtensionXllCustomFunctions; + [property: string]: any; +} + +export interface PreferCOMAddin { + /** + * Program ID of the alternate com extension. Maximum length is 64 characters. + */ + progId: string; +} + +export interface ExtensionXllCustomFunctions { + /** + * File name for the XLL extension. Maximum length is 254 characters. + */ + fileName?: string; + [property: string]: any; +} + +/** + * Specifies limitations on which clients the add-in can be installed on, including + * limitations on the Office host application, the form factors, and the requirement sets + * that the client must support. + * + * Specifies the Office requirement sets. + */ +export interface RequirementsExtensionElement { + capabilities?: Capability[]; + /** + * Identifies the scopes in which the add-in can run. Supported values: 'mail', 'workbook', + * 'document', 'presentation'. + */ + scopes?: RequirementsScope[]; + /** + * Identifies the form factors that support the add-in. Supported values: mobile, desktop. + */ + formFactors?: FormFactor[]; +} + +export interface Capability { + /** + * Identifies the name of the requirement sets that the add-in needs to run. + */ + name: string; + /** + * Identifies the minimum version for the requirement sets that the add-in needs to run. + */ + minVersion?: string; + /** + * Identifies the maximum version for the requirement sets that the add-in needs to run. + */ + maxVersion?: string; +} + +export type FormFactor = "desktop" | "mobile"; + +export type RequirementsScope = "mail" | "workbook" | "document" | "presentation"; + +export interface ExtensionAutoRunEventsArray { + requirements?: RequirementsExtensionElement; + /** + * Specifies the type of event. For supported types, please see: + * https://learn.microsoft.com/en-us/office/dev/add-ins/outlook/autolaunch?tabs=xmlmanifest#supported-events. + */ + events: Event[]; +} + +export interface Event { + type: string; + /** + * The ID of an action defined in runtimes. Maximum length is 64 characters. + */ + actionId: string; + /** + * Configures how Outlook responds to the event. + */ + options?: Options; +} + +/** + * Configures how Outlook responds to the event. + */ +export interface Options { + sendMode: SendMode; +} + +export type SendMode = "promptUser" | "softBlock" | "block"; + +/** + * Content runtime is for 'ContentApp', which can be embedded directly into Excel or + * PowerPoint documents. + */ +export interface ExtensionContentRuntimeArray { + /** + * Specifies the Office requirement sets for content add-in runtime. If the user's Office + * version doesn't support the specified requirements, the component will not be available + * in that client. + */ + requirements?: ContentRuntimeRequirements; + /** + * A unique identifier for this runtime within the app. This is developer specified. + */ + id: string; + /** + * Specifies the location of code for this runtime. Depending on the runtime.type, add-ins + * use either a JavaScript file or an HTML page with an embedded