diff --git a/.gitignore b/.gitignore
index 2c183ddde3..59d3fefd21 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,16 @@
# Build results
[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+ecf/
+rcf/
bones/
.vs/
@@ -19,4 +29,4 @@ bones/
.config/
# Python virtual environments
-**/*env/
\ No newline at end of file
+**/*env/
diff --git a/schemas/skills/SchemaManifestTests/SchemaManifestTests.csproj b/schemas/skills/SchemaManifestTests/SchemaManifestTests.csproj
new file mode 100644
index 0000000000..69029b71c9
--- /dev/null
+++ b/schemas/skills/SchemaManifestTests/SchemaManifestTests.csproj
@@ -0,0 +1,25 @@
+
+
+
+ netcoreapp3.1
+
+ false
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
diff --git a/schemas/skills/SchemaManifestTests/SchemaManifestTests.sln b/schemas/skills/SchemaManifestTests/SchemaManifestTests.sln
new file mode 100644
index 0000000000..47f561e1e2
--- /dev/null
+++ b/schemas/skills/SchemaManifestTests/SchemaManifestTests.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31409.214
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SchemaManifestTests", "SchemaManifestTests.csproj", "{53609E6C-0C2C-4DB2-864C-628BE29495E4}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {53609E6C-0C2C-4DB2-864C-628BE29495E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {53609E6C-0C2C-4DB2-864C-628BE29495E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {53609E6C-0C2C-4DB2-864C-628BE29495E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {53609E6C-0C2C-4DB2-864C-628BE29495E4}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {DB082524-0CE2-4C94-BA0E-A9FFDBE8EE42}
+ EndGlobalSection
+EndGlobal
diff --git a/schemas/skills/SchemaManifestTests/ValidateSchemaTests.cs b/schemas/skills/SchemaManifestTests/ValidateSchemaTests.cs
new file mode 100644
index 0000000000..29538bdc15
--- /dev/null
+++ b/schemas/skills/SchemaManifestTests/ValidateSchemaTests.cs
@@ -0,0 +1,111 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json.Schema;
+using NJsonSchema;
+using NJsonSchema.Generation;
+using Xunit;
+using JsonSchema = NJsonSchema.JsonSchema;
+using JsonSchemaResolver = NJsonSchema.Generation.JsonSchemaResolver;
+
+namespace SchemaManifestTests
+{
+ ///
+ /// Validates sample manifests against schemas.
+ ///
+ ///
+ /// There are some differences on the validation provided by Newtonsoft and NJsonSchema so we use both libraries in the tests
+ /// to ensure better compatibility.
+ ///
+ public class ValidateSchemaTests
+ {
+ // List of schema version folders to test.
+ private static readonly List _schemaVersionFolders = new List
+ {
+ "v2.0",
+ "v2.1",
+ "v2.2"
+ };
+
+ // Path to the folder containing the schema files and samples (relative to where the test is executing).
+ private static readonly string _schemasRootFolder = Path.Combine(Directory.GetCurrentDirectory(), "../../../../");
+
+ ///
+ /// Builds the list of manifest schemas and samples to validate from the file system.
+ ///
+ public static TheoryData GetManifestAndSamples()
+ {
+ var manifestAndSamples = new TheoryData();
+
+ foreach (var schemaVersion in _schemaVersionFolders)
+ {
+ var schemaFolder = Path.Combine(_schemasRootFolder, schemaVersion);
+ var samplesFolder = Path.Combine(schemaFolder, "Samples");
+ var sampleManifestFiles = Directory.GetFileSystemEntries(samplesFolder, "*.json", SearchOption.AllDirectories);
+ foreach (var manifestFile in sampleManifestFiles)
+ {
+ var manifestRelativePath = Path.GetRelativePath(schemaFolder, manifestFile);
+ manifestAndSamples.Add(schemaVersion, manifestRelativePath);
+ }
+ }
+
+ return manifestAndSamples;
+ }
+
+ [Theory]
+ [MemberData(nameof(GetManifestAndSamples))]
+ public async Task ValidateManifestSamplesAgainstSchemasUsingNJsonSchemaAsync(string schemaVersion, string sampleManifest)
+ {
+ // Arrange
+ var manifestSchemaPath = Path.Combine(_schemasRootFolder, schemaVersion, "skill-manifest.json");
+ var manifestSchema = await GetSchemaAsync(manifestSchemaPath);
+
+ var sampleManifestPath = Path.Combine(_schemasRootFolder, schemaVersion, sampleManifest);
+ var sampleManifestText = await File.ReadAllTextAsync(sampleManifestPath);
+
+ // Act
+ var validationErrors = manifestSchema.Validate(sampleManifestText);
+
+ // Assert
+ Assert.Empty(validationErrors);
+ }
+
+ [Theory]
+ [MemberData(nameof(GetManifestAndSamples))]
+ public async Task ValidateManifestSamplesAgainstSchemasUsingNewtonsoftSchemaAsync(string schemaVersion, string sampleManifest)
+ {
+ // Note: you can use https://www.jsonschemavalidator.net/ for an interactive version.
+
+ // Arrange
+ var manifestSchemaPath = Path.Combine(_schemasRootFolder, schemaVersion, "skill-manifest.json");
+ var manifestSchema = JSchema.Parse(await File.ReadAllTextAsync(manifestSchemaPath), new JSchemaUrlResolver());
+
+ var sampleManifestPath = Path.Combine(_schemasRootFolder, schemaVersion, sampleManifest);
+ var json = JToken.Parse(await File.ReadAllTextAsync(sampleManifestPath));
+
+ // Act
+ json.IsValid(manifestSchema, out IList validationErrors);
+
+ // Assert
+ Assert.Empty(validationErrors);
+ }
+
+ private static async Task GetSchemaAsync(string schemaPath)
+ {
+ var rawSchemaText = await File.ReadAllTextAsync(schemaPath);
+
+ return await JsonSchema.FromJsonAsync(rawSchemaText, null, x =>
+ {
+ var schemaResolver = new JsonSchemaResolver(x, new JsonSchemaGeneratorSettings());
+ var referenceResolver = new JsonReferenceResolver(schemaResolver);
+ referenceResolver.AddDocumentReference("http://json-schema.org/draft-07/schema", JsonSchema.CreateAnySchema());
+
+ return referenceResolver;
+ });
+ }
+ }
+}
diff --git a/schemas/skills/readme.md b/schemas/skills/readme.md
index 40d96bf0e4..81d744f25d 100644
--- a/schemas/skills/readme.md
+++ b/schemas/skills/readme.md
@@ -10,7 +10,7 @@ The skill manifest JSON schema is published at:
Example:
-`https://schemas.botframework.com/schemas/skills/v2.1/skill-manifest.json`
+`https://schemas.botframework.com/schemas/skills/v2.2/skill-manifest.json`
You should use the published version when referencing this schema.
diff --git a/schemas/skills/v2.1/skill-manifest.json b/schemas/skills/v2.1/skill-manifest.json
index 0ed5345428..116615a8de 100644
--- a/schemas/skills/v2.1/skill-manifest.json
+++ b/schemas/skills/v2.1/skill-manifest.json
@@ -1,7 +1,7 @@
{
"$id": "https://schemas.botframework.com/schemas/skills/v2.1/skill-manifest.json",
"$schema": "http://json-schema.org/draft-07/schema#",
- "$version": "2.1.0",
+ "$version": "2.1.1",
"title": "Skill Manifest Schema",
"description": "A schema for Bot Framework skill manifests",
"type": "object",
@@ -206,8 +206,11 @@
"properties": {
"type": {
"type": "string",
+ "title": "Activity Type",
"description": "The activity type",
- "const": "event"
+ "enum": [
+ "event"
+ ]
},
"name": {
"type": "string",
@@ -245,8 +248,11 @@
"properties": {
"type": {
"type": "string",
+ "title": "Activity Type",
"description": "The activity type",
- "const": "invoke"
+ "enum": [
+ "invoke"
+ ]
},
"name": {
"type": "string",
@@ -284,8 +290,9 @@
"type": {
"type": "string",
"description": "The activity type",
- "const": "message",
- "default": "message"
+ "enum": [
+ "message"
+ ]
},
"description": {
"type": "string",
@@ -315,11 +322,8 @@
"type": {
"type": "string",
"title": "Activity Type",
- "description": "Contains the activity type (message, event, invoke, etc.)",
+ "description": "The activity type",
"enum": [
- "message",
- "event",
- "invoke",
"messageReaction",
"endOfConversation",
"handoff",
@@ -332,8 +336,7 @@
"deleteUserData",
"messageUpdate",
"messageDelete"
- ],
- "pattern":"^(?!(message|event|invoke)$)((messageReaction|endOfConversation|handoff|typing|conversationUpdate|trace|installationUpdate|contactRelationUpdate|suggestion|deleteUserData|messageUpdate|messageDelete)$)"
+ ]
}
},
"additionalProperties": true
@@ -369,4 +372,4 @@
"additionalProperties": false
}
}
-}
\ No newline at end of file
+}
diff --git a/schemas/skills/v2.2/samples/complex-pva-manifest.json b/schemas/skills/v2.2/samples/complex-pva-manifest.json
new file mode 100644
index 0000000000..f8c1bc5e96
--- /dev/null
+++ b/schemas/skills/v2.2/samples/complex-pva-manifest.json
@@ -0,0 +1,164 @@
+{
+ "$schema": "https://schemas.botframework.com/schemas/skills/v2.2/skill-manifest.json",
+ "$id": "SkillBot",
+ "name": "Sample skill definition that can handle multiple types of activities",
+ "version": "1.0",
+ "description": "This is a sample skill definition for multiple activity types",
+ "publisherName": "Microsoft",
+ "privacyUrl": "https://myskill.contoso.com/privacy.html",
+ "copyright": "Copyright (c) Microsoft Corporation. All rights reserved.",
+ "license": "",
+ "iconUrl": "https://myskill.contoso.com/icon.png",
+ "tags": [
+ "sample",
+ "travel",
+ "weather"
+ ],
+ "endpoints": [
+ {
+ "name": "americas",
+ "protocol": "BotFrameworkV3",
+ "description": "Production endpoint for SkillBot in the Americas",
+ "endpointUrl": "http://myskill.contoso.com/api/messages",
+ "msAppId": "00000000-0000-0000-0000-000000000000"
+ }
+ ],
+ "dispatchModels": {
+ "languages": {
+ "en": [
+ {
+ "name": "SkillBot LU (English)",
+ "contentType": "application/lu",
+ "url": "http://sample.com/SkillBot-en.lu",
+ "description": "English language model for the skill"
+ },
+ {
+ "name": "SkillBot QnA LU (English)",
+ "contentType": "application/qna",
+ "url": "http://sample.com/SkillBot-QnA-en.qna",
+ "description": "English language model for the skill (QnAMaker)"
+ }
+ ]
+ },
+ "intents": [
+ "bookFlight",
+ "getWeather"
+ ]
+ },
+ "activities": {
+ "bookFlight": {
+ "description": "Books a flight",
+ "type": "event",
+ "name": "BookFlight",
+ "value": {
+ "$ref": "#/definitions/bookingInfo"
+ },
+ "resultValue": {
+ "$ref": "#/definitions/bookingInfo"
+ }
+ },
+ "getWeather": {
+ "description": "Retrieves and returns the weather for the user's location",
+ "type": "invoke",
+ "name": "GetWeather",
+ "value": {
+ "$ref": "#/definitions/location"
+ },
+ "resultValue": {
+ "$ref": "#/definitions/weatherReport"
+ }
+ },
+ "message": {
+ "type": "message",
+ "description": "Receives the user's' utterance and attempts to resolve it using the skill's LU models"
+ },
+ "typing": {
+ "type": "typing"
+ },
+ "conversationUpdate": {
+ "type": "conversationUpdate"
+ }
+ },
+ "definitions": {
+ "localeValue": {
+ "type": "object",
+ "properties": {
+ "locale": {
+ "type": "string",
+ "description": "The current user's locale ISO code"
+ }
+ }
+ },
+ "bookingInfo": {
+ "type": "object",
+ "required": [
+ "origin"
+ ],
+ "properties": {
+ "origin": {
+ "type": "string",
+ "description": "this is the origin city for the flight"
+ },
+ "destination": {
+ "type": "string",
+ "description": "this is the destination city for the flight"
+ },
+ "date": {
+ "type": "string",
+ "description": "The date for the flight in YYYY-MM-DD format"
+ }
+ }
+ },
+ "weatherReport": {
+ "type": "array",
+ "description": "Array of forecasts for the next week.",
+ "items": [
+ {
+ "type": "string"
+ }
+ ]
+ },
+ "location": {
+ "type": "object",
+ "description": "Location metadata",
+ "properties": {
+ "latitude": {
+ "type": "number",
+ "title": "Latitude"
+ },
+ "longitude": {
+ "type": "number",
+ "title": "Longitude"
+ },
+ "postalCode": {
+ "type": "string",
+ "title": "Postal code"
+ }
+ }
+ }
+ },
+ "activitiesSent": {
+ "flightUpdated": {
+ "type": "event",
+ "name": "FlightUpdated",
+ "description": "Event which is sent by the skill when there is an update in flight info",
+ "value": {
+ "type": "object",
+ "description": "Flight update information",
+ "properties": {
+ "flightNumber": {
+ "type": "string"
+ },
+ "departureDate": {
+ "type": "string",
+ "description": "The departure date for the flight in YYYY-MM-DD format"
+ },
+ "departureTime": {
+ "type": "string",
+ "description": "The departure time for the flight in HH-MM format"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/schemas/skills/v2.2/samples/complex-skillmanifest.json b/schemas/skills/v2.2/samples/complex-skillmanifest.json
new file mode 100644
index 0000000000..854857a21a
--- /dev/null
+++ b/schemas/skills/v2.2/samples/complex-skillmanifest.json
@@ -0,0 +1,199 @@
+{
+ "$schema": "https://schemas.botframework.com/schemas/skills/v2.2/skill-manifest.json",
+ "$id": "SkillBot",
+ "name": "Sample skill definition that can handle multiple types of activities",
+ "version": "1.0",
+ "description": "This is a sample skill definition for multiple activity types",
+ "publisherName": "Microsoft",
+ "privacyUrl": "https://myskill.contoso.com/privacy.html",
+ "copyright": "Copyright (c) Microsoft Corporation. All rights reserved.",
+ "license": "",
+ "iconUrl": "skillIcon.png",
+ "tags": [
+ "sample",
+ "travel",
+ "weather"
+ ],
+ "endpoints": [
+ {
+ "name": "americas",
+ "protocol": "BotFrameworkV3",
+ "description": "Production endpoint for SkillBot in the Americas",
+ "endpointUrl": "http://myskill.contoso.com/api/messages",
+ "msAppId": "00000000-0000-0000-0000-000000000000"
+ },
+ {
+ "name": "eu",
+ "protocol": "BotFrameworkV3",
+ "description": "Production endpoint for SkillBot in Europe",
+ "endpointUrl": "http://myskill.contoso.com/api/messages",
+ "msAppId": "11111111-0000-0000-0000-000000000000"
+ }
+ ],
+ "dispatchModels": {
+ "languages": {
+ "en": [
+ {
+ "name": "SkillBot LU (English)",
+ "contentType": "application/lu",
+ "url": "http://sample.com/SkillBot-en.lu",
+ "description": "English language model for the skill"
+ },
+ {
+ "name": "SkillBot QnA LU (English)",
+ "contentType": "application/qna",
+ "url": "http://sample.com/SkillBot-QnA-en.qna",
+ "description": "English language model for the skill (QnAMaker)"
+ }
+ ],
+ "es-ES": [
+ {
+ "name": "SkillBot LU (Spanish-Spain)",
+ "contentType": "application/lu",
+ "url": "http://sample.com/SkillBot-es-ES.lu",
+ "description": "Spanish (Spain) language model for the skill"
+ },
+ {
+ "name": "SkillBot QnA LU (Spanish-Spain)",
+ "contentType": "application/qna",
+ "url": "http://sample.com/SkillBot-QnA-es-ES.qna",
+ "description": "Spanish (Spain) language model for the skill (QnAMaker)"
+ }
+ ],
+ "es-MX": [
+ {
+ "name": "SkillBot LU (Spanish-Mexico)",
+ "contentType": "application/lu",
+ "url": "http://sample.com/SkillBot-es-MX.lu",
+ "description": "Spanish (Mexico) language model for the skill"
+ },
+ {
+ "name": "SkillBot QnA LU (Spanish-Mexico)",
+ "contentType": "application/qna",
+ "url": "http://sample.com/SkillBot-QnA-es-MX.qna",
+ "description": "Spanish (Mexico) language model for the skill (QnAMaker)"
+ }
+ ]
+ },
+ "intents": [
+ "bookFlight",
+ "getWeather"
+ ]
+ },
+ "activities": {
+ "bookFlight": {
+ "description": "Books a flight",
+ "type": "event",
+ "name": "BookFlight",
+ "value": {
+ "$ref": "#/definitions/bookingInfo"
+ },
+ "resultValue": {
+ "$ref": "#/definitions/bookingInfo"
+ }
+ },
+ "getWeather": {
+ "description": "Retrieves and returns the weather for the user's location",
+ "type": "invoke",
+ "name": "GetWeather",
+ "value": {
+ "$ref": "#/definitions/location"
+ },
+ "resultValue": {
+ "$ref": "#/definitions/weatherReport"
+ }
+ },
+ "message": {
+ "type": "message",
+ "description": "Receives the user's' utterance and attempts to resolve it using the skill's LU models"
+ },
+ "typing": {
+ "type": "typing"
+ },
+ "conversationUpdate": {
+ "type": "conversationUpdate"
+ }
+ },
+ "definitions": {
+ "localeValue": {
+ "type": "object",
+ "properties": {
+ "locale": {
+ "type": "string",
+ "description": "The current user's locale ISO code"
+ }
+ }
+ },
+ "bookingInfo": {
+ "type": "object",
+ "required": [
+ "origin"
+ ],
+ "properties": {
+ "origin": {
+ "type": "string",
+ "description": "this is the origin city for the flight"
+ },
+ "destination": {
+ "type": "string",
+ "description": "this is the destination city for the flight"
+ },
+ "date": {
+ "type": "string",
+ "description": "The date for the flight in YYYY-MM-DD format"
+ }
+ }
+ },
+ "weatherReport": {
+ "type": "array",
+ "description": "Array of forecasts for the next week.",
+ "items": [
+ {
+ "type": "string"
+ }
+ ]
+ },
+ "location": {
+ "type": "object",
+ "description": "Location metadata",
+ "properties": {
+ "latitude": {
+ "type": "number",
+ "title": "Latitude"
+ },
+ "longitude": {
+ "type": "number",
+ "title": "Longitude"
+ },
+ "postalCode": {
+ "type": "string",
+ "title": "Postal code"
+ }
+ }
+ }
+ },
+ "activitiesSent": {
+ "flightUpdated": {
+ "type": "event",
+ "name": "FlightUpdated",
+ "description": "Event which is sent by the skill when there is an update in flight info",
+ "value": {
+ "type": "object",
+ "description": "Flight update information",
+ "properties": {
+ "flightNumber": {
+ "type": "string"
+ },
+ "departureDate": {
+ "type": "string",
+ "description": "The departure date for the flight in YYYY-MM-DD format"
+ },
+ "departureTime": {
+ "type": "string",
+ "description": "The departure time for the flight in HH-MM format"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/schemas/skills/v2.2/samples/echo-skillmanifest.json b/schemas/skills/v2.2/samples/echo-skillmanifest.json
new file mode 100644
index 0000000000..760cf0252c
--- /dev/null
+++ b/schemas/skills/v2.2/samples/echo-skillmanifest.json
@@ -0,0 +1,31 @@
+{
+ "$schema": "https://schemas.botframework.com/schemas/skills/v2.2/skill-manifest.json",
+ "$id": "EchoSkill",
+ "name": "Echo skill",
+ "version": "1.0",
+ "description": "This skill echoes whatever the user says",
+ "publisherName": "Microsoft",
+ "privacyUrl": "https://myskill.contoso.com/privacy.html",
+ "copyright": "Copyright (c) Microsoft Corporation. All rights reserved.",
+ "license": "",
+ "iconUrl": "https://myskill.contoso.com/icon.png",
+ "tags": [
+ "sample",
+ "echo"
+ ],
+ "endpoints": [
+ {
+ "name": "default",
+ "protocol": "BotFrameworkV3",
+ "description": "Production endpoint for SkillBot.",
+ "endpointUrl": "http://myskill.contoso.com/api/messages",
+ "msAppId": "a0000f00-Ad00-a000-a000-a0000aaaAaa0"
+ }
+ ],
+ "activities": {
+ "message": {
+ "type": "message",
+ "description": "A message activity containing the utterance that the skill will echo back to the user"
+ }
+ }
+}
diff --git a/schemas/skills/v2.2/samples/relativeUris/complex-skillmanifest.json b/schemas/skills/v2.2/samples/relativeUris/complex-skillmanifest.json
new file mode 100644
index 0000000000..e0f74ee152
--- /dev/null
+++ b/schemas/skills/v2.2/samples/relativeUris/complex-skillmanifest.json
@@ -0,0 +1,199 @@
+{
+ "$schema": "https://schemas.botframework.com/schemas/skills/v2.2/skill-manifest.json",
+ "$id": "SkillBot",
+ "name": "Sample skill definition that can handle multiple types of activities",
+ "version": "1.0",
+ "description": "This is a sample skill definition for multiple activity types",
+ "publisherName": "Microsoft",
+ "privacyUrl": "privacy.html",
+ "copyright": "Copyright (c) Microsoft Corporation. All rights reserved.",
+ "license": "",
+ "iconUrl": "skillIcon.png",
+ "tags": [
+ "sample",
+ "travel",
+ "weather"
+ ],
+ "endpoints": [
+ {
+ "name": "americas",
+ "protocol": "BotFrameworkV3",
+ "description": "Production endpoint for SkillBot in the Americas",
+ "endpointUrl": "http://myskill.contoso.com/api/messages",
+ "msAppId": "00000000-0000-0000-0000-000000000000"
+ },
+ {
+ "name": "eu",
+ "protocol": "BotFrameworkV3",
+ "description": "Production endpoint for SkillBot in Europe",
+ "endpointUrl": "http://myskill.contoso.com/api/messages",
+ "msAppId": "11111111-0000-0000-0000-000000000000"
+ }
+ ],
+ "dispatchModels": {
+ "languages": {
+ "en": [
+ {
+ "name": "SkillBot LU (English)",
+ "contentType": "application/lu",
+ "url": "language-understanding/SkillBot-en.lu",
+ "description": "English language model for the skill"
+ },
+ {
+ "name": "SkillBot QnA LU (English)",
+ "contentType": "application/qna",
+ "url": "knowledge-base/SkillBot-QnA-en.qna",
+ "description": "English language model for the skill (QnAMaker)"
+ }
+ ],
+ "es-ES": [
+ {
+ "name": "SkillBot LU (Spanish-Spain)",
+ "contentType": "application/lu",
+ "url": "language-understanding/SkillBot-es-ES.lu",
+ "description": "Spanish (Spain) language model for the skill"
+ },
+ {
+ "name": "SkillBot QnA LU (Spanish-Spain)",
+ "contentType": "application/qna",
+ "url": "knowledge-base/SkillBot-QnA-es-ES.qna",
+ "description": "Spanish (Spain) language model for the skill (QnAMaker)"
+ }
+ ],
+ "es-MX": [
+ {
+ "name": "SkillBot LU (Spanish-Mexico)",
+ "contentType": "application/lu",
+ "url": "language-understanding/SkillBot-es-MX.lu",
+ "description": "Spanish (Mexico) language model for the skill"
+ },
+ {
+ "name": "SkillBot QnA LU (Spanish-Mexico)",
+ "contentType": "application/qna",
+ "url": "knowledge-base/SkillBot-QnA-es-MX.qna",
+ "description": "Spanish (Mexico) language model for the skill (QnAMaker)"
+ }
+ ]
+ },
+ "intents": [
+ "bookFlight",
+ "getWeather"
+ ]
+ },
+ "activities": {
+ "bookFlight": {
+ "description": "Books a flight",
+ "type": "event",
+ "name": "BookFlight",
+ "value": {
+ "$ref": "#/definitions/bookingInfo"
+ },
+ "resultValue": {
+ "$ref": "#/definitions/bookingInfo"
+ }
+ },
+ "getWeather": {
+ "description": "Retrieves and returns the weather for the user's location",
+ "type": "invoke",
+ "name": "GetWeather",
+ "value": {
+ "$ref": "#/definitions/location"
+ },
+ "resultValue": {
+ "$ref": "#/definitions/weatherReport"
+ }
+ },
+ "message": {
+ "type": "message",
+ "description": "Receives the user's' utterance and attempts to resolve it using the skill's LU models"
+ },
+ "typing": {
+ "type": "typing"
+ },
+ "conversationUpdate": {
+ "type": "conversationUpdate"
+ }
+ },
+ "definitions": {
+ "localeValue": {
+ "type": "object",
+ "properties": {
+ "locale": {
+ "type": "string",
+ "description": "The current user's locale ISO code"
+ }
+ }
+ },
+ "bookingInfo": {
+ "type": "object",
+ "required": [
+ "origin"
+ ],
+ "properties": {
+ "origin": {
+ "type": "string",
+ "description": "this is the origin city for the flight"
+ },
+ "destination": {
+ "type": "string",
+ "description": "this is the destination city for the flight"
+ },
+ "date": {
+ "type": "string",
+ "description": "The date for the flight in YYYY-MM-DD format"
+ }
+ }
+ },
+ "weatherReport": {
+ "type": "array",
+ "description": "Array of forecasts for the next week.",
+ "items": [
+ {
+ "type": "string"
+ }
+ ]
+ },
+ "location": {
+ "type": "object",
+ "description": "Location metadata",
+ "properties": {
+ "latitude": {
+ "type": "number",
+ "title": "Latitude"
+ },
+ "longitude": {
+ "type": "number",
+ "title": "Longitude"
+ },
+ "postalCode": {
+ "type": "string",
+ "title": "Postal code"
+ }
+ }
+ }
+ },
+ "activitiesSent": {
+ "flightUpdated": {
+ "type": "event",
+ "name": "FlightUpdated",
+ "description": "Event which is sent by the skill when there is an update in flight info",
+ "value": {
+ "type": "object",
+ "description": "Flight update information",
+ "properties": {
+ "flightNumber": {
+ "type": "string"
+ },
+ "departureDate": {
+ "type": "string",
+ "description": "The departure date for the flight in YYYY-MM-DD format"
+ },
+ "departureTime": {
+ "type": "string",
+ "description": "The departure time for the flight in HH-MM format"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/schemas/skills/v2.2/samples/relativeUris/knowledge-base/SkillBot-QnA-en.qna b/schemas/skills/v2.2/samples/relativeUris/knowledge-base/SkillBot-QnA-en.qna
new file mode 100644
index 0000000000..5f3a60698e
--- /dev/null
+++ b/schemas/skills/v2.2/samples/relativeUris/knowledge-base/SkillBot-QnA-en.qna
@@ -0,0 +1,3 @@
+# ? Sample Question
+- sample answer 1
+- sample answer 2
diff --git a/schemas/skills/v2.2/samples/relativeUris/knowledge-base/SkillBot-QnA-es-ES.qna b/schemas/skills/v2.2/samples/relativeUris/knowledge-base/SkillBot-QnA-es-ES.qna
new file mode 100644
index 0000000000..5f3a60698e
--- /dev/null
+++ b/schemas/skills/v2.2/samples/relativeUris/knowledge-base/SkillBot-QnA-es-ES.qna
@@ -0,0 +1,3 @@
+# ? Sample Question
+- sample answer 1
+- sample answer 2
diff --git a/schemas/skills/v2.2/samples/relativeUris/knowledge-base/SkillBot-QnA-es-MX.qna b/schemas/skills/v2.2/samples/relativeUris/knowledge-base/SkillBot-QnA-es-MX.qna
new file mode 100644
index 0000000000..5f3a60698e
--- /dev/null
+++ b/schemas/skills/v2.2/samples/relativeUris/knowledge-base/SkillBot-QnA-es-MX.qna
@@ -0,0 +1,3 @@
+# ? Sample Question
+- sample answer 1
+- sample answer 2
diff --git a/schemas/skills/v2.2/samples/relativeUris/language-understanding/SkillBot-en.lu b/schemas/skills/v2.2/samples/relativeUris/language-understanding/SkillBot-en.lu
new file mode 100644
index 0000000000..8e2a9f49c9
--- /dev/null
+++ b/schemas/skills/v2.2/samples/relativeUris/language-understanding/SkillBot-en.lu
@@ -0,0 +1,7 @@
+# bookFlight
+- Utterance 1
+- Utterance 2
+
+# getWeather
+- Utterance 3
+- Utterance 4
diff --git a/schemas/skills/v2.2/samples/relativeUris/language-understanding/SkillBot-es-ES.lu b/schemas/skills/v2.2/samples/relativeUris/language-understanding/SkillBot-es-ES.lu
new file mode 100644
index 0000000000..8e2a9f49c9
--- /dev/null
+++ b/schemas/skills/v2.2/samples/relativeUris/language-understanding/SkillBot-es-ES.lu
@@ -0,0 +1,7 @@
+# bookFlight
+- Utterance 1
+- Utterance 2
+
+# getWeather
+- Utterance 3
+- Utterance 4
diff --git a/schemas/skills/v2.2/samples/relativeUris/language-understanding/SkillBot-es-MX.lu b/schemas/skills/v2.2/samples/relativeUris/language-understanding/SkillBot-es-MX.lu
new file mode 100644
index 0000000000..8e2a9f49c9
--- /dev/null
+++ b/schemas/skills/v2.2/samples/relativeUris/language-understanding/SkillBot-es-MX.lu
@@ -0,0 +1,7 @@
+# bookFlight
+- Utterance 1
+- Utterance 2
+
+# getWeather
+- Utterance 3
+- Utterance 4
diff --git a/schemas/skills/v2.2/samples/relativeUris/privacy.html b/schemas/skills/v2.2/samples/relativeUris/privacy.html
new file mode 100644
index 0000000000..9375381073
--- /dev/null
+++ b/schemas/skills/v2.2/samples/relativeUris/privacy.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+ Sample privacy notice
+
+
+TODO
+
+
diff --git a/schemas/skills/v2.2/samples/relativeUris/skillIcon.png b/schemas/skills/v2.2/samples/relativeUris/skillIcon.png
new file mode 100644
index 0000000000..37c81be7d6
Binary files /dev/null and b/schemas/skills/v2.2/samples/relativeUris/skillIcon.png differ
diff --git a/schemas/skills/v2.2/samples/simple-skillmanifest.json b/schemas/skills/v2.2/samples/simple-skillmanifest.json
new file mode 100644
index 0000000000..b42b96aa3e
--- /dev/null
+++ b/schemas/skills/v2.2/samples/simple-skillmanifest.json
@@ -0,0 +1,24 @@
+{
+ "$schema": "https://schemas.botframework.com/schemas/skills/v2.2/skill-manifest.json",
+ "$id": "CatchAllSkill",
+ "name": "Simple Skill",
+ "version": "1.0",
+ "description": "This is the simplest skill you can think of, it just defines an endpoint that can receive any activity",
+ "publisherName": "Microsoft",
+ "privacyUrl": "https://myskill.contoso.com/privacy.html",
+ "copyright": "Copyright (c) Microsoft Corporation. All rights reserved.",
+ "license": "",
+ "iconUrl": "https://myskill.contoso.com/icon.png",
+ "tags": [
+ "sample"
+ ],
+ "endpoints": [
+ {
+ "name": "default",
+ "protocol": "BotFrameworkV3",
+ "description": "Production endpoint for SkillBot.",
+ "endpointUrl": "http://myskill.contoso.com/api/messages",
+ "msAppId": "00000000-0000-0000-0000-000000000000"
+ }
+ ]
+}
diff --git a/schemas/skills/v2.2/skill-manifest.json b/schemas/skills/v2.2/skill-manifest.json
new file mode 100644
index 0000000000..6106fc8b58
--- /dev/null
+++ b/schemas/skills/v2.2/skill-manifest.json
@@ -0,0 +1,406 @@
+{
+ "$id": "https://schemas.botframework.com/schemas/skills/v2.2/skill-manifest.json",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$version": "2.2.0",
+ "title": "Skill Manifest Schema",
+ "description": "A schema for Bot Framework skill manifests",
+ "type": "object",
+ "required": [
+ "$id",
+ "$schema",
+ "name",
+ "version",
+ "publisherName",
+ "endpoints"
+ ],
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "format": "uri",
+ "title": "Manifest schema",
+ "description": "The schema to verify this skill manifest against"
+ },
+ "$id": {
+ "type": "string",
+ "title": "Manifest ID",
+ "description": "The identifier for the skill manifest"
+ },
+ "name": {
+ "type": "string",
+ "title": "Skill name",
+ "description": "Name of the skill"
+ },
+ "version": {
+ "type": "string",
+ "title": "Skill version",
+ "description": "Skill version"
+ },
+ "description": {
+ "type": "string",
+ "title": "Skill description",
+ "description": "A human readable description for the skill"
+ },
+ "publisherName": {
+ "type": "string",
+ "title": "Publisher name",
+ "description": "The name of the skill publisher"
+ },
+ "privacyUrl": {
+ "type": "string",
+ "format": "uri-reference",
+ "title": "Privacy URL",
+ "description": "The URL with the privacy description for the skill"
+ },
+ "copyright": {
+ "type": "string",
+ "title": "Copyright",
+ "description": "The copyright for the skill"
+ },
+ "license": {
+ "type": "string",
+ "title": "License",
+ "description": "The license agreement for the skill"
+ },
+ "iconUrl": {
+ "type": "string",
+ "format": "uri-reference",
+ "title": "Icon URL",
+ "description": "Optional icon to be shown for the skill"
+ },
+ "tags": {
+ "type": "array",
+ "uniqueItems": true,
+ "title": "Tags",
+ "description": "An array of strings with tags for the skill",
+ "items": {
+ "type": "string"
+ }
+ },
+ "endpoints": {
+ "type": "array",
+ "minItems": 1,
+ "uniqueItems": true,
+ "title": "Skill endpoints",
+ "description": "List of endpoints supported by the skill",
+ "items": {
+ "$ref": "#/definitions/endpoint"
+ }
+ },
+ "dispatchModels": {
+ "type": "object",
+ "title": "Language models",
+ "description": "Language models and top level intents for the skill",
+ "properties": {
+ "languages": {
+ "type": "object",
+ "description": "List of languages supported by the skill",
+ "minProperties": 1,
+ "additionalProperties": {
+ "type": "array",
+ "minItems": 1,
+ "uniqueItems": true,
+ "description": "List of language models supported by the skill",
+ "items": {
+ "$ref": "#/definitions/languageModel"
+ }
+ }
+ },
+ "intents": {
+ "type": "array",
+ "uniqueItems": true,
+ "description": "A list of the skill's top level intents that can be used by the consumer to filter out language files.",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "additionalProperties": false
+ },
+ "activities": {
+ "type": "object",
+ "title": "Activities",
+ "description": "Definition of activities to enable skill host tooling to programmatically interact with the skill.",
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/eventActivity"
+ },
+ {
+ "$ref": "#/definitions/invokeActivity"
+ },
+ {
+ "$ref": "#/definitions/messageActivity"
+ },
+ {
+ "$ref": "#/definitions/otherActivities"
+ }
+ ]
+ }
+ },
+ "activitiesSent": {
+ "type": "object",
+ "title": "Activities sent",
+ "description": "Definitions of activities to enable skill host tooling to handle activities which may be sent by the skill.",
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/eventActivity"
+ },
+ {
+ "$ref": "#/definitions/messageActivity"
+ },
+ {
+ "$ref": "#/definitions/otherActivities"
+ }
+ ]
+ }
+ },
+ "definitions": {
+ "type": "object",
+ "description": "Definitions of the structure of object payloads",
+ "additionalProperties": {
+ "$ref": "http://json-schema.org/draft-07/schema#"
+ }
+ }
+ },
+ "additionalProperties": false,
+ "definitions": {
+ "endpoint": {
+ "type": "object",
+ "title": "Skill endpoint",
+ "description": "Skill endpoint definition",
+ "required": [
+ "name",
+ "endpointUrl",
+ "msAppId"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "title": "Endpoint name",
+ "description": "Unique name for the endpoint",
+ "default": "default"
+ },
+ "protocol": {
+ "type": "string",
+ "title": "Endpoint protocol",
+ "description": "Supported protocol",
+ "default": "BotFrameworkV3"
+ },
+ "description": {
+ "type": "string",
+ "title": "Endpoint description",
+ "description": "Description of the endpoint",
+ "examples": [
+ "Production bot"
+ ]
+ },
+ "endpointUrl": {
+ "type": "string",
+ "format": "uri",
+ "title": "Endpoint URL",
+ "description": "Endpoint for the skill",
+ "examples": [
+ "http://contoso.com/api/messaages"
+ ]
+ },
+ "msAppId": {
+ "type": "string",
+ "title": "Microsoft App Id",
+ "pattern": "^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$",
+ "description": "The Microsoft AppId for the skill. This app ID is used to authenticate requests"
+ }
+ },
+ "additionalProperties": false
+ },
+ "eventActivity": {
+ "type": "object",
+ "title": "Event Activity",
+ "description": "An activity with Type=Event where the Name property indicates the task that the skill will execute",
+ "required": [
+ "type",
+ "name"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "title": "Activity type",
+ "description": "The activity type",
+ "enum": [
+ "event"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "title": "Event name",
+ "description": "The name for the event",
+ "examples": [
+ "BookFlight"
+ ]
+ },
+ "description": {
+ "type": "string",
+ "title": "Event description",
+ "description": "Description for the activity"
+ },
+ "value": {
+ "$ref": "http://json-schema.org/draft-07/schema#",
+ "type": "object",
+ "title": "Value",
+ "description": "The JsonSchema definition of the shape of the value property that this event expects"
+ },
+ "resultValue": {
+ "$ref": "http://json-schema.org/draft-07/schema#",
+ "type": "object",
+ "title": "Result",
+ "description": "The JsonSchema definition of the shape of the resultValue that this event may produce"
+ }
+ },
+ "additionalProperties": false
+ },
+ "invokeActivity": {
+ "type": "object",
+ "title": "Invoke Activity",
+ "description": "An activity with Type=Invoke where the Name property indicates the task that the skill will execute",
+ "required": [
+ "type",
+ "name"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "title": "Activity type",
+ "description": "The activity type",
+ "enum": [
+ "invoke"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "title": "Invoke name",
+ "description": "The name property for the invoke activity",
+ "examples": [
+ "GetWeather"
+ ]
+ },
+ "description": {
+ "type": "string",
+ "title": "Description",
+ "description": "Description for the activity"
+ },
+ "value": {
+ "type": "object",
+ "title": "Value",
+ "description": "The JsonSchema definition of the shape of the value property that this event expects",
+ "$ref": "http://json-schema.org/draft-07/schema#"
+ },
+ "resultValue": {
+ "type": "object",
+ "title": "Result",
+ "description": "The JsonSchema definition of the shape of the resultValue that this event may produce",
+ "$ref": "http://json-schema.org/draft-07/schema#"
+ }
+ },
+ "additionalProperties": false
+ },
+ "messageActivity": {
+ "type": "object",
+ "title": "Message Activity",
+ "description": "An activity with Type=Message where the utterance is passed to the skill in the Text property",
+ "required": [
+ "type"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "title": "Activity type",
+ "description": "The activity type",
+ "enum": [
+ "message"
+ ]
+ },
+ "description": {
+ "type": "string",
+ "title": "Description",
+ "description": "Description for the activity"
+ },
+ "value": {
+ "type": "object",
+ "title": "Value",
+ "description": "The JsonSchema definition of the shape of the value property that this message would like to have",
+ "$ref": "http://json-schema.org/draft-07/schema#"
+ },
+ "resultValue": {
+ "type": "object",
+ "title": "Result",
+ "description": "The JsonSchema definition of the shape of the resultValue that this message may produce",
+ "$ref": "http://json-schema.org/draft-07/schema#"
+ }
+ },
+ "additionalProperties": false
+ },
+ "otherActivities": {
+ "type": "object",
+ "title": "Activity",
+ "required": [
+ "type"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "title": "Activity type",
+ "description": "The activity type",
+ "enum": [
+ "messageReaction",
+ "endOfConversation",
+ "handoff",
+ "typing",
+ "conversationUpdate",
+ "trace",
+ "installationUpdate",
+ "contactRelationUpdate",
+ "suggestion",
+ "deleteUserData",
+ "messageUpdate",
+ "messageDelete"
+ ]
+ }
+ },
+ "additionalProperties": true
+ },
+ "languageModel": {
+ "type": "object",
+ "title": "Language model",
+ "description": "A language model for a given culture. The name is a combination of an ISO 639 two-letter lowercase culture code associated with a language and an ISO 3166 two-letter uppercase subculture code associated with a country or region",
+ "required": [
+ "name",
+ "contentType",
+ "url"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "title": "Language model name",
+ "description": "Name for the language model"
+ },
+ "contentType": {
+ "type": "string",
+ "title": "Language model content type",
+ "description": "Type of the language model"
+ },
+ "url": {
+ "type": "string",
+ "format": "uri-reference",
+ "title": "Language model URL",
+ "description": "An absolute or relative URI for the location of the language model file"
+ },
+ "description": {
+ "type": "string",
+ "title": "Description",
+ "description": "Description for the language model"
+ }
+ },
+ "additionalProperties": false
+ }
+ }
+}